START OF WEEK 3 of C <<>> -if you are viewing a copy of this file -check the original's timestamp -it may have been modified since you copied it <> -in stdio.h -reads a line from a stream (can be stdin) -includes the newline character, if one was found (like fgets) -will allocate a correct-sized buffer for us, if arguments set properly -will re-allocate buffer if we made it too small /*Source: readOneLine.c */ /*Input: a string any length */ /*Output: the string and its length */ /*Purpose: example of getline */ #include #include #include #include int removeNewline(char *S); int main(void) { char *str = NULL; //NULL tells getline to allocate for us size_t n=0; //length of original S ssize_t len; //getline's return value (new S length) printf("Enter a string: "); len = getline(&str,&n,stdin); if ( len == -1 ) { printf("error reading input\n"); exit(1) ; } printf("string is:%s:\n",str); printf("len is:%zu:\n",len); //gcc might allow %ld len = len - removeNewline(str); printf("\nremoved newline and now\n"); printf("string is:%s:\n",str); printf("length is:%zu:\n",len); exit(0); } //returns 1 if a newline was removed, 0 otherwise int removeNewline(char *S) { char *p; if ((p=strchr(S, '\n')) != NULL) {//if there is a newline *p = '\0'; return 1 ; } return 0; } Can test case of no newline by: echo -n "hi" | a.out readline (stdio.h) is similar, but "prompts" the user and allows user to edit their input, if desired. <> when compiler encounters a string constant, it stores it in a string table generates a pointer to the string (address) char *p; p="A String"; printf("%s",p); Note that the following is BAD: char *p; fgets(p,40,stdin); printf("%s",p); Why bad? p has no storage allocated for its string. It might run OK sometimes. Other times it won't, because the value pointed to by p is not an accessible storage location, or it IS, but that location gets overwritten. Suppose Memory: //has junk in it, as usual 1200 1208 1212 ... ------------------------ ... | 0 |1025 | 982 | ... ------------------------ char *p; 1200 1208 1212 ... ------------------------ ... | 0 |1025 | 982 | ... ------------------------ p fgets(p,40,stdin); //tries to store string at memory location //0, which is reserved for OS. Error. <> all arguments in C passed by value Same for arrays. But the value happens to be a pointer. int A[20]; 100 104 108 112 116 ... 176 ... 300 ------------------------ ----- --- | | | | | |... | |... |100| ------------------------ ----- --- A[0] A[1] A[2] A[3] A[4] ... A[19] A A is the address where the array starts (address of A[0]) Thus, a function call such as sum(A) passes the value of A (i.e., 100) which is ADDRESS of A[0]; so any changes to A within sum are retained (LIKE call by reference.) 100 104 108 112 116 ... 176 ... 300 ... 500 ------------------------ ----- --- --- | | | | | |... | |... |100| ... |100| ------------------------ ----- --- --- A[0] A[1] A[2] A[3] A[4] ... A X /*Source: passArray.c */ #include #define LEN 5 void doubleit(int *X) { int i; for (i=0;i #include void add1 (int *x) { *x = *x + 1 ; } int main (void) { int a=5; add1(&a); printf("a is %d\n",a); } This shows memory just after int temp; 1200 1204 1208 ... 1530 ... 2708 ... 2724 ---------------------- -------- -------- ------ ... | 0 | | 2 | ... | 1200 |...| 1208 |...| ?? | ---------------------- -------- -------- ------ i j arg1 arg2 temp /*Source: sample14.c*/ void swap(int *arg1, int *arg2); int main(void) { int i=0, j=2; printf("i is %d, j is %d \n", i,j); swap(&i,&j); printf("i is %d, j is %d \n", i,j); exit(0); } /*Function: swap Purpose: swap values of 2 integers Input: addresses of the 2 integers to swap Output: none */ void swap(int *arg1, int *arg2 ) { int temp; temp = *arg1; *arg1=*arg2; *arg2=temp; } prints? When pass an array, its POINTER is passed, so array values are modified int A[2] = {1,2}; 100 104 ... 300 500 ---------- --- --- | 1 | 2 |... |100| |100| ---------- --- --- A[0] A[1] A X /*Source: sample15.c */ void swap1 (int X[2] ); int main(void) { int A[2]={1,2}; printf("%d %d \n", A[0], A[1] ); swap1(A); printf("%d %d \n", A[0], A[1] ); exit(0); } void swap1 (int X[2] ) { int temp; temp = X[0]; X[0] = X[1]; X[1] = temp; } These function prototypes are all the same: void swap1(int X[2]); void swap1(int X[]); void swap1(int *X); <> A FUNCTION may return a pointer /*Source: sample16.c*/ #include #include #include char *addXX(char *B); int main(void) { char str1[25], *str2; gets(str1); str2=addXX(str1); puts(str2); exit(0); } char *addXX(char *B) { strcat(B,"XX"); return (B); } Q1: Does str2 have storage allocated? Q2: What would puts(str1) print after the call to addXX? Q3: What might happen if the user enters a string that is more than 22 chars long? HMWK: 1. Write a function with prototype: void swap_string1(char *A, char *B); The function swaps the strings in A and B. It does this by looping through the string(s) and swapping individual characters. If your function needs to know the length of a string, it can use strlen from string.h, or it can just look for the '\0' at the string end. Write a main program to test swap_string1. Your main must allocate all the storage required for the strings. DO NOT copy junk from memory. Only copy characters from the strings. <> char **mp, *p, ch; 1080 ... 1248 ... 1272 ----------- ---------- ---------- ... | | ... | | ... | | ----------- ---------- ---------- mp p ch p=&ch; mp=&p; **mp='A'; 1080 ... 1248 ... 1272 ----------- ---------- ---------- ... | 1248 | ... | 1272 | ... | A | ----------- ---------- ---------- mp p ch //same result as : ch='A' or *p='A' <> See below for meaning of operators, and meaning of associativity Operator Precedence Associativity (see below) () [] -> . left ++ -- sizeof ! ~ (type) + - * & right (ALL ARE UNARY OPERATORS) * / % left + - left (BINARY) << >> left < <= > >= left == != left & left (BINARY) ^ left | left && left || left ?: right = += -= *= /= %= &= ^= |= <<= >>= right , left Associativity means if 2 operators with equal precedence are in an expression, they will be done -from left-to-right if left-assoc -from right-to-left if right-assoc ~ is one's complement (unary) ! is logical not: if (!x) ++ -- are unary inc/decrement prefix or postfix: i++ or ++i unary * unary & are ptr dereference, address of << >> left bit shift, right bit shift && || logical and, or & | ^ bitwise and, or, Xor ?: ternary operation (for conditional e.g., z = (a>b) ? a : b ; resolves to: z = a OR z = b , depending Note: **a means *(*a) &(&a) and &&a are invalid &a[1] means &(a[1]) *a[1] means *(a[1]) Q4: WHAT IS THE PURPOSE OF THE mystery FUNCTION? int mystery( char *A) { int x=0; while ( *A++) x++; return x; } char *str="xyz"; Q5: Suppose it is called from main: x=mystery(str); Does mystery corrupt main's str? HMWK: Modify encode.c from bottom of c1.txt so that it reverses a string IN PLACE, using a swap function repeatedly. i.e., "abcdef" becomes "fedcba" Print string before, and after, the encoding ------------------------------------------------------------------- Begin OPTIONAL <> //source sqrt.c //compile with math library flag: gcc sqrt.c -lm #include #include #include #define POSITIVE 25 #define NEGATIVE -25 int main(void){ double ret; errno = 0; //declared in errno.h ret = sqrt(NEGATIVE); if (errno == EDOM) /*EDOM Signifies Domain Error*/ //printf("Domain Error : Invalid Input To Function\n"); perror("sqrt"); else printf("Valid Input To Function\n"); errno = 0; ret = sqrt(POSITIVE); if (errno == EDOM) printf("Domain Error : Invalid Input To Function\n"); else printf("Valid Input To Function\n"); return 0; } If a function sets errno, it is stated in man page. e.g., man scanf then search for errno then search for ERROR End OPTIONAL ------------------------------------------------------------------- <> main is a function--so can pass arguments to it using Argument Count, and Argument Vector int main(int argc, char *argv[]) suppose -create executable: > gcc -o cla cla.c -and execute: > cla abc "de f" 74 -then inside cla.c: argc is 4 // # args + 1 (#elts in argv) argv[0] is "cla" // program name argv[1] is "abc" // argument 1 argv[2] is "de f" // argument 2 argv[3] is "74" // argument 3 Program could print its COMMAND LINE ARGUMENTS (CLAs) as follows: //Source: cla.c int main (int argc, char *argv[]) { int i; for (i=1; i #include int main (int argc, char *argv[]) { int i, /*for loop index*/ sum=0; /*the sum of the arguments*/ for (i=1; i #include int main (int argc, char *argv[]) { if (argc != 3 ) { fprintf(stderr, "Usage: %s int1 int2\n", argv[0]); exit(BadInput); } int x=atoi(argv[1]); if (atoi (argv[1]) > atoi (argv[2]) ) fprintf(stdout, "%s\n", argv[1]); else fprintf(stdout, "%s\n", argv[2]); exit(GoodInput); } <> already used fprintf/fputs to print to FILEs stdout, stderr fprintf(stderr, "bad input: %d\n", x); Others: fscanf (fp, format, arg_list...) fgetc (fp); fputc (ch, fp) fgets (str, length, fp); fputs (str, fp) getline(&str, &n, fp); Open file named myfile for reading: FILE *fp; // FILE in stdio.h if ( (fp = fopen("myfile","r")) == NULL ) { fprintf(stderr,"fopen: Error opening file myfile \n"); //perror("fopen"); exit(1); } fscanf(fp, "%d", &i); Mode (similar to Python's modes for open) ---- r open text file for read (from beginning) w " " write (create if !exist or clobber if exist) a append to text file (write at end) ------------------------------------------------------------- OPTIONAL w+ open t.f. for r/w (create if not exist, or clobber if exists) r+ open t.f. for r/w (at beginning) won't create; won't clobber. a+ append or create for r/w (read from start, write to end) Note: w & w+: if file doesn't exist, created if exists, destroyed & new created (clobbered) r+ reads and writes from same pointer which starts at beginning of file. If write first, overwrites existing file, and file pointer left at end of writing. Subsequent reading starts reading where write left off. Try program tryrplus.c which uses file tryrplusFILE, which should be the same as tryrplusFILEorig before running program w+ no point reading immediately, because file is empty. If write first, file pointer ends up at end of writing. To subsequently read what wrote, frewind(fp); to get pointer back to start. Get filepointer position, or move it: fseek, ftell, rewind, fgetpos, fsetpos ------------------------------------------------------------- Close a file: fclose(fp); 0 if successful EOF if not exit will close all files before pgm termination anyway. But... if you have a long-running program, and you keep the file open when not needed, you are using resources unnecessarily. Also, if your program opens a lot of files, you may run out of file descriptors, since each program is allocated a fixed number of fd's by the system. fgetc(fp); returns: char read if succ (cast to int) EOF otherwise fputc(ch,fp); returns: char written if succ (cast to int) EOF otherwise /*Source cio.c Purpose: write a string into file myfile1 */ #include #include #define GOOD 0 #define BAD 1 int main(void) { char str[40] = "String to write onto disk"; FILE *fp; char ch, *p; if ((fp = fopen("myfile1","w"))==NULL) { fprintf(stderr,"fopen: Cannot open file\n"); //perror("fopen"); exit(BAD); } p=str; while (*p) if (fputc(*p++,fp)==EOF) { fprintf(stderr,"fuptc: Error writing file\n"); //perror("fputc"); fclose(fp); /*good form?*/ exit(BAD); } fclose(fp); /*good form?*/ exit(GOOD); } Q6: Why did we use p at all? Why not just do *str++ ? /*Source: cio2.c Purpose: display contents of myfile on stdout */ #include #include #define GOOD 0 #define BAD 1 int main(void) { FILE *fp; int ch; if ((fp=fopen("myfile", "r"))==NULL) { fprintf(stderr,"fopen: Cannot open file\n"); exit(BAD); } while (( ch = fgetc(fp)) != EOF) fputc(ch,stdout); fclose(fp); exit(GOOD); } HMWK: Modify cio2.c to turn it into a simple version of unix utility "cat". If no cmd-line input, prints stdin on stdout. If cmd-line inputs (files), print each on stdout /*Source: copy.c Purpose: copy file f1 to file f2 Usage: copy f1 f2 */ #include #include #include #define GOOD 0 #define BAD 1 int main (int argc, char *argv[]) { FILE *f1, *f2; int ch; if (argc != 3) { fprintf(stderr, "Usage: %s \n",argv[0]); exit(BAD); } if ((f1=fopen(argv[1],"r"))==NULL) { fprintf(stderr, "Cannot open %s\n",argv[1]); exit(BAD); } if ((f2=fopen(argv[2],"w"))==NULL) { fprintf(stderr, "Cannot open %s\n",argv[2]); exit(BAD); } while ((ch=fgetc(f1)) != EOF) fputc(ch,f2); fclose(f1); fclose(f2); exit(GOOD); } fgetc() returns EOF if 1) error in reading 2) hits EndOfFile can test for these separately feof(fp) non-0 if at EOF 0 otherwise ferror(fp) non-0 if error reading file 0 otherwise e.g., while-loop of copy pgm could be: while (!feof(f1)) { ch=fgetc(f1); if (ferror(f1)) { fprintf(stderr, "Error reading ..." exit(BAD); } if (!feof(f1)) fputc(ch,f2); if (ferror(f2)) { fprintf(stderr, "Error writing ... " exit(BAD); } } recall: fgets(str, length, fp); reads chars from FILE fp and stores them in array str, until 1. '\n' read (and transfered to s) 2. (length-1) chars have been read, or 3. EOF returns ptr to str if successful NULL ptr otws Note: -already used it with fp as stdin -the file read pointer advanced "length-1" chars in the file. e.g., fgets(X,3,f1); fgets(Y,3,f1); for file with 12345 abcde will put "12" in X and "34" in Y fputs(str,fp) writes str to "file" ptd to by fp returns EOF if error; positive int otws HMWK: 1. Rewrite copy.c using fgets, fputs, feof and ferror assuming max line length in file is 128 chars. HMWK: 1. Write a program named prt.c that reads text from stdin until EOF and prints the number of lines and/or characters that were read in. The program takes one command line argument, which is l or c or b. If the argument is l, the program prints the number of lines, if c, the number of characters, and if b, it prints the number of both lines and chars. 2. Re-write program prt.c above except this time, the program takes 2 arguments, the second argument being the name of a file from which to read the text. Perform *full* error checking. 3. Re-write the above prt.c so that any number of files are processed (number of chars/lines printed for EACH, in order processed.) 4. Use fscanf and fprintf to read a file with 2 columns of integers, such as: 2 4 7 9 10 6 and write the sum of each line to another file, e.g, 6 16 16 The file names are given on the command line -------------------------------------------------------------- Begin OPTIONAL <> You could also use function perror (print error) instead of fprintf(stderr...); When any system or library function is called *and fails*, its return code (integer) is put in a system variable that perror can access. (clobbered with each failure). perror translates the integer to an (human understandable) phrase and prints on stderr, prefixed by its string argument e.g., FILE *fp; if ( (fp = fopen("myfile","r")) == NULL ) { perror("fopen"); exit(1); } if "myfile" did not exist, stderr gets: fopen: No such file or directory (most of the built-in C functions use perror) All the error codes are defined in /usr/include/asm-generic/errno.h perror is in section 3 of man pages (man 3 perror) See example file per.c Can also manipulate BINARY files. End OPTIONAL ------------------------------------------------------------------- <<< Dynamic Memory Allocation (DMA) >>> So far, have allocated storage (memory) at compile-time (stack memory) e.g., int x, A[MAX]; Can also allocate/free storage at run-time (heap memory) with DMA Why use DMA? -need the memory maintained after function return (stack memory is lost upon return) -need LOTS of memory. Typical stack size is 1mb, so risk crash if need more -need to build up a structure (graph/list/array etc) which may get huge, or whose size changes dynamically, or whose size is too difficult to calculate in advance. With DMA, can request/free memory chunk by chunk, as required -need ability to free no-longer-needed memory during program execution calloc(NumItems, ItemSize); NumItems: number of items of storage requested ItemSize: size of each item (in bytes) calloc returns -a void (generic) pointer -often cast to desired type, to improve readability, but not required/necessary -NULL if storage cannot be obtained storage is initialized to zeros see trycalloc.asciiart for memory illustration malloc(Size); Size: number of bytes like calloc, except -must do the math ourselves -storage not initialized e.g., to reserve an area for array of n integers /*source: trycalloc.c */ #include int main(void) { int n; /*num elts of future array*/ int *p, *tmp; /*pointers to the future array*/ int i; scanf("%d",&n); p = (int *) calloc (n, sizeof(int)); if (p == NULL) { fprintf(stderr,"calloc failed\n"); //or perror("calloc"); exit(0); } tmp = p; /*load array with 0,1,2,...(n-1) */ for (i=0; i #include int main(void){ char *A; int max=0; //need to add error-checking printf("enter max string length: "); scanf("%d",&max); while ((getchar())!='\n'); //get past \n char A=(char *)malloc(max+1); //room for null char printf("enter string: "); fgets(A,max+1,stdin); printf("Third char is: %c\n",*(A+2)); //or A[2] printf("string is: %s\n",A); exit(0); } Swap 2 strings without moving their characters in memory: //Source: swap_string1.c #include void swap_string1( char **A, char **B) { char *Z; Z=*A; *A=*B; *B=Z; } int main (void) { char *X="abc", *Y="def"; //why not char X[4]="abc", Y[4]="def" ? printf("X: %s ", X); printf("Y: %s\n", Y); swap_string1(&X, &Y); printf("X: %s ", X); printf("Y: %s\n", Y); } Before call to function swap_string1: suppose this part is string table 50 58 100 101 102 300 301 302 ----------------------------------------------------------- | 100 | 300 | | | | a | b | c | ... | d | e | f | ... ----------------------------------------------------------- X Y In call to function swap_string1 (after Z=*A): 50 58 100 101 102 300 301 302 ---------------------------------------------------------- | 100 | 300 |50 |58 |100|...| a | b | c | ... | d | e | f | ... ---------------------------------------------------------- X Y A B Z After call to function swap_string1: 50 58 100 101 102 300 301 302 -------------------------------------------------------- | 300 | 100 | | | | a | b | c | ... | d | e | f | ... -------------------------------------------------------- X Y Swapping 2 non-hard-coded strings allocated using DMA //Source: swap_string2.c #define _GNU_SOURCE //may be required #include #include #include #define L 6 void swap_string2( char **A, char **B) { char *Z; Z=*A; *A=*B; *B=Z; } int main (void) { char *X, *Y; X=malloc(L); //error-check this Y=malloc(L); //error-check this //L-2 below to ensure \n is read (otws Y gets tail of line1). printf("Enter string1 (max length %d) : ",L-2); fgets(X,L,stdin); //should error check length *(strchrnul(X,'\n'))='\0'; //remove newline, if there printf("Enter string2 (max length %d) : ",L-2); fgets(Y,L,stdin); *(strchrnul(Y,'\n'))='\0'; printf("X: %s ", X); printf("Y: %s\n", Y); swap_string2(&X, &Y); printf("X: %s ", X); printf("Y: %s\n", Y); }