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 <stdio.h>
#include <stdlib.h>
#include <ctype.h>
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<LTRS;i++) letter[i]=0;     //initialize array to zeros

  while((c=getchar()) != EOF)           //^d on Linux (end of input)
    if (isupper(c)) ++letter[c - 'A'];  //increment right slot

  for (i=0; i<LTRS; ++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 input redirected from a file
e.g., > ./a.out <some_file


DW shows VLA limitations and preview of calloc


<< Variable Length Array (VLA) >>

  -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
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 <stdio.h>
#include <stdlib.h>
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 <stdio.h>    
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 <stdio.h>
#include <stdlib.h>
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
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<j) printf("%c%c",str[i],str[j]);
    else printf("%c", str[i]); /*i==j*/ 
    i++; j--;
  }
  printf("\n");
  return 0;
}

Try tracing with "My Dogs":

i   j   output
-   -   ------
0   6   Ms
1   5   yg
2   4   _o
3   3   D

          Giving:  Msyg oD



<<<Pointers>>>

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




<<Pointers and Arrays>>

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 <stdio.h>
#include <stdlib.h>
#include <ctype.h>
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 <stdio.h>
#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 <stdio.h>
#include <math.h>
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 <stdio.h>
#include <stdlib.h>

#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 <stdio.h>
#include <stdlib.h>
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?