# Mastering the Basics of C --- CS 130 // 2021-09-13 ## Administrivia - Assignment 2 will be released this week and due next Wednesday # Questions ## ...about anything? # Quiz 1 # C, Continued ## Pointers - A **pointer** is a variable whose value is the address of a memory location - To declare a variable as a pointer, you use a `*` before the variable name: ```c[1|2-3|4|5] int *p; // p is a pointer int x = 5; p = &x; // p points to the memory location of x int y = *p; // "follows" the pointer to retrieve 5 *p = 10; // changes the value at x to be 10 ``` ## Arrays are Pointers - In C, arrays are just **pointers** ```c int arr[10]; // arr is a pointer to the address of the // first element of the array ``` - You can use array notation on pointers and pointer notation on arrays ```c int *p = arr; // p points to what arr is pointing to p[0] = 100; // changes arr[0] to be 100 int x = *arr; // gets 100 back ``` ## Exercise 1 - Suppose I want to create a function called `duplicate` that takes an array of integers as input, creates an identical copy of it, and returns it - Think through the issues of how to implement such a function ## A First Attempt ```c int *duplicate(int *arr, int n) { int new_arr[n]; for (int i = 0; i < n; i++) { new_arr[i] = arr[i]; } return new_arr; } ``` - This is incorrect! - The array `new_arr` is **locally scoped** and its memory is freed once the function returns! # Stack / Heap Memory ## Stack Memory - **Stack memory** is the space where local variables, function calls, etc., are allocated - Locally scoped variables are freed when the code block they were defined in finishes ## Stack Memory ```c int factorial(n) { if (n == 0) { return 1; } else { return n * factorial(n-1); } } int main() { int fact = factorial(5); printf("Factorial of 5 is: %d", fact); return 0; } ``` [C Tutor Visualization](https://pythontutor.com/c.html#code=%23include%20%3Cstdio.h%3E%0A%0Aint%20factorial%28n%29%0A%7B%0A%20%20%20%20if%20%28n%20%3D%3D%200%29%20%7B%0A%20%20%20%20%20%20%20%20return%201%3B%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20return%20n%20*%20factorial%28n-1%29%3B%0A%20%20%20%20%7D%0A%7D%0A%0Aint%20main%28%29%0A%7B%0A%20%20%20%20int%20fact%20%3D%20factorial%285%29%3B%0A%20%20%20%20printf%28%22Factorial%20of%205%20is%3A%20%25d%22,%20fact%29%3B%0A%20%20%20%20return%200%3B%0A%7D&curInstr=0&mode=display&origin=opt-frontend.js&py=c_gcc9.3.0&rawInputLstJSON=%5B%5D) ## Heap Memory - **Heap memory** persists between function calls + In C, we must manually create and destroy heap memory ## Heap Memory - `malloc(n)` creates `n` bytes of heap memory ```c // Creates an array on the heap with enough // space to hold 10 ints int *arr = malloc(10*sizeof(int)); ``` - `sizeof(int)` is the number of bytes of one `int` - `free(...)` is destroys heap memory ```c free(arr); // releases the memory ``` - Must call free on the pointer returned by `malloc` - These functions are in the `stdlib.h` library ## A Second Attempt ```c int *duplicate(int *arr, int n) { int *new_arr = malloc(n * sizeof(int)); for (int i = 0; i < n; i++) { new_arr[i] = arr[i]; } return new_arr; } ``` [C Tutor Visualization](https://pythontutor.com/c.html#code=int%20*duplicate%28int%20*arr,%20int%20n%29%20%7B%0A%20%20%20%20int%20*new_arr%20%3D%20malloc%28n%20*%20sizeof%28int%29%29%3B%0A%20%20%20%20for%20%28int%20i%20%3D%200%3B%20i%20%3C%20n%3B%20i%2B%2B%29%20%7B%0A%20%20%20%20%20%20%20%20new_arr%5Bi%5D%20%3D%20arr%5Bi%5D%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20return%20new_arr%3B%0A%7D%0A%0Aint%20main%28%29%20%7B%0A%20%20int%20arr%5B5%5D%20%3D%20%7B100,%20200,%20300,%20400,%20500%7D%3B%0A%20%20%0A%20%20int%20*dup%20%3D%20duplicate%28arr,%205%29%3B%0A%20%20dup%20%3D%20duplicate%28arr,%205%29%3B%0A%20%20%0A%20%20return%200%3B%0A%7D&curInstr=0&mode=display&origin=opt-frontend.js&py=c_gcc9.3.0&rawInputLstJSON=%5B%5D) ## Example - Let's write a function that does this: + Takes two strings as input + Concatenates them together into a new string + Returns the string ## Example Solution ```c char *str_concat(char *s1, char *s2) { int len1 = strlen(s1); int len2 = strlen(s2); int len3 = len1 + len2 + 1; char *s3 = malloc(len3*sizeof(char)); int pos = 0; for (int i = 0; i < len1; i++, pos++) { s3[pos] = s1[i]; } for (int i = 0; i < len2; i++, pos++) { s3[pos] = s2[i]; } s3[pos] = '\0'; return s3; } ``` [C Tutor Visualization](http://www.pythontutor.com/c.html#code=%23include%20%3Cstdio.h%3E%0A%23include%20%3Cstdlib.h%3E%0A%23include%20%3Cstring.h%3E%0A%0Achar*%20str_concat%28char%20*s1,%20char%20*s2%29%0A%7B%0A%20%20%20%20int%20len1%20%3D%20strlen%28s1%29%3B%0A%20%20%20%20int%20len2%20%3D%20strlen%28s2%29%3B%0A%20%20%20%20int%20len3%20%3D%20len1%20%2B%20len2%20%2B%201%3B%0A%20%20%20%20char%20*s3%20%3D%20%28char*%29%20malloc%28len3*sizeof%28char%29%29%3B%0A%0A%20%20%20%20int%20pos%20%3D%200%3B%0A%20%20%20%20for%20%28int%20i%20%3D%200%3B%20i%20%3C%20len1%3B%20i%2B%2B,%20pos%2B%2B%29%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20s3%5Bpos%5D%20%3D%20s1%5Bi%5D%3B%0A%20%20%20%20%7D%0A%20%20%20%20for%20%28int%20i%20%3D%200%3B%20i%20%3C%20len2%3B%20i%2B%2B,%20pos%2B%2B%29%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20s3%5Bpos%5D%20%3D%20s2%5Bi%5D%3B%0A%20%20%20%20%7D%0A%20%20%20%20s3%5Bpos%5D%20%3D%20'%5C0'%3B%0A%20%20%20%20%0A%20%20%20%20return%20s3%3B%0A%7D%0A%0Aint%20main%28%29%0A%7B%0A%20%20%20%20char%20*arr1%20%3D%20%22abc%22%3B%0A%20%20%20%20char%20arr2%5B7%5D%20%3D%20%22defghi%22%3B%0A%20%20%20%20char%20*arr3%20%3D%20str_concat%28arr1,%20arr2%29%3B%0A%0A%20%20%20%20printf%28%22arr1%20is%3A%20%25s%5Cn%22,%20arr1%29%3B%0A%20%20%20%20printf%28%22arr2%20is%3A%20%25s%5Cn%22,%20arr2%29%3B%0A%20%20%20%20printf%28%22arr3%20is%3A%20%25s%5Cn%22,%20arr3%29%3B%0A%0A%20%20%20%20free%28arr3%29%3B%0A%7D&curInstr=0&mode=display&origin=opt-frontend.js&py=c&rawInputLstJSON=%5B%5D) ## Exercise 2 - One convenient method in Python is the `str.join()` method. For example: ```py >>> " ".join(["The", "quick", "brown", "fox"]) "The quick brown fox" ``` - What would the function prototype have to be? ```c // Takes an array of n strings and returns // a new string allocated on the heap char *join(char **arr, int n) ``` - Finish implementing this # `struct` / `typedef` ## Create a `struct` - A `struct` is a user-defined type that groups multiple values into one - Suppose we wanted a type to represent a 2D point: ```c struct point { double x; double y; }; ``` - Now we can create a `point` and modify it using: ```c struct point p; p.x = 5; p.y = 4; ``` ## Using `typedef` - The `typedef` keyword allows us to create aliases for types - For example, we could create an alias for `char *` named `string` for convenience: ```c typedef char string[10]; ... string x = "abc"; ``` - Alternatively, we could do: ```c typedef char *string; ``` ## Using `typedef` with `struct` - It is common practice to use `typedef` with `struct`s to avoid needing the `struct` keyword everywhere ```c struct point { double x; double y; } typedef struct point POINT; ``` ```c POINT p; p.x = 4; p.y = 5; ``` ```c POINT p = {.x=4, .y=5}; ``` ## Using `typedef` with `struct` - We can even avoid generating two names for the struct and simply do: ```c typedef struct { double x; double y; } POINT; ``` ## Example: Euclidean Distance - Suppose we'd like to write a function to calculate the distance between two points + $d = \sqrt{(x_1-x_2)^2 + (y_1-y_2)^2}$ --- ```c #include
double distance(POINT p1, POINT p2) { double xdiff = p1.x - p2.x; double ydiff = p1.y - p2.y; return sqrt(xdiff * xdiff + ydiff * ydiff); } ``` ## Using `math.h` - For historical reasons, the math library `math.h` is not a standard library - Back in the day, floating point math was **slow** and these functions took up a lot of extra memory - Thus, to compile a program that uses `math.h` you must explicitly tell `gcc` to include it: ```bash $ gcc distance.c -lm ``` - The `-lm` stands for "link math" # Files ## Reading from a File - File reading/writing is included in `stdio.h` - To open a file in C, you use the `fopen` function: ```c FILE *fp = fopen("input.txt", "r"); ``` + The `"r"` opens the file for **reading** and the path is relative to the directory of your program ## Reading from a File - Once a file is open, you can **read** text from it using: ```c FILE *fp = fopen("input.txt", "r"); char buffer[80]; ``` ```c // Reads until a newline character is found. Similar to // the readline() method in Python. // Stops after 79 characters to not overfill the buffer fgets(buffer, 80, fp); ``` - `fgets` returns `buffer` if it reads at least one character and returns `NULL` otherwise - Just like Python, it **includes** the newline characters at the end of the string ## Looping Over a File - One common pattern is looping over a file: ```c FILE *fp = open("my_file.txt", "r"); char *buffer[80]; while (fgets(buffer, 80, fp) != NULL) { // buffer contains the current line here } fclose(fp); ``` ## Writing to a File - To open a file for writing you use: ```c FILE *fp = fopen("input.txt", "w"); ``` + If `input.txt` already exists, it is deleted + If it doesn't exit, it is created - You can use `fprintf` to print to a file: ```c fprintf(fp, "%d %d %d\n", 100, 200, 300); ``` ## Closing Files - It is important to **close** your files to inform your OS that you're done with the file - You can do this with: ```c fclose(fp); ``` ## Exercise 3 - Suppose we have have some 2D points in a file - We could encode them in the following way: ```text 4 0 0 1 1 0 5 3.14 2.71 ``` - First line tells you how many points are in the file and then every other line is of the form `x y` ## Exercise 3 - Write a function called `read_points` with the following signature: ```c POINT *read_points(char *filename) ``` - Should parse the file into an array of `POINT` objects - Remember to use `malloc` to allocate the memory on the heap!