START OF WEEK 2 of C <<< FILE TIMESTAMP >>> -The up to date file is in: /usr/courses/cps393/dwoit/courseNotes/ -If you are viewing your own copy, check it is up to date -If you are viewing from a link in the Course Outline, be aware it may be outdated. <<< Static Functions and Variables >>> A static variable is known only to the function in which it is declared, and retains its value from one call of the function to the next. A static function is known only in the file in which it is defined/declared (like being private to that file.) e.g., Function q is only available to code in file f.c. It is not available to main or any other code. If you add q's prototype and try to use q() from main, it gives "undefined" error //Source f.h int f (int x); //Source f.c static int q(int x) ; int f (int x) { return (q(x)); } static int q(int x) { return(x*x); } //Source: m.c #include #include "f.h" int main(void) { int y=f(7); printf("%d\n",y); //y=q(4); return(0); } --------------------- Static variables within functions: On a moon, the following program prints: 1 1 1 1 1 1 2 3 4 5 //Source: accum.c int fs(int x) { static int count=0; count=count+x; return (count); } int fn(int x) { int count=0; count=count+x; return (count); } int main(void) { int i; for (i=0;i<5;i++) printf("%d ",fn(1)); putchar('\n'); for (i=0;i<5;i++) printf("%d ",fs(1)); putchar('\n'); } -A function,f, that is not declared "static" will be exportable. i.e., you can put f's definition in f.c and declaration in f.h Then if you include f.h in another file, M.c, and compile both M.c and f.c, f will be available to the code in M.c -If f were static, then it would NOT be available to code in M.c DW shows Arrays and Strings <<< Arrays and Strings >>> Some differences from Java. There are no vectors in C; use arrays instead. << 1-Dimensional >> int A[20]; //allocates A[0] A[1] ... A[19] Assign: A[14]=1005; Reference: printf("%d", A[14]); Initialize at declaration: int A[5] = {1,2,7,15,3}; int B[5] = {1,2}; //missing ones get zero or later: int A[5]; for (i=0; i<5; i++) scanf("%d",&A[i]); No bounds checking: printf("%d",A[92]); compiles OK and runs See outOfBounds.c char a[10], b[10]; for (i=0; i<10; i++) b[i]=i; Incorrect: a = b; //compiler error Correct: for (i=0; i<10; i++) a[i]=b[i]; /*Source: FindLtr.c Purpose: output occurrences of A,B,...Z found in input Input: chars from stdin Output: each upper case letter and its occurrences */ #include #include int main(void) { int i, /*index */ c, /*char from stdin*/ letter[26]; /*array for upper case letters*/ for (i=0; i<26; i++) letter[i]=0; /*initialize array*/ while((c=getc(stdin)) != EOF) /*^d on Linux */ if (isupper(c)) ++letter[c - 'A']; /*increment right slot*/ for (i=0; i<26; ++i) { /*print results*/ if (i%6 == 0) printf("\n"); /*new line every 6 chrs*/ printf("%5c: %4d", 'A'+i, letter[i]); } printf("\n\n"); exit(0); } Try running above executable with some input redirected from an input file e.g., a.out > -non-constant size allowed, e.g., scanf("%d",&n); int A[n]; -but some limitations. Can't use VLA if -array is static or extern (later) -array is member of a structure or union (later) -compiler is one from before standard allowed VLAs -VLA must be either block scope or function prototype scope -block scope: statements within { } -function prototype scope: within function declaration only -if a situation where can't use VLA, or -if defining array within a function, and want its storage to persist after function ends, need something else... Could use calloc (more on it later) scanf("%d",&n); //assume read in 10 //assume read in 10 int *A = calloc (n, sizeof(int)); //A is array with storage for for (i=0; i> Similar to Java except In C, no string type. Instead: string: null-terminated char array null is '\0' (= byte 00000000) declare array 1 larger than string length (for \0) compiler automatically null-terminates string constants char S[12]="Hello There"; //include one for \0 gets() in stdio.h reads chars up to and including carriage return (or EOF) discards the '\n' (or EOF) and null-terminates string gets OK for cps393, but its use is depreciated No gets prototype in but implementation still in gcc so still works--for now. /*Source: str1.c */ #include int main(void) { int i; /*index*/ char str[40]; /*string max 39 chars*/ printf("Enter a string (max 39 chars):"); gets(str); for (i=0; str[i]; i++) /*loops until str[i] is '\0' */ printf("%c",str[i]); /* same as printf("%s",str); */ putchar('\n'); exit(0); } DW shows fgets Some compilers give error instead of warning for gets. Better to use: fgets -similar, but "safe". -no potential memory overwrite. Not depreciated. fgets(str, length, fp); reads chars from "file" pointed to by fp (fp could be stdin) and stores them in array str, until one of: 1. '\n' read (and transferred to str) 2. (length-1) chars have been read, or 3. EOF terminates str with a '\0' returns ptr to str if successful NULL ptr otws e.g., fgets(str,40,stdin); would stop memory overwrite However, it also puts \n in str (if str not full) If you don't want the \n, you have to remove it yourself (copy a '\0' over the '\n'). /*Source:tryfgets.c*/ #include int main(void) { int i; /*index*/ char str[7]; /*string max 6 chars*/ printf("Enter a string (max 5 chars):"); //leave slots for newline and null fgets(str,7,stdin); fputs(str,stdout); exit(0); } Note: Prompt for string of 5 chars to leave room for (\n and \0) enter <=5 chars, it includes the newline in str enter >5 chars, there is no \n, and 7th slot (str[6]) gets \0 In string.h: strcpy, strcat, strcmp, strlen etc. man string.h for macros, functions man string for brief descriptions of functions char a[40], b[40]; strcpy(b,"Hello"); strcpy(a,b); printf("%s %s",a,b); //prints Hello Hello strcpy(a,"Hi"); strcat(a," There"); printf("%s",a); //prints Hi There strcmp(x,y) returns: 0 if same <0 if x < y in dictionary order (str length irrelevant) >0 if x > y strcpy(b, "Hello"); strcpy(a, "Hellp"); printf("b has length %zu",strlen(b)); //%d works too printf("%d", strcmp(a,b)); //prints a positive (1) since a > b printf("%d", strcmp(b,a)); //prints a negative (-1) since a > b printf("%d", strcmp("Hello",b)); //prints 0 null string contains only '\0' strcpy(a,""); /*or*/ a[0]='\0'; int i; i=atoi("127"); //in stdlib.h -converts digit string to integer printf("%d",i); // prints integer 127 -"-127" OK too -Also atol (long), atof (float), etc DW mentions sprintf, sscanf Other useful string functions are: sprintf, sscanf sprintf(str, formatString ...); -like printf, but prints into string stf instead of stdout char S[20]; double x=94.12345; printf( "%.2lf", x); --> "94.12" printed on stdout sprintf(S, "%.2lf", x); --> S is "94.12" the '\0' added automatically sscanf(str, formatString ...); does a scanf from a string instead of stdin -could use gets (or getline below ) to read whole line including \n into str (no worry about scanf "stuck" on the \n) -then use sscanf to read from str char S[40]; int x,y; gets(S); sscanf(S, "%d %d", &x,&y); DW shows MDAs << Multi-dimensional Arrays >> int X[3][4]; /*3 rows (0-2), 4 columns (0-3)*/ int X[3][4][2]; /*visualize as a cube*/ /*3 4x2 arrays */ Initialization: int D[2][3] = { 2,4,7, 6,9,5 }; /*D[0][0] is 2; D[0][1] is 4, etc */ or use more braces to help visualize: int D[2][3] = { { 2,4,7 }, { 6,9,5 } }; /*D[0][0] is 2; D[0][1] is 4, etc */ e.g., /*Source: 3dimary.c*/ #include #include int main(void){ //A and B contain same values int A[2][3][4]= {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24}; int B[2][3][4] = { { {1 , 2, 3, 4}, {5 , 6, 7, 8}, {9 ,10,11,12} }, { {13,14,15,16}, {17,18,19,20}, {21,22,23,24} } }; int i,j,k; for (i=0; i<2; i++) for (j=0; j<3; j++) for (k=0; k<4; k++) printf("B[%d][%d][%d]=%d\n",i,j,k,B[i][j][k]); exit(0); } Array Initialization: -Automatic (local) arrays NOT initialized by compiler -Global & Static initialized to 0 by compiler (like all global and static variables) Unsized Arrays: (compiler counts # of constants to find size needed) int A[] = {1,4,2,6}; /*size 4*/ char UserMsg[] = "Enter a number: "; int b[][2] = { 1,4, 7,9, 3,5 }; Need to include one dimension-count to help compiler Why unsized? easy modification, especially if use sentinel when looping (no change of declaration size and no change of loop index max) DW shows Arrays of Strings Arrays of Strings: char colors[5][9]; /*5 strings, each string max 9 chars incl null*/ char colors[][7] = { "blue", "red", "pink", "purple" }; printf("%s",colors[2]); //prints pink printf("%c",colors[2][3]); //prints k char names[3][40]; gets(names[0]); gets(names[1]); gets(names[2]); /*fills array*/ for (i=0;<3; i++) printf("%s\n",names[i]); DW mentions /*Source: printword.c Purpose: print the english word equivalent of a digit Input: a digit, e.g., 9 Output: the word equivalent, e.g., nine Note: should exit with bad code if invalid input */ #include int main(void) { /*set up array of english words for digits*/ char digits [10][6]= { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" }; char num; /*digit to read in*/ printf("Enter a digit: "); num = getchar(); num = num - '0'; if (num >= 0 && num < 10) printf("\n%s\n", digits[num]); else printf("\nThat wasn't a digit\n"); return 0; } ASCII for '0' is 48, for '1' is 49 etc so if enter 2: num = '2' - '0' = 50 - 48 = 2 so digit[2] is printed Could use scanf("%d"...) instead, but need additional logic in case user enters something like: A DW shows passing arrays to Fns Passing arrays to Functions: /*Source: trypas.c*/ #include #include void f1(int num[],int loc), f2(int num[],int loc); int main(void) { int i,loc=0; int count[] = {0,0,0,0,0}; f1(count,loc); f2(count,loc); printf("count="); for (i=0; i<5; i++) printf("%d ", count[i]); printf("\nloc=%d\n",loc); exit(0); } void f1(int num[],int loc) { num[0] = 99; loc=99; } void f2(int num[],int loc) { num[1] = 22; loc=22; } It prints: count=99 22 0 0 0 loc=0 Why? loc makes sense, but why was count changed? C passes call-by-value, (a COPY of the value is passed), so what's happening here? *Address* of arrays are passed down (only for arrays, not regular variables) Therefore, it's LIKE arrays are passed call by reference (but they're not, really). Will be more clear after pointers (see Pointers as Parameters) /*Source: encode.c Purpose: encode a string by alternately taking chars off each end (starting with left side) and stopping when middle of string reached e.g., "Hi there" would become "Heir eth" */ #include #include #include int main(void) { char str[80]; int i,j; printf("Enter string to be encoded: "); gets(str); /*encode it*/ i=0; j=strlen(str)-1; while (i <=j) { if (i>> Special data type allows storage of location (address) of data rather than data itself (code in firstPtr.c) Memory: 1200 1204 1208 ... 1531 ------------------------ ---------- ... | | | | ... | | ------------------------ ---------- int Num; 1200 1204 1208 ... 1531 ------------------------ ---------- ... | | | | ... | | ------------------------ ---------- Num Num=5; 1200 1204 1208 ... 1531 ------------------------ ---------- ... | 5 | | | ... | | ------------------------ ---------- Num int *NumPtr; 1200 1204 1208 ... 1531 ------------------------ ---------- ... | 5 | | | ... | | ------------------------ ---------- Num NumPtr NumPtr = &Num; 1200 1204 1208 ... 1531 ------------------------ ---------- ... | 5 | | | ... | 1200 | ------------------------ ---------- Num NumPtr Num is type int (value is 5) NumPtr is type "pointer to int" (value is 1200 here) &Num is the address of variable Num (1200 here) *NumPtr is the value at address in NumPtr (de-reference NumPtr) or, what NumPtr "points to" (5 here) printf("%d", *NumPtr ); //prints 5 *NumPtr = 7; 1200 1204 1208 ... 1531 ------------------------ ---------- ... | 7 | | | ... | 1200 | ------------------------ ---------- Num NumPtr printf(" %p %d %d ", NumPtr, *NumPtr, Num ); //1200 7 7 <> an array name is an address (i.e., a pointer) (address of the 1st item of the array) (code in ptr1.c) int A[5] = {1,2,3,4,5}; 100 104 108 112 116 ... 236 ... 300 ------------------------ ----- --- | 1 | 2 | 3 | 4 | 5 |... | |... |100| ------------------------ ----- --- A[0] A[1] A[2] A[3] A[4] A int *p; 100 104 108 112 116 ... 236 ... 300 ------------------------ ----- --- | 1 | 2 | 3 | 4 | 5 |... | |... |100| ------------------------ ----- --- A[0] A[1] A[2] A[3] A[4] p A p=A; 100 104 108 112 116 ... 236 ... 300 ------------------------ ----- --- | 1 | 2 | 3 | 4 | 5 |... |100|... |100| ------------------------ ----- --- A[0] A[1] A[2] A[3] A[4] p A printf("%d %d %d ", *p, *(p+1), *(p+2) ); putchar('\n'); printf("%d %d %d ", p[0], p[1], p[2] ); putchar('\n'); printf("%p %p %p ", p, p+1, p+2 ); 1 2 3 1 2 3 100 104 108 //assuming above storage placement why 104 ? 4 byte ints on our machine--use sizeof(int) to see. ^ sizeof is a keyword (built-in like "for") depends on type of p. if int *p then p+1 is p + (4 bytes) if char *p then p+1 is p + (1 byte) char B[] = "bcd"; char *p = B; //same as: char *p = &B[0]; assuming &B[0] is 100, printf("%p %p %p ", p, p+1, p+2 ); prints? //100 101 102 SAME AS ABOVE: char B[] = "bcd"; char *p; p=B; //or p=&B[0] 100 101 102 103 104 ... 236 ... 300 ------------------------ ----- --- | b | c | d | \0 | |... |100|... |100| ------------------------ ----- --- B[0] B[1] B[2] B[3] p B Note p[1] same as B[1] same as *(p+1) i.e., 'c' p[0] same as B[0] same as *p i.e., 'b' May encounter code such as the following: //Source: cptr.c char S[9]="Pointers"; char *cptr; cptr=S; while ( putchar(*cptr++) ); //putchar returns the char it printed //this prints the '\0' but the following //do not: // or, more clearly // while ( *cptr ) putchar(*cptr++); // or even more clearly // while ( *cptr != '\0' ) putchar(*cptr++); // or even more clearly // while ( *cptr != '\0' ) { // putchar(*cptr); // cptr++; // } /*Source: sample12d.c */ /*Input: a string max length 39 */ /*Output: the string in upper case */ /*Purpose: convert string to upper case */ #include #include #include int main(void) { char str[40], *p; printf("Enter a string: "); (void) gets(str); //if don't want warning see sample12dF.c p=&str[0]; //or p=str; while ( *p != '\0' ) { *p = toupper(*p); p++; } printf("%s\n",str); //printf("str=%c str+1=%c str+2=%c\n",*str,*str+1,*str+2); exit(0); } Try entering abc 100 101 102 103 236 300 --------------------------- ----- ----- | a | b | c | \0 | ... | | ... |100| --------------------------- ----- ----- str[0] str[1] str[2] str[3] p str If uncomment the last printf, what does it print when enter abc? What does it print if enter arm? Why? (think about precedence). /*Source highest.c*/ #define ELTS 5 #include #include int main(void) { int i, *high, *p; int array[ELTS]={200,34,78,600,45}; high=array; p=array; for ( i=1; i< ELTS; i++ ) { p++; if (*p > *high) high = p; } printf("the highest number is %d \n", *high); printf("at address %p \n", high); printf("at index %ld of array \n", high-array); exit(0); } prints?