{ "cells": [ { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from IPython.display import Image" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "C Crash Course\n", "====\n", "\n", "C functions are typically split into header files (`.h`) where things are declared but not defined, and implementation files (`.c`) where they are defined. When we run the C compiler, a complex sequence of events is triggered with the usual successful outcome begin an executable file as illustrated at http://www.codingunit.com/\n", "\n", "![Compilation process](http://www.codingunit.com/images/preprocessor-compiler-linker.jpg)\n", "\n", "The preprocessor merges the contents of the header and implementation files, and also expands any macros. The compiler then translates these into low level object code (`.o`) for each file, and the linker then joins together the newly generated object code with pre-compiled object code from libraries to form an executable. Sometimes we just want to generate object code and save it as a library (e.g. so that we can use it in Python)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## A tutorial example - coding a Fibonacci function in C" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Python version" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def fib(n):\n", " a, b = 0, 1\n", " for i in range(n):\n", " a, b = a+b, a\n", " return a" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "354224848179261915075" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fib(100)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### C version" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Header file" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing fib.h\n" ] } ], "source": [ "%%file fib.h\n", "\n", "double fib(int n);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Implemetnation file" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing fib.c\n" ] } ], "source": [ "%%file fib.c\n", "\n", "double fib(int n) {\n", " double a = 0, b = 1;\n", " for (int i=0; i // for printf()\n", "#include // for atoi())\n", "#include \"fib.h\" // for fib()\n", "\n", "int main(int argc, char* argv[]) {\n", " int n = atoi(argv[1]);\n", " printf(\"%f\", fib(n));\n", "}\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Makefile" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting Makefile\n" ] } ], "source": [ "%%file Makefile\n", "\n", "CC=clang\n", "CFLAGS=-Wall\n", "\n", "fib: main.o fib.o\n", "\t $(CC) $(CFLAGS) -o fib main.o fib.o\n", "\n", "main.o: main.c fib.h\n", "\t $(CC) $(CFAGS) -c main.c\n", "\n", "fib.o: fib.c\n", "\t $(CC) $(CFLAGS) -c fib.c\n", "\n", "clean:\n", "\t rm -f *.o" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Compile" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "clang -c main.c\n", "clang -Wall -c fib.c\n", "clang -Wall -o fib main.o fib.o\n" ] } ], "source": [ "! make " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Run executable file" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "354224848179261997056.000000" ] } ], "source": [ "! ./fib 100" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "C Basics\n", "----" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Types in C\n", "\n", "The basic types are very simple - use int, float and double for numbers. In general, avoid float for plain C code as its lack of precision may bite you unless you are writing CUDA code. Strings are quite nasty to use in C - I would suggest doing all your string processing in Python ...\n", "\n", "Structs are sort of like classes in Python\n", "\n", "```c\n", "struct point {\n", " double x;\n", " double y;\n", " double z;\n", "};\n", "\n", "struct point p1 = {.x = 1, .y = 2, .z = 3};\n", "struct point p2 = {1, 2, 3};\n", "struct point p3;\n", "p3.x = 1;\n", "p3.y = 2;\n", "p3.z = 3;\n", "```\n", "\n", "You can define your own types using `typedef` -.e.g.\n", "```c\n", "#include \n", "struct point {\n", " double x;\n", " double y;\n", " double z;\n", "};\n", "\n", "typedef struct point point;\n", "\n", "int main() {\n", " point p = {1, 2, 3};\n", " printf(\"%.2f, %.2f, %.2f\", p.x, p.y, p.z);\n", "};\n", "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Operators\n", "Most of the operators in C are the same in Python, but an important difference is the increment/decrement operator. That is\n", "```c\n", "int c = 10;\n", "c++; // same as c = c + 1, i.e., c is now 11\n", "c--; // same as c = c - 1, i.e.. c is now 10 again\n", "```\n", "\n", "There are two forms of the increment operator - postfix ```c++``` and prefix ```++c```. Both increment the variable, but in an expression, the postfix version returns the value before the increment and the prefix returns the value after the increment.\n" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing increment.c\n" ] } ], "source": [ "%%file increment.c\n", "#include \n", "#include \n", "\n", "int main()\n", "{\n", " int x = 3, y;\n", " y = x++; // x is incremented and y takes the value of x before incrementation\n", " printf(\"x = %d, y = %d\\n\", x, y); \n", " y = ++x; // x is incremented and y takes the value of x after incrementation\n", " printf(\"x = %d, y = %d\\n\", x, y); \n", "}" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x = 4, y = 3\n", "x = 5, y = 5\n" ] } ], "source": [ "%%bash\n", "\n", "clang -Wall increment.c -o increment\n", "./increment" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Ternary operator\n", "\n", "The ternary operator `expr = condition ? expr1 : expr2` allows an if-else statement to be put in a single line. In English, this says that if condition is True, expr1 is assigned to expr, otherwise expr2 is assigned to expr. We used it in the tutorial code to print a comma between elements in a list unless the element was the last one, in which case we printed a new line '\\n'.\n", "\n", "Note: There is a similar ternary construct in Python `expr = expr1 if condition else epxr2`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Control of program flow\n", "\n", "Very similar to Python or R. The examples below should be self-explanatory." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### if-else" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```C\n", "// Interpretation of grades by Asian parent\n", "if (grade == 'A') {\n", " printf(\"Acceptable\\n\");\n", "} else if (grade == 'B') {\n", " printf(\"Bad\\n\");\n", "} else if (grade == 'C') {\n", " printf(\"Catastrophe\\n\");\n", "} else if (grade == 'D') {\n", " printf(\"Disowned\\n\");\n", "} else {\n", " printf(\"Missing child report filed with local police\\n\")\n", "}\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### for, while, do" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```C\n", "// Looping variants\n", "\n", "// the for loop in C consists of the keyword if followed\n", "// (initializing statement; loop condition statement; loop update statement)\n", "// followed by the body of the loop in curly braces\n", "int arr[3] = {1, 2, 3};\n", "for (int i=0; i 0) {\n", " i--;\n", "}\n", "\n", "int i = 3;\n", "do {\n", " i==;\n", "} while (i > 0);\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Arrays and pointers" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Automatic arrays\n", "If you know the size of the arrays at initialization (i.e. when the program is first run), you can usually get away with the use of fixed size arrays for which C will automatically manage memory for you.\n", "\n", "```c\n", "int len = 3;\n", "\n", "// Giving an explicit size\n", "double xs[len];\n", "for (int i=0; i\n", "\n", "int main()\n", "{\n", " int i = 2;\n", " int j = 3;\n", " int *p;\n", " int *q;\n", " *p = i;\n", " q = &j;\n", " printf(\"p = %p\\n\", p);\n", " printf(\"*p = %d\\n\", *p);\n", " printf(\"&p = %p\\n\", &p);\n", " printf(\"q = %p\\n\", q);\n", " printf(\"*q = %d\\n\", *q);\n", " printf(\"&q = %p\\n\", &q);\n", "}" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "p = 0x7fff55d34898\n", "*p = 2\n", "&p = 0x7fff55d34870\n", "q = 0x7fff55d34878\n", "*q = 3\n", "&q = 0x7fff55d34868\n" ] } ], "source": [ "%%bash\n", "\n", "clang -Wall -Wno-uninitialized pointers.c -o pointers\n", "./pointers" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing address.c\n" ] } ], "source": [ "%%file address.c\n", "#include \n", "\n", "void change_arg(int *p) {\n", " *p *= 2;\n", "}\n", "int main()\n", "{\n", " int x = 5;\n", " change_arg(&x);\n", " printf(\"%d\\n\", x);\n", "}" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10\n" ] } ], "source": [ "%%bash\n", "\n", "clang -Wall address.c -o address\n", "./address" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Pointer arithmetic\n", "If we want to store a whole sequence of ints, we can do so by simply allocating more memory:\n", "\n", "```c\n", "int *ps = malloc(5 * sizeof(int)); // ps is a pointer to an integer\n", "for (int i=0; i<5; i++) {\n", " ps[i] = i;\n", "}\n", "```\n", "\n", "The computer will find enough space in the heap to store 5 consecutive integers in a **contiguous** way. Since C arrays are all fo the same type, this allows us to do **pointer arithmetic** - i.e. the pointer `ps` is the same as `&ps[0]` and `ps + 2` is the same as `&ps[2]`. An example at this point is helpful." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing pointers2.c\n" ] } ], "source": [ "%%file pointers2.c\n", "#include \n", "#include \n", "\n", "int main()\n", "{\n", " int *ps = malloc(5 * sizeof(int));\n", " for (int i =0; i < 5; i++) {\n", " ps[i] = i + 10;\n", " }\n", "\n", " printf(\"%d, %d\\n\", *ps, ps[0]); // remmeber that *ptr is just a regular variable outside of a declaration, in this case, an int\n", " printf(\"%d, %d\\n\", *(ps+2), ps[2]); \n", " printf(\"%d, %d\\n\", *(ps+4), *(&ps[4])); // * and & are inverses\n", "}" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10, 10\n", "12, 12\n", "14, 14\n" ] } ], "source": [ "%%bash\n", "\n", "clang -Wall pointers2.c -o pointers2\n", "./pointers2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Pointers and arrays\n", "\n", "An array name is actually just a constant pointer to the address of the beginning of the array. Hence, we can dereference an array name just like a pointer. We can also do pointer arithmetic with array names - this leads to the following legal but weird syntax:\n", "\n", "```c\n", "arr[i] = *(arr + i) = i[arr]\n", "```" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing array_pointer.c\n" ] } ], "source": [ "%%file array_pointer.c\n", "#include \n", "\n", "int main()\n", "{\n", " int arr[] = {1, 2, 3};\n", " printf(\"%d\\t%d\\t%d\\t%d\\t%d\\t%d\\n\", *arr, arr[0], 0[arr], *(arr + 2), arr[2], 2[arr]);\n", "}" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\t1\t1\t3\t3\t3\n" ] } ], "source": [ "%%bash\n", "\n", "clang -Wall array_pointer.c -o array_pointer\n", "./array_pointer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### More on pointers\n", "\n", "**Different kinds of nothing**: There is a special null pointer indicated by the keyword NULL that points to nothing. It is typically used for pointer comparisons, since NULL pointers are guaranteed to compare as not equal to any other pointer (including another NULL). In particular, it is often used as a sentinel value to mark the end of a list. In contrast a void pointer (void \\*) points to a memory location whose type is not declared. It is used in C for generic operations - for example, `malloc` returns a void pointer. To totally confuse the beginning C student, there is also the NUL keyword, which refers to the `'\\0'` character used to terminate C strings. NUL and NULL are totally different beasts.\n", "\n", "**Deciphering pointer idioms**: A common C idiom that you should get used to is `*q++ = *p++` where p and q are both pointers. In English, this says\n", "\n", "* \\*q = \\*p (copy the variable pointed to by p into the variable pointed to by q)\n", "* increment q\n", "* increment p" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing pointers3.c\n" ] } ], "source": [ "%%file pointers3.c\n", "#include \n", "#include \n", "\n", "int main()\n", "{\n", " // example 1\n", " typedef char* string;\n", " char *s[] = {\"mary \", \"had \", \"a \", \"little \", \"lamb\", NULL};\n", " for (char **sp = s; *sp != NULL; sp++) {\n", " printf(\"%s\", *sp);\n", " }\n", " printf(\"\\n\");\n", "\n", " // example 2\n", " char *src = \"abcde\";\n", " char *dest = malloc(5); // char is always 1 byte by C99 definition\n", " \n", " char *p = src + 4;\n", " char *q = dest;\n", " while ((*q++ = *p--)); // put the string in src into dest in reverse order\n", "\n", " for (int i = 0; i < 5; i++) {\n", " printf(\"i = %d, src[i] = %c, dest[i] = %c\\n\", i, src[i], dest[i]);\n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "mary had a little lamb\n", "i = 0, src[i] = a, dest[i] = e\n", "i = 1, src[i] = b, dest[i] = d\n", "i = 2, src[i] = c, dest[i] = c\n", "i = 3, src[i] = d, dest[i] = b\n", "i = 4, src[i] = e, dest[i] = a\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "pointers3.c:7:19: warning: unused typedef 'string' [-Wunused-local-typedef]\n", " typedef char* string;\n", " ^\n", "1 warning generated.\n" ] } ], "source": [ "%%bash\n", "\n", "clang -Wall pointers3.c -o pointers3\n", "./pointers3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Functions" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing square.c\n" ] } ], "source": [ "%%file square.c\n", "#include \n", "\n", "double square(double x)\n", "{\n", " return x * x;\n", "}\n", "\n", "int main()\n", "{\n", " double a = 3;\n", " printf(\"%f\\n\", square(a));\n", "}" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "9.000000\n" ] } ], "source": [ "%%bash\n", "\n", "clang -Wall square.c -o square\n", "./square" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Function pointers\n", "\n", "How to make a nice function pointer: Start with a regular function declaration func, for example, here func is a function that takes a pair of ints and returns an int\n", "\n", "```\n", "int func(int, int);\n", "```\n", "\n", "To turn it to a function pointer, just add a `*` and wrap in parenthesis like so\n", "\n", "```\n", "int (*func)(int, int);\n", "```\n", "\n", "Now `func` is a pointer to a function that takes a pair of ints and returns an int. Finally, add a typedef so that we can use `func` as a new type\n", "```\n", "typedef int (*func)(int, int);\n", "```\n", "which allows us to create arrays of function pointers, higher order functions etc as shown in the following example." ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing square2.c\n" ] } ], "source": [ "%%file square2.c\n", "#include \n", "#include \n", "\n", "// Create a function pointer type that takes a double and returns a double\n", "typedef double (*func)(double x);\n", "\n", "// A higher order function that takes just such a function pointer\n", "double apply(func f, double x)\n", "{\n", " return f(x);\n", "}\n", "\n", "double square(double x)\n", "{\n", " return x * x;\n", "}\n", "\n", "double cube(double x)\n", "{\n", " return pow(x, 3);\n", "}\n", "\n", "int main()\n", "{\n", " double a = 3;\n", " func fs[] = {square, cube, NULL};\n", "\n", " for (func *f=fs; *f; f++) {\n", " printf(\"%.1f\\n\", apply(*f, a));\n", " } \n", "}" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "9.0\n", "27.0\n" ] } ], "source": [ "%%bash\n", "\n", "clang -Wall -lm square2.c -o square2\n", "./square2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Using make to compile C programs\n", "\n", "As you have seen, the process of C program compilation can be quite messy, with all sorts of different compiler and linker flags to specify, libraries to add and so on. For this reason, most C programs are compiled using the `make` built tool that you are already familiar with. Here is a simple generic makefile that you can customize to compile your own programs adapted from the book 21st Century C by Ben Kelmens (O'Reilly Media).\n", "\n", "* **TARGET**: Typically the name of the executable\n", "* **OBJECTS**: The intermediate object files - typically there is one file.o for every file.c\n", "* **CFLAGS**: Compiler flags, e.g. -Wall (show all warnings), -g (add debug information), -O3 (use level 3 optimization). Also used to indicate paths to headers in non-standard locations, e.g. -I/opt/include\n", "* **LDFLAGS**: Linker flags, e.g. -lm (link against the libmath library). Also used to indicate path to libraries in non-standard locations, e.g. -L/opt/lib\n", "* **CC**: Compiler, e.g. gcc or clang or icc\n", "\n", "In addition, there are traditional dummy flags\n", "* **all**: Builds all targets (for example, you may also have html and pdf targets that are optional)\n", "* **clean**: Remove intermediate and final products generated by the makefile" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting makefile\n" ] } ], "source": [ "%%file makefile\n", "TARGET = \n", "OBJECTS = \n", "CFLAGS = -g -Wall -O3 \n", "LDLIBS = \n", "CC = c99 \n", "\n", "all: TARGET\n", " \n", "clean:\n", "\t rm $(TARGET) $(OBJECTS)\n", "\n", "$(TARGET): $(OBJECTS)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Just fill in the blanks with whatever is appropriate for your program. Here is a simple example where the main file `test_main.c` uses a function from `stuff.c` with declarations in `stuff.h` and also depends on the libm C math library." ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing stuff.h\n" ] } ], "source": [ "%%file stuff.h\n", "#include \n", "#include \n", "\n", "void do_stuff();" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing stuff.c\n" ] } ], "source": [ "%%file stuff.c\n", "#include \"stuff.h\"\n", "\n", "void do_stuff() {\n", " printf(\"The square root of 2 is %.2f\\n\", sqrt(2));\n", "}" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing test_make.c\n" ] } ], "source": [ "%%file test_make.c\n", "#include \"stuff.h\"\n", "\n", "int main()\n", "{\n", " do_stuff();\n", "}" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting makefile\n" ] } ], "source": [ "%%file makefile\n", "TARGET = test_make\n", "OBJECTS = stuff.o\n", "CFLAGS = -g -Wall -O3 \n", "LDLIBS = -lm\n", "CC = clang\n", "\n", "all: $(TARGET)\n", " \n", "clean:\n", "\t rm $(TARGET) $(OBJECTS)\n", "\n", "$(TARGET): $(OBJECTS)" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "clang -g -Wall -O3 -c -o stuff.o stuff.c\n", "clang -g -Wall -O3 test_make.c stuff.o -lm -o test_make\n" ] } ], "source": [ "! make" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The square root of 2 is 1.41\r\n" ] } ], "source": [ "! ./test_make" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "make: Nothing to be done for `all'.\r\n" ] } ], "source": [ "# Make is clever enough to recompile only what has been changed since the last time it was called\n", "! make" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "rm test_make stuff.o\r\n" ] } ], "source": [ "! make clean" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "clang -g -Wall -O3 -c -o stuff.o stuff.c\n", "clang -g -Wall -O3 test_make.c stuff.o -lm -o test_make\n" ] } ], "source": [ "! make" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exercise" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Debugging programs (understanding compiler warnings and errors)\n", "\n", "Try to fix the following buggy program." ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing buggy.c\n" ] } ], "source": [ "%%file buggy.c\n", "\n", "# Create a function pointer type that takes a double and returns a double\n", "double *func(double x);\n", "\n", "# A higher order function that takes just such a function pointer\n", "double apply(func f, double x)\n", "{\n", " return f(x);\n", "}\n", "\n", "double square(double x)\n", "{\n", " return x * x;\n", "}\n", "\n", "double cube(double x)\n", "{\n", " return pow(3, x);\n", "}\n", "\n", "double mystery(double x)\n", "{\n", " double y = 10;\n", " if (x < 10)\n", " x = square(x);\n", " else\n", " x += y;\n", " x = cube(x);\n", " return x;\n", "}\n", "\n", "int main()\n", "{\n", " double a = 3;\n", " func fs[] = {square, cube, mystery, NULL}\n", "\n", " for (func *f=fs, f != NULL, f++) {\n", " printf(\"%d\\n\", apply(f, a));\n", " } \n", "}" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1mbuggy.c:2:3: \u001b[0m\u001b[0;1;31merror: \u001b[0m\u001b[1minvalid preprocessing directive\u001b[0m\r\n", "# Create a function pointer type that takes a double and returns a double\r\n", "\u001b[0;1;32m ^\r\n", "\u001b[0m\u001b[1mbuggy.c:5:3: \u001b[0m\u001b[0;1;31merror: \u001b[0m\u001b[1minvalid preprocessing directive\u001b[0m\r\n", "# A higher order function that takes just such a function pointer\r\n", "\u001b[0;1;32m ^\r\n", "\u001b[0m\u001b[1mbuggy.c:6:14: \u001b[0m\u001b[0;1;31merror: \u001b[0m\u001b[1munknown type name 'func'\u001b[0m\r\n", "double apply(func f, double x)\r\n", "\u001b[0;1;32m ^\r\n", "\u001b[0m\u001b[1mbuggy.c:18:12: \u001b[0m\u001b[0;1;35mwarning: \u001b[0m\u001b[1mimplicitly declaring library function 'pow' with type\r\n", " 'double (double, double)'\u001b[0m\r\n", " return pow(3, x);\r\n", "\u001b[0;1;32m ^\r\n", "\u001b[0m\u001b[1mbuggy.c:18:12: \u001b[0m\u001b[0;1;30mnote: \u001b[0minclude the header or explicitly provide a\r\n", " declaration for 'pow'\u001b[0m\r\n", "\u001b[1mbuggy.c:35:9: \u001b[0m\u001b[0;1;31merror: \u001b[0m\u001b[1mexpected ';' after expression\u001b[0m\r\n", " func fs[] = {square, cube, mystery, NULL}\r\n", "\u001b[0;1;32m ^\r\n", "\u001b[0m\u001b[0;32m ;\r\n", "\u001b[0m\u001b[1mbuggy.c:35:10: \u001b[0m\u001b[0;1;31merror: \u001b[0m\u001b[1muse of undeclared identifier 'fs'\u001b[0m\r\n", " func fs[] = {square, cube, mystery, NULL}\r\n", "\u001b[0;1;32m ^\r\n", "\u001b[0m\u001b[1mbuggy.c:35:13: \u001b[0m\u001b[0;1;31merror: \u001b[0m\u001b[1mexpected expression\u001b[0m\r\n", " func fs[] = {square, cube, mystery, NULL}\r\n", "\u001b[0;1;32m ^\r\n", "\u001b[0m\u001b[1mbuggy.c:35:17: \u001b[0m\u001b[0;1;31merror: \u001b[0m\u001b[1mexpected expression\u001b[0m\r\n", " func fs[] = {square, cube, mystery, NULL}\r\n", "\u001b[0;1;32m ^\r\n", "\u001b[0m\u001b[1mbuggy.c:35:5: \u001b[0m\u001b[0;1;35mwarning: \u001b[0m\u001b[1mexpression result unused [-Wunused-value]\u001b[0m\r\n", " func fs[] = {square, cube, mystery, NULL}\r\n", "\u001b[0;1;32m ^~~~\r\n", "\u001b[0m2 warnings and 7 errors generated.\r\n" ] } ], "source": [ "! clang -g -Wall buggy.c -o buggy" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Why not C? \n", "\n", "What other language has an annual Obfuscated Code Contest ? In particular, the following features of C are very conducive to writing unreadable code:\n", "\n", "* lax rules for identifiers (e.g. _o, _0, _O, O are all valid identifiers)\n", "* chars are bytes and pointers are integers\n", "* pointer arithmetic means that `array[index]` is the same as `*(array+index)` which is the same as `index[array]`!\n", "* lax formatting rules especially with respect to whitespace (or lack of it)\n", "* Use of the comma operator to combine multiple expressions together with the ?: operator\n", "* Recursive function calls - e.g. main calling main repeatedly is legal C\n", "\n", "Here is one winning entry from the 2013 IOCCC [entry](http://www.ioccc.org/2013/dlowe/hint.html) that should warm the heart of statisticians - it displays sparklines (invented by Tufte).\n", "\n", "```c\n", "main(a,b)char**b;{int c=1,d=c,e=a-d;for(;e;e--)_(e)<_(c)?c=e:_(e)>_(d)?d=e:7;\n", "while(++e_(d)?d=e:7;\n", "while(++e" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.1" } }, "nbformat": 4, "nbformat_minor": 0 }