Friday, February 10, 2017 Lab Notes Topics for today Structures in C Redirection of input and output in a Unix-like environment Command line arguments More pre-processor options Programs: Finish Program 1, begin Program 2 due 2/24 1. Structures in C An array is a collection of variables of the same type. C lets us define structures which potentially contain items of different types. Here is an example similar to Fig. 2.40 struct person char initial; char last[15]; int age; int salary; char gender; Having defined type person we can declare variables of the type person Bill, Amy, Mike, Kate; And access the individual fields using dot notation as follows Bill.gender = M ; Bill.salary = Bill.age * 10; printf( %s\n,bill.last); Amy.salary = Mike.salary; Kate.age++; We can have arrays of structures Assign Bill person Department [50]; Department[0] = Bill; and find the average salary of the department members sum = 0; for (i=0; i<50; i++) sum+=department[i].salary; printf( Average is %d\n,sum/50); Comp 162 Lab Notes Page 1 of 6 February 10, 2017
2. Redirection of input and output in a Unix-like environment A C program has three built-in file streams: stdin, stderr and stdout. When you use scanf you are reading from stdin. When you use printf you are writing to stdout. You can write to stderr by using fprintf(stderr,.. When running programs in a Unix environment, a simple way to read data from a file into a program and to save results and/or error messages to a file without making any changes to the program source code is to use the Unix shell redirection. The following table shows how you can redirect the three streams when calling a.out. Command Stdin Stdout Stderr a.out Keyboard Screen Screen a.out < Datafile Datafile Screen Screen a.out > Results Keyboard Results Screen a.out < Datafile > Results Datafile Results Screen a.out >& Allout Keyboard Allout Allout a.out < Datafile >& Allout Datafile Allout Allout (a.out > Results) >& Errors Keyboard Results Errors (a.out < Datafile > Results) >& Errors Datafile Results Errors Comp 162 Lab Notes Page 2 of 6 February 10, 2017
Example: stdin, stdout, stderr psmith@redwood:~/comp162$ cat averages.c #include <stdio.h> // reads N followed by N integers. Outputs average. int main() int i, count, number, sum=0; scanf("%d",&count); if (count==0) fprintf(stderr,"error: Count is zero\n"); return; for (i=0; i<count; i++) scanf("%d",&number); sum+=number; printf("average of %d numbers is %5.3f\n",count,(float)sum/(float)count); psmith@redwood:~/comp162$ cat gooddata 7 34 21 12 55 90 2 83 psmith@redwood:~/comp162$ cat baddata 0 psmith@redwood:~/comp162$ gcc averages.c psmith@redwood:~/comp162$ (./a.out < gooddata > goodresults ) >& errorlist psmith@redwood:~/comp162$ cat goodresults Average of 7 numbers is 42.429 psmith@redwood:~/comp162$ cat errorlist psmith@redwood:~/comp162$ (./a.out < baddata > badresults ) >& errorlist psmith@redwood:~/comp162$ cat badresults psmith@redwood:~/comp162$ cat errorlist Error: Count is zero psmith@redwood:~/comp162$ exit Comp 162 Lab Notes Page 3 of 6 February 10, 2017
3. Command line arguments In general, the main function in a C program can have the following heading int main(int argc, char *argv[]) or int main(int argc, char **argv) When the program runs, the operating system: puts into argc (argument count) the number of tokens on the command line. This count includes the program name itself. Puts into the elements of array argv (argument vector) pointers to the texts of the tokens. Here is a simple program; it just echoes the components of the command line back to the standard output. Example #include <stdio.h> int main (int argc, char* argv[]) int i; printf("argc is: %d\n", argc); for (i=0; i<argc; i++) printf("argv [%d] is %s\n",i,argv[i]); Here is the compilation and a run of the program. hp9k2 22: gcc ctesta.c hp9k2 23: a.out one two three argc is: 4 argv [0] is a.out argv [1] is one argv [2] is two argv [3] is three Comp 162 Lab Notes Page 4 of 6 February 10, 2017
4. More pre-processor options The pre-processor runs before the compiler and processes lines beginning with #. Thus, it handles the #include lines. We can use #define to define symbols as in #define MAXMEM 4096 #define PI 3.14159 The pre-processor replaces instance of the symbol name by the corresponding number. For example, we could declare int memory[maxmem]. Also we can define symbols to control debugging as in the following example. #define DEBUG 1 main() int a[] = 2, 4, 3, -1, 7 ; int i,sum=0; for (i=0; i<5; i++) sum += a[i]; #ifdef DEBUG printf ("sum is now %d\n",sum); #endif printf("sum is %d\n", sum); The text between #ifdef and #endif is only included in the output from the preprocessor (and hence input to the compiler) if the symbol DEBUG is defined (ifdef) with a non-zero value. There is also #ifndef (if not defined) hp9k2 22: gcc ctestb.c hp9k2 23: a.out sum is now 2 sum is now 6 sum is now 9 sum is now 8 sum is now 15 sum is 15 To remove the debugging information from the input to the compiler we need only change the value of the symbol to 0 (or to omit its definition altogether) thus Comp 162 Lab Notes Page 5 of 6 February 10, 2017
#define DEBUG 0 main() int a[] = 2, 4, 3, -1, 7 ; int i,sum=0; for (i=0; i<5; i++) sum += a[i]; #ifdef DEBUG printf ("sum is now %d\n",sum); #endif printf("sum is %d\n", sum); Now when the program is compiled the debug statements are omitted hp9k2 26: gcc ctestb.c hp9k2 27: a.out sum is 15 You can probably see how debugging can also be controlled through command-line arguments. 5. Programs Program 1 is due today. Submit source code and testing results electronically. For program 2, breaking the solution into functions will help in development and testing. Think about setting up an index to the cellar perhaps using an array of structures. When testing the program, consider creating a file with a test sequence then use redirection as described in section 2 above. Comp 162 Lab Notes Page 6 of 6 February 10, 2017