System calls The C language and C compiler has no knowledge about system calls. They are just function calls to precompiled functions. Example: fd = open( test.c, O_RDONLY); This will open the file test.c in read-only mode. The function value is a descriptor number or -1 if open failed. Typical use of open: If((fd=open( test.c, O_RDONLY)) < 0), printf( Could not open the file test.c \n ); } Reading from a file The read system call looks like this: fd is a file descriptor (from open for example) char buf[4000]; n = read(fd, buf, 4000); n is the number of characters actually read. It is sometimes less than 4000. If n is 0 it means end of file If n < 0 it means that an error has occured. Closing a file To close a file the close system call is used. n = close(fd); n is -1 if there was an error otherwise 0. 1
Error handling for system calls Compile time statements Error returns for system calls are usually indicated by the function value. The function value is something impossible (often -1). Furthermore more information on the error is stored in the external (int) variable errno. You shouldn t declare errno but include <errno.h> insstead To get printed explanation use perror function: if ((n=read(..)) < 0) perror( Could not read input file ); This will print the text and an explanation of the current errno value. We have already seen the #include statement. There are two of them #include <filename.h> searches for the filename.h in /usr/include (in general) #include filename.h searches first in the directory where the source file is then as the alternative above. Compile time statements #define defines a macro. In its simplest form it looks like this: #define TABSIZE 100. int table[tabsize]; for (i = 0; i < TABSIZE; i++). table*i+.. gcc has an enormous number of flags. The man command for gcc generates about 170 pages. Usually we need no flags gcc test.c will compile the file and generate an a.out file gcc -o testprog test.c will compile test.c and give a testprog executable file 2
gcc -c test.c means that test.c should be compiled into assembly text which is assembled but the result is left in test.o No linking is done. gcc -S test.c means that test.c should be compiled into assembly text which is left in test.s gcc sends some flags to the linker. The relevant flag here is the -l flag. gcc test.c -lalib (note, no space after -l) The -l flag is sent to the linker asking it to first link the file test.o and then search alib for unresolved references and then the standard libraries. The placement of -l flag is important. gcc test.c lalib test2.c Pitfalls to avoid (1) Remember the difference between operators = and == The first is an assignment while the second is a test for equality. Since the C-language is rather forgiving the error is seldom detected by the compiler. if (i == j+1) or if (i = j+1) are both correct C but with different meanings. Pitfalls to avoid (2) Don t delete necessary unnecessary parenthesis! if ((fd = open( test.c, O_RDONLY)) < 0) perror( Unable to open test.c ); read(fd, buf, 2000); This is correct C but isn t it tempting to delete one of the paired parenthesis? 3
Pitfalls to avoid (2) Pitfalls to avoid (3) Then we get this. It is valid C but with very different result if (fd = open( test.c, O_RDONLY) < 0) perror( Unable to open test.c ); read(fd, buf, 2000); If open fails the program takes correctly the error path. If open succeeds fd will be set to 0 and the read will read from standard input (keyboard). Not expected! You must always test system calls for error returns and give appropriate errormessage! If for example an open fails we cannot do reads on the file! If we try we will probably use -1 as the descriptor number that will make read to fail. Pitfalls to avoid (4) Remember that arrays (both native arrays and arrays pointed to by pointers) are indexed from 0! Pitfalls to avoid (5) Remember the differens between the logical operators && ( ) and the bitwise operators & ( ). They are different and create errors if mixed that the compiler never will find and that can work in some special cases. 4
Debugging advice Debugging advice When you are debugging the program use plenty of printf statements to report the progress in the program and relevant intermediate results. Use fflush(null) after each printf statement to assure that the text is written out and not just be sitting in a buffer. When the program is working you delete these statements and test the resulting program again. Example: if ((fd = open( test.c, O_RDONLY)) < 0) perror( unable to open test.c ); printf( Open of test.c succeeded, fd = %d\n,fd); fflush(null); 5