Writing large C programs Recall our linked list program from previous class typedef struct node { int val; struct node *next; } node; void push(node **head, int v) { node *n = (node *)malloc(sizeof(node)); n->val = v; n->next = *head; *head = n; } void pop(node **head) { node *n = *head; if (n) { *head = n->next; } } void main() { node *head = NULL; for (int i = 0; i < 1000000; i++) { push(&head, i); pop(&head); } } What if another program file wants to use my queue implementation? queue.c ... queue.h typedef struct node { int val; struct node *next; } node; //exported function signatures void push(node **head, int v); void pop(node **head); main.c #include "queue.h" Exporting Global variables add the two global variables to queue.c int n_pushes; //variable definition, announces the type int n_pops; in queue.h extern int n_pushes; //variable declaration, sets aside space for variable extern int n_pops; C has no notion of explicit namespaces; scope of a global variable is across files Static keyword: restrict the scope of a global variable to within a single source file (avoid polluting the global namespace) static int n_pushes; static int n_pops; Ditto with functions static int my_helper_function() { } Static used in front of local variables means completely different things (it's unfortunate static has different meanings) Normal local variables are deallocated when function resturns static local variables are NOT deallocated (offer private, persistent storage within a function) Standard library void assert(int expression); isdigit(c) isupper(c) islower(c) isspace(c) tolower(c) log(x) log10(x) pow(x,y) sqrt(x), ... fopen, fclose, fread, fwrite, printf malloc, free, atoi, rand strlen, strcpy, strcat, strcmp The documentation for these functions are located in man pages, section 3 man 3 strlen The C pre-processor #include is a pre-processor directive #include "queue.h" <--- replaced by the contents of the file queue.h Macro subsitituion #define name replacement_text It's better to replace arbitary constant values with #define #define NITER 10000 for (int i = 0; i < NITER; i++) { ... } It's even better to use const qualifier static const int niter = 10000; Macro can have arguments #define SQUARE(X) X*X Macro is not a function call, it's a pure subsitution a = SQUARE(2) b = SQUARE(i+1) c = SQUARE(i++) Macro is hard to debug, if you can avoid it, do it.