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. 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 10 ints: A[0] A[1] ... A[19] //vs Java: int [] A = new int[20]; Assign: A[14]=1005; Access: printf("%d", A[14]); Initialize at declaration: int A[ ] = {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]); Variable Length Array (VLA): int SIZE=10; int A[SIZE]; //initialize at declaration not allowed //(for other limitations see below) No bounds checking: printf("%d",A[92]); compiles OK and runs See outOfBounds.c char a[10], b[10]={1,2,3,4,5,6,7,8,9}; 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 occurencs */ #include #include #include int main(void) { int LTRS=26, //number of A,B,...Z i, //index c, //char from stdin letter[LTRS]; //array for upper case letters for (i=0;i ./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) -if n gets 10, calloc makes array size 10 and automatically initializes all entries to zero scanf("%d",&n); //assume read in 10 int *A = calloc (n, sizeof(int)); << Strings >> 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 still works for now, but compiler warning /*Source:trygets.c*/ #include #include #include int main(void) { int i; //index char str[10]; //string max 9 chars printf("Enter a string (max 9 chars):"); gets(str); //reads stdin \n but discards puts(str); //outputs a \n at end /*these two also output str (but no \n at end) printf("%s",str); for (i=0; str[i]; i++) printf("%c",str[i]); 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,10,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*/ /*Note this program prints one more \n than trygets.c this is because fgets stores a \n in str (one \n) and puts always prints a \n at the end (second \n) */ #include #include #include int main(void) { int i; //index char str[10]; //string max 9 chars printf("Enter a string (max 9 chars):"); fgets(str,10,stdin); //stores \n before \0 if room puts(str); //outputs a \n at end /*these two also output str (but no \n at end) printf("%s",str); for (i=0; str[i]; i++) printf("%c",str[i]); putchar('\n'); */ exit(0); } In string.h: strcpy, strcat, strcmp, strlen etc. man string.h for functions, macros man string for brief descriptions of functions char a[40], b[40]; strcpy(b,"Hello"); printf("b has length %zu",strlen(b)); //%d or %lu work too 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 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 in c3 ) 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[2][3][4]; /*visualize as a cube*/ /*2 3x4 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("%c"...) instead. 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 in c3 notes.) /*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). <<< Intro to Macros >>> -macros are compiler preprocessor directives -get replaced BEFORE compile -performance, easy global changes, sharing, readability OBJECT-LIKE MACROS #define MAX 100 -replaces all MAX with 100 BEFORE compile -but typically would use a variable instead unless -program is complex (shared by multiple program files) -macro is array size and want to INITIALIZE at declaration (compiler does not allow initialization of VLA) //Source: readArray.c #include #define MAX 10 int main (void) { int i; //initializing is not allowed if MAX is a variable int A[MAX]={1,2,3,4,5}; printf("Backwards those are: "); for (i=MAX-1;i>=0;i--) printf("%d ",A[i]); printf("\n"); return 0; C standard libraries often provide macros for us. e.g., -man math.h to see M_PI, M_SQRT2 (Pi, square root of 2) -man stdbool.h to see true, false, bool -man stdio.h to see EOF, FILE, stdin, stdout, stderr //Source: printPi.c // gcc printPi.c -lm #include #include int main (void) { printf("Pi to 18 decimal places is %.18lf \n",M_PI); return 0; } PRE-DEFINED MACROS (first 3 are strings): __FILE__ __DATE__ __TIME__ __LINE__ printf("this executable was compiled from source file %s\n",__FILE__); printf("current line number is %d\n",__LINE__); FUNCTION-LIKE MACROS #define ADD(x,y) x+y -then later printf("%d", ADD(3,2)); //expands to printf("%d", 3+2); -use parentheses for precedence issues, e.g., #define SQUARE(x) x * x means z=SQUARE(v+3); expands to: z=v+3 * v+3; which gives wrong answer (since 3 * v is done first) so do: #define SQUARE(x) (x) * (x) /*Source: macros.c Purpose: to use some macros Input: length and width of the room (ints) Output: Name of source code file and area of room */ #include #include #define GOOD 0 #define BAD 1 #define MUL(x,y) (x)*(y) int main(void) { int len, wid, scanReturn; printf("Original source code of this executable is %s\n",__FILE__); printf("and was compiled on date %s\n",__DATE__); printf("Enter length and width of the room (integers): "); scanReturn = scanf ("%d %d",&len, &wid); if (scanReturn != 2 ) { printf("bad input!\n"); //e.g., entered: 17 a87 exit(BAD); } printf("The Area of a %d x %d room is: %d\n", len, wid, MUL(len,wid)); exit (GOOD); } -may see more about macros later /*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?