High-performance computing and programming Intro to C on Unix/Linux IT Uppsala universitet
What is C? An old imperative language that remains rooted close to the hardware C is relatively small and easy to understand C is a nice language for writing small programs or kernels in larger programs C is not C++ C++ is modern in ways that C is not C++ is useful in ways that C is not
Additional material Google is your friend. Want more practice with C? Translate an old Matlab code or something from an other course.
Hello World #include <stdio.h> // provides printf() // This function prints Hello, World! int main( int argc, char** argv ) { printf("hello, World! %d arguments\n", argc - 1); printf( My name is %s\n, argv[0]); return 0; }
Function declaration/definition // Declaration: tells compiler how this function // will be used. // *Must* come before function is called. // Often placed in a.h file. double random(double min, double max); // Definition: what the function does when called. // Placed in.c file, turned into object code by compiler. double random(double min, double max) { double r = (double)rand()/rand_max; r = r*(max-min)+min; return r; }
Input & output in C Command line arguments are handled via arguments to main() The library <stdio.h> handles: Reading from files or keyboard (stdin) e.g. fscanf, scanf Writing to files or terminal (stdout) e.g. fprintf, printf Convert from string to numbers: <stdlib.h>: atoi, atof
Using printf() printf( double=%f\n, double); printf( in file %s: line %d\n, FILE, LINE ); printf( array starts at %p, last element is at %p\n, array, &array[n-1]);
Variable scope in C A variable exists within its set of {} int myglobal; int main(int argc, char** argv){ int myvar; if(argc > 1){ int myvar = argc; } printf( argc > 1? %d\n, myvar); }
Pointers in C A pointer is a variable that contains a memory address It is an integer double foo = 5.5; double * foo_ptr = &foo; //address of foo double foo2 = *foo_ptr; //pointer dereference Use pointers to pass data by reference BEWARE OF SCOPE!
Pointers to stack variables int * dont_do_this{} { int a; a = 5; return &a; } What happens when you try to use the value returned by dont_do_this?
Memory layout The virtual memory map of a running process:
The Stack Heap vs Stack Call a function, stuff pops on the stack Return a function, stuff pops off the stack Local variables vanish at the end of their scope The Heap User-managed Allocated with malloc() or calloc() Stays around until free() is called #1 cause of programmer error
Segmentation faults A program tries to access a forbidden segment of memory triggers the signal SIGSEGV Easiest to debug with a debugger (e.g. gdb) Common causes: Using uninitialized pointers Array out of bounds Using a freed pointer Dereferencing NULL pointers
Arrays C has static and dynamic arrays //static: double a[3] = {0.3, 0.4, 0.5}; //dynamic: double *b; b = (double*) malloc(3*sizeof(double)); b[1] = a[0]; printf( %f\n,b[1]); free(b);
Pointer arithmetic double *a; a = (double*) malloc(3*sizeof(double)); for(i = 0; i < 3; i++) a[i] = i; double *b = a; for(i = 0; i < 3; i++){ printf( %f\n, *b); b++; }
Arrays vs Pointers int static[3] = {0,2,4}; int *dynamic = (int*) malloc(3*sizeof(int)); printf( %d\n,sizeof(static)); // 12 printf( %d\n,sizeof(dynamic)); // 4 /* dynamic = static; // legal static = dynamic; // illegal dynamic++; // legal static++; // illegal */
On the stack: 2D arrays double array[3][4]; On the heap: int rows = 3, columns = 4; double **array; array = (double**)malloc(rows*sizeof(double*)); for( int i=0; i<rows; i++) { array[i]=(double*)malloc(columns*sizeof(double)); }
2D arrays, contiguous int rows = 5, columns = 6; double * data; data = malloc(rows*columns*sizeof(double)); double ** array; array = (double **)malloc(rows*sizeof(double*)); for( int i=0; i<rows; i++) { } array[i] = &data[i*columns];
Structures & typedef typedef struct point { double a, b; } point_t; struct triangle { point_t a, b, c; }; int main(){ } struct triangle my_triangle; my_triangle.a.a = 1.0;
Bitwise operators char a = 1; // 0000 0001 char b = a << 2; // 0000 0100 b == 4; // TRUE char c = a & b; // 0000 0000 c = a b; // 0000 0101 c == 5; // TRUE char d = ~c; // 1111 1010 c = c 2; // 0000 0111 d = d & ~(1<<3); // 1111 0010 char e = c ^ d; // 1111 0101
Assert #include <assert.h> assert( false-if-bug ) Asserts are used to debug as you code. When you write a function, think about: Invariants: properties of a data structure that must be guaranteed Pre-conditions: what is true at the start of a function Post-conditions: what is true at the end of a function When you re happy with your bug-free code, compile with -DNDEBUG This automatically removes all assert statements!