Feb 10, 98 17:56 cprog.hints.txt Page 1/5 C programming survival hints. - Brian R. Gaeke, brg@csua.berkeley.edu, 10 February 1997. For more Information: -------------------- A handout of this size can't tell you all the interesting and cool things about C. Therefore ... ... you should check out the excellent book, "The C Programming Language,, Prentice Hall 2nd ed., by Brian W. Kernighan and Dennis M. Ritchie. Also, for a lot of the functions listed in this handout, you can get help from the Unix system: for example, if you want to get more information on malloc, type 'man malloc, to your Unix prompt. Data typest ---------- Some of the intrinsic data types in C are: int an integer (size depends on your compiler, normally 32 bits) char a character (B bits) i.e. la', lb', 'cl, short a small integer (size 16 bits) long a large integer (size 32 bits) double a double-precision floating point number float a single-precision floating point number it's easy to declare one of these: int focw /* an integer named fee char x; /1 a character named x I/ You can also add the "unsigned" qualification to this to get a larger number-. long a; /1 -2147483648 to 2147483647 I/ unsigned long b; /1 0 to 4294967295 You can also make arrays of these: int user-id[601; /1 user-id[Ol, user-id[l], ... user-id[591 are all integers you can access individually */ There's also a data type that means 'no value', called "void', and you can't declare variables of type void. It's used when you want to say that a function returns no value (i.e., you say that it returns the 'void' type.) C does have strings, as well; however, you must define strings as character arrays: char foo[j = 'mumble"; You can automatically initialize variables using the syntax shown above, as in int f = 56; Then, wherever f is defined, you can count on its initial value being equal to 56 without any further thought. (otherwise, initial values are unspecified -- you can't count on them being 0, or indeed anything at all.) Program structure: ----------------- A C program is composed of global declarations and functions. The first function that is called by your program is always main', and it takes two arguments, argc, which is an integer, and argv, which is an array of strings. You can have as many other functions in your program Feb 10, 98 17:56 cprog.hints.b(t Page 2/5 as you want, as well. Here's how to declare a function: int main (int argc, char largv[)) /1 Statements ... The instructions in your function go inside the braces. Let's pick this definition apart: int main (int argc, char largvf]) return type name arguments This means that "main" is a function that returns an integer, and takes an integer named "argcl and an array of strings largvl of unknown length as arguments. Here's another function, much simpler than the first: void foo (void) This shows that "foo" is a function which takes no arguments, and returns no value. Output methods: -------------- Output from the C language, which would normally be done with System.out in Java or cout in C++, is accomplished with the C library function printfo, The first argument is a 'format', and then come the (optional) extra arguments, which are translated to strings automatically. To gain access to the printfo function, you must include the header file Istdio.hl in your code, which is accomplished by putting #include at the first line of your source file (more on that later.) so if you want to print a string, use: printf(,This is a test.\n'); which wil '1 output This is a test. The I\n" is a C mnemonic for the newline character (like endl in C++). Also, if you have a character string variable like char ttl = fun'; then you can try printf('This is %s.\n",t); which will output This is fun. The %s means to insert the next string variable, in this case t, into the output where the %a appears in the string. This is called a format specifier,, and it is perhaps best explained with a few more examples. If you have the following declarations: char apples(] = apples'; char oranges[] = oranges'; char pears(] = "pears'; then the line printf(II have %a, %s, and %s.\nl, apples, oranges, pears); gives Feb 10, 98 17:56 cprog.hints.txt Page 3/5 1 have apples, oranges, and pears. but printf('l have %s, %a, and %s.\n', oranges, pears, apples); gives I have oranges, pears, and apples. You can use other kinds of arguments with printf as well: %d integer %ld long %f double %u unsigned %ul unsigned long %c char You can change the format specifier to change how a value is output, as well: %x integer as hexadecimal %lx long integer as hexadecimal %o integer as octal There are a lot of options to printf, especially for determining how wide things are in their printed representations. If you don't want to bother with printf, you can say putchar(lal); to print a single character or puts(I'stuff") to print "stuff". Input methods: ------------- use fgets to read into a string buffer: char user_name[80]; printf('What is your name? 11); fgets(user@ame,80,stdin); This will allow 80 characters to be read from the standard input to the buffer named user_name, as a string. You can also use an analogous function to printf, called scanf, to insert one or more user inputs into non-string variables. This fragment, for example: int x; printf('Enter a number between 1 and 10: scanf("%d',&x); will place the next number the user types into x (the & sign passes the memory location of x to scanf, which is necessary because C does not have references. Converting between types: ------------------------ There are a few functions to convert between types: i = atoi(x) converts x, which is a string, to an integer and returns it as an integer. sprintf(x, I%dl, i) is shorthand for converting i, an integer, into a string, x. sprintf works just like printf, only your output goes into a string, which is the first argument, according to the format, which is the second argument. Feb 10, 98 17:56 cprog.hints.txt Page 4/t> Declaring your own types: ------------------------ You can aggregate a bunch of normal intrinsic variables into one single data structure using the Istructl concept: struct user { char name[801; int age; long id; Then you can declare variables of type Istruct user,, just like you can declare variables of type lint', and you can access its fields (like instance variables in a Java/C++ class) using the canonical "dot" syntax, like so: struct user bob; sprintf(bob.name,"Bobl'); bob.age 20; bob.id 12345678; Pointers: If you want to take the address of a variable, you can use the operator. If you want a variable which holds such an address, which is a pointer, you can declare it using the pointer syntax: int b; int la = &b; /* a points to b Then you can access the value of b by accessing *a-. b = 3; printf(I'The value is %d.\n",*a); gives The value is 3. You can declare linked lists of integers like this: struct node f struct node *next; long data; Then you can allocate a new pointer to a Istruct node" element using struct node *head; head = (struct node 1) malloc(sizeof(struct node)); The Imalloc' function allocates a new chunk of memory; its argument is a certain number of bytes. The number of bytes you want, in this case, is the amount of memory required to represent a "struct node" structure, which is given by the C built-in operator "sizeof'. This pretty much does all you need, the only problem being that C knows that malloc returns a pointer to char, or 'char *11. You need to tell the compiler that you are going to use this space to hold Istruct node' variables, not characters, so you have to give the parenthesized type (struct node *) in order to "type-cast, the newly returned storage correctly. If the system can't get any more memory for you, the return value from malloc will be NULL. The NULL pointer is equal to zero, and it's la good thing to initialize pointer variables with. Feb 10, 98 17:56 cprog.hints.txt Page 5/5 The same idea works for arrays of size you don't know until ruii-tiitiot int Id = NULL; d = (int I)-malloc(500 I sizeof(int)); This newly-allocated space can then be accessed just like an array having 500 elements, numbered 0 to 499. Be careful not to write off the end of the array, because C won't tell you and you are likely to crash your program with a 'segmentation fault, error. To de-allocate space you're done using, you can use the 'free" function: int Id = NULL; d = (int 1) malloc (5 00 I sizeof (int) /* ... some stuff here ... if (done) free(d); It's not safe to access space after you've freed it, but there's nothing wrong with mallocling it again later. Some schools of thought say that you should set your pointers to NULL after freeing them, in fact, so that you can be sure that you aren't ever using space you have deallocated. You can do arithmetic on pointers, too. Adding 1 to a pointer makes it point to the next element of the space: int Id (int *) malloc(10 I sizeof(int)); int If; f = d; /* f points to d[Ol f++; /1 f points to d[l) f++; /* f points to d(21 Asserts: It's possible to do simple checking of your code using 'assert". Basically, if you assert something in your code, and it's not true, your program will exit with an error message of exactly where the assert failed. For example: int c = 2; assert(c==l); will cause your program to fail. A common use of this is checking that malloc was able to get the memory you asked for, and did not return a NULL pointer. t C Structures in Memory Remember: a structure is basically an array of words. 1 4 1 4 1 4 struct person f char *name; - int sex; - double vital-statis 1; Stupid C Tricks Optimization for Fun and Profit Jon Blow XCF (117 Cory Hall) October 11, 1994 Why Be Triclky? Direct speed improvement: By eliminating unnecessary operations, we can make programs go a little faster because they're doing less. Indirect speed improvement: If you reduce a program's memory usage, it runs faster because there is less VM activity (swapping to/from disk). If you increase the locality of data references, you reduce dm number of cache misses and TLB misses. But: "Optimization" is a poor substitue for wise algorithm choice. Optimization can hinder program development. Keep optimization in mind while designing ... but don't do it Itil the end! ' The Refrigerator typedef struct bullet typedef struct fridge float x; void *next; float y; void *whatever; int who-fired_this; *fridge; *bullet; bullet.x bullet.y bul let.wh@fired-this fridge.next The Refrigerator bullet get_bulleto bullet b; if (bullet-fridge == NULL) make-bullets(100); b = (bullet)bullet_fridge; bullet-fridge = bullet-fridge->next; return b; void free-bullet(bullet b) fridge f (fridge)b; f->next bullet-fridge; bullet-fridge = f; bullet_fridge NULL Allocating Beyond End-of-Structure If we want a dynamically allocated array to be a member of a structure, we can speed access by removing a level of indirection. The usual approach: typedef struct skipnode int height; void *data; struct skipnode **next; *skipnode; skipnode make_skipnode(int height) skipnode s NEW(skipnode); S->height height; s->next = (struct skipnode **)malloc@ height * sizeof(struct skipnode return s; The Refrigerator fridge bullet-fridge = NULL; void make -; bullets(int n) char memory; ftidge f; memory = (char *)malloc(n * sizeof(struct bullet)); for (i = 0; i < n; i++) f f = (fridge)memory + i * sizeof(struct bullet); f->next = bullet_fridge; bullet_fridge = f; bullet-fridge NULL The Refrigerator: Getting Around malloco o Allocate hundreds (?) of a structure type all at once. * Use those structures' own memory to build a free list. Improved locality. Low memory overhead. The First Structure Element is Often Fastest ... because you don't need to add an offset to get to it. Pseudo-machine code for a structure access: load mem-100 -> reg_l add reg_l + offset -> reg_2 loadi reg-2 -> reg_3 Ir offset is 0, we can eliminate the second instruction. malloco is icky A typical linked list: typedef struct listnode f struct listnode *next; void *data; ) *listnode; : I I I malloc header fragment listnode.data listnode.next malloco is icky malloc-. * is slow 9 incurs memory overhead 9 causes memory fragmentation