This was / will be presented in the evening section on Tuesday October 15th, and in the day section on Monday October 28th. Two topics: #ifdef and argv. ------------------------------------------------------------------------------ 1. Here are notes about #ifdef, edited from tutorial notes from Francois Pitt. Often we find ourselves writing a pair of programs which are essentially two separate programs, but which differ only in a tiny place. Writing them as completely separate programs is a very bad organization; hard to maintain; considerable extra work over time. * Instead of writing two separate programs, it is possible to use a feature of C to take care of the difference: "conditional compilation". You write the whole thing as one .c file, then only some of it gets compiled, depending on how you compile it. * As an example, suppose that you want to write code that will correctly initialize a string to the name of the currency for the country that the code was compiled in. We may write code like the one below: #define CANADA 0 #define ENGLAND 1 #define FRANCE 2 #define COUNTRY CANADA #if COUNTRY == CANADA char currency[] = "dollar"; #elif COUNTRY == ENGLAND char currency[] = "pound"; #elif COUNTRY == FRANCE char currency[] = "franc"; #else #error Illegal country code: unable to compile. #endif * When this is preprocessed (just before being actually compiled), the code given above would be replaced by exactly one line of code: char currency[] = "dollar"; before being passed on to the compiler. In this way, the preprocessor can be used to change the code that will be compiled, based on the values of some preprocessor constants. * In fact, instead of "hard-coding" the definition of COUNTRY in the source file, it is possible to leave it out entirely and to specify it only at the time of compilation by using a special option for the compiler. For example, if we remove the line ``#define COUNTRY CANADA'' from the code above, we could compile it using the following command-line (assuming it's in a file "program.c"): gcc -Wall -ansi -pedantic -DCOUNTRY=0 program.c The ``-D'' option allows us to define the value of a symbolic constant at compile time and is equivalent to adding a corresponding #define directive at the beginning of the .c file (or of each of the .c files listed on that command line, if you specify multiple files). g++ has an identical -D option, as does cc and do all unix C or C++ compilers. The only difference in this case is that we have to remember the numerical value of each country code (since the compiler does not know the meaning of the constants CANADA, ENGLAND, and FRANCE until it actually reads the file). This -D option is often put in a Makefile. ( Note the "else if" preprocessor directive (spelled "#elif"). In the C language proper, there is no "end if", so there is no difference between "else if" and a hypothetical "elif", so we don't have an "elif". In the C pre-processor, if we wrote "#else" and "#if ...", then we would need an additional "#endif" to close each additional "#if". ) ------------------------------------------------------------------------------ 2. Here are some notes about argv. As with java, C (or C++) provides a facility to pick up "command-line arguments". For example, when you type "cat foobar.c", the "cat" command has to be able to find that string "foobar.c". C uses a mechanism which you will find reminiscent of java's command-line- argument mechanism (the "String[] args" argument to main()). There is an alternate allowable definition of main() in C and C++, whose declaration looks like this: int main(int argc, char **argv) "argc" stands for "argument count". It is the number of elements in "argv", "argument vector" (while "vector" and "array" mean different things in java, traditionally they are synonyms). While these are theoretically arbitrary parameter names, the use of the names "argc" and "argv" is SO standard that you should not use any other name. Since a string can be passed around as a value of type pointer-to-char, argv should be an array of pointers-to-char. And when argv is passed to main() by the operating system, it will decay into a pointer to its zeroth element, so the data will be of type pointer-to-pointer-to-char. The array is one element larger than you would think. The program name itself is argv[0], so the first command-line argument is argv[1], and so on, and argc reflects the total number of items in the argv array *including* this argv[0]. Often we ignore argv[0] altogether, it's not extremely useful, and you surely will ignore it for this assignment. Example 1: Write a program which first checks that argc is at least 2, then prints the value of argv[1] (using %s). A session with this program might look like this, where '%' is your shell prompt: % ./a.out hello, world hello, % Since "world" is argv[2], it didn't print that part. The shell (command interpreter) divides up the arguments based on spaces. Solution: #include int main(int argc, char **argv) { if (argc >= 2) printf("%s\n", argv[1]); return 0; } We check that argc >= 2 before accessing argv[1], because it is illegal to exceed the bounds of an array in C, and it is UNDIAGNOSED... be careful! Example 2: Write a program which prints all of the argv values in a loop. This is like the `echo' command in unix. Sample session (our program is not going to be quite as tidy as `echo', as shown here): % echo hello, world, how are you hello, world, how are you % ./a.out hello, world, how are you hello, world, how are you % Answer: #include int main(int argc, char **argv) { int i; for (i = 1; i < argc; i++) printf("%s\n", argv[i]); return 0; } Note the ignoring of argv[0], which would be "./a.out".