├── malloc ├── Makefile ├── lib.h ├── malloc.h ├── tests │ ├── runtests.sh │ └── malloc_tests.c ├── malloc.c └── README.md ├── queue ├── Makefile ├── lib.h ├── queue.h ├── tests │ ├── runtests.sh │ └── queue_tests.c └── queue.c ├── fizzbuzz ├── Makefile ├── fizzbuzz.h ├── tests │ ├── runtests.sh │ └── fizzbuzz_tests.c ├── fizzbuzz.c └── README.md ├── pointers ├── Makefile ├── pointers.h ├── tests │ ├── runtests.sh │ └── pointers_tests.c ├── pointers.c └── README.md ├── quicksort ├── Makefile ├── quicksort.h ├── lib.h ├── tests │ ├── runtests.sh │ └── quicksort_tests.c ├── quicksort.c └── README.md ├── strings ├── Makefile ├── strings.h ├── tests │ ├── runtests.sh │ └── strings_tests.c ├── strings.c └── README.md ├── structs ├── Makefile ├── lib.h ├── structs.h ├── tests │ ├── runtests.sh │ └── structs_tests.c ├── structs.c └── README.md ├── utils ├── utils.h ├── minunit.h └── dbg.h ├── .gitignore ├── hangman ├── hangman.c └── README.md ├── mainbuild.mk ├── TROUBLESHOOTING.md ├── README.md └── FAQ.md /malloc/Makefile: -------------------------------------------------------------------------------- 1 | include ../mainbuild.mk -------------------------------------------------------------------------------- /queue/Makefile: -------------------------------------------------------------------------------- 1 | include ../mainbuild.mk -------------------------------------------------------------------------------- /fizzbuzz/Makefile: -------------------------------------------------------------------------------- 1 | include ../mainbuild.mk -------------------------------------------------------------------------------- /pointers/Makefile: -------------------------------------------------------------------------------- 1 | include ../mainbuild.mk -------------------------------------------------------------------------------- /quicksort/Makefile: -------------------------------------------------------------------------------- 1 | include ../mainbuild.mk -------------------------------------------------------------------------------- /strings/Makefile: -------------------------------------------------------------------------------- 1 | include ../mainbuild.mk -------------------------------------------------------------------------------- /structs/Makefile: -------------------------------------------------------------------------------- 1 | include ../mainbuild.mk -------------------------------------------------------------------------------- /fizzbuzz/fizzbuzz.h: -------------------------------------------------------------------------------- 1 | #ifndef fizzbuzz_h 2 | #define fizzbuzz_h 3 | 4 | int fizzbuzz(int n); 5 | 6 | #endif -------------------------------------------------------------------------------- /quicksort/quicksort.h: -------------------------------------------------------------------------------- 1 | #ifndef quicksort_h 2 | #define quicksort_h 3 | 4 | void quicksort(int values[], int left, int right); 5 | 6 | #endif -------------------------------------------------------------------------------- /strings/strings.h: -------------------------------------------------------------------------------- 1 | #ifndef strings_h 2 | #define strings_h 3 | 4 | int string_length(char s[]); 5 | 6 | char *reverse_string(char rv[], char s[]); 7 | 8 | #endif -------------------------------------------------------------------------------- /malloc/lib.h: -------------------------------------------------------------------------------- 1 | #ifndef lib_h 2 | #define lib_h 3 | 4 | #include 5 | 6 | int string_length(char *s) 7 | { 8 | return strlen(s); 9 | } 10 | 11 | #endif -------------------------------------------------------------------------------- /quicksort/lib.h: -------------------------------------------------------------------------------- 1 | #ifndef lib_h 2 | #define lib_h 3 | 4 | void swap(int* a, int* b) 5 | { 6 | int temp = *a; 7 | *a = *b; 8 | *b = temp; 9 | } 10 | 11 | #endif -------------------------------------------------------------------------------- /queue/lib.h: -------------------------------------------------------------------------------- 1 | #ifndef lib_h 2 | #define lib_h 3 | 4 | #include 5 | 6 | void *resize_memory(void *ptr, int new_size) 7 | { 8 | return realloc(ptr, new_size); 9 | } 10 | 11 | #endif -------------------------------------------------------------------------------- /structs/lib.h: -------------------------------------------------------------------------------- 1 | #ifndef lib_h 2 | #define lib_h 3 | 4 | #include 5 | 6 | char *strdup(const char *); 7 | 8 | char *string_dup(char *src) 9 | { 10 | return strdup(src); 11 | } 12 | 13 | #endif -------------------------------------------------------------------------------- /malloc/malloc.h: -------------------------------------------------------------------------------- 1 | #ifndef malloc_h 2 | #define malloc_h 3 | 4 | char *string_dup(char *src); 5 | 6 | void *mem_copy(void *x, const void *y, int n); 7 | 8 | void *resize_memory(void *ptr, int old_size, int new_size); 9 | 10 | #endif -------------------------------------------------------------------------------- /structs/structs.h: -------------------------------------------------------------------------------- 1 | #ifndef structs_h 2 | #define structs_h 3 | 4 | typedef struct Person { 5 | char *name; 6 | int age; 7 | int height; 8 | int weight; 9 | } Person; 10 | 11 | Person *createPerson(char *name, int age, int height, int weight); 12 | 13 | void destroyPerson(Person *who); 14 | 15 | #endif -------------------------------------------------------------------------------- /pointers/pointers.h: -------------------------------------------------------------------------------- 1 | #ifndef pointers_h 2 | #define pointers_h 3 | 4 | void swap(int *a, int *b); 5 | 6 | int string_length(char *s); 7 | 8 | void string_copy(char *a, char *b); 9 | 10 | int string_compare(char *m, char *n); 11 | 12 | char *find_char(char *str, int c); 13 | 14 | char *find_string(char *haystack, char *needle); 15 | 16 | #endif -------------------------------------------------------------------------------- /queue/queue.h: -------------------------------------------------------------------------------- 1 | #ifndef stack_h 2 | #define stack_h 3 | 4 | typedef struct Queue { 5 | unsigned int length; 6 | unsigned capacity; 7 | int *storage; 8 | } Queue; 9 | 10 | Queue *createQueue(unsigned capacity); 11 | 12 | void enqueue(Queue *q, int item); 13 | 14 | int dequeue(Queue *q); 15 | 16 | void destroyQueue(Queue *q); 17 | 18 | #endif -------------------------------------------------------------------------------- /queue/tests/runtests.sh: -------------------------------------------------------------------------------- 1 | echo "Running unit tests:" 2 | 3 | for i in tests/*_tests tests/*_tests.exe 4 | do 5 | if test -f $i 6 | then 7 | if $VALGRIND ./$i 2>> tests/tests.log 8 | then 9 | echo $i PASS 10 | else 11 | echo "ERROR in test $i: here's tests/tests.log" 12 | echo "-----" 13 | tail tests/tests.log 14 | exit 1 15 | fi 16 | fi 17 | done 18 | 19 | echo "" -------------------------------------------------------------------------------- /fizzbuzz/tests/runtests.sh: -------------------------------------------------------------------------------- 1 | echo "Running unit tests:" 2 | 3 | for i in tests/*_tests tests/*_tests.exe 4 | do 5 | if test -f $i 6 | then 7 | if $VALGRIND ./$i 2>> tests/tests.log 8 | then 9 | echo $i PASS 10 | else 11 | echo "ERROR in test $i: here's tests/tests.log" 12 | echo "-----" 13 | tail tests/tests.log 14 | exit 1 15 | fi 16 | fi 17 | done 18 | 19 | echo "" -------------------------------------------------------------------------------- /malloc/tests/runtests.sh: -------------------------------------------------------------------------------- 1 | echo "Running unit tests:" 2 | 3 | for i in tests/*_tests tests/*_tests.exe 4 | do 5 | if test -f $i 6 | then 7 | if $VALGRIND ./$i 2>> tests/tests.log 8 | then 9 | echo $i PASS 10 | else 11 | echo "ERROR in test $i: here's tests/tests.log" 12 | echo "-----" 13 | tail tests/tests.log 14 | exit 1 15 | fi 16 | fi 17 | done 18 | 19 | echo "" -------------------------------------------------------------------------------- /pointers/tests/runtests.sh: -------------------------------------------------------------------------------- 1 | echo "Running unit tests:" 2 | 3 | for i in tests/*_tests tests/*_tests.exe 4 | do 5 | if test -f $i 6 | then 7 | if $VALGRIND ./$i 2>> tests/tests.log 8 | then 9 | echo $i PASS 10 | else 11 | echo "ERROR in test $i: here's tests/tests.log" 12 | echo "-----" 13 | tail tests/tests.log 14 | exit 1 15 | fi 16 | fi 17 | done 18 | 19 | echo "" -------------------------------------------------------------------------------- /quicksort/tests/runtests.sh: -------------------------------------------------------------------------------- 1 | echo "Running unit tests:" 2 | 3 | for i in tests/*_tests tests/*_tests.exe 4 | do 5 | if test -f $i 6 | then 7 | if $VALGRIND ./$i 2>> tests/tests.log 8 | then 9 | echo $i PASS 10 | else 11 | echo "ERROR in test $i: here's tests/tests.log" 12 | echo "-----" 13 | tail tests/tests.log 14 | exit 1 15 | fi 16 | fi 17 | done 18 | 19 | echo "" -------------------------------------------------------------------------------- /strings/tests/runtests.sh: -------------------------------------------------------------------------------- 1 | echo "Running unit tests:" 2 | 3 | for i in tests/*_tests tests/*_tests.exe 4 | do 5 | if test -f $i 6 | then 7 | if $VALGRIND ./$i 2>> tests/tests.log 8 | then 9 | echo $i PASS 10 | else 11 | echo "ERROR in test $i: here's tests/tests.log" 12 | echo "-----" 13 | tail tests/tests.log 14 | exit 1 15 | fi 16 | fi 17 | done 18 | 19 | echo "" -------------------------------------------------------------------------------- /structs/tests/runtests.sh: -------------------------------------------------------------------------------- 1 | echo "Running unit tests:" 2 | 3 | for i in tests/*_tests tests/*_tests.exe 4 | do 5 | if test -f $i 6 | then 7 | if $VALGRIND ./$i 2>> tests/tests.log 8 | then 9 | echo $i PASS 10 | else 11 | echo "ERROR in test $i: here's tests/tests.log" 12 | echo "-----" 13 | tail tests/tests.log 14 | exit 1 15 | fi 16 | fi 17 | done 18 | 19 | echo "" -------------------------------------------------------------------------------- /utils/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef utils_h 2 | #define utils_h 3 | 4 | int check_arrays(int input[], int expected[], int n, int m) 5 | { 6 | int i; 7 | 8 | if (n != m) { 9 | return 0; 10 | } 11 | 12 | for (i = 0; i < n; i++) { 13 | if (input[i] != expected[i]) { 14 | return 0; 15 | } 16 | } 17 | 18 | return 1; 19 | } 20 | 21 | int check_strings(char *input, char *expected) 22 | { 23 | for ( ; *input == *expected; input++, expected++) { 24 | if (*input == '\0') { 25 | return 0; 26 | } 27 | } 28 | 29 | return *input - *expected; 30 | } 31 | 32 | #endif -------------------------------------------------------------------------------- /fizzbuzz/fizzbuzz.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | Your fizzbuzz implementation should initialize a counter, then 5 | iterate n times. If the ith iteration is divisible by 3, print 6 | "Fizz". If it's divisible by 5, print "Buzz". If it's divisible 7 | by both 3 and 5, print "FizzBuzz". Increment the counter variable 8 | every time that nothing gets printed and return the counter. 9 | Don't forget to include newlines '\n' in your printf statements! 10 | */ 11 | int fizzbuzz(int n) 12 | { 13 | 14 | } 15 | 16 | #ifndef TESTING 17 | int main(void) 18 | { 19 | fizzbuzz(20); 20 | 21 | return 0; 22 | } 23 | #endif 24 | -------------------------------------------------------------------------------- /fizzbuzz/tests/fizzbuzz_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../../utils/minunit.h" 3 | 4 | char *test_fizzbuzz() 5 | { 6 | mu_assert(fizzbuzz(3) == 2, "Your fizzbuzz function did not return 2 for n = 3."); 7 | mu_assert(fizzbuzz(5) == 3, "Your fizzbuzz function did not return 3 for n = 5."); 8 | mu_assert(fizzbuzz(10) == 5, "Your fizzbuzz function did not return 5 for n = 10."); 9 | mu_assert(fizzbuzz(100) == 53, "Your fizzbuzz function did not return 53 for n = 100."); 10 | 11 | return NULL; 12 | } 13 | 14 | char *all_tests() 15 | { 16 | mu_suite_start(); 17 | 18 | mu_run_test(test_fizzbuzz); 19 | 20 | return NULL; 21 | } 22 | 23 | RUN_TESTS(all_tests); 24 | -------------------------------------------------------------------------------- /utils/minunit.h: -------------------------------------------------------------------------------- 1 | #undef NDEBUG 2 | #ifndef _minuit_h_ 3 | #define _minuit_h_ 4 | 5 | #include 6 | #include 7 | #include "dbg.h" 8 | 9 | #define mu_suite_start() char *message = NULL 10 | 11 | #define mu_assert(test, message) if(!(test)) {\ 12 | log_err(message); return message; } 13 | 14 | #define mu_run_test(test) debug("\n-----%s", " " #test); \ 15 | message = test(); tests_run++; if (message) return message; 16 | 17 | #define RUN_TESTS(name) int main(int argc, char *argv[]) {\ 18 | argc = 1;\ 19 | debug("----- RUNNING: %s", argv[0]);\ 20 | printf("-----\nRUNNING: %s\n", argv[0]);\ 21 | char *result = name();\ 22 | if (result != 0) {\ 23 | printf("FAILED: %s\n", result);\ 24 | } else {\ 25 | printf("ALL TESTS PASSED\n");\ 26 | }\ 27 | printf("Tests run: %d\n", tests_run);\ 28 | exit(result != 0);\ 29 | } 30 | 31 | int tests_run; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Prerequisites 4 | *.d 5 | 6 | # Object files 7 | *.o 8 | *.ko 9 | *.obj 10 | *.elf 11 | 12 | # Linker output 13 | *.ilk 14 | *.map 15 | *.exp 16 | 17 | # Precompiled Headers 18 | *.gch 19 | *.pch 20 | 21 | # Libraries 22 | *.lib 23 | *.a 24 | *.la 25 | *.lo 26 | 27 | # Shared objects (inc. Windows DLLs) 28 | *.dll 29 | *.so 30 | *.so.* 31 | *.dylib 32 | 33 | # Executables 34 | *.exe 35 | *.out 36 | *.app 37 | *.i*86 38 | *.x86_64 39 | *.hex 40 | 41 | # Debug files 42 | *.dSYM/ 43 | *.su 44 | *.idb 45 | *.pdb 46 | 47 | # Kernel Module Compile Results 48 | *.mod* 49 | *.cmd 50 | .tmp_versions/ 51 | modules.order 52 | Module.symvers 53 | Mkfile.old 54 | dkms.conf 55 | 56 | *.log 57 | */tests/*_tests 58 | 59 | # Executables 60 | fizzbuzz/fizzbuzz 61 | hangman/hangman 62 | malloc/malloc 63 | pointers/pointers 64 | queue/queue 65 | quicksort/quicksort 66 | strings/strings 67 | structs/structs 68 | -------------------------------------------------------------------------------- /hangman/hangman.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | char HANG_STATES[7][10 * 9] = 6 | { 7 | " + +---- +---- +---- +---- +---- +---- +---- ", 8 | " | | | O | O | O | O | O | O ", 9 | " | | | | + | --+ | --+-- | --+-- | --+--", 10 | " | | | | | | | | | | | | | ", 11 | " | | | | | | | / | / \\ ", 12 | " | | | | | | | | ", 13 | "/*****\\ /*****\\ /*****\\ /*****\\ /*****\\ /*****\\ /*****\\ /*****\\ /*****\\ " 14 | }; 15 | 16 | int main(int argc, char *argv[]) { 17 | /* Your code here */ 18 | 19 | return 0; 20 | } -------------------------------------------------------------------------------- /quicksort/tests/quicksort_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../../utils/minunit.h" 3 | #include "../../utils/utils.h" 4 | 5 | char *test_quicksort() 6 | { 7 | int single_int[] = { 100 }; 8 | int expected1[] = { 100 }; 9 | quicksort(single_int, 0, 0); 10 | mu_assert(check_arrays(single_int, expected1, 1, 1), "Your quicksort implementation failed on an array with a single element."); 11 | 12 | int arr1[] = {100, 55, 4, 98, 10, 18, 90, 95, 43, 11, 47, 67, 89, 42, 49, 79}; 13 | int expected2[] = {4, 10, 11, 18, 42, 43, 47, 49, 55, 67, 79, 89, 90, 95, 98, 100}; 14 | int n = sizeof(arr1) / sizeof(arr1[0]); 15 | int m = sizeof(expected2) / sizeof(expected2[0]); 16 | quicksort(arr1, 0, n-1); 17 | mu_assert(check_arrays(arr1, expected2, n, m) == 1, "Your quicksort implementation did not return the expected output."); 18 | 19 | return NULL; 20 | } 21 | 22 | char *all_tests() 23 | { 24 | mu_suite_start(); 25 | 26 | mu_run_test(test_quicksort); 27 | 28 | return NULL; 29 | } 30 | 31 | RUN_TESTS(all_tests); 32 | -------------------------------------------------------------------------------- /utils/dbg.h: -------------------------------------------------------------------------------- 1 | #ifndef __dbg_h__ 2 | #define __dbg_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef NDEBUG 9 | #define debug(M, ...) 10 | #else 11 | #define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n", __FILE__, __LINE__, ##__VA_ARGS__) 12 | #endif 13 | 14 | #define clean_errno() (errno == 0 ? "None" : strerror(errno)) 15 | 16 | #define log_err(M, ...) fprintf(stderr, "[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__) 17 | 18 | #define log_warn(M, ...) fprintf(stderr, "[WARN] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__) 19 | 20 | #define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n", __FILE__, __LINE__, ##__VA_ARGS__) 21 | 22 | #define check(A, M, ...) if(!(A)) { log_err(M, ##__VA_ARGS__); errno=0; goto error; } 23 | 24 | #define sentinel(M, ...) { log_err(M, ##__VA_ARGS__); errno=0; goto error; } 25 | 26 | #define check_mem(A) check((A), "Out of memory.") 27 | 28 | #define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__); errno=0; goto error; } 29 | 30 | #endif -------------------------------------------------------------------------------- /strings/strings.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* 5 | Given a character array s (as a pointer), return the number of 6 | characters in the string. 7 | 8 | Do not just use the `strlen` function from the standard libary. 9 | */ 10 | int string_length(char *s) 11 | { 12 | 13 | } 14 | 15 | /* 16 | Write a function that reverses the order of string s and outputs 17 | the reversed string to the input array rv. The rv array will have 18 | enough space for the reversed string. Don't forget to terminate 19 | the reversed string with a null character. Return the rv array. 20 | */ 21 | char *reverse_string(char *rv, char *s) 22 | { 23 | 24 | } 25 | 26 | #ifndef TESTING 27 | int main(void) 28 | { 29 | char quote1[] = "Don't forget to be awesome"; 30 | char quote2[] = "a man a plan a canal panama"; 31 | 32 | char rv[512]; 33 | 34 | printf("The string 'Don't forget to be awesome' has %d characters.\n", string_length(quote1)); 35 | printf("The string 'a man a plan a canal panama' reversed is: '%s'\n", reverse_string(rv, quote2)); 36 | 37 | return 0; 38 | } 39 | #endif 40 | 41 | -------------------------------------------------------------------------------- /structs/tests/structs_tests.c: -------------------------------------------------------------------------------- 1 | #include "../../utils/minunit.h" 2 | #include "../../utils/utils.h" 3 | #include 4 | 5 | char *test_person_create() 6 | { 7 | Person *tony = createPerson("Tony Stark", 32, 64, 140); 8 | 9 | mu_assert(check_strings(tony->name, "Tony Stark") == 0, "Person_create struct name field does not match the expected result."); 10 | mu_assert(tony->age == 32, "Person_create struct age field does not match the expected result.") 11 | mu_assert(tony->height == 64, "Person_create struct height field does not match the expected result.") 12 | mu_assert(tony->weight == 140, "Person_create struct weight field does not match the expected result.") 13 | 14 | free(tony->name); 15 | free(tony); 16 | 17 | return NULL; 18 | } 19 | 20 | char *test_person_destroy() 21 | { 22 | Person *kurt = createPerson("Kurt Hugo Schneider", 29, 63, 130); 23 | destroyPerson(kurt); 24 | 25 | return NULL; 26 | } 27 | 28 | char *all_tests() 29 | { 30 | mu_suite_start(); 31 | 32 | mu_run_test(test_person_create); 33 | mu_run_test(test_person_destroy); 34 | 35 | return NULL; 36 | } 37 | 38 | RUN_TESTS(all_tests); -------------------------------------------------------------------------------- /quicksort/quicksort.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "lib.h" 3 | 4 | /* 5 | Implement the Quicksort algorithm. You'll likely want to use the `swap` 6 | function, which is already being included for you in this file. 7 | 8 | The `low` and `high` parameters indicate the lowest and highest indices 9 | of the array that is getting passed in. This is necessary because the 10 | function is being passed a pointer to the array, not the contents of the 11 | array. We can't easily figure out the length of the array through the 12 | pointer, so the function receives the highest and lowest indices as 13 | parameters to circumvent this issue. 14 | 15 | Do not just use the `qsort` function from the standard library. 16 | */ 17 | void quicksort(int *arr, int low, int high) 18 | { 19 | 20 | } 21 | 22 | #ifndef TESTING 23 | int main(void) 24 | { 25 | int arr1[] = {100, 55, 4, 98, 10, 18, 90, 95, 43, 11, 47, 67, 89, 42, 49, 79}; 26 | int n = sizeof(arr1) / sizeof(arr1[0]); 27 | quicksort(arr1, 0, n-1); 28 | 29 | for (int i = 0; i < n; i++) { 30 | printf("%d ", arr1[i]); 31 | } 32 | 33 | printf("\n"); 34 | 35 | return 0; 36 | } 37 | #endif 38 | -------------------------------------------------------------------------------- /mainbuild.mk: -------------------------------------------------------------------------------- 1 | SRC=$(wildcard *.c) 2 | EXE=$(subst .c,,$(SRC)) 3 | 4 | $(EXE): $(SRC) 5 | gcc -Wall -Wextra -std=c99 -g -o $@ $^ 6 | 7 | test: tests 8 | 9 | .PHONY: clean test tests 10 | 11 | # Sean's testing stuff below: 12 | 13 | CFLAGS=-g -O2 -Wall -Wextra -I. -DTESTING -DNDEBUG $(OPTFLAGS) 14 | LIBS=-ldl $(OPTLIBS) 15 | PREFIX?=/usr/local 16 | 17 | SOURCES=$(wildcard *.c) 18 | OBJECTS=$(patsubst %.c,%.o,$(SOURCES)) 19 | 20 | TEST_SRC=$(wildcard tests/*_tests.c) 21 | TESTS=$(patsubst %.c,%,$(TEST_SRC)) 22 | 23 | TARGET=build/liblcthw.a 24 | SO_TARGET=$(patsubst %.a,%.so,$(TARGET)) 25 | 26 | # The Target Build 27 | tests: clean $(TARGET) $(SO_TARGET) tests2 28 | 29 | dev: CFLAGS=-g -std=c99 -Wall -Isrc -Wall -Wextra %(OPTFLAGS) 30 | dev: all 31 | 32 | $(TARGET): CFLAGS += -fPIC 33 | $(TARGET): build $(OBJECTS) 34 | ar rcs $@ $(OBJECTS) 35 | ranlib $@ 36 | $(SO_TARGET): $(TARGET) $(OBJECTS) 37 | $(CC) -shared -o $@ $(OBJECTS) 38 | 39 | build: 40 | @mkdir -p build 41 | @mkdir -p bin 42 | 43 | # The Unit Tests 44 | .PHONY: tests 45 | #tests2: CFLAGS += $(TARGET) 46 | tests2: $(TESTS) 47 | sh ./tests/runtests.sh 48 | 49 | $(TESTS): $(TEST_SRC) 50 | $(CC) $(CFLAGS) $^ $(TARGET) -o $@ 51 | 52 | # The Cleaner 53 | clean: 54 | rm -rf build $(OBJECTS) $(TESTS) 55 | rm -f tests/tests.log 56 | find . -name "*.gc" -exec rm -f {} \; 57 | rm -rf `find . -name "*.dSYM" -print` 58 | rm -f $(EXE) 59 | -------------------------------------------------------------------------------- /structs/structs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "lib.h" 4 | 5 | /* 6 | Define the Person struct by specifying the fields that make up the 7 | Person type. Don't forget to specify the type of each field. A 8 | Person should have the fields `name`, `age`, `height`, and `weight`. 9 | */ 10 | typedef struct Person { 11 | 12 | } Person; 13 | 14 | /* 15 | Creates an instance of the Person struct that receives all the relevant 16 | pieces of data associated with a Person instance. 17 | Allocates the appropriate amount of memory to hold an instance of the 18 | Person struct, and then sets the struct's fields with the input data. 19 | When setting the `name` field, use your previously-built string_dup 20 | function, which handles the allocation of memory for the new string. 21 | */ 22 | Person *createPerson(char *name, int age, int height, int weight) 23 | { 24 | 25 | } 26 | 27 | /* 28 | Given a pointer to a Person struct, frees up the memory that holds the 29 | Person's name as well as the memory that holds the Person instance. 30 | */ 31 | void destroyPerson(Person *who) 32 | { 33 | 34 | } 35 | 36 | #ifndef TESTING 37 | int main(void) 38 | { 39 | Person *tony = createPerson("Tony Stark", 32, 64, 140); 40 | 41 | printf(" Name: %s\n", tony->name); 42 | printf(" Age: %d\n", tony->age); 43 | printf("Height: %d\n", tony->height); 44 | printf("Weight: %d\n", tony->weight); 45 | 46 | destroyPerson(tony); 47 | 48 | return 0; 49 | } 50 | #endif -------------------------------------------------------------------------------- /strings/tests/strings_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../../utils/minunit.h" 3 | #include "../../utils/utils.h" 4 | 5 | char *test_string_length() 6 | { 7 | char quote[] = "Don't forget to be awesome"; 8 | char another_quote[] = "...My spirit is a roaring sea"; 9 | 10 | mu_assert(string_length("") == 0, "Your string_length function did not return the expected length."); 11 | mu_assert(string_length(quote) == 26, "Your string_length function did not return the expected length."); 12 | mu_assert(string_length(another_quote) == 29, "Your string_length function did not return the expected length."); 13 | 14 | return NULL; 15 | } 16 | 17 | char *test_reverse_string() 18 | { 19 | char rv1[512]; 20 | char rv2[512]; 21 | char str1[] = "hello world"; 22 | char str2[] = "a man a plan a canal panama"; 23 | 24 | mu_assert(check_strings(reverse_string(rv1, str1), "dlrow olleh") == 0, "Your reverse_string function did not correctly reverse a string."); 25 | mu_assert(strlen(rv1) == 11, "Your reversed string does not have the same length as the original string."); 26 | 27 | mu_assert(check_strings(reverse_string(rv2, str2), "amanap lanac a nalp a nam a") == 0, "Your reverse_string function did not correctly reverse a string."); 28 | mu_assert(strlen(rv2) == 27, "Your reversed string does not have the same length as the original string."); 29 | 30 | return NULL; 31 | } 32 | 33 | char *all_tests() 34 | { 35 | mu_suite_start(); 36 | 37 | mu_run_test(test_string_length); 38 | mu_run_test(test_reverse_string); 39 | 40 | return NULL; 41 | } 42 | 43 | RUN_TESTS(all_tests); -------------------------------------------------------------------------------- /pointers/pointers.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | Given a character pointer x (that points to an array of chars), and a 5 | character pointer y, copies the character contents of y over to x. Pointer 6 | arithmetic is necessary here. Also, make sure x points to a null terminator 7 | at its end to terminate it properly. 8 | 9 | Example call: 10 | 11 | char buffer[1024]; 12 | 13 | string_copy(buffer, "Hello!"); 14 | printf("%s", buffer); // Prints "Hello!" 15 | */ 16 | void string_copy(char *x, char *y) 17 | { 18 | 19 | } 20 | 21 | /* 22 | Searches the input string `str` for the first instance of the 23 | character `c` (an unsigned char). This function returns a pointer 24 | that points to the first instance of the character `c` in the 25 | input string `str`. 26 | 27 | Do not use the `strchr` function from the standard library. 28 | */ 29 | char *find_char(char *str, char c) 30 | { 31 | 32 | } 33 | 34 | /* 35 | Searches the input string `haystack` for the first instance of 36 | the string `needle`. This function returns a pointer that points 37 | to the first instance of the string `needle` in the input 38 | string `haystack`. 39 | 40 | Do not use the `strstr` function from the standard library. 41 | */ 42 | char *find_string(char *haystack, char *needle) 43 | { 44 | 45 | } 46 | 47 | #ifndef TESTING 48 | int main(void) 49 | { 50 | char *found_char = find_char("hello", 'e'); 51 | char *found_string = find_string("world", "or"); 52 | 53 | printf("Found char: %s\n", found_char); 54 | printf("Found string: %s\n", found_string); 55 | 56 | return 0; 57 | } 58 | #endif 59 | -------------------------------------------------------------------------------- /queue/queue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "lib.h" 4 | 5 | typedef struct Queue { 6 | unsigned int length; 7 | unsigned int capacity; 8 | int *storage; 9 | } Queue; 10 | 11 | /* 12 | Creates a queue by allocating the appropriate amount of memory for an 13 | instance of the Queue struct, and initializes all of the fields of the 14 | struct. Also allocates memory for the queue's storage structure. 15 | */ 16 | Queue *createQueue(unsigned capacity) 17 | { 18 | 19 | } 20 | 21 | /* 22 | Adds the given item to the end of the queue. If the queue does 23 | not have room, expand the queue's available storage so that it 24 | does have room for the additional item. 25 | */ 26 | void enqueue(Queue *q, int item) 27 | { 28 | 29 | } 30 | 31 | /* 32 | Removes the item at the front of the queue and returns it. 33 | If the queue is empty, this function should return -1. 34 | */ 35 | int dequeue(Queue *q) 36 | { 37 | 38 | } 39 | 40 | /* 41 | Frees the memory used to hold the queue instance and its 42 | associated storage. 43 | */ 44 | void destroyQueue(Queue *q) 45 | { 46 | 47 | } 48 | 49 | 50 | #ifndef TESTING 51 | int main(void) 52 | { 53 | Queue *q = createQueue(4); 54 | 55 | enqueue(q, 1); 56 | enqueue(q, 2); 57 | enqueue(q, 3); 58 | enqueue(q, 4); 59 | enqueue(q, 5); 60 | enqueue(q, 6); 61 | 62 | printf("%d\n", dequeue(q)); 63 | printf("%d\n", dequeue(q)); 64 | printf("%d\n", dequeue(q)); 65 | printf("%d\n", dequeue(q)); 66 | printf("%d\n", dequeue(q)); 67 | printf("%d\n", dequeue(q)); 68 | 69 | destroyQueue(q); 70 | 71 | return 0; 72 | } 73 | #endif -------------------------------------------------------------------------------- /TROUBLESHOOTING.md: -------------------------------------------------------------------------------- 1 | ## `runtests.sh: 4: Syntax error: word unexpected (expecting "do")` 2 | 3 | _Chief. Windows_ 4 | 5 | If you see this error: 6 | 7 | ``` 8 | Running unit tests: 9 | : not foundtests.sh: 2: ./tests/runtests.sh: 10 | ./tests/runtests.sh: 4: ./tests/runtests.sh: Syntax error: word unexpected (expecting "do") 11 | ``` 12 | 13 | You have two options: 14 | 15 | 1. Open the file `tests/runtests.sh` in VS Code in whatever subproject folder 16 | you’re working in, e.g. `fizzbuzz`. Click on the lower right of the screen 17 | where it says `CRLF`. Choose `LF`. Save the file. Then the error should go 18 | away. 19 | 20 | 2. You can do this from the command line with the `tr` command: 21 | 22 | ``` 23 | cd tests 24 | cat runtests.sh | tr -d '\r' > runtests.tmp 25 | mv runtests.tmp runtests.sh 26 | ``` 27 | 28 | The root of the problem is a setting in git that causes all newlines (LF) to 29 | be converted to carriage-return/newline (CRLF). The script `runtests.sh` is a 30 | _bash script_ that bash runs, and bash *hates* `\r` and pukes everywhere. 31 | 32 | To cause git to _not_ do newline conversion for future clones, run the 33 | following: 34 | 35 | ``` 36 | git config --global core.autocrlf false 37 | ``` 38 | 39 | ## Mac: `malformed object` error when running `make tests` 40 | 41 | This is caused by an older version of the `ar` and `ranlib` packages being installed. 42 | Sometimes these conflict with the versions installed with xcode. 43 | 44 | If running MacPorts: 45 | 46 | ``` 47 | sudo port selfupdate 48 | sudo port upgrade cctools 49 | ``` 50 | 51 | If running Brew: 52 | 53 | ``` 54 | sudo brew update 55 | sudo brew upgrade gcc 56 | ``` 57 | -------------------------------------------------------------------------------- /quicksort/README.md: -------------------------------------------------------------------------------- 1 | # Quicksort 2 | 3 | For this module, you're going to implement a quicksort function. Feel free to reference prior implementation(s) and simply transpose the code from one language to the other. Typically this is a good way to practice new languages you're trying to learn: by transposing code you've written from one language to another. 4 | 5 | In case you haven't seen the quicksort algorithm before, here's a Python implementation that you can use as a reference: 6 | ```python 7 | def quicksort(alist, begin=0, end=None): 8 | if end is None: 9 | end = len(alist) - 1 10 | if begin < end: 11 | pivot = partition(alist, begin, end) 12 | quicksort(alist, begin, pivot-1) 13 | quicksort(alist, pivot+1, end) 14 | 15 | def partition(alist, begin, end): 16 | pivot = begin 17 | 18 | for i in range(begin+1, end+1): 19 | if alist[i] <= alist[begin]: 20 | pivot += 1 21 | alist[i], alist[pivot] = alist[pivot], alist[i] 22 | 23 | alist[pivot], alist[begin] = alist[begin], alist[pivot] 24 | 25 | return pivot 26 | ``` 27 | Keep in mind that since Python is a higher-level language than C, it provides language features and constructs that aren't available in C. For example, the swapping syntax in Python isn't going to work in C without some additional work. The `swap` that you implemented earlier in the sprint has been included in this directory in the `lib.h` header file though, so you can use it here to swap values within an array in-place. Another Python language feature that you can't replicate in C are the default function parameters. 28 | 29 | Navigate to the `quicksort.c` file. Implement your quicksort algorithm, then type `make tests` to check if you have all the tests passing. -------------------------------------------------------------------------------- /pointers/tests/pointers_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../../utils/minunit.h" 4 | #include "../../utils/utils.h" 5 | 6 | char *test_string_copy() 7 | { 8 | char *string = "hello, world"; 9 | char empty[20]; 10 | 11 | string_copy(empty, string); 12 | mu_assert(check_strings(empty, string) == 0, "Your string_copy did not correctly copy the given string."); 13 | 14 | return NULL; 15 | } 16 | 17 | char *test_find_char() 18 | { 19 | char *str = "LambdaSchool"; 20 | char *found = find_char(str, 'b'); 21 | 22 | mu_assert(strcmp(found, "bdaSchool") == 0, "Your find_char function did not work as expected."); 23 | 24 | found = find_char(str, 'S'); 25 | 26 | mu_assert(strcmp(found, "School") == 0, "Your find_char function did not work as expected."); 27 | 28 | found = find_char(str, 's'); 29 | 30 | mu_assert(!found, "Your find_char function return a char when it should have returned NULL."); 31 | 32 | return NULL; 33 | } 34 | 35 | char *test_find_string() 36 | { 37 | char *str = "LambdaSchool"; 38 | char *found = find_string(str, "School"); 39 | 40 | mu_assert(strcmp(found, "School") == 0, "Your find_string function did not find the expected substring."); 41 | 42 | found = find_string(str, "Lambda"); 43 | 44 | mu_assert(strcmp(found, "LambdaSchool") == 0, "Your find_sting function did not find the expected substring."); 45 | 46 | found = find_string(str, "lambda"); 47 | 48 | mu_assert(!found, "Your find_string function found a substring when it should have returned NULL."); 49 | 50 | return NULL; 51 | } 52 | 53 | char *all_tests() 54 | { 55 | mu_suite_start(); 56 | 57 | mu_run_test(test_string_copy); 58 | mu_run_test(test_find_char); 59 | mu_run_test(test_find_string); 60 | 61 | return NULL; 62 | } 63 | 64 | RUN_TESTS(all_tests); -------------------------------------------------------------------------------- /fizzbuzz/README.md: -------------------------------------------------------------------------------- 1 | # Functions in C 2 | 3 | Functions in C are not much different from functions in JavaScript. Probably the most notable difference, at least from a syntactic point of view, is that you need to specify types on all your variables and input parameters, along with needing to specify the return type of the function. Remember, C is a _strongly_ typed language, as opposed to JavaScript which is a _loosely_ typed language where types are entirely inferred by the interpreter. The C compiler ensures that you specify your types and that your code is consistent with the types you specify. 4 | 5 | Here is a non-comprehensive list of C data types: [https://codeforwin.org/2015/05/list-of-all-format-specifiers-in-c-programming.html](https://codeforwin.org/2015/05/list-of-all-format-specifiers-in-c-programming.html) 6 | 7 | To reiterate, you might declare a function in C like this: 8 | ```c 9 | int foo(int param1, int param2) 10 | { 11 | ... 12 | return 0; 13 | } 14 | ``` 15 | This function signature states that it receives two integer parameters and returns an integer. 16 | 17 | Here's another example: 18 | ```c 19 | void bar(int[]) 20 | { 21 | ... 22 | } 23 | ``` 24 | This function signature states that it receives a single integer array and doesn't return anything. You'll often see this kind of function signature when you want the function to simply mutate the input and/or print the contents of the input. 25 | 26 | For printing output, you'll want to use the `printf` function, which is included in the `stdlib` library. 27 | 28 | ## Implementing fizzbuzz 29 | 30 | Your first task is going to be writing good ol' fizzbuzz in C! Your `fizzbuzz` function should receive some integer n, then loop up till n, printing out "Fizz" if the current iteration i is divisible by 3, "Buzz" if it's divisible by 5, or "FizzBuzz" if it is divisible by both 3 and 5. Additionally, for each iteration where nothing is printed, your function should increment a counter and return the result of that counter at the end of its execution. 31 | 32 | Navigate to the `fizzbuzz.c` file. You'll write your code in there. When you want to test it, type `make tests` into your terminal (make sure you're doing this from the directory where the `Makefile` is located), which will run the `Makefile` and compile your program and, if the compilation was successful, run the tests for it as well. 33 | -------------------------------------------------------------------------------- /queue/tests/queue_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "../utils/minunit.h" 6 | #include "../utils/utils.h" 7 | 8 | char *test_queue_creation() 9 | { 10 | unsigned int capacity = 5; 11 | Queue *q = createQueue(capacity); 12 | 13 | mu_assert(q, "Your createQueue function did not return a valid pointer to the created queue."); 14 | mu_assert(q->length == 0, "Your createQueue function did not initialize a queue length of 0."); 15 | mu_assert(q->capacity == capacity, "Your createQueue function did not initialize a queue with the expected capacity."); 16 | mu_assert(q->storage, "Your createQueue function did not initialize a pointer for storage in the queue."); 17 | 18 | destroyQueue(q); 19 | 20 | return NULL; 21 | } 22 | 23 | char *test_enqueue_and_dequeue() 24 | { 25 | srand(time(NULL) + getpid()); 26 | 27 | unsigned int capacity = (rand() % 30) + 1; 28 | 29 | Queue *q = createQueue(capacity); 30 | int *rand_values = malloc(sizeof(int) * capacity); 31 | 32 | unsigned int i; 33 | 34 | for (i = 0; i < capacity; i++) { 35 | rand_values[i] = (rand() % 100) + 1; 36 | } 37 | 38 | for (i = 0; i < capacity; i++) { 39 | enqueue(q, rand_values[i]); 40 | } 41 | 42 | for (i = 0; i < capacity; i++) { 43 | mu_assert(dequeue(q) == rand_values[i], "Your queue did not return an expected value."); 44 | } 45 | 46 | destroyQueue(q); 47 | 48 | return NULL; 49 | } 50 | 51 | char *test_queue_expansion() 52 | { 53 | srand(time(NULL) + getpid()); 54 | 55 | unsigned int capacity = (rand() % 30) + 1; 56 | unsigned int increased_cap = capacity + 5; 57 | 58 | Queue *q = createQueue(capacity); 59 | int *rand_values = malloc(sizeof(int) * increased_cap); 60 | 61 | unsigned int i; 62 | 63 | for (i = 0; i < increased_cap; i++) { 64 | rand_values[i] = (rand() % 100) + 1; 65 | } 66 | 67 | for (i = 0; i < increased_cap; i++) { 68 | enqueue(q, rand_values[i]); 69 | } 70 | 71 | for (i = 0; i < increased_cap; i++) { 72 | mu_assert(dequeue(q) == rand_values[i], "Your queue did not return an expected value."); 73 | } 74 | 75 | destroyQueue(q); 76 | 77 | return NULL; 78 | } 79 | 80 | 81 | char *all_tests() 82 | { 83 | mu_suite_start(); 84 | 85 | mu_run_test(test_queue_creation); 86 | mu_run_test(test_enqueue_and_dequeue); 87 | mu_run_test(test_queue_expansion); 88 | 89 | return NULL; 90 | } 91 | 92 | RUN_TESTS(all_tests); -------------------------------------------------------------------------------- /malloc/tests/malloc_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../../utils/minunit.h" 5 | #include "../../utils/utils.h" 6 | #include "../../pointers/pointers.h" 7 | 8 | char *test_string_dup() 9 | { 10 | char *s = "Some string to duplicate."; 11 | char *dup = string_dup(s); 12 | mu_assert(s != dup, "Your string_dup function did not actually duplicate the given string."); 13 | mu_assert(check_strings(dup, s) == 0, "Your string_dup function did not correctly duplicate the given string."); 14 | 15 | free(dup); 16 | 17 | return NULL; 18 | } 19 | 20 | char *test_mem_copy() 21 | { 22 | int numbers[] = {100, 55, 4, 98, 10, 18, 90, 95, 43, 11, 47, 67, 89, 42, 49, 79}; 23 | int n = sizeof(numbers) / sizeof(numbers[0]); 24 | int *target = malloc(n * sizeof(int)); 25 | 26 | mem_copy(target, numbers, n * sizeof(int)); 27 | mu_assert(check_arrays(target, numbers, n, n) == 1, "Your mem_copy function did not correctly copy an array of integers."); 28 | 29 | char src[50] = "http://bloomtech.com"; 30 | char dest[50]; 31 | 32 | mem_copy(dest, src, strlen(src)+1); 33 | mu_assert(check_strings(dest, src) == 0, "Your mem_copy function did not correctly copy an array of characters."); 34 | 35 | free(target); 36 | 37 | return NULL; 38 | } 39 | 40 | char *test_resize_memory() 41 | { 42 | char *url = strdup("http://bloomtech.com"); 43 | char *path = strdup("/students/"); 44 | int url_length = strlen(url); 45 | int path_length = strlen(path); 46 | 47 | int new_length = url_length + path_length + 1; // +1 for the NUL terminator 48 | char *new_url = resize_memory(url, url_length, new_length); 49 | char *p = new_url + url_length; 50 | 51 | while (*path != '\0') { 52 | *p = *path; 53 | p++; 54 | path++; 55 | } 56 | 57 | *p = '\0'; 58 | 59 | mu_assert(check_strings(new_url, "http://bloomtech.com/students/") == 0, "Your resize_memory function did not increase the size of the given string correctly."); 60 | 61 | char *new_new_url = resize_memory(new_url, new_length, 8); 62 | 63 | mu_assert(memcmp(new_new_url, "http://l", 8) == 0, "Your resize_memory function did not truncate the size of the given string correctly."); 64 | 65 | return NULL; 66 | } 67 | 68 | char *all_tests() 69 | { 70 | mu_suite_start(); 71 | 72 | mu_run_test(test_string_dup); 73 | mu_run_test(test_mem_copy); 74 | mu_run_test(test_resize_memory); 75 | 76 | return NULL; 77 | } 78 | 79 | RUN_TESTS(all_tests) 80 | -------------------------------------------------------------------------------- /malloc/malloc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "lib.h" 5 | 6 | /* 7 | Duplicates the input string by dynamically allocating memory for 8 | the duplicate string using `malloc` and then copying the string 9 | into the allocated memory. Returns a pointer to the allocated memory. 10 | You may want to use the string_length function to figure out the 11 | length of the input string. 12 | */ 13 | char *string_dup(char *src) 14 | { 15 | 16 | } 17 | 18 | /* 19 | A generic version of string_copy, mem_copy receives a block of memory 20 | of any type and copies its contents to the destination pointer (dest). 21 | You may want to cast the input pointers to char pointers first before 22 | performing the copying. `n` is the amount of data that should be copied 23 | from `src` to `dest`. 24 | */ 25 | void mem_copy(void *dest, const void *src, int n) 26 | { 27 | 28 | } 29 | 30 | /* 31 | Given a pointer of `malloc`'d memory, this function will 32 | attempt to resize the allocated memory to the new specified 33 | size. Any data that was previously in the old `malloc`'d 34 | memory should be intact in the new resized block of memory. 35 | Some edge cases to consider: how should resizing be handled 36 | in the case when old_size < new_size? What about when 37 | old_size > new_size? 38 | 39 | Do not use the `realloc` function from the standard libary. 40 | */ 41 | void *resize_memory(void *ptr, int old_size, int new_size) 42 | { 43 | 44 | } 45 | 46 | #ifndef TESTING 47 | int main(void) 48 | { 49 | char *s = "Some string to duplicate."; 50 | char *dup = string_dup(s); 51 | 52 | printf("Duplicated string: %s\n", dup); 53 | 54 | int numbers[] = {100, 55, 4, 98, 10, 18, 90, 95, 43, 11, 47, 67, 89, 42, 49, 79}; 55 | int n = sizeof(numbers) / sizeof(numbers[0]); 56 | int *target = malloc(n * sizeof(int)); 57 | 58 | mem_copy(target, numbers, n * sizeof(int)); 59 | 60 | printf("Copied array: "); 61 | 62 | for (int i = 0; i < n; i++) { 63 | printf("%d ", target[i]); 64 | } 65 | 66 | printf("\n"); 67 | 68 | char *url = string_dup("http://bloomtech.com"); 69 | char *path = string_dup("/students/"); 70 | int url_length = string_length(url); 71 | int path_length = string_length(path); 72 | 73 | int new_length = url_length - 1 + path_length; 74 | char *new_url = resize_memory(url, url_length, new_length); 75 | char *p = new_url + url_length; 76 | 77 | while (*path != '\0') { 78 | *p = *path; 79 | p++; 80 | path++; 81 | } 82 | 83 | printf("Full path string: %s\n", new_url); 84 | 85 | return 0; 86 | } 87 | #endif 88 | -------------------------------------------------------------------------------- /hangman/README.md: -------------------------------------------------------------------------------- 1 | # Hangman 2 | 3 | ## Objective 4 | Write a program that allows you play a game of Hangman. All of your code can be written in the `hangman.c` file. 5 | 6 | ## How your Program Should Work 7 | The main loop of the program prints the current state of the game and then waits for input from the player. When the player types in input (either a single letter guess or a word guess), the game checks if the letter is one that is in the target word or if the word guess matches the target word. If the guess is correct, the game state updates accordingly by revealing the guess as part of the target word. If the guess is incorrect, the game state updates accordingly by showing that letter in the 'incorrect guesses' list and adding another limb to the hangman drawing. 8 | 9 | You'll compile your program with 10 | ``` 11 | gcc -o hangman hangman.c 12 | ``` 13 | This will create an executable called `hangman` that you'll execute with the command `./hangman` once you've successfully compiled the program. 14 | 15 | For this implementation, your program should receive a single word parameter; that will be the word that the player is trying to guess. So running your program might look like this 16 | ``` 17 | ./hangman antidisestablishmentarianism 18 | ``` 19 | This will start the game loop, which will then continue running until the player wins, the player loses, or the player exits the game. 20 | 21 | ## How to Tackle the Problem 22 | This problem can be broken down into many subproblems. Each subproblem can be further broken down into chunks that you can think about how to implement in code. These include, but are not limited to, the following: 23 | - [ ] Reading input from the user and recording it properly 24 | - [ ] Figuring out how to parse command line arguments 25 | - [ ] Properly recording game state 26 | - [ ] Keeping track of all letters/words that have been inputted 27 | - [ ] Recording the incorrect guesses 28 | - [ ] Printing the proper UI given the game's current state 29 | - [ ] Implementing the ability to accept both single letter guesses as well as entire word guesses 30 | - [ ] Having the game UI respond appropriately when a player makes a correct / incorrect guess 31 | - [ ] Adding the ability for the user to exit the game by typing in the proper keyword 32 | 33 | ## Rules of Hangman 34 | Just in case you happen to be someone who _doesn't_ know the rules of Hangman, we'll list them here. 35 | 36 | The game has you guess a word. You're given 8 strikes. If you guess an incorrect letter (that you haven't already guessed before), or take a stab at guessing the entire word and don't get the correct word, that's one strike. The game keeps track of each letter that you've guessed. You win the game when you guess the correct word. 37 | 38 | -------------------------------------------------------------------------------- /strings/README.md: -------------------------------------------------------------------------------- 1 | # Arrays in C 2 | 3 | In C, arrays are nothing more than contiguous chunks in memory that store a _single_ type of data. Additionally, arrays are static in size, at least in their most basic form. That means that we'll typically need to know the size and/or the amount of data a particular array will be storing when we initialize it. Unlike in higher-level languages, we can't just simply declare an empty array and then start pushing elements onto it willy-nilly. 4 | 5 | If we wanted to declare an array of integers, we might do it like so: 6 | ```c 7 | int int_array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 8 | ``` 9 | Note the syntax here. We need the square brackets after the array's name to indicate that it is an array to the compiler, and when we declare the initial values of the array, we surround them with curly braces, not square brackets. 10 | 11 | Another way we might declare an array is the following: 12 | ```c 13 | int another_int_array[20]; 14 | ``` 15 | With this, we've declared a statically-sized integer array that has space for 20 integers. There's nothing in it initially, but we can now start populating it with integers (and only integers) up to it's specified capacity. 16 | ```c 17 | int n = 20 / sizeof(int); 18 | 19 | for (int i = 0; i < n; i++) { 20 | another_int_array[i] = i; 21 | } 22 | ``` 23 | While we allocated 20 bytes worth of space into the array, keep in mind that a single integer actually takes up more than a single byte of space in memory. To get around this, we can use the `sizeof` operator to find out exactly how many bytes a single `int` occupies on your machine, and then divide the array capacity by that to get the actual number of integers that can fit inside the array. 24 | 25 | If we try to add more integers than the array has capacity for, we'll get a segmentation fault error, which means we've tried to access memory we don't have permission to access. That's what happens when we go outside of an array's bounds. 26 | 27 | We'll talk later on about how to handle dynamically-sized data. 28 | 29 | # Strings in C 30 | 31 | So what do arrays have to do with strings? Everything, as it turns out. Strings in C are actually nothing more than arrays of characters. In fact, when declaring a string, you might do it like this: 32 | ```c 33 | char[] = "Hello world!"; 34 | ``` 35 | Under the hood, this declaration becomes 36 | ```c 37 | char[] = { 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\0' }; 38 | ``` 39 | The `\0` character at the end represents the null character; _every_ C string is terminated with one of these, and in fact, if you wanted to write a function to find the number of characters in a string, you'd basically just loop through the character array and increment a counter until you found the `\0` character. 40 | 41 | IMPORTANT NOTE: So far, every language you've worked in has not differentiated between single and double quotes. In C, these actually make a difference. Single quotes delineate a single character, while double quotes delineate string literals. So for example, `"a"` is a string literal complete with a terminating null character at the end of it, as opposed to `'a'` which is just the individual character. 42 | 43 | The compiler _will_ yell at you if you try to store multiple characters in single quotes. 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction to the C Programming Language 2 | 3 | Welcome to your first C sprint! 4 | 5 | The two main goals of this sprint are as follows. The first goal is to give 6 | students the experience of learning a different programming language. This is 7 | important because it is _very_ rarely the case where you are familiar and 8 | comfortable with all of the technologies/programming languages that you'll be 9 | using on a job as a professional developer. Being able to pick up 10 | languages/technologies you aren't familiar with is a skill that we want to 11 | cultivate in students during their time here at Lambda, which is why we expose 12 | students to both C and Python during their time here. 13 | 14 | The second goal is to start peeling away the layers of abstraction that students 15 | are used to from working with JavaScript and Python. Indeed, when you boil it 16 | all down, both JavaScript and Python are implemented in C in the first place! C 17 | supports very few of the abstractions that students are used to, because C has 18 | you working directly with the memory on your machine. Higher level languages 19 | abstract this fact away from you, at the cost of raw performance and control. 20 | Essentially, you're getting exposed to the nitty-gritty seedy underbelly of your 21 | computer's inner workings, and being in such a place for extended periods of 22 | time is not for everybody. 23 | 24 | That being said, we the instructors do believe that every developer needs to get 25 | a taste of C in order to really be able to delve into the inner workings of how 26 | exactly software communicates and interfaces with hardware. That's not really 27 | something JavaScript or Python will expose you to. Additionally, once you get 28 | acclimated to C, the language provides you with unparalleled control over how 29 | your code gets executed on your machine. Some people find the sheer amount of 30 | control that C provides you intoxicating. 31 | 32 | But before any of that happens, you've gotta get your feet wet with C. So without further ado, let's get started! 33 | 34 | ## What you'll be doing in this sprint 35 | 36 | If you haven't gotten your C compiler up and running, please do that first. You'll find instructions on how to do that for your specific platform here: https://github.com/BloomInstituteOfTechnology/CS-Wiki/wiki/Getting-Your-C-Compiler-Installed. 37 | 38 | This sprint is structured very much like how your introductory JavaScript sprints are structured. There are independent modules, each with their own set of exercises that you'll be tasked with implementing. There's an associated set of tests for all the exercises in each module, which you'll run against your code to check to see if it behaves and functions correctly. The order in which you should tackle these exercises is this: 39 | 40 | - [ ] fizzbuzz 41 | - [ ] strings 42 | - [ ] pointers 43 | - [ ] malloc 44 | - [ ] structs 45 | 46 | **Stretch Goal Problems** 47 | - [ ] queue 48 | - [ ] quicksort 49 | - [ ] hangman 50 | 51 | Inside a module directory, type `make tests` (just like with the `npm install` 52 | command needing a `package.json` file, the `make` command requires a 53 | `Makefile`), which will do a couple of things as specified by the `Makefile`. 54 | These include compiling your source code inside that module, linking all of the 55 | included header files and libraries, and running the tests for that module. Once 56 | you've gotten all of the tests to pass for that module, move on to the next one. 57 | 58 | The code inside the `main` functions are for if you want to compile your file 59 | into an executable for debugging purposes. You don't need to edit any of the 60 | code in the `main` functions. Just type `make` to compile an executable. Then 61 | type `./NAME_OF_EXECUTABLE` (not in caps) to run it, which will execute the code 62 | inside that program's `main` function. 63 | 64 | ## Troubleshooting 65 | 66 | For things that go wrong, check out the [troubleshooting](TROUBLESHOOTING.md) doc. 67 | -------------------------------------------------------------------------------- /malloc/README.md: -------------------------------------------------------------------------------- 1 | # Dynamic Memory 2 | 3 | Remember that a few modules ago when we were talking about arrays in C being entirely static? That's a pretty big limitation since we're forced to know up front what kind of data we want to store in the array and how much data there is before we can fit it into an array. We aren't psychics here. How do we get around this? 4 | 5 | The answer to that is the `malloc` function. Its signature is this: 6 | ```c 7 | void *malloc(size_t size); 8 | ``` 9 | A `size_t` type is nothing more than a really large int for representing large sizes. The other thing to note is that the `malloc` function returns a `void` pointer. The `void` type simply means the type could be anything; essentially, we don't know the type up front. So `malloc` returns us a pointer of unspecified type that points to a chunk of memory with the specified size. 10 | 11 | Well, if there's this `void` type, what's to stop us from just declaring everything as a `void` type? The drawback to the `void` type is that the compiler then can't infer much of anything about the data that a void pointer points to. It can't determine how much data a `void` pointer is pointing to, plus `void` pointers cannot be dereferenced until they are _cast_ (their type is changed) to a known type. Basically, it won't be able to provide us nearly as many compile-time protections as it can if we have all of our types declared. The moral of the story is that the `void` type should only be used when you actually do not know the type of the data up front. `malloc` returning a `void` pointer is a great use-case for this. 12 | 13 | Here are some pretty typical `malloc` calls: 14 | ```c 15 | int *100_ints = malloc(100 * sizeof(int)); 16 | 17 | char *50_chars = malloc(51); 18 | ``` 19 | The first `malloc` call allocates enough space for 100 ints. The `malloc` call initially returns a `void` pointer, but it then gets _cast_ to an `int` pointer on the left-hand side. You'll also note that we're using the `sizeof` operator in order to find the size of an individual integer, and then multiplying by the number of integers we're looking to store in order to get the total amount of memory we need. We have to do this because integers are not stored in a single byte, and integer sizes may fluctuate from platform to platform. 20 | 21 | With the second `malloc` call, you'll see we don't do that. We're still performing the cast to a `char` pointer, but there's no call to the `sizeof` operator. This is because `char`s each fit within a single byte, so 51 bytes will hold 51 `char`s, which is exactly what we want. Once we have a pointer to the `malloc`'d memory, we can work with it exactly how we've been working with pointers. 22 | 23 | So, we still need to know how much data we want to fit inside this chunk of `malloc`'d memory, which still isn't all that helpful since that's still a hefty limitation. There exist other library functions, such as `realloc`, which can receive a pointer to `malloc`d memory and then resize that chunk of memory, but for this module we're just going to stick with `malloc`. 24 | 25 | We'll talk about one of the main usages of `malloc` in the next module. 26 | 27 | ## Working with Void Pointers Directly 28 | 29 | Getting a void pointer back from a `malloc` call isn't the only time we'll need to deal with void pointers. Sometimes we'll need to explicitly cast a void pointer that's already referring some data, unlike the chunk of memory we're getting back from `malloc` call, which starts off empty and uninitialized. 30 | 31 | A common use-case for void pointers is when we want to write a function that is generic over types, i.e., it doesn't care what the types of its inputs are. Let's assume we have the following function signature: 32 | 33 | ```c 34 | void foo(void *a, void *b); 35 | ``` 36 | 37 | Here, `foo` takes two void pointers, which will contain some data of a certain type, but `foo` either doesn't know what those types are or doesn't care. If we want to do something with these two void pointers though, we'll have to explicitly cast them to some actual type we can work with. We can do that like this: 38 | 39 | ```c 40 | char *cast_a = (char *) a; 41 | char *cast_b = (char *) b; 42 | ``` 43 | 44 | Here we cast both void pointers to be char pointers. Note that this doesn't actually transform the data these two pointers point to, we aren't changing the underlying data's type. We just need to temporarily assign a concrete type to these pointers so that we can do something with them inside our `foo` function. 45 | 46 | We pick `char *`s here because the `char` type is guaranteed by the C specification to be one byte each. This is desirable because it's the most granular type we can pick. If we're going to be touching some memory and not treating as the type that it actually is, then to avoid any sort of memory corruption, we want to be as granular as possible when handling that underlying data, hence the usage or chars and not some other type. -------------------------------------------------------------------------------- /structs/README.md: -------------------------------------------------------------------------------- 1 | # Structs 2 | 3 | Another limitation that exists in C that we haven't brought up yet is the fact that arrays in C can only hold a single type of data. Being able to stick any type of data all into the same array is a feature that you probably take for granted coming from JavaScript. Well, you can't do that in C! 4 | 5 | Enter structs. Structs are basically C's version of a poor man's object. We can store values of different types inside a struct, which is awesome, but we need to declare the types of the fields that the struct can hold up front. That being said, structs are instrumental when it comes to implementing data structures and more complex types in C. 6 | 7 | ## Initializing structs 8 | 9 | Let's look at a simple struct definition: 10 | 11 | ```c 12 | struct Cat { 13 | char *breed; 14 | char *color; 15 | int age; 16 | }; 17 | ``` 18 | This declares the shape of the Cat struct. Now we'll need to create an instance of it if we want to want to actually use it. Well, what actually defines an instance of something? 19 | 20 | In this case, an instance of a struct is some chunk of memory that has been initialized with the shape of said struct. So we'll need to allocate memory for our Cat struct. We can do that with this: 21 | 22 | ```c 23 | struct Cat *a_cat = malloc(sizeof(struct Cat)); 24 | ``` 25 | So we're handed a pointer to a chunk of memory that holds exactly the right amount of data for a single instance of a Cat. We take that and cast it to a pointer with the type of `struct Cat` since that's the type that is being held in this chunk of memory. We'll also need to go and initialize the values of our Cat instance like so: 26 | 27 | ```c 28 | a_cat->breed = NULL; 29 | a_cat->color = NULL; 30 | a_cat->age = 0; 31 | ``` 32 | What's with these funky arrows? Coming from JavaScript, you're used to using dot notation for accessing members of an object. Structs are poor man's objects, so shouldn't dot notation work here? 33 | 34 | The answer is that dot notation is a thing in C, but because in this case we have a _pointer_ to a struct, we need to perform a dereference first in order to get to the actual data the struct contains. That's what the arrow syntax does. It performs a dereference first and then accesses the struct member. 35 | 36 | Basically, you can think of it like this: 37 | ```c 38 | a_cat->breed == (*a_cat).breed 39 | ``` 40 | 41 | Allocating memory for structs is one of the most important functions that `malloc` serves. Keep in mind though that when we declare a struct, every type in the struct needs to have a known size. Looking at the Cat struct, `char *`s and `int`s all have a known size, so the entire size of the struct can be calculated. However, we don't know the sizes of the data that the `char *`s may be pointing to. 42 | 43 | Let's say we have a function called `name_cat` that will assign a Cat instance with a given input name. It might look like this: 44 | 45 | ```c 46 | void name_cat(struct Cat *cat, char *name) 47 | { 48 | int name_length = string_length(name); 49 | 50 | cat->name = malloc(name_length); 51 | string_copy(cat->name, name); 52 | } 53 | ``` 54 | That's right, we'll need to call `malloc` to allocate the appropriate amount of memory to the `cat->name` field, and then we can call `string_copy` in order to copy each character in the input `name` string to the memory that `cat->name` now points to. 55 | 56 | ## Freeing memory 57 | 58 | Yet another abstraction that you're used to from higher-level languages is the garbage collector. A garbage collector is typically part of a languages runtime, and its job is to clean up memory that is used up by your programs. 59 | 60 | For example, after you write some JS program that has a bunch of arrays and objects, what happens to the memory that was needed to order to hold those instances? In a high-level language, a garbage collector goes through and figures out that those arrays and objects are no longer being used. It then deallocates or frees up that memory so that it can be reused by other programs. You the programmer never needs to worry about cleaning up after yourself; it's all handled for you automatically! 61 | 62 | C doesn't baby you around like that. It expects _you_ to handle the clean up like the grown up you are. Learning to clean up after yourself can get difficult when you're writing more advanced programs in C. For simple programs though, it can be as simple as calling the function `free` after you're done with some piece of `malloc`'d memory. 63 | 64 | The `free` function receives a pointer that points to a piece of `malloc`'d memory, and it deallocates it so that that memory may be re-used. As you can imagine, once some `malloc`'d memory has been `free`'d, whatever data was in that chunk of memory is gone, so make sure you're done with the data before `free`ing it! 65 | 66 | Let's codify this into a simple heuristic that isn't too hard to memorize: 67 | > For every call to `malloc` there should be an accompanying call to `free`. 68 | 69 | A couple of common errors that arise when it comes to `free`ing memory are: 70 | 1. Forgetting to call `free` on some `malloc`'d piece of memory. This is a problem because that memory doesn't get cleaned up, and you end up with what's called a 'memory leak'. A single memory leak isn't the end of the world, but if you continually forget to `free` your memory, they'll start to pile up and waste precious memory resources that could be used by other programs that actually need that memory. 71 | 2. Calling `free` on a pointer to memory that has already been `free`'d. This is called a 'double free', and it should be avoided because it may corrupt the state of the memory manager (which is what keeps track of all of the pieces of memory that have been handed out so far), which might cause existing blocks of memory to get corrupted or for future allocations to fail in bizarre ways (for example, the same memory getting handed out on two different successive calls of malloc). 72 | 73 | Going back to our Cat struct, we might want to have a `Cat_destroy` function that will handle the cleaning up of a Cat instance for us. It'll free all the memory that was allocated during the Cat instance's lifetime. 74 | 75 | ```c 76 | void Cat_destroy(struct Cat *cat) 77 | { 78 | if (cat->name != NULL) { 79 | free(cat->name); 80 | } 81 | 82 | if (cat->breed != NULL) { 83 | free(cat->breed); 84 | } 85 | 86 | if (cat != NULL) { 87 | free(cat); 88 | } 89 | } 90 | ``` 91 | 92 | ## The `typedef` Keyword 93 | 94 | In all the previous example code, we've had to prepend every type we created with `struct`. That's because `struct` is part of the newly-created type's name, and as of now, we can't leave it out whenever we're using one of our own artificial types. C provides the `typdef` keyword which will ultimately save us some keystrokes. It will allow us to not have to prepend `struct` to any of our created types. Here's how we use it: 95 | 96 | ```c 97 | typedef struct Cat { 98 | char *breed; 99 | char *color; 100 | int age; 101 | } Cat; 102 | ``` 103 | 104 | We'll add the `typedef` keyword in front of our type definition. Then, after the closing brace of our struct, we specify the aliased type we'd like for this user-created type to have. Here we'll just keep it the same as the name of the struct itself, though you could call it something else if you wanted to. 105 | 106 | Now, whenever we want to use our `Cat` type, we don't need to prepend `struct` in front of it. The compiler now knows that when we just specify `Cat` that we're referring to our `struct Cat` type definition. It's a one-time cost that we pay when we define our types, but it'll save us a lot of keystrokes later on, especially if we're using our types a lot! -------------------------------------------------------------------------------- /pointers/README.md: -------------------------------------------------------------------------------- 1 | # Pointers 2 | 3 | From the perspective of C, your entire computer is nothing more than a giant array of memory into which values can be written. Given this analogy, when we're dealing with arrays, how do we access some value stored inside of said array? 4 | 5 | We do it with indices that point to a specific spot in the array. As it turns out, pointers in C are simply just that: indices into the giant array of memory that is your computer. More formally, a pointer is a memory address that tells the program where to go and find some variable value. 6 | 7 | Those funky asterisks you might have seen already indicate a pointer (you might have heard them referred to as _references_ in other languages). Let's look at the function signature we had for the `reverse_string` function you wrote in the `strings` module: 8 | ```c 9 | char *reverse_string(char s[]) 10 | { 11 | ... 12 | } 13 | ``` 14 | The `char *` that is in the spot where the return type of the function usually goes is saying that this function will return a pointer to an array of characters. 15 | 16 | It turns out that pointers and arrays in C are very much interconnected, so much so that it's pretty difficult to separate the topic of pointers from arrays. Every pointer points to the first spot of a contiguous portion of memory, and as we've already established, C pretty much just treats your computer as a giant array of memory. 17 | 18 | Indeed, there are many similarities between how you work with arrays and how you work with pointers. However, let's first talk about the _differences_ between them. 19 | 20 | ## Differences between arrays and pointers 21 | 22 | Even though C let's you work with pointers and arrays in many of the same ways, don't think of them as synonomous. C's `sizeof` operator allows us to find the size of something in bytes. Let's say for example we had the following array: 23 | ```c 24 | int integers[] = { 9, 49, 1, 6, 10, 15 }; 25 | ``` 26 | Calling `sizeof(integers)` gets us the total amount of data in the array. Then let's say we had a pointer to the same chunk of data: 27 | ```c 28 | int *pointer_to_integers = integers; 29 | ``` 30 | So we have an array named `integers` and a pointer called `pointer_to_integers` that points to the same spot in memory where the `integers` array is stored. If we were to call `sizeof(pointer_to_integers)`, we would actually get back the size of the pointer, not the size of the data it's pointing to. 31 | 32 | There are a few other such sorts of edge cases, but for the most part they're pretty nuanced. So while, again, you shouldn't be thinking of pointers being arrays and vice versa, you would interface with them in many of the same ways. 33 | 34 | ## Declaring pointers 35 | 36 | As showcased above, declaring a pointer is as simple as putting an asterisk after the type declaration of a variable. This signifies that we have a pointer to the declared type with the specified name. 37 | 38 | ```c 39 | /* Declaring two ints x and y, and an int array z */ 40 | int x = 1, y = 2, z[10]; 41 | int *ip; /* ip is a pointer to an int */ 42 | 43 | ip = &x; /* ip now points to x */ 44 | y = *ip; /* y is now 1 */ 45 | *ip = 0; /* x is now 0 */ 46 | ip = &z[0]; /* ip now points to z[0] */ 47 | ``` 48 | 49 | There's nothing new going on in the first two lines of this block. Starting at the third line with `ip = &x;`, we see the `&` operator. This means we're grabbing the _address_ of the variable `x`, or in other words, we're asking for the address where the value of `x` (`1` in this case) is being stored. We do this because `ip` is a pointer, which stores an address, not a value. If we did `ip = x;`, that would be saying "store the value of the variable x in the variable `ip`", which would not compile since we've declared `ip` to be an integer pointer, not an integer value. 50 | 51 | In the next line, `y = *ip`, note that when we use the `*` operator not in a declaration, it signifies that we're want to grab the _value_ of the operand. So we're saying "set the variable `y` to be the value of the pointer `ip`". Again, since `ip` is a pointer, it stores an address. When we prepend the `*` operator in front of a pointer, we're asking for the value that that pointer points to. Put another way, we're indexing into the giant array of memory that is our computer and asking for the value at the given index. 52 | 53 | Next we have `*ip = 0;`. Here we're setting the value that `ip` points at to be 0. Before this, `ip` pointed to whatever `x`'s value was, because of the line `ip = &x;`. So now that we changed the value at some address, any other variables or pointers that also referenced that same address also got changed! If we were to print out the value of the `x` variable now, it would be `0`! 54 | 55 | Lastly, we have `ip = &z[0];`, which declaring that `ip` now points to the first element of the `z` array. Again, we use the `&` operator in order to grab the _address_, not the _value_, of `z[0]`, since `ip` is a pointer that stores an address. 56 | 57 | ## Pointer Arithmetic 58 | 59 | When we index into arrays in JavaScript, we can do things like: 60 | ```javascript 61 | const someArray = []; 62 | 63 | for (let i = 0; i < array.length; i++) { 64 | someArray[i] = array[i + 1]; 65 | } 66 | ``` 67 | We can index into arrays by performing arithmetic on the index. With pointers in C, we can do the exact same thing! 68 | 69 | Let's say we have a pointer to a string like so: 70 | ```c 71 | char *str = "Some string"; 72 | ``` 73 | We can loop through the characters in this string by doing this: 74 | ```c 75 | while (*str != '\0') { 76 | printf("%c", *str); 77 | str++; 78 | } 79 | ``` 80 | This loop will print out each character in the string. Indeed, this loop is pretty much analogous to iterating through an array. More precisely though, on each iteration of this while loop, we're incrementing the spot the pointer points to by one. At the beginning of the loop, `*str` points to the first character in the string, `S`. Then, on the next iteration, it gets incremented and then prints out `o`. This keeps going until the pointer points to the null character, which terminates the loop. 81 | 82 | Armed with this knowledge regarding pointers and pointer arithmetic, we can rewrite the `reverse_string` function from the last module to use pointers instead of allocating additional memory for the reversed string. This has the added benefit of performing the reverse in-place. 83 | ```c 84 | void *reverse_string(char *s) 85 | { 86 | char temp; 87 | int n = string_length(s); 88 | 89 | for (int i = 0; i < n/2; i++) { 90 | temp = s[i]; 91 | s[i] = s[n-i-1]; 92 | s[n-i-1] = temp; 93 | } 94 | } 95 | ``` 96 | 97 | ## Use cases of pointers 98 | 99 | Lastly, let's talk a bit about why pointers are useful. The number 1 most important reason as to why pointers exists, the motivation for their invention in the first place, is that the C compiler needs every type to have a known size at compile time. This is a pretty big restriction, and it's one that comes with the territory of working in a strongly-typed language. 100 | 101 | But there's lots of data that we won't know the size of until runtime. What if we need to accept user input? How do we know the size of that input before the user even gives it to us? What if we need to add data to some data structure at runtime? These are all valid questions, and the workaround to them is pointers. 102 | 103 | We can not tell the compiler the size of certain types upfront, so what we do instead is use something of a known size to refer to things of unknown size. That is exactly what pointers are. They're a type with a known size that tells us how to access something of an unknown size. 104 | 105 | So whenever we need to hold something like a string or a data structure whose size depends on something that can only be known at runtime, you can bet such structures will be referred to by a pointer. 106 | 107 | A slightly related use case is passing by reference vs. passing by value when we're talking about passing parameters to functions. You've probably at least heard of these terms used in other languages. Passing by value means that we're passing a _copy_ of the value to a function as a parameter. This results in additional work and memory overhead, but means we have a fresh copy of the data to work with, which is desirable in certain scenarios. 108 | 109 | On the flip side, passing by reference means we're passing a pointer to the data. In other words, the function doesn't have access to the data itself, but it is able to find that data in memory via the passed-in pointer. There's no need to copy the data, but then that also means the function doesn't have exclusive access to the data either. 110 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # Intro to C FAQ 2 | 3 | ## Contents 4 | 5 | ### Common Errors 6 | 7 | * [`runtests.sh: $'\r': command not found` or `Syntax error: word unexpected (expecting "do")`](#q100) 8 | * [`runtests.sh: 56059 Segmentation fault: 11 $VALGRIND`](#q200) 9 | * [Mac: `malformed object` error when running `make tests`](#q300) 10 | * [What does the "implicit declaration of function" warning mean?](#q1500) 11 | * [What is a segmentation fault and how do I stop it?](#q2100) 12 | * [What's the `incompatible integer to pointer conversion` error?](#q3400) 13 | * [What is an `undefined symbol` linker error?](#q5300) 14 | * [What is "undefined behavior" in C?](#q5000) 15 | * [Should I bother fixing compiler warnings?](#q400) 16 | 17 | ### General C 18 | 19 | * [Can I accidentally destroy my computer running C code?](#q500) 20 | * [Why do we subtract '0' from a char to convert it from ASCII to a numeric value?](#q1100) 21 | * [What's the difference between `puts()`, `fprintf()`, and `printf()`?](#q1600) 22 | * [Why does `025 == 21`?](#q1700) 23 | * [What is the "true dev workflow" in C?](#q1800) 24 | * [Does C have garbage collection?](#q1900) 25 | * [Why is C code faster than other languages?](#q2000) 26 | * [Are variables automatically initialized to zero when I declare them?](#q2500) 27 | * [What VS Code plugins are good for C development?](#q2700) 28 | * [What are some additional C resources?](#q2800) 29 | * [How do I get the debugger working?](#q2900) 30 | * [Does C have closures?](#q3100) 31 | * [If I look at an uninitialized variable, will the garbage in it ever be leftover data from another process?](#q3200) 32 | * [What are `stdin`, `stdout`, and `stderr`?](#q3900) 33 | * [How do I know which header files to `#include` for any particular function?](#q4000) 34 | * [What are bits, bytes, kilobytes, megabytes, and all that?](#q4400) 35 | * [What's the difference between `#include` with double quotes and `#include` with angle brackets?](#q4600) 36 | * [Why does `main()` return `0`? What does the return value mean?](#q5800) 37 | * [Is there a difference between `exit()` and `return`?](#q4800) 38 | * [What is "undefined behavior" in C?](#q5000) 39 | * [How do I write preprocessor macros with `#define`?](#q5200) 40 | * [How do I make my own header files and what do I put in them?](#q5400) 41 | * [How do I make my own Makefile?](#q5500) 42 | * [Why are there so many `printf()` variants? How do I know which one to use?](#q5600) 43 | * [Why is `main()` always at the bottom of the file?](#q5700) 44 | * [Do we have to have a `main()`? Can there be more than one?](#q5900) 45 | * [Can `main()` return `void`? What about `main()` with no parameters?](#q6000) 46 | * [Do we need a semicolon at the end of every line?](#q6100) 47 | 48 | ### Strings 49 | 50 | * [Can you use `+` to concatenate two strings?](#q2400) 51 | * [For string literals like `"Hello"`, are those stored on the stack or heap?](#q3600) 52 | * [Why does `strcmp()` return `0` when strings match? Since `0` means "false" in C, that seems backwards.](#q4900) 53 | 54 | ### Structs 55 | 56 | * [Is a `struct` comparable to something in Python or JS? Is it like a class?](#q600) 57 | * [Can you have default parameters in the structs?](#q700) 58 | * [Why do functions tend to return pointers to structs, and not just copies of the struct?](#q1000) 59 | 60 | ### Types 61 | 62 | * [Why does unsigned char type accept a number when it's clearly referring to a character?](#q800) 63 | * [What's the difference between a `float` and a `double`, or between an `int` and a `long`?](#q2300) 64 | * [What type should I use to hold numbers bigger than an `int` can hold?](#q2600) 65 | * [When do I have to explicitly cast a type to another type?](#q4100) 66 | * [In C, can we assume an `int` is 32 bits?](#q4500) 67 | 68 | ### Arrays 69 | 70 | * [When I pass an array as an argument to a function, when do I use pointer notation and when do I use array notation ?](#q900) 71 | 72 | ### Pointers 73 | 74 | * [When I pass an array as an argument to a function, when do I use pointer notation and when do I use array notation ?](#q900) 75 | * [Why do functions tend to return pointers to structs, and not just copies of the struct?](#q1000) 76 | * [When do I need a pointer to a pointer?](#q1200) 77 | * [Do other languages use pointers?](#q1300) 78 | * [What's the difference between "`int *x`" and "`int* x`"?](#q1400) 79 | * [How do I print a pointer with `printf`?](#q3000) 80 | * [How many levels of indirection can you have with pointers? `int******`?](#q3300) 81 | * [What's the `incompatible integer to pointer conversion` error?](#q3400) 82 | * [Should I declare a pointer to a thing, or just declare the thing?](#q4700) 83 | * [When you free a pointer, does it get set to `NULL` automatically?](#q5100) 84 | * [Can a pointer pointer to more than one thing? What about to arrays and `struct`s?](#q6200) 85 | * [If variables are stored in memory, where are pointers stored?](#q6300) 86 | 87 | ### Heap, Stack, Dynamic Memory 88 | 89 | * [What happens if my program exits but I forgot to `free()` some memory I allocated?](#q2200) 90 | * [Are there any other ways besides `malloc()` to store things on the heap?](#q3500) 91 | * [Is the C stack like the stack data structure?](#q3700) 92 | * [Is the C heap like a binary heap data structure?](#q3800) 93 | * [Is `realloc()` the same as calling `malloc()`, copying the data over, then calling `free()` on the original pointer?](#q4200) 94 | * [What happens if I `free()` a `NULL` pointer?](#q4300) 95 | * [When you free a pointer, does it get set to `NULL` automatically?](#q5100) 96 | 97 | ## Questions 98 | 99 | 100 | ### `runtests.sh: $'\r': command not found` or `Syntax error: word unexpected (expecting "do")` 101 | 102 | If you see this error: 103 | 104 | ```shell 105 | Running unit tests: 106 | : not foundtests.sh: 2: ./tests/runtests.sh: 107 | ./tests/runtests.sh: 4: ./tests/runtests.sh: Syntax error: word unexpected (expecting "do") 108 | ``` 109 | 110 | You have two options: 111 | 112 | 1. Open the file `tests/runtests.sh` in VS Code in whatever subproject folder 113 | you’re working in, e.g. `fizzbuzz`. Click on the lower right of the screen 114 | where it says `CRLF`. Choose `LF`. Save the file. Then the error should go 115 | away. 116 | 117 | 2. You can do this from the command line with the `tr` command: 118 | 119 | ```shell 120 | cd tests 121 | cat runtests.sh | tr -d '\r' > runtests.tmp 122 | mv runtests.tmp runtests.sh 123 | ``` 124 | 125 | The root of the problem is a setting in git that causes all newlines (LF) to 126 | be converted to carriage-return/newline (CRLF). The script `runtests.sh` is a 127 | _bash script_ that bash runs, and bash *hates* `\r` and pukes everywhere. 128 | 129 | To cause git to _not_ do newline conversion for future clones, run the 130 | following: 131 | 132 | ``` 133 | git config --global core.autocrlf false 134 | ``` 135 | 136 | ------------------------------------------------------------------------ 137 | 138 | 139 | ### `runtests.sh: 3: 56059 Segmentation fault: 11 $VALGRIND` 140 | 141 | This means you got a segfault in your program. See [What is a segmentation fault 142 | and how do I stop it?](#what-is-segfault) 143 | 144 | ------------------------------------------------------------------------ 145 | 146 | 147 | ### Mac: `malformed object` error when running `make tests` 148 | 149 | This is caused by an older version of the `ar` and `ranlib` packages being installed. 150 | Sometimes these conflict with the versions installed with xcode. 151 | 152 | If running MacPorts: 153 | 154 | ``` 155 | sudo port selfupdate 156 | sudo port upgrade cctools 157 | ``` 158 | 159 | If running Brew: 160 | 161 | ``` 162 | sudo brew update 163 | sudo brew upgrade gcc 164 | ``` 165 | 166 | ------------------------------------------------------------------------ 167 | 168 | 169 | ### Should I bother fixing compiler warnings? 170 | 171 | YES! 172 | 173 | In C, a warning is the compiler saying, "I can build that, and I will, but it's 174 | probably going to do something really messed up that you don't want." 175 | 176 | There are only a few warnings you can safely ignore. 177 | 178 | If you're _absolutely sure_ the "unused variable" warning is OK, then you could ignore it. Or, better, add a line of code that silences the warning: 179 | 180 | ```c 181 | void foo(int a) 182 | { 183 | (void)a; // Do nothing, but at least the compiler will be quiet 184 | ``` 185 | 186 | ------------------------------------------------------------------------ 187 | 188 | 189 | ### Can I accidentally destroy my computer running C code? 190 | 191 | Nope! Not with a modern OS. 192 | 193 | If you're running MS-DOS, then sure, you can do all kinds of things. 194 | I once accidentally blew away all my BIOS settings with a program I 195 | wrote and my computer wouldn't boot. 196 | 197 | But Windows, Linux, macOS, BSD, or any other mainstream OS from this century all 198 | offer memory and resource protection that prevents you from changing memory 199 | you're not supposed to, or wiping out a disk you're not supposed to, etc. 200 | 201 | The worst you'll see is a `Segmentation fault` message which means your program 202 | tried to do something bad and the OS killed it. 203 | 204 | ------------------------------------------------------------------------ 205 | 206 | 207 | ### Is a `struct` comparable to something in Python or JS? Is it like a class? 208 | 209 | It's like a class, except with only data (fields, properties) attached to it. 210 | There are no methods (functions) associated with it. 211 | 212 | If you _really_ want to pretend that you have methods on a `struct`, you can add 213 | them as fields that are _pointers to functions_. The syntax is pretty obtuse, 214 | and it's not a natural or idiomatic thing to do in C. 215 | 216 | Example: 217 | 218 | ```c 219 | struct animal { 220 | char *name; 221 | 222 | // make_sound is a pointer to a function with no parameters that returns void 223 | void (*make_sound)(void); 224 | } 225 | 226 | // Note how bleat() matches the signature for make_sound(), above 227 | void bleat(void) 228 | { 229 | printf("Baaaahhhh!\n"); 230 | } 231 | 232 | int main(void) 233 | { 234 | struct animal goat; 235 | 236 | // C doesn't have the concept of a constructor, so we have to do it by hand: 237 | 238 | goat.name = "goat"; 239 | goat.make_sound = bleat; 240 | 241 | // Call the "method": 242 | 243 | goat.make_sound(); // Baaaahhhh! 244 | } 245 | ``` 246 | 247 | ------------------------------------------------------------------------ 248 | 249 | 250 | ### Can you have default parameters in the structs? 251 | 252 | No. The best you can do is have a helper function set the defaults. 253 | 254 | ```c 255 | void foo_default(struct foo *f) 256 | { 257 | f->a = 10; // Set defaults 258 | f->b = 20; 259 | f->c = 30; 260 | } 261 | ``` 262 | 263 | ```c 264 | struct foo x; 265 | 266 | foo_default(&x); // Set defaults 267 | 268 | x.a = 99; // Override default 269 | ``` 270 | 271 | When you declare a `struct`, you can also use an initializer to set the field 272 | values: 273 | 274 | ```c 275 | struct foo x = { .a = 10, .b = 20, .c = 30 }; 276 | ``` 277 | 278 | ------------------------------------------------------------------------ 279 | 280 | 281 | ### Why does unsigned char type accept a number when it's clearly referring to a character? 282 | 283 | Deep down, computers just deal in numbers (`1`s and `0`s). They don't know what 284 | a character is. We humans have come up with a system wherein a number 285 | _represents_ a certain character. For example, we've agreed that `A` is `65`. 286 | 287 | (For information on what number represents what character, look up more detail 288 | on [ASCII](https://en.wikipedia.org/wiki/ASCII) encoding, or its modern superset 289 | [UTF-8](https://en.wikipedia.org/wiki/UTF-8).) 290 | 291 | With that in mind, C really only deals in numbers. Even when you put a character 292 | in single quotes, it's still just a number. The only difference is in how we 293 | _interpret_ that number. That is, is it a value, like 65, or is it a character, 294 | like `A`? 295 | 296 | ```c 297 | unsigned char c = 'A'; 298 | 299 | printf("%c\n", c); // Prints "A" 300 | printf("%d\n", c); // Prints 65 301 | ``` 302 | 303 | ```c 304 | unsigned char c = 'A'; 305 | int x = c + 10; 306 | 307 | printf("%d", x); // Prints 75, since 'A' == 65 308 | ``` 309 | 310 | In C, whenever you have a character in single quotes like `'A'`, the compiler 311 | treats it just like you'd put the number `65` there. (Or `66` for `'B'`, and so 312 | on.) 313 | 314 | The only difference between `unsigned char` and `unsigned int` is the number of 315 | bytes that are used to represent the number. A `char` is onGe byte, and an `int` 316 | is typically 4 bytes (but not always). 317 | 318 | You can think of these additional bytes as analogous to adding more digits to 319 | your numbers. The more digits you have, the more range you can store. Two 320 | decimal digits only gets you from 0 to 99, but 8 digits gets you from 0 to 321 | 99999999. Similarly, one byte only gets you from 0 to 255, but 4 bytes gets you 322 | from 0 to 4,294,967,295. 323 | 324 | If you never needed numbers larger than 255, you could use `unsigned char` for 325 | all your variables! (But since modern computers are at least as fast with `int`s 326 | as they are with `char`s, people just use `int`s.) 327 | 328 | ------------------------------------------------------------------------ 329 | 330 | 331 | ### When I pass an array as an argument to a function, when do I use pointer notation and when do I use array notation ? 332 | 333 | It's a little-known FunFact that C doesn't actually pass entire arrays to 334 | functions. It only passes pointers to the first element in that array. 335 | 336 | ```c 337 | int a[2000]; 338 | 339 | // "a" is a pointer to the first element in the array. 340 | // It's the same as &(a[0]). 341 | foo(a); 342 | ``` 343 | 344 | So when you declare your function, you can do any of these: 345 | 346 | ```c 347 | void foo(int *a) 348 | ``` 349 | ```c 350 | void foo(int a[]) 351 | ``` 352 | ```c 353 | void foo(int a[1]) 354 | ``` 355 | ```c 356 | void foo(int a[2000]) 357 | ``` 358 | ```c 359 | void foo(int a[999999999]) 360 | ``` 361 | 362 | and it treats them all as if you'd used: 363 | 364 | ```c 365 | void foo(int *a) 366 | ``` 367 | 368 | There's a difference if you want to use multidimensional arrays. You must 369 | declare all the dimensions except the first one, which is optional. The compiler 370 | needs to know the other dimensions so it can do its array indexing computations 371 | correctly. 372 | 373 | ```c 374 | int foo(int x[][30]) // 30 wide 375 | { 376 | return x[2][4]; 377 | } 378 | 379 | int main(void) 380 | { 381 | int a[10][30]; // 30 wide 382 | 383 | foo(a); 384 | ``` 385 | 386 | This only applies for multidimensional arrays. For 1-dimensional arrays, the 387 | rule still applies; you still need to specify all dimensions except the first 388 | one... but since there is only one, you never need to specify it. 389 | 390 | ------------------------------------------------------------------------ 391 | 392 | 393 | ### Why do functions tend to return pointers to structs, and not just copies of the struct? 394 | 395 | It's possible to do this: 396 | 397 | ```c 398 | struct foo my_func(void) 399 | { 400 | struct foo f; 401 | 402 | f.x = 10; 403 | 404 | return f; // Return a copy of f 405 | } 406 | ``` 407 | 408 | as opposed to: 409 | 410 | ```c 411 | struct foo *my_func(void) 412 | { 413 | struct foo *p = malloc(sizeof(struct foo)); 414 | 415 | p->x = 10; 416 | 417 | return p; // Return a copy of p 418 | } 419 | ``` 420 | 421 | But in C, it's more idiomatic to return a copy of the pointer to the memory 422 | allocated than it is to return a copy of the `struct` itself. 423 | 424 | Part of the reason for this is that it takes time to copy data. A `struct` can 425 | be very large depending on how many fields it has in it, but your average 426 | pointer is only 8 bytes. 427 | 428 | Since every time you `return` a thing, a copy of that thing gets made, it is 429 | faster to copy a pointer than it is to copy a `struct` of any non-trivial size. 430 | 431 | Finally, note that this variant always invokes undefined behavior and should 432 | never be used: 433 | 434 | ```c 435 | struct foo *my_func(void) 436 | { 437 | struct foo f; 438 | 439 | f.x = 10; 440 | 441 | return &f; // Return a copy of a pointer to f 442 | } 443 | ``` 444 | 445 | The reason is because `f` vaporizes as soon as the function returns (since it's 446 | just a local variable), so any pointers to it are invalid. 447 | 448 | ------------------------------------------------------------------------ 449 | 450 | 451 | ### Why do we subtract '0' from a char to convert it from ASCII to a numeric value? 452 | 453 | The code typically looks like this: 454 | 455 | ```c 456 | char c = '2'; // ASCII '2' 457 | 458 | int v = c - '0'; // Convert into numeric value 2 459 | 460 | printf("%d\n", v); // prints decimal 2 461 | ``` 462 | 463 | Remember that in C, a `char` is like a small `int`, and when you have a 464 | character in single quotes like `'2'`, C replaces that with the 465 | [ASCII](https://en.wikipedia.org/wiki/ASCII) value of that character. 466 | 467 | In the case of our example, the ASCII value of `'2'` is `50`. And we want to 468 | convert that to the numeric value `2`. So we clearly have to subtract `48` from 469 | it, since `50 - 48 = 2`. But why the `'0'`, then? 470 | 471 | Here's part of the ASCII table, just the numbers: 472 | 473 | |Character|ASCII value| 474 | |:-------:|:---------:| 475 | | `'0'` | 48 | 476 | | `'1'` | 49 | 477 | | `'2'` | 50 | 478 | | `'3'` | 51 | 479 | | `'4'` | 52 | 480 | | `'5'` | 53 | 481 | | `'6'` | 54 | 482 | | `'7'` | 55 | 483 | | `'8'` | 56 | 484 | | `'9'` | 57 | 485 | 486 | It's no coincidence it's done this way. Turns out that if you subtract `48` from 487 | any ASCII character that is a digit, you'll end up with the numeric value of 488 | that ASCII character. 489 | 490 | Example: `'7'` is value `55` (from the table), compute `55 - 48` and you get 491 | `7`. 492 | 493 | And since `'0'` is `48`, it's become idiomatic in C to convert ASCII digits to 494 | values by subtracting `'0'` from them. 495 | 496 | ------------------------------------------------------------------------ 497 | 498 | 499 | ### When do I need a pointer to a pointer? 500 | 501 | There are a few reasons you might need one, but the most common is when you pass 502 | a pointer to a function, and the function needs to modify the pointer. 503 | 504 | Let's take a step back and see when you just need to use a pointer. 505 | 506 | ```c 507 | void foo(int a) 508 | { 509 | a = 12; 510 | } 511 | 512 | int main(void) 513 | { 514 | int x = 30; 515 | 516 | printf("%d\n", x); // prints 30 517 | 518 | foo(x); 519 | 520 | printf("%d\n", x); // prints 30 again--not 12! Why? 521 | } 522 | ``` 523 | 524 | In the above example, `foo()` wants to modify the value of `x` back in main. 525 | But, alas, it can only modify the value of `a`. When you call a function, all 526 | arguments get _copied_ into their respective parameters. `a` is merely a _copy_ 527 | of `x`, so modifying `a` has no effect on `x`. 528 | 529 | What if we want to modify `x` from `foo()`, though? This is where we have to use 530 | a pointer. 531 | 532 | ```c 533 | void foo(int *a) 534 | { 535 | *a = 12; // Set the thing `a` points at to 12 536 | } 537 | 538 | int main(void) 539 | { 540 | int x = 30; 541 | 542 | printf("%d\n", x); // prints 30 543 | 544 | foo(&x); 545 | 546 | printf("%d\n", x); // prints 12! 547 | } 548 | ``` 549 | 550 | In this example, `foo()` gets a copy of a pointer to `x`. (Everything gets 551 | copied into the parameters when you make a call, even pointers.) 552 | 553 | Then it changes the thing the pointer points to to `12`. That pointer was 554 | pointing to `x` back in main, so it changes `x`'s value to `12`. 555 | 556 | Great! 557 | 558 | So what about pointers to pointers? It's the same idea. Let's do a broken 559 | example: 560 | 561 | ```c 562 | void alloc_ints(int *p, int count) 563 | { 564 | p = malloc(sizeof(int) * count); // Allocate space for ints 565 | } 566 | 567 | int main(void) 568 | { 569 | int *q = NULL; 570 | 571 | alloc_ints(q, 10); // Alloc space for 10 ints 572 | 573 | printf("%p\n", q); // Prints NULL still!! 574 | 575 | q[2] = 10; // UNDEFINED BEHAVIOR, CRASH? 576 | } 577 | ``` 578 | 579 | What happened? 580 | 581 | When we call `alloc_ints()`, a _copy_ of `q` is made in `p`. We then assign into 582 | `p` with the `malloc()`, but since `p` is just a copy of `q`, `q` is unaffected. 583 | 584 | It's just like our first version of `foo()`, above. 585 | 586 | Solution? We need to pass a pointer to `q` to `alloc_ints()` so that 587 | `alloc_ints()` can modify the value of `q`. 588 | 589 | But `q` is already a pointer! It's an `int *`! So when we take the address-of it 590 | (AKA get a pointer to it), we'll end up with a pointer to a pointer, or an `int 591 | **`! 592 | 593 | ```c 594 | void alloc_ints(int **p, int count) 595 | { 596 | // Allocate space for ints, store the result in the thing that 597 | // `p` points to, namely `q`: 598 | 599 | *p = malloc(sizeof(int) * count); 600 | } 601 | 602 | int main(void) 603 | { 604 | int *q = NULL; 605 | 606 | alloc_ints(&q, 10); // Alloc space for 10 ints 607 | 608 | printf("%p\n", q); // Prints some big number, good! 609 | 610 | q[2] = 10; // works! 611 | } 612 | ``` 613 | 614 | Success! 615 | 616 | ------------------------------------------------------------------------ 617 | 618 | 619 | ### Do other languages use pointers? 620 | 621 | Most all of them do, but some are more explicit about it than others. In 622 | languages like Go, C, C++, and Rust, you have to use the proper operators when 623 | using pointers and references. 624 | 625 | But languages like JavaScript and Python do a lot of that stuff behind your 626 | back. Take this Python example: 627 | 628 | ```python 629 | class Foo: 630 | def __init__(self, x): 631 | self.x = x 632 | 633 | def bar(a): 634 | a.x = 12 # Sets `f.x` to 12--why? 635 | 636 | a = None # Does NOT destroy `f`--why not? 637 | 638 | 639 | f = Foo(2) 640 | 641 | print(f.x) # Prints 2 642 | 643 | bar(f) 644 | 645 | print(f.x) # Prints 12--why? 646 | ``` 647 | 648 | Let's look what happened there. We made a new object `f`, and we passed that 649 | object to function `bar()`, which modified its `x` property. 650 | 651 | After enough time with Python, we learn that it passes objects _by reference_. 652 | This is another way of saying it's using pointers behind your back. Behind the 653 | scenes in Python, `a` is a pointer to `f`. 654 | 655 | That's why when we modify `a.x`, it actually modifies `f.x`. 656 | 657 | And it's also why when we set `a` to `None`, it doesn't change `f` at all. `a` 658 | is just a pointer to `f`, not `f` itself. 659 | 660 | Let's look at the C version of that Python program. This works exactly the same way: 661 | 662 | ```c 663 | #include 664 | 665 | struct foo { 666 | int x; 667 | }; 668 | 669 | void bar(struct foo *a) 670 | { 671 | a->x = 12; // Sets f.x to 12--why? 672 | 673 | a = NULL; // Does NOT destroy `f`--why not? 674 | } 675 | 676 | int main(void) 677 | { 678 | struct foo f = { 2 }; 679 | 680 | printf("%d\n", f.x); // Prints 2 681 | 682 | bar(&f); 683 | 684 | printf("%d\n", f.x); // Prints 12--why? 685 | } 686 | ``` 687 | 688 | `a` is a pointer to `f`. So we when do `a->x`, we're saying "set the `x` 689 | property on the thing that `a` points to". 690 | 691 | And when we set `a` to `NULL`, it's just modifying `a`, not the thing that `a` 692 | points to (namely `f`). 693 | 694 | ------------------------------------------------------------------------ 695 | 696 | 697 | ### What's the difference between "`int *x`" and "`int* x`"? 698 | 699 | Syntactically, nothing. They're equivalent. 700 | 701 | That said, the recommendation is that you use the form `int *x`. 702 | 703 | Here's why. These two lines are equivalent: 704 | 705 | ```c 706 | int* x, y; 707 | ``` 708 | 709 | ```c 710 | int *x, y; 711 | ``` 712 | 713 | In both of them, `x` is type `int*`, and `y` is type `int`. But by putting the 714 | asterisk right next to the `int`, it makes it look like both `x` and `y` are of 715 | type `int*`, when in fact only `x` is. 716 | 717 | If we reverse the order of `x` and `y`, we must necessarily move the asterisk 718 | with `x`: 719 | 720 | ```c 721 | int y, *x; // Also equivalent to the previous two examples 722 | ``` 723 | 724 | It's idiomatic to keep the asterisk tucked up next to the variable that's the 725 | pointer. 726 | 727 | ------------------------------------------------------------------------ 728 | 729 | 730 | ### What does the "implicit declaration of function" warning mean? 731 | 732 | This is the compiler saying "Hey, you're calling a function but I haven't seen a 733 | declaration for that function yet." Basically you're calling a function before 734 | you've declared it. 735 | 736 | If you're calling a library function like `printf()` or a syscall like `stat()`, 737 | the most common cause of this warning is failure to `#include` the header file 738 | associated with that function. Check the `man` page for exactly which. 739 | 740 | But what if you're getting the error on one of your own functions? Again, it 741 | means you're calling that function before you've declared it. 742 | 743 | But what does _declared_ mean? 744 | 745 | A declaration can either be a function definition, or a function prototype. 746 | 747 | Let's look at a broken example: 748 | 749 | ```c 750 | #include 751 | 752 | int main(void) 753 | { 754 | foo(); // Implicit declaration warning!! 755 | } 756 | 757 | void foo(void) 758 | { 759 | printf("Foo!\n"); 760 | } 761 | ``` 762 | 763 | In that example, `main()` calls `foo()`, but the compiler hasn't seen a 764 | declaration of `foo()` yet. We can fix it by defining `foo()` _before_ `main()`: 765 | 766 | ```c 767 | #include 768 | 769 | // Just moved foo()'s definition before main(), that's all 770 | 771 | void foo(void) 772 | { 773 | printf("Foo!\n"); 774 | } 775 | 776 | int main(void) 777 | { 778 | foo(); // No problem! 779 | } 780 | ``` 781 | 782 | You can also use a _function prototype_ to declare a function before it is used, 783 | like so: 784 | 785 | ```c 786 | #include 787 | 788 | void foo(void); // This is the prototype! It's a declaration of foo(). 789 | 790 | int main(void) 791 | { 792 | foo(); // No problem 793 | } 794 | 795 | void foo(void) // This is the definition of foo() 796 | { 797 | printf("Foo!\n"); 798 | } 799 | ``` 800 | 801 | Prototypes for functions that are callable from other source files typically 802 | go in header files, and then those other source files `#include` them. 803 | 804 | For functions that aren't used outside the current `.c` file (e.g. little helper 805 | functions that no other file will even need to call), those usually are either 806 | defined at the top of the file before their first call. If that's inconvenient, 807 | a prototype can be placed at the top of the `.c` file, instead. 808 | 809 | ------------------------------------------------------------------------ 810 | 811 | 812 | ### What's the difference between `puts()`, `fprintf()`, and `printf()`? 813 | 814 | `puts()` simply outputs a string. It does no formatting of variables. Its only 815 | argument is a single string. Additionally, it prints a newline at the end for 816 | you. 817 | 818 | ```c 819 | // This prints "Hello, world %d!" and then a newline: 820 | puts("Hello, world %d!"); 821 | ``` 822 | 823 | `printf()` does formatted output of variables, and strings as well. It's a 824 | superset of `puts()`, in that way. 825 | 826 | ```c 827 | int x = 12; 828 | 829 | // This prints "Hello, world 12!\n": 830 | printf("Hello, world %d!\n", x); 831 | ``` 832 | 833 | `fprintf()` is just like `printf()`, except it allows you to print to an open file. 834 | 835 | ```c 836 | FILE *fp = fopen("foo.txt", "w"); 837 | int x = 12; 838 | 839 | // This writes "Hello, world 12!\n" to the file "foo.txt": 840 | fprintf(fp, "Hello, world %d!\n", x); 841 | ``` 842 | 843 | Incidentally, there's already a file open for you called `stdout` (_standard 844 | output_) which normally prints to the screen. These two lines are equivalent: 845 | 846 | ```c 847 | printf("Hello, world!\n"); 848 | fprintf(stdout, "Hello, world!\n"); // Same thing! 849 | ``` 850 | 851 | There's another already-opened file called `stderr` (_standard error_) that is 852 | typically used to print error messages. Example: 853 | 854 | ```c 855 | if (argc != 2) { 856 | fprintf(stderr, "You must specify a command line argument!\n"); 857 | exit(1); 858 | } 859 | ``` 860 | 861 | ------------------------------------------------------------------------ 862 | 863 | 864 | ### Why does `025 == 21`? 865 | 866 | In C, any time you have a plain leading `0` on front of a number, the compiler 867 | thinks your number is _base-8_ or _octal_. 868 | 869 | Converting `025` to decimal can be done like so: 870 | 871 | `2*8 + 5*1 = 16 + 5 = 21` 872 | 873 | Octal is rarely used in practice, and it's common for new C programmers to put 874 | `0` in front of a number in error. 875 | 876 | One of the last common places to see octal numbers is in [Unix file 877 | permissions](https://en.wikipedia.org/wiki/File_system_permissions#Numeric_notation). 878 | 879 | ------------------------------------------------------------------------ 880 | 881 | 882 | ### What is the "true dev workflow" in C? 883 | 884 | There is none. 885 | 886 | Initially, it was in a Unix-like system probably using 887 | [Makefiles](https://en.wikipedia.org/wiki/Makefile) to build the software. This 888 | is the system we use at Lambda. 889 | 890 | And modern C development under Unix still follows this pattern, except maybe 891 | using [autotools](https://en.wikipedia.org/wiki/GNU_Build_System) or 892 | [CMake](https://en.wikipedia.org/wiki/CMake). 893 | 894 | But dev for specific platforms like Windows probably happens in [Visual 895 | Studio](https://en.wikipedia.org/wiki/Microsoft_Visual_Studio) instead of using 896 | `make` and the rest of it. 897 | 898 | ------------------------------------------------------------------------ 899 | 900 | 901 | ### Does C have garbage collection? 902 | 903 | Nope! 904 | 905 | When it comes to freeing up memory that is no longer needed by the program, 906 | there are basically two schools of thought: 907 | 908 | * Have the programmer manually manage that memory by explicitly allocating and 909 | freeing it. (C's `malloc()` and `free()` functions.) 910 | * Have the runtime automatically manage all that for you. (Garbage collection, 911 | automatic reference counting, etc.) 912 | 913 | C is too low-level to automatically manage memory usage for you. 914 | 915 | One exception is that C automatically allocates and frees _local variables_ just 916 | like other languages you're used to. You don't have to explicitly call `free()` 917 | for locals (and it's an error to do so). You must call free for any and all 918 | pointers to data that you got back from `malloc()` when you're done with them. 919 | 920 | Also, when a program exits, all memory associated with it is freed by the OS, 921 | whether locals or `malloc()`d data. 922 | 923 | ------------------------------------------------------------------------ 924 | 925 | 926 | ### Why is C code faster than other languages? 927 | 928 | The big thing is _interpreted_ versus _compiled_. 929 | 930 | Python and JavaScript are interpreted languages, which means another program 931 | runs your program. It's software running software. So you run python code with 932 | the `python` program and JavaScript code with `node`, for example. 933 | 934 | So in that case, we have the CPU running `python`, and the Python running your 935 | Python program. Python is the middleman, and that takes execution time. 936 | 937 | C is a compiled language. The compiler takes your C code, and produces machine 938 | code. The CPU runs it directly. No middleman, so it's faster. 939 | 940 | But other languages are compiled (like Go, Swift, Rust, C++, and so on). Why is 941 | C faster than them, typically? 942 | 943 | It's because C is a no-frills, minimalist language. The code you write in C is 944 | actually quite close to the machine code that gets produced by the compiler, so 945 | it doesn't have to do a lot of things behind your back. 946 | 947 | Additionally, people have been working on optimizing the output from C compilers 948 | for over 45 years. That's a big head start over other languages. 949 | 950 | ------------------------------------------------------------------------ 951 | 952 | 953 | ### What is a segmentation fault and how do I stop it? 954 | 955 | It means you've accessed some memory you weren't supposed to. The OS killed your 956 | process to prevent it from doing so. 957 | 958 | The trick is to find the line that's causing the problem. If you get a debugger 959 | installed, this can really help. 960 | 961 | In lieu of that, use well-positioned `printf` calls to figure out what the last 962 | thing your program does before it crashes. 963 | 964 | The bug almost certainly has to do with pointers or arrays (which are just 965 | pointers behind syntactic sugar). 966 | 967 | Maybe you're accessing a `NULL` pointer, or an array out of bounds, or modifying 968 | something you're not allowed to modify. 969 | 970 | ------------------------------------------------------------------------ 971 | 972 | 973 | ### What happens if my program exits but I forgot to `free()` some memory I allocated? 974 | 975 | All memory associated with a process is freed when the program exits, even if 976 | you forgot to `free()` it. 977 | 978 | It's considered shoddy programming to not `free()` all the things you 979 | `malloc()`d, though. The OS will free it, but it's bad style to rely on that. 980 | 981 | ------------------------------------------------------------------------ 982 | 983 | 984 | ### What's the difference between a `float` and a `double`, or between an `int` and a `long`? 985 | 986 | It's all about the range of numbers you want to be able to store. 987 | 988 | `double` can hold a more precise number than a `float`. 989 | 990 | A `float` might only be precise up to `3.14159`, but a `double` could hold 991 | `3.1416925358979`, for example. 992 | 993 | Likewise, an `int` might only be able to hold numbers up to 2 billion or so, but 994 | a `long` could hold much larger numbers. 995 | 996 | Use as little as you need. If a `float` or `int` can do the job, use them. If 997 | you need more precision or larger numbers, step up to the next larger type. 998 | 999 | ------------------------------------------------------------------------ 1000 | 1001 | 1002 | ### Can you use `+` to concatenate two strings? 1003 | 1004 | No. 1005 | 1006 | The reason is that strings are represented as `char*` types, and adding two 1007 | `char*`s together is not a defined operation in C. 1008 | 1009 | Use the `strcat()` function in `` to concatenate one string onto 1010 | another. 1011 | 1012 | ------------------------------------------------------------------------ 1013 | 1014 | 1015 | ### Are variables automatically initialized to zero when I declare them? 1016 | 1017 | No. 1018 | 1019 | Always explicitly initialize your variables, whether they be pointers or regular 1020 | types. If you don't, random garbage will be in them when you use them. 1021 | 1022 | > Exception: local variable declared with `static` storage class (this concept 1023 | > is out of scope for Lambda) and global variables get initialized to zero 1024 | > automatically. But it's still good form to explicitly initialize them. 1025 | 1026 | ------------------------------------------------------------------------ 1027 | 1028 | 1029 | ### What type should I use to hold numbers bigger than an `int` can hold? 1030 | 1031 | If you don't need negative numbers, try `unsigned int`. 1032 | 1033 | If that's not enough, try `long`. 1034 | 1035 | If that's not big enough, try `long long` (yes, that's a real thing). 1036 | 1037 | If those aren't enough, try `unsigned long long`. 1038 | 1039 | If you just need big numbers, but not a lot of precision, you can use `double` or `long double`. 1040 | 1041 | If you need big numbers _and_ a lot of precision _and_ none of the above are big 1042 | enough, check out the [GNU Multiple Precision library](https://gmplib.org/). It 1043 | does arbitrary precision arithmetic to as much precision as you have RAM. 1044 | 1045 | ------------------------------------------------------------------------ 1046 | 1047 | 1048 | ### What VS Code plugins are good for C development? 1049 | 1050 | "C/C++ IntelliSense, debugging, and code browsing" by Microsoft is a good one. 1051 | 1052 | ------------------------------------------------------------------------ 1053 | 1054 | 1055 | ### What are some additional C resources? 1056 | 1057 | A great C book is _The C Programming Language_ Second Edition, by Kernighan [the 1058 | "g" is silent] and Ritchie. It's affectionately referred to simply as _K&R2_. 1059 | 1060 | A less great book that is free online is [Beej's Guide to C 1061 | Programming](https://beej.us/guide/bgc/). 1062 | 1063 | A good, comprehensive FAQ is the [comp.lang.c FAQ](http://c-faq.com/). 1064 | 1065 | There's no "one true source" of C info online, unfortunately. 1066 | 1067 | Googling `printf example`, for example, will get you good results. 1068 | 1069 | Googling `man printf` will bring up the `man` page for `printf`. 1070 | 1071 | ------------------------------------------------------------------------ 1072 | 1073 | 1074 | ### How do I get the debugger working? 1075 | 1076 | The commonly-used debugger is called `gdb` (GNU Debugger). 1077 | 1078 | Lambda's own Brian Ruff got it working on the Mac, and made a 1079 | [video](https://youtu.be/BgMOqjdpy5Y) covering it. 1080 | 1081 | [These 1082 | instructions](https://www.thegeekstuff.com/2010/03/debug-c-program-using-gdb/) 1083 | are reported good for WSL on Windows. 1084 | 1085 | [The CS Wiki 1086 | page](https://github.com/BloomInstituteOfTechnology/CS-Wiki/wiki/C-and-Cpp-Debugging-in-VS-Code) 1087 | might help, but it's slightly outdated since VS Code is in heavy development. 1088 | 1089 | [This video](https://www.youtube.com/watch?v=aWIs6Kv1MvE) is reported good, as 1090 | well. 1091 | 1092 | If you're not seeing program output in the `Output` tab, try adding this to your 1093 | `launch.json`: 1094 | 1095 | ```json 1096 | "externalConsole": true 1097 | ``` 1098 | 1099 | We recommend Googling for `vscode gdb setup macos`, substituting whatever 1100 | platform you're on for `macos` and setting the search date range to be recent. 1101 | 1102 | ------------------------------------------------------------------------ 1103 | 1104 | 1105 | ### How do I print a pointer with `printf`? 1106 | 1107 | Use the `%p` format specifier. This will print the value of the pointer (i.e. 1108 | the memory address), not what it's pointing to (i.e. the value stored at that 1109 | memory address.) 1110 | 1111 | In practice, pointers are rarely printed except for debugging. 1112 | 1113 | ------------------------------------------------------------------------ 1114 | 1115 | 1116 | ### Does C have closures? 1117 | 1118 | No, not in the way that you're used to from higher-level languages. What C does have that essentially acts as a poor man's closure is function pointers. These are literally what their name implies: pointers to functions. 1119 | 1120 | The standard library implementation of the quicksort sorting algorithm provides a good example of this. It's function signature is 1121 | ``` 1122 | void qsort(void *base, size_t nitems, size_t size, int (*compare)(const void *, const void *)) 1123 | ``` 1124 | 1125 | The parameter to notice is the `int (*compare)(const void *, const void *)`. This is a function pointer, mainly denoted by the asterisk in front of the function name all wrapped in parentheses and followed by another set of parentheses containing a parameter signature. 1126 | 1127 | This signature is saying that the `qsort` function, in addition to the other parameters it receives, also receives a pointer to a function that receives two `const void *`s and returns an `int`. 1128 | 1129 | Function pointers are used to fulfill the same use case as closures. They can be used to pass some dynamic logic into another function. In the case of `qsort`, the function pointer points to a function that specifies how the comparison for the sorting should be done. 1130 | 1131 | ------------------------------------------------------------------------ 1132 | 1133 | 1134 | ### If I look at an uninitialized variable, will the garbage in it ever be leftover data from another process? 1135 | 1136 | Not on a modern OS. It would be a security risk, so the OS makes sure this never 1137 | happens. 1138 | 1139 | ------------------------------------------------------------------------ 1140 | 1141 | 1142 | ### How many levels of indirection can you have with pointers? `int******`? 1143 | 1144 | It's effectively unlimited. But the more you have, the less readable your code 1145 | is. 1146 | 1147 | In real life: 1148 | 1149 | * 99.8% (roughly) of pointer usage is single indirection, like `int*`. 1150 | * 1.5% (roughly) is double indirection, like `char**`. 1151 | * And the remaining 0.5% is the rest of it. 1152 | 1153 | ------------------------------------------------------------------------ 1154 | 1155 | 1156 | ### What's the `incompatible integer to pointer conversion` error? 1157 | 1158 | This means you have a type mismatch in your assignment. 1159 | 1160 | One side of the `=` has pointer type, and the other side has integer type. 1161 | 1162 | If you have a pointer in your assignment, both side of the `=` must be the same 1163 | pointer type. 1164 | 1165 | Maybe you meant to take the address of the right hand side? Or dereference the 1166 | right hand side? 1167 | 1168 | ------------------------------------------------------------------------ 1169 | 1170 | 1171 | ### Are there any other ways besides `malloc()` to store things on the heap? 1172 | 1173 | Short answer: no. 1174 | 1175 | (We're assuming that by `malloc()` we mean `malloc()`, `calloc()`, and 1176 | `realloc()`.) 1177 | 1178 | The longer answer is that you can make a syscall and request more RAM from the 1179 | operating system. In practice, this is very rare; people just call `malloc()`. 1180 | 1181 | In Unix, that syscall is `brk()` (or `sbrk()`). The behavior of this call is a bit strange no 1182 | 1183 | ------------------------------------------------------------------------ 1184 | 1185 | 1186 | ### For string literals like `"Hello"`, are those stored on the stack or heap? 1187 | 1188 | Neither. 1189 | 1190 | Consider it to be stored in such a way that it is perpetually accessible from 1191 | the entire program for the entire run and is never freed. So sort of like the 1192 | heap. 1193 | 1194 | This code is just fine: 1195 | 1196 | ```c 1197 | char *hello(void) 1198 | { 1199 | char *s = "hello!"; 1200 | 1201 | return s; 1202 | } 1203 | ``` 1204 | 1205 | `s` is a local variable that is set to point to the string `"hello!"`, and `s` 1206 | is deallocated as soon as the function returns. But the data `s` points to 1207 | (namely the `"hello!"`) persists for the entire life of the program and is never 1208 | freed. 1209 | 1210 | It's not actually on the heap, though. The C memory map looks like this, 1211 | typically: 1212 | 1213 | ``` 1214 | +--------------------+ 1215 | | Stack | 1216 | | | | 1217 | | v | 1218 | +- - - - - - - - - - + 1219 | | | 1220 | | | 1221 | | | 1222 | +- - - - - - - - - - + 1223 | | ^ | 1224 | | | | 1225 | | Heap | 1226 | +--------------------+ 1227 | | Uninitialized data | 1228 | +--------------------+ 1229 | | Initialized data | 1230 | | (Read-Write) | 1231 | +--------------------+ 1232 | | Initialized data | 1233 | | (Read-Only) | 1234 | +--------------------+ 1235 | | Program code | 1236 | +--------------------+ 1237 | ``` 1238 | 1239 | Constant strings are found in the read-only initialized data section of memory. 1240 | 1241 | If you try to write to one, your program will likely crash: 1242 | 1243 | ```c 1244 | char *s = "Hello!"; 1245 | 1246 | *s = 'B'; // segfault! 1247 | ``` 1248 | 1249 | ------------------------------------------------------------------------ 1250 | 1251 | 1252 | ### Is the C stack like the stack data structure? 1253 | 1254 | Yup! It's used by C to allocate space for local variables when you call functions. 1255 | 1256 | When you return from a function, all those local variables are popped off the 1257 | stack and thrown away. (Which is why local variables only last as long as the 1258 | function!) 1259 | 1260 | ------------------------------------------------------------------------ 1261 | 1262 | 1263 | ### Is the C heap like a binary heap data structure? 1264 | 1265 | No--it's just a name collision. 1266 | 1267 | Just assume the heap is a big, contiguous chunk of memory. It can be used for 1268 | whatever, but in C, it is typically managed by `malloc()` and `free()` so that 1269 | we don't have to worry about it. 1270 | 1271 | ------------------------------------------------------------------------ 1272 | 1273 | 1274 | ### What are `stdin`, `stdout`, and `stderr`? 1275 | 1276 | These are the three files that are automatically opened for a process when it is first created. 1277 | 1278 | |Stream | File Name | Device | 1279 | |-----------------|:---------:|:--------:| 1280 | | Standard Input | `stdin` | Keyboard | 1281 | | Standard Output | `stdout` | Screen | 1282 | | Standard Error | `stderr` | Screen | 1283 | 1284 | `stderr` is typically used specifically for error messages, even though it goes 1285 | to the same place as `stdout`. (The idea is that you can redirect all normal 1286 | output to one place, and all error output to another place. Or suppress normal 1287 | output while allowing error output.) 1288 | 1289 | ------------------------------------------------------------------------ 1290 | 1291 | 1292 | ### How do I know which header files to `#include` for any particular function? 1293 | 1294 | Check the man page for the function in question. It'll show it in the _Synopsis_ 1295 | section. 1296 | 1297 | Example for `printf()`: 1298 | 1299 | > **SYNOPSIS** 1300 | > ```c 1301 | > #include 1302 | > ``` 1303 | > ```c 1304 | > int 1305 | > printf(const char * restrict format, ...); 1306 | > ``` 1307 | 1308 | Note that if you type `man` on the command line for a particular function, you 1309 | might a manual page for another command that isn't the C function. In that case, 1310 | you have to specify the proper _section_ of the manual for the function. 1311 | 1312 | Try section 3 for library functions, and section 2 for syscalls. 1313 | 1314 | Example looking for `printf()` in section 3: 1315 | 1316 | ```shell 1317 | man 3 printf 1318 | ``` 1319 | 1320 | And section 2 for the `mkdir()` syscall: 1321 | 1322 | ```shell 1323 | man 2 mkdir 1324 | ``` 1325 | 1326 | ------------------------------------------------------------------------ 1327 | 1328 | 1329 | ### When do I have to explicitly cast a type to another type? 1330 | 1331 | Barely ever. 1332 | 1333 | C is pretty good about conversions, and you should be able to build almost 1334 | everything without casting. 1335 | 1336 | What if you need constant types? 1337 | 1338 | ```c 1339 | // Print a double: 1340 | // (Floating point constants are double by default.) 1341 | printf("%lf\n", 3.14); 1342 | 1343 | // Print a float: 1344 | printf("%f\n", 3.14f); 1345 | 1346 | // Print a long double 1347 | printf("%Lf\n", 3.14L); 1348 | 1349 | // Print a long integer: 1350 | printf("%ld\n", 99L); 1351 | 1352 | // Print a long long integer: 1353 | printf("%lld\n", 99LL); 1354 | 1355 | // Produce a floating result of a calculation by making sure at least 1356 | // one of the operands is a float: 1357 | float sort_of_pi = 22.0f / 7; 1358 | double double_pi = 22.0 / 7; 1359 | ``` 1360 | 1361 | What if you need to cast a void pointer? 1362 | 1363 | ```c 1364 | void foo(void *p) 1365 | { 1366 | // convert p to a char* 1367 | char *q = p; 1368 | 1369 | // Don't need to cast return value from malloc 1370 | int *z = malloc(sizeof(int) * 100); 1371 | ``` 1372 | 1373 | Some exceptions: 1374 | 1375 | ```c 1376 | void foo(int a) 1377 | { 1378 | // Cast an unused variable to type void to suppress compiler warnings: 1379 | (void)a; 1380 | 1381 | // If the compiler is warning about an unused return value: 1382 | (void)printf("Hello, world!\n"); 1383 | 1384 | // Cast to a char pointer to iterate over bytes of an object: 1385 | // (C99 6.3.2.3 paragraph 7 allows this.) 1386 | float f = 3.14; 1387 | unsigned char *c = (unsigned char *)&f; 1388 | 1389 | for (unsigned i = 0; i < sizeof f; i++) { 1390 | printf("%02x ", c[i]); 1391 | } 1392 | printf("\n"); 1393 | ``` 1394 | 1395 | ------------------------------------------------------------------------ 1396 | 1397 | 1398 | ### Is `realloc()` the same as calling `malloc()`, copying the data over, then calling `free()` on the original pointer? 1399 | 1400 | Effectively, yes, it's the same. Practically, you should use `realloc()`. 1401 | 1402 | `realloc()` might be more efficient because in some cases it might not have to 1403 | copy. 1404 | 1405 | If you grow the space and `realloc()` knows there's extra unused memory right 1406 | after the existing space, it will simply tack that addition space onto the end 1407 | of the memory region and not bother moving the data. 1408 | 1409 | Also, if you shrink the space, `realloc()` will likely not copy the data. It'll 1410 | just truncate it. 1411 | 1412 | ------------------------------------------------------------------------ 1413 | 1414 | 1415 | ### What happens if I `free()` a `NULL` pointer? 1416 | 1417 | Nothing. It's a [no-op](https://en.wikipedia.org/wiki/NOP_(code)). 1418 | 1419 | Basically, inside the library code for `free()`, there's something that looks 1420 | like this: 1421 | 1422 | ```c 1423 | void free(void *ptr) 1424 | { 1425 | if (ptr == NULL) { 1426 | return; 1427 | } 1428 | ``` 1429 | 1430 | According to the [C99 1431 | spec](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf) section 1432 | 7.20.3.2p2: 1433 | 1434 | > The `free` function causes the space pointed to by `ptr `to be deallocated, 1435 | > that is, made available for further allocation. **If `ptr` is a null pointer, 1436 | > no action occurs**. Otherwise, if the argument does not match a pointer 1437 | > earlier returned by the `calloc`, `malloc`, or `realloc` function, or if the 1438 | > space has been deallocated by a call to `free` or `realloc`, the behavior is 1439 | > undefined. 1440 | 1441 | ------------------------------------------------------------------------ 1442 | 1443 | 1444 | ### What are bits, bytes, kilobytes, megabytes, and all that? 1445 | 1446 | A [_bit_](https://www.youtube.com/watch?v=_fGujzulsas) is a single `1` or `0`. 1447 | That's all the numbers it can represent. 1448 | 1449 | A _nibble_ is 4 bits. It can represent numbers from `0b0000` to `0b1111` (binary 1450 | numbers), which is equivalen to `0` to `15` in decimal. 1451 | 1452 | A _byte_ is 8 bits. It can represent numbers from `0b00000000` to `0b11111111`, 1453 | or `0` to `255` decimal. (Historically, bytes could be other numbers of bits, 1454 | but on all modern systems, it's always 8 bits. _Octet_ is another term for a 1455 | number that is specifically 8 bits long.) 1456 | 1457 | A _kilobyte_ is 1024 bytes. (1024 is 210.) 1458 | 1459 | A _megabyte_ is 1024 kilobytes (1,048,576 bytes). 1460 | 1461 | A _gigabyte_ is 1024 megabytes (1,073,741,824 bytes). 1462 | 1463 | A _terabyte_ is 1024 gigabytes (1,099,511,627,776 bytes). 1464 | 1465 | A _petabyte_ is 1024 terabytes (1,125,899,906,842,624 bytes). 1466 | 1467 | If you're used to [SI unit 1468 | prefixes](https://en.wikipedia.org/wiki/International_System_of_Units#Prefixes), 1469 | you might be wondering why in computers _kilo_ means 1024 instead of 1000 like 1470 | it normally does. In short, it's for historic reasons. 1024 was close enough, so 1471 | computer programmers adopted the SI prefixes, albeit with a slightly different 1472 | value. 1473 | 1474 | And that gets confusing. When I say _kilobyte_, do I mean 1000 bytes or 1024 bytes? 1475 | 1476 | In almost every single case, _kilobyte_ means 1024 bytes. (Hard drive and SSD 1477 | sizes are sometimes an exception to this rule.) 1478 | 1479 | To remove the ambiguity, you can use a [binary 1480 | prefix](https://en.wikipedia.org/wiki/Binary_prefix), where you'd say _kibibyte_ 1481 | if you specifically meant 1024 bytes. 1482 | 1483 | That said, in conversation, if someone says _kilobyte_, odds are extremely high 1484 | they mean 1024 bytes, not 1000 bytes. _kibibyte_ is uncommonly used in 1485 | conversation. 1486 | 1487 | ------------------------------------------------------------------------ 1488 | 1489 | 1490 | ### In C, can we assume an `int` is 32 bits? 1491 | 1492 | No. 1493 | 1494 | You can assume an `int` is _at least_ 16 bits (2 bytes). 1495 | 1496 | There is only one type that has a guaranteed size: `sizeof(char)` will always be 1497 | `1` byte. (Same for `unsigned char` and `signed char`.) 1498 | 1499 | Never write code that hardcodes or assumes the size of anything other than 1500 | `char`. **Always** use `sizeof` to get the size. 1501 | 1502 | There's a [great Wikipedia article](https://en.wikipedia.org/wiki/C_data_types) 1503 | that lists the minimum sizes of the types. If you want your code to be portable 1504 | to other compilers and systems, choose a type with a minimum size that works for 1505 | the numbers you need to hold. 1506 | 1507 | ------------------------------------------------------------------------ 1508 | 1509 | 1510 | ### What's the difference between `#include` with double quotes and `#include` with angle brackets? 1511 | 1512 | In general, use double quotes for your own header files, and angle brackets for 1513 | built-in system header files like ``. 1514 | 1515 | When you `#include "foo.h"`, it looks for `foo.h` in the same directory as the 1516 | source file doing the including. 1517 | 1518 | You can also use relative paths, and it'll look relative to the including source file: 1519 | 1520 | ```c 1521 | #include "../bar.h" 1522 | #include "somedir/baz.h" 1523 | ``` 1524 | 1525 | When you `#include `, it looks in the _system include directories_ for 1526 | the header file. This is where all the built-in header files are installed. On 1527 | Unix machines, this tends to be the `/usr/include` directory, but it depends on 1528 | the OS and compiler. 1529 | 1530 | ------------------------------------------------------------------------ 1531 | 1532 | 1533 | ### Should I declare a pointer to a thing, or just declare the thing? 1534 | 1535 | It depends on if you want a thing or not, or if you just want to point to 1536 | another, **already-existing** thing. 1537 | 1538 | If there is not an already-existing thing, then making a pointer doesn't make 1539 | sense. There's no existing thing for it to point to. 1540 | 1541 | This does not declare an `int`: 1542 | 1543 | ```c 1544 | int *p; 1545 | ``` 1546 | 1547 | There's no `int` there. We have an `int` pointer, but it's uninitialized, so it 1548 | points to garbage and can't be used. 1549 | 1550 | So the question to ask is, "Do I already have an existing thing that I can point 1551 | to? And if so, do I want to point to it?" If the answer to either is "no", then 1552 | don't use a pointer. 1553 | 1554 | Example: 1555 | 1556 | ```c 1557 | int a = 12; // here's an existing thing 1558 | ``` 1559 | 1560 | So the answer to the first part of the question is yes. And do we want a pointer 1561 | to it? Sure, why not? 1562 | 1563 | ```c 1564 | int *p = &a; // and there's a pointer to it 1565 | ``` 1566 | 1567 | ------------------------------------------------------------------------ 1568 | 1569 | 1570 | ### Is there a difference between `exit()` and `return`? 1571 | 1572 | If you're in the `main()` function, then no. 1573 | 1574 | If you're in any other function, then yes. 1575 | 1576 | `exit()` always exits the running process, no matter where you call it from. 1577 | 1578 | If you're in `main()`, `return` also exits the running process. 1579 | 1580 | If you're in any other function, `return` just returns from that function. 1581 | 1582 | ------------------------------------------------------------------------ 1583 | 1584 | 1585 | ### Why does `strcmp()` return `0` when strings match? Since `0` means "false" in C, that seems backwards. 1586 | 1587 | `strcmp()` returns the _difference_ between two strings. If the strings are the 1588 | same, there is zero difference, so it returns zero. 1589 | 1590 | This gives `strcmp()` a little extra power over just returning a boolean 1591 | true/false value. 1592 | 1593 | For example, if you run this: 1594 | 1595 | ```c 1596 | strcmp("Antelope", "Buffalo"); 1597 | ``` 1598 | 1599 | it will return less-than zero because "Antelope" is alphabetically less than 1600 | "Buffalo". 1601 | 1602 | So not only can it tell you if the strings are the same, it can tell you their 1603 | relative sort order. And that means you can pass it in as the comparator 1604 | function to the library built-in `qsort()` function. 1605 | 1606 | ------------------------------------------------------------------------ 1607 | 1608 | 1609 | ### What is "undefined behavior" in C? 1610 | 1611 | There are a number of things you're allowed to do in C where the compiler is 1612 | allowed to produce code that can have any indeterminate effect. It could work, 1613 | it could crash, it could sort of work, it could crash sometimes and not others, 1614 | it could crash on some machines and not others. 1615 | 1616 | When you write code that does that, we say the code has _undefined behavior_. 1617 | 1618 | [Wikipedia has a number of practical 1619 | examples](https://en.wikipedia.org/wiki/Undefined_behavior), and if you look in 1620 | the [C99 Language Specification, Annex 1621 | J.2](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf) you can get a 1622 | list of _all_ the things you can do that cause undefined behavior. 1623 | 1624 | At Lambda, the most common things you can do to get UB is using bad pointer references. 1625 | 1626 | * Accessing memory you've already `free()`d. 1627 | * Freeing memory more than once. 1628 | * Accessing an array off the end of its bounds. 1629 | * Dereferencing a pointer that points to garbage. 1630 | * Dereferencing a `NULL` pointer. 1631 | * Returning a pointer to a local variable and dereferencing that. 1632 | 1633 | GCC with `-Wall -Wextra` should warn on a lot of these. This is why it's 1634 | _really_ important to fix all those warnings. 1635 | 1636 | ------------------------------------------------------------------------ 1637 | 1638 | 1639 | ### When you free a pointer, does it get set to `NULL` automatically? 1640 | 1641 | No. 1642 | 1643 | Furthermore, `free()` can't do that even if it wanted to. 1644 | 1645 | ```c 1646 | int *p = malloc(100 * sizeof(int)); 1647 | 1648 | free(p); 1649 | ``` 1650 | 1651 | When we call `free()`, it gets a _copy_ of the pointer we pass in. (**All** 1652 | functions **always** get _copies_ of **all** arguments you pass in.) As such, 1653 | `free()` could set its copy of `p` to `NULL`, but that doesn't affect our 1654 | original `p`. 1655 | 1656 | `p` remains whatever value was in it until we set it to something else. 1657 | 1658 | ```c 1659 | int *p = malloc(100 * sizeof(int)); 1660 | 1661 | free(p); 1662 | 1663 | p = NULL; // NOW p is NULL 1664 | ``` 1665 | 1666 | (Note that it's [undefined behavior](#ub) to _dereference_ a pointer after 1667 | you've `free()`d it. But it's still OK to change that pointer's value.) 1668 | 1669 | ------------------------------------------------------------------------ 1670 | 1671 | 1672 | ### How do I write preprocessor macros with `#define`? 1673 | 1674 | You've probably already seen simple cases of `#define` like this: 1675 | 1676 | ```c 1677 | #define antelopes 10 1678 | 1679 | int main(void) 1680 | { 1681 | printf("Antelopes: %d\n", antelopes); // prints 10 1682 | ``` 1683 | 1684 | What's actually happening here is the _preprocessor_ runs through the code 1685 | before the compiler ever sees it. It manipulates the above code to read: 1686 | 1687 | ```c 1688 | int main(void) 1689 | { 1690 | printf("Antelopes: %d\n", 10); // prints 10 1691 | ``` 1692 | 1693 | and _then_ hands it off to the compiler. The compiler itself knows nothing about 1694 | `#define`. 1695 | 1696 | These `#define` _macros_ can also accept parameters that make them behave like 1697 | functions in a way. 1698 | 1699 | Example: 1700 | 1701 | ```c 1702 | #define square(x) x * x // Not quite Right. See below. 1703 | 1704 | int main(void) 1705 | { 1706 | printf("9 squared is %d\n", square(9)); 1707 | ``` 1708 | 1709 | Then the preprocessor generates this code for the compiler: 1710 | 1711 | ```c 1712 | int main(void) 1713 | { 1714 | printf("9 squared is %d\n", 9 * 9); 1715 | ``` 1716 | 1717 | It just substitutes the parameters in as-is. 1718 | 1719 | Another example: 1720 | 1721 | ```c 1722 | #define square(x) x * x // Not quite Right. See below. 1723 | 1724 | int main(void) 1725 | { 1726 | printf("3 + 2 squared is %d\n", square(3 + 2)); 1727 | ``` 1728 | 1729 | Then the preprocessor generates this code for the compiler, merely substituting 1730 | in exactly what the dev entered as an argument: 1731 | 1732 | ```c 1733 | int main(void) 1734 | { 1735 | printf("3 + 2 squared is %d\n", 3 + 2 * 3 + 2); 1736 | ``` 1737 | 1738 | Except that prints `11`, when it should print `25` (3 + 2 is 5, and 5 squared is 1739 | 25)! We have a bug! 1740 | 1741 | Of course, this has to do with the order of operations. We wrote: 1742 | 1743 | ```c 1744 | 3 + 2 * 3 + 2 1745 | ``` 1746 | 1747 | when what we really wanted was: 1748 | 1749 | ```c 1750 | (3 + 2) * (3 + 2) 1751 | ``` 1752 | 1753 | For this reason, you should **always** put extra parentheses around the macro 1754 | body, and around every parameter in the body: 1755 | 1756 | ```c 1757 | #define square(x) ((x) * (x)) 1758 | ``` 1759 | 1760 | And now the expansion of our line will be: 1761 | 1762 | ```c 1763 | ((3 + 2) * (3 + 2)) 1764 | ``` 1765 | 1766 | That will work in all expected cases. 1767 | 1768 | ------------------------------------------------------------------------ 1769 | 1770 | 1771 | ### What is an `undefined symbol` linker error? 1772 | 1773 | This happens when you've called a function, and the _linker_ can't find it in 1774 | any of the source files or libraries that you're using. 1775 | 1776 | Do you have a warning about an implicit function declaration from the compiler 1777 | before this error? If so, fix that first. 1778 | 1779 | If not, it could be that you haven't specified all the source files needed on 1780 | the command line. If you have two sources `one.c` and `two.c`, and one calls 1781 | functions that are in the other, then you have to pass both into the compiler: 1782 | 1783 | ``` 1784 | gcc -Wall -Wextra -o myexe one.c two.c 1785 | ``` 1786 | 1787 | Alternately, is there a `Makefile` present? If so, the author of the software 1788 | probably intends for you to use that to build the project, rather than trying to 1789 | figure out the command line on your own. 1790 | 1791 | Try just running: 1792 | 1793 | ``` 1794 | make 1795 | ``` 1796 | 1797 | and seeing if that works. Make will show you the command lines it's running to 1798 | make the build happen so that you don't have to. 1799 | 1800 | The linker is part of the entire compilation system. Basically, the compiler 1801 | takes your C source files, makes sure they're syntactically correct, and turns 1802 | them into things called _object files_, one per source file. These object files 1803 | might refer to functions that they don't contain, like `printf()`, for example. 1804 | 1805 | Then the linker takes all the object files and libraries and puts them together 1806 | into a single binary executable that you can run. It makes sure that all the 1807 | functions used are present in the files specified. 1808 | 1809 | (Normally this whole process takes place behind the scenes and you don't have to 1810 | think about it. Sometimes Makefiles will generate object files that you might 1811 | see, e.g. `foo.o`. `.o` is the extension for object files on Unix, or `.obj` in 1812 | Windows.) 1813 | 1814 | If the linker can't find a function in any of the object files or libraries, it 1815 | pukes out an error. It can't call a function if it can't find it. 1816 | 1817 | In this example, the code calls a function `foobaz()`, but the linker can't find 1818 | that in any of the object files: 1819 | 1820 | ``` 1821 | Undefined symbols for architecture x86_64: 1822 | "_foobaz", referenced from: 1823 | _main in foo-133c47.o 1824 | ld: symbol(s) not found for architecture x86_64 1825 | clang: error: linker command failed with exit code 1 (use -v to see invocation) 1826 | ``` 1827 | 1828 | (Ignore the leading underscores on the function names.) 1829 | 1830 | To fix, we need to figure out which file `foobaz()` is defined in, and make sure 1831 | to pass that filename to the compiler when we build. 1832 | 1833 | ------------------------------------------------------------------------ 1834 | 1835 | 1836 | ### How do I make my own header files and what do I put in them? 1837 | 1838 | If you have a `.c` file and you want to be able to use functions, `#define`s, or 1839 | globals from that file in another file, you'll need to make a _header file_ 1840 | (`.h` file) to hold the function prototypes, `#define`s, and references to any 1841 | globals. 1842 | 1843 | Example: 1844 | 1845 | ```c 1846 | // square.c 1847 | 1848 | long square(long x) 1849 | { 1850 | return x * x; 1851 | } 1852 | ``` 1853 | 1854 | If you want to use `square()` from a different file, you need a way for that 1855 | file to know the `square()` function declaration so the compiler can check to 1856 | see that you're using it correctly. 1857 | 1858 | For that, we need a corresponding `.h` file: 1859 | 1860 | ```c 1861 | // square.h 1862 | 1863 | #ifndef SQUARE_H // Prevent multiple #includes 1864 | #define SQUARE_H 1865 | 1866 | // Function prototypes 1867 | extern long square(long x); 1868 | 1869 | #endif 1870 | ``` 1871 | 1872 | The `#ifndef SQUARE_H` is called the _guard macro_, and is there to prevent the 1873 | header file from being included multiple times. (This can be a problem if you 1874 | have header files that include other header files in a cycle.) `#ifndef` means 1875 | "if not defined". Basically, `SQUARE_H` is acting as a boolean that gets set the 1876 | first time through and so prevents the content of the header file from being 1877 | included again. 1878 | 1879 | All header files have wrappers like that. The name of the preprocessor variable 1880 | is conventionally `FILENAME_H`, but could be anything as long as it's unique in 1881 | the project. 1882 | 1883 | The keyword `extern` indicates to the compiler that the function in question is 1884 | not defined here; it is defined in another file. In this header file, `extern` 1885 | is the default _storage class_ for functions, so it's redundant. But it's really 1886 | common to see in any case. 1887 | 1888 | Now that we have a header file, we can `#include` that from somewhere else. Use 1889 | double quotes in the `#include` to indicate that the compiler should look in the 1890 | current directory for the header file. 1891 | 1892 | ```c 1893 | #include "square.h" 1894 | 1895 | int main(void) 1896 | { 1897 | int x = square(5); 1898 | ``` 1899 | 1900 | When you build, you must specify all `.c` files on the command line: 1901 | 1902 | ``` 1903 | gcc -Wall -Wextra -o main main.c square.c 1904 | ``` 1905 | 1906 | ------------------------------------------------------------------------ 1907 | 1908 | 1909 | ### How do I make my own Makefile? 1910 | 1911 | **IMPORTANT: Any lines shown _indented_ in any Makefile _must_ begin with a 1912 | single TAB character! Spaces will _not_ work. If you use spaces to indent, 1913 | you'll likely see a `Missing separator` error when you try to `make`.** 1914 | 1915 | ```make 1916 | myexecutable: mysource1.c mysource2.c 1917 | gcc -Wall -Wextra -o myexecutable mysource1.c mysource2.c 1918 | ``` 1919 | 1920 | The above Makefile says: 1921 | 1922 | "If `mysource1.c` or `mysource2.c` are _newer_ than `myexecutable`, then run the 1923 | following commands in the indented block below this line." 1924 | 1925 | And by "newer", we mean they have a more up-to-date timestamp, i.e. they've been 1926 | saved more recently than `myexecutable` has been created. 1927 | 1928 | If any of the sources are newer than the executable, the executable should be 1929 | rebuilt to get it up to date. And that's what make does. 1930 | 1931 | We say that `myexecutable` _depends on_ `mysource1.c` and `mysource2.c`. If 1932 | either of those dependencies change, `myexecutable` must be rebuilt. 1933 | 1934 | This also works: 1935 | 1936 | ```make 1937 | myexecutable: mysource1.c mysource2.c 1938 | gcc -Wall -Wextra -o $@ $^ 1939 | ``` 1940 | 1941 | `$@` is a make _macro_ that means "substitute whatever is left of the `:` right 1942 | here" (in this example, `myexecutable`). 1943 | 1944 | `$^` is a make macro that means "substitute whatever is to the right of the `:` 1945 | right here" (in this example, `mysource1.c mysource2.c`). 1946 | 1947 | You can also define constants: 1948 | 1949 | ```make 1950 | SRCS=mysource1.c mysource2.c 1951 | TARGET=myexecutable 1952 | 1953 | $(TARGET): $(SRCS) 1954 | gcc -Wall -Wextra -o $@ $^ 1955 | ``` 1956 | 1957 | You can have multiple _recipes_ per Makefile: 1958 | 1959 | ```make 1960 | target1: source1.c 1961 | gcc -Wall -Wextra -o $@ $^ 1962 | 1963 | target2: source2.c 1964 | gcc -Wall -Wextra -o $@ $^ 1965 | ``` 1966 | 1967 | If you type `make`, it will build the first _target_ in the file by default. A 1968 | target is a file that is generated by running `make`, i.e. a file to the left of 1969 | a `:`. You can also make specific targets by specifying them on the command line: 1970 | 1971 | ```shell 1972 | $ make target1 1973 | gcc -Wall -Wextra -o target1 source1.c 1974 | $ make target2 1975 | gcc -Wall -Wextra -o target2 source2.c 1976 | ``` 1977 | 1978 | If you want all targets to get built by default, you can put a dummy first 1979 | target in that depends on the other targets. This target is called `all` by 1980 | convention. It typically doesn't run any commands and is only there to set up 1981 | the dependency hierarchy with the other recipes. 1982 | 1983 | ```make 1984 | # Recipe `all` depends on `target1` and `target2`: 1985 | all: target1 target2 1986 | 1987 | target1: source1.c 1988 | gcc -Wall -Wextra -o $@ $^ 1989 | 1990 | target2: source2.c 1991 | gcc -Wall -Wextra -o $@ $^ 1992 | 1993 | .PHONY: all 1994 | ``` 1995 | 1996 | That `.PHONY: all` line is a GNU make extension that indicates that `all` is not 1997 | a real file. Normally targets refer to real files, and make will check to see if 1998 | that file exists on disk or not before trying to build it. But in this case, 1999 | `all` is not a file; it's just recipe we're using to get all targets to build by 2000 | default. 2001 | 2002 | * [GNU make documentation](https://www.gnu.org/software/make/manual/make.html) 2003 | 2004 | ------------------------------------------------------------------------ 2005 | 2006 | 2007 | ### Why are there so many `printf()` variants? How do I know which one to use? 2008 | 2009 | The way to approach it is when you think, "I need something just like 2010 | `printf()`, except instead of to the screen, it prints to _x_," then you look in 2011 | the man page and see if there's a `printf()` variant that suits your needs. 2012 | 2013 | The first letters let you know what speciality each one has: 2014 | 2015 | * `printf()`: stock, no frills. 2016 | * `fprintf()`: "file printf"; print to a specified `FILE*` instead of to `stdout`. 2017 | * `sprintf()`: "string printf"; print to a string instead of to `stdout`. 2018 | * `snprintf()`: "string printf, with a limited count"; print to a string instead 2019 | of to `stdout`, and also specify the maximum number of characters that 2020 | `snprintf()` is allowed to store in the buffer. This is good to protect 2021 | against buffer overruns, and there's a valid argument that you should _never_ 2022 | use `sprintf()`, preferring `snprintf()` instead. 2023 | * `vprintf()`: "variadic printf"; anything that starts with a `v` in `printf` 2024 | land has to do with [variadic 2025 | functions](https://www.gnu.org/software/libc/manual/html_node/Variadic-Functions.html), 2026 | i.e. functions with argument lists of variable lengths. These are out of scope 2027 | at Lambda. 2028 | * etc. 2029 | 2030 | ------------------------------------------------------------------------ 2031 | 2032 | 2033 | ### Why is `main()` always at the bottom of the file? 2034 | 2035 | C has the feature that you have to _declare_ a function before you can use it. 2036 | So any functions `main()` needs have to be declared before `main()`, which means 2037 | "above" it in the file. 2038 | 2039 | You can also declare functions with _prototypes_ and then put the definition of 2040 | `main()` before the definition of the other functions. 2041 | 2042 | It's more common for C devs to put `main()` at the bottom of the file that 2043 | contains it, and C devs expect it that way, but it's not wrong or frowned upon 2044 | to use prototypes to put it at the top instead. 2045 | 2046 | ------------------------------------------------------------------------ 2047 | 2048 | 2049 | ### Why does `main()` return `0`? What does the return value mean? 2050 | 2051 | It doesn't _have_ to. The return value from `main()` is the _exit status_ of the 2052 | process. This is passed back to the program that first launched your program, so 2053 | it can do different things based on the exit status of your program. 2054 | 2055 | Think of it like a way for an exiting program to pass a small piece of 2056 | information (an integer) back to the program that spawned it. 2057 | 2058 | `0` by convention means "success". Non-zero means "failure". The idea there is 2059 | that there's typically only one way for a program to succeed, but many ways for 2060 | it to fail, and you can communicate those different ways with different exit 2061 | codes. 2062 | 2063 | Note that you can also use the `exit()` call, passing an exit status code to it 2064 | as an argument. Using `return` in `main()` is equivalent to calling `exit()`. 2065 | 2066 | In bash, you can look at the exit status of the previous command by `echo`ing 2067 | the `$?` shell variable. In the following example, we use `grep` incorrectly, 2068 | and it exits with status `2` to indicate that. Whereas we use `ls` correctly, 2069 | and it exits with successful status `0`: 2070 | 2071 | ```shell 2072 | $ grep 2073 | usage: grep [-abcDEFGHhIiJLlmnOoqRSsUVvwxZ] [-A num] [-B num] [-C[num]] 2074 | [-e pattern] [-f file] [--binary-files=value] [--color=when] 2075 | [--context[=num]] [--directories=action] [--label] [--line-buffered] 2076 | [--null] [pattern] [file ...] 2077 | $ echo $? 2078 | 2 2079 | $ ls -d . 2080 | . 2081 | $ echo $? 2082 | 0 2083 | ``` 2084 | 2085 | At the OS level, `fork()` is used to create a new process, and `wait()` is used 2086 | to get the exit status back from that process. 2087 | 2088 | ------------------------------------------------------------------------ 2089 | 2090 | 2091 | ### Do we have to have a `main()`? Can there be more than one? 2092 | 2093 | Only if you want to have a program that you can run. When you first launch a 2094 | program, it looks for a function called `main`. 2095 | 2096 | There can only be one `main()` in a program. (There can only be one of any 2097 | function, for that matter.) 2098 | 2099 | It could be that individual files don't have a `main()` in them, but when the 2100 | whole project it built, `main()` is brought in from another source file. 2101 | 2102 | Also, there's a thing called a 2103 | [library](https://en.wikipedia.org/wiki/Library_(computing)) which is a 2104 | collection of functionality that your program makes use of, but doesn't have a 2105 | `main()`, itself. Your program has the `main()`, and it just _calls_ routines 2106 | that are in the library. 2107 | 2108 | [The C Standard Library](https://en.wikipedia.org/wiki/C_standard_library) is a 2109 | library that holds all the standard C functionality (e.g. `printf()`, `sqrt()`, 2110 | etc.) but doesn't have a `main()` of its own. Other programs simply use the 2111 | library. 2112 | 2113 | ------------------------------------------------------------------------ 2114 | 2115 | 2116 | ### Can `main()` return `void`? What about `main()` with no parameters? 2117 | 2118 | No. The function signature for `main()` must be one of the following: 2119 | 2120 | ```c 2121 | int main(void) 2122 | int main(int argc, char *argv[]) 2123 | int main(int argc, char **argv) 2124 | ``` 2125 | 2126 | Note that the second two are equivalent. Use the first form if you don't need to 2127 | process command line arguments. 2128 | 2129 | Historically, these were also equivalent, but the second form is now obsolete: 2130 | 2131 | ```c 2132 | int main(void) // OK 2133 | int main() // Obsolete 2134 | ``` 2135 | 2136 | ------------------------------------------------------------------------ 2137 | 2138 | 2139 | ### Do we need a semicolon at the end of every line? 2140 | 2141 | Yes. 2142 | 2143 | Or, more technically, at the end of every statement or expression. 2144 | 2145 | C won't fill them in automatically like JavaScript will. 2146 | 2147 | ------------------------------------------------------------------------ 2148 | 2149 | 2150 | ### Can a pointer point to more than one thing? What about to arrays and `struct`s? 2151 | 2152 | A pointer is a memory address. A single memory address, a single index number 2153 | into your memory array. 2154 | 2155 | As such, it can only point to a single byte in memory. 2156 | 2157 | For single-byte and multi-byte entities, the pointer _always points to the first 2158 | first byte of that entity_. 2159 | 2160 | If you have an `int` made up of 4 bytes, a pointer to that `int` points to the 2161 | address in memory of the first byte of that `int`. 2162 | 2163 | If you have a `struct`, it points to the first byte of that `struct`. 2164 | 2165 | If you have an array of a zillion `struct`s, it points to the first byte of the 2166 | 0th `struct` in that array. 2167 | 2168 | ------------------------------------------------------------------------ 2169 | 2170 | 2171 | ### If variables are stored in memory, where are pointers stored? 2172 | 2173 | Pointers themselves are variables. 2174 | 2175 | Variables are stored in memory. 2176 | 2177 | Therefore pointers are also stored in memory. 2178 | 2179 | Remember that a pointer is just an index into memory. It's just a number; in 2180 | fact it's an integer number. We can store integer numbers in memory without a 2181 | hassle. 2182 | 2183 | The only difference between a pointer and an integer is that you can dereference 2184 | the pointer to see what it's pointing to. You can't dereference an integer. In 2185 | memory, they're both just stored as numbers. 2186 | 2187 | ------------------------------------------------------------------------ 2188 | --------------------------------------------------------------------------------