You are on page 1of 139

C Course Outline

Part I

(1)  Why learn C?
(2)  Programming building blocks
(3)  Basic memory model
(4)  Basic C Syntax
(5)  Allocating memory
(6)  Arithmetic expressions
(7)  Logical expressions
(8)  The switch statement
(9)  Looping
(10) Selected Exercises

Part II

(11) Flowcharting
(12) Pseudocoding
(13) Using the string library
(14) Character (byte) manipulation
(15) Accessing bytes via pointers
(16) Character based i/o
(12) Character macros
(13) File i/o
(14) Reading a file character by character
(15) Writing to a file
(13) Using the math library
(14) Type conversion functions
(15) Selected exercises

Part III

(16) Arrays
(17) Two dimensional arrays
(18) Pointers
(19) Functions
(20) Writing functions as macros
(21) Parameter passing
(22) Passing arrays into functions
(23) Scope of data
(24) Scope of functions
(25) Selected exercises

Part IV : 

(26) Design tips:
      . flowcharting, pseudocoding, C coding, debugging
(27) Structures
(28) Reading and writing to structures
(29) Run­time models
(30) How do we use malloc() and free()?
(31) Tips on using malloc() and free()
(32) Allocating structures at run­time
(33) Definining types
(34) Include files
(35) Unions
(36) Advantages of unions
(37) Using unions in real life
(38) Function pointers
(39) Application of function pointers
(40) Bit­wise operators
(41) Encoding bit fields
(42) Bit fields in customer records
(43) Selected exercises

Part V : Projects
Why learn C?

. C is a versatile all­purpose programming language that
  allows programmers to create many types of applications at
  a variety of levels from low level device drivers to high level
  graphics applications and expert systems, hence its popularity. 
. C programs, if written properly, can compile almost directly
  into machine code making them very fast.

. C is a non­verbose language, crisp and concise, allowing
  for a minimum of coding at a maximum of power.

. C is an industry standard on major platforms including:
     Unix workstations
     Micros

. C is the basis of C++, the most widely used OO language
Programming building blocks

All programs can be seen as a collection of instructions that:

(1) Perform input/output to/from external devices

(2) Read/write data to/from memory

(3) Perform calculations on data

(4) Test conditions on data

(5) Loop, repeat the above tasks any number of times

This is a powerful simplification of the core components of a program.  Being able to every 
operation in C programs as belonging to a category, greatly simplifies your understanding of the 
language.

Overview of a computer system

        ­­­­­­­­         ­­­­­­­­­­         ­­­­­­­­­­­­­­­­  
       |   CPU  |       |  MEMORY  |       |  I/O DEVICES   |
       |        |  <­>  |          |  <­>  |  & peripherals |
        ­­­­­­­­         ­­­­­­­­­­         ­­­­­­­­­­­­­­­­

                             /\
                             ||
                             \/

                          ­­­­­­­­­­
                         | NETWORKS |
                         | to other |
                         | computer |
                         | systems  |
                          ­­­­­­­­­­
Basic Memory Model

When you begin to code C programs you will have to become intimate with memory, especially 
when the concept of pointers to memory locations crops up.

To begin our understanding of the C language, let's look at a simplified model of the computer's 
memory.

   ­­­­­­­­­­­­
  |System memory  } Not allowed to be accessed by your program
  |                          
  |­­­­­­­­­­­
  |Code section   }<­­ program executable (machine code)
  |­­­­­­­­­­­­   }
  |Text section   }     (constant data)
  |­­­­­­­­­­­­
  |Variable space }<­­ your program uses this
  |section             area of memory to do
  |­­­­­­­­­­­­        useful work while it is running
  |..
  |  <free memory> }<­­ for other programs
  |­­­­­­­­­­­­
 

The C compiler takes your program source, syntax checks it, and creates object module(s) in 
machine code format on disk.

The C linker combines object module(s) and libraries to create an executable, a runnable 
program, which when loaded into memory, performs that which you have told it to do.

As your program runs, the variable space portion of memory changes over time and various 
operations are carried out on any of the input/output devices connected to your computer.
Basic C syntax

. All C code must be enclosed inside a function which is denoted by curly braces, {..}

. All C statements must be terminated by a ';' character

. Every C program must have a main() function declaration,
  the entry point into the program 

. The '#' character denotes a compiler command or compiler directive, the most common being:
   
   #include,
   #define,
   #ifndef

. /* .. */ delimiters may be used to add comments and increase
  readability of C programs.  All characters in between the
  /* and */ are ignored by the compiler at compile time.

The general syntax for a function in C is:

<Return argument> FunctionName(<type1> <arg1>,<type2> <arg2>
                                     ...,<typeN> <argN>)
{
  ... function body consists of 0 or more C statements
}

EX1: A program to display a message to the screen

#include <stdio.h>

void main( ){
|     |   |
|     |   ­­­­­­­­­­ no arguments
|     ­­­­­­­­­­­ function name
|­­­   no return argument
printf("My first C program\n");
}
            |   
          |
              ­­­­­ <arg1> is a string because it is wrapped
                       in double quotes
Allocating memory
The variable space portion of memory is directly manipulated by allocating and assigning data 
types in C programs.

The basic types of data that C supports are:

char        ­ 1 byte character data, range 0 ­ 255
int         ­ 2 byte integers, range ­32767 ­> 32767
long int    ­ 4 byte integer, range now 32 bits as opposed to 16
float       ­ 4 byte real
double      ­ 8 byte real
long double ­ 16 byte real

With these memory building blocks we can allocate all types of
common data.

EX1: Read a 2 byte integer from the keyboard into memory
     and display it on the terminal.

#include <stdio.h> 

void main()
{
     int aNumber = 6; /* default a 2 byte integer to the value 6 */
     printf("Please enter a #:");

     /* scan keyboard integer into mem loc <&aNumber> */

     scanf("%d",&aNumber);
     printf("You entered %d\n",aNumber);
}

Memory map of variable space:

 ­­­
| 6 | aNumber (2 bytes)
 ­­­
What exactly happens when this program runs:

A 2 byte cell is reserved in the variable space portion of memory referred to as <aNumber> and 
the value 6 is assigned there.

The message "Please enter a #:" appears on the screen and the computer waits for the user to 
enter a number and press enter.

The number is transferred into the <aNumber> cell thus overwriting the 6 that was there.

This memory location is read and the number that is there is transferred to screen memory after 
the text "You entered". 

EX 2: Using character and real types

/* PROGRAM to allocate variable space for a character,
   a real number and a string and allow the user to enter
   the values for the real and the string */

#include <stdio.h>  /* standard i/o library functions
                        <printf,scanf> are declared here */

/* create a constant called MAX_CHARS whose value is 80 */

#define MAX_CHARS 80

void main()
{
     // allocate 1 + 4 + 80 bytes in memory */

     char ch;
     float f;
     char name[MAX_CHARS];

     /* assign and print a character */

     ch = 'A';
     printf("ch=%c\n",ch);

     /* load a float from keyboard into memory */
     /* display 6 digits total, 3 after decimal */

     scanf("%f",&f);
     printf("You entered %6.3f\n",f);

     /* get a string from keyboard into <name> */
     /* <name> already is an address, we don't need & */

     scanf("%s",name);

     /* print the name in different ways */

     printf("%6s\n",name);  /* output first 6 chars */
     printf("%­10s\n",name); /* left justify 10 chars */
     printf("%+10s\n",name); /* right justify 10 chars */

Memory map:

    1    4       80
   ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
  | A | 32.567 | Martha \0 ...  |
   ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
    ch   f         name
           
Arithmetic expressions

If we want to do any tests or calculations, we have to create a
mathematical expression.  The following standard arithmetic operators are supported:

{+,­,*,/,++,­­,%}

Let's look at some calculations:

The rule is :

<var> = <expression>;

Result   Expression        Description

[12]   int n = 3*2 + 6;
[5]    int k = (n­2)/2;  /* integer division */
[2]    int j = n % k;    /* mod function or remainder function */
[3]    j++;              /* post increment */
[2]    int i = ­­j;      /* pre­decrement */
[1]    float f1= 3/2;    /* truncates result */
[1.5]  float f2= 3/2.0;  /* gives precision in answer */
[1]    int z = 3/2;      /* integer division always truncated */
Logical expressions
The rule is:

if (<logical expression is true>)
{
     /* do some code */

}
else
{
     /* do some other code */
}

Logical expressions can be any combination of arithmetic
expressions or relational operators:

&&      AND
||      OR
!       NOT
>       Greater than
>=      Greater than or equal
<       Less than
<=      Less than or equal
==      Equal to
!=      Not equal to

Ex 1:

#define X 6
#define Y X+2

void main()
{
int k = X+Y;
if ((k == 14) && (X > Y))
{
      printf("Case1\n");
}
else
{
      printf("Case2\n");
}
}
Assignments and tests can be mixed in one expression:

Ex 2:

if ((k = (3+2)*(5­4*8)) > 6)
{
     ...
}

This statement first assigns k = (3+2)*(5­4*8)
then it checks if (k > 6)

Tip: Be careful with single and double equals!!

Single equal is an assignment which overwrites memory
Double equal is a "safe" test, no memory is overwritten
Ex 3: Assignment versus test

int i,j;
i = 3;
j == 4;   /* wrong! j is compared to 4 */

Ex 4: test for equality

if (j == 4)
printf("j is equal to 4\n");
else
printf("j is not equal to 4\n");

Ex 5: test for inequality

if (j != 4)
printf("j is not equal to 4\n");
 
  or equivalently

if (!(j == 4))
printf("j is not equal to 4\n");

Ex 6: test for non­zero state via the ! expression

if (!j || !i)
printf("Either i or j or both are zero\n");

  or equivalently

if ( (j != 0) || (i != 0) )
printf("Either i or j or both are zero\n");
The switch statement
A convenient way to decode characters or integers case by case is with a switch statement rather 
than coding a cumbersome chain of if..else if statements.

int mailCode;
float price = 0;
printf("Enter your mailing code,0 to exit");
scanf("%d",&mailCode);
switch (mailCode)
{
case 0:
break;
case 1:
price = 0.45;
        break;
case 2:
   price = 0.99;
         break;
case 3:
case 4:
price = 3.49;
     default:
printf("Not a valid mailing code!\n");
};

Equivalent code using if statements:

int mailCode;
float price = 0;
printf("Enter your mailing code,0 to exit");
scanf("%d",&mailCode);
if (mailCode == 0)
{}
else if (mailCode == 1)
price = 0.45;
else if (mailCode == 2)
price = 0.99;
else if ((mailCode == 3) || (mailCode == 4))
mailCode = 3.49;
Looping

There are three styles of loop constructs in C:

(A) for loop,         (B) while loop,     (C) do while loop

       |                          \      /
  Good for looping           Good for looping on
  on fixed range             fixed condition(s)

(A) for syntax:

 for (<var> = <start exp>; <condition exp>; <index update exp> )
 {
     .. code
 }

/* Ex 1: looping upward on an integer */

int i;                      output
for (i = 1; i < 10; i++)      1 2 3 .. 9
     printf("%d ",i);

/* Ex 2: looping downward on a float */

float f;
for (f = 1.2; f > 0.2; f = f­0.2)   output
     printf("%f ",f);                1.2 1.0 0.8 0.6 0.4

The keywords break and continue are possible variations available
inside a loop.

break    terminates the loop completely
continue skips execution of the current iteration

/* Ex 3: premature exit, skipping values */

int i;                                      output
for (i=10; ;i+=20)                           10 70 90
{
     if (i > 90)
          break;
     if ( (i == 30) || (i == 50) )
          continue;
     printf("%d ",i);
}     
(B) while syntax:

while (<exp>)    <­­ condition is tested BEFORE entering the loop
{
     .. code
}

/* Ex 4 */

int age = 10;
while (age > 0)
{
     scanf("%d",&age);
     printf("age=%d\n",age);
}

(C) do while syntax:

do
{
     ... code
} while (<exp>); <­­ code is executed at least once before
                     condition is tested

/* Ex 5 */

int age = 10;
do
{
printf("Enter your age");
scanf("%d",&age);
} while (age > 0);
Part I Exercises
(1) Write a program psquare.c that displays the perfect squares of the first ten non­zero integers. 
Output:

 1  1
 2  4
 3  9
 4  16
 ...

(2) Write a utility celsius.c which asks the user for a temperature in celsius, a floating point 
number which must be ­30 < f < 30 and converts the result to farenheit.  The program also 
informs the user about the range.  The possible ranges are:

[­30,­20]  Very cold
[­20,0]    Cold
[0,25]     Agreeable
[25,30]    Hot

Note: 0  degrees celsius = 32 degrees farenheit 
      20 degrees celsius = 68 degrees farenheit

(3) Enhance the celsius­farenheit program to repeat asking for temperatures until the user enters a 
negative celsius number.

(4) Write a number guesser game, guesser.c where the computer picks a random number from 
1..N and it is the job of the player to guess that number.  Of course your program gives the user 
hints after each guess, whether or not the number is higher or lower than the player's last guess. 
If the user guesses the number, your program outputs the number of guesses made.  After K 
unsuccessful guesses the computer gives the answer.  N and K can be entered by the user or fixed 
inside your program.  If the user enters a negative number at any time, the program terminates.

Sample output:

Number guesser: I have picked a number from 1..100
Your guess?   32
Too high!
Your guess?   8
Too low!
Your guess    29
...

You guessed my number in 6 tries
Play again (y/n?) Y

* Use the system help to find out how to generate random numbers 
Flowcharting
We have looked at the  the basic structure of C programs.  Before writing actual code, we usually 
sketch the flow of the program on paper.  Why?  So we can walk through the logic and discover 
any logic errors with the first round design.  Some standard methods of sketching solutions are 
recommended.  Let's look at two common techniques, flowcharting and pseudocoding:

Programming              Flow chart           Pseudocode
Building block            Symbol                Ex

Output                  ­­­­­­­­­­­­
                        |   x      |           display x
                        ­­­­­­­    |
                                \­­  

Input                           ­­­­
                               /   |           input y
                        ­­­­­­­    |
                        |    y     |
                        ­­­­­­­­­­­­

Calculation            ­­­­­­­­­­­­­­
                       | z = x + y  |          z = x + y
                       ­­­­­­­­­­­­­­   

Test                        /\
                          /    \    Yes
                        /        \ ___         if (x > y)
                        \  x > y /    
                          \    /
                            \/
                             | No
                             |

Repetition                ­>    ­­         (1) while (...)
                         |        |        (2) repeat until (...)
                         |        |        (3) for i = 1 to 10
                          ­­­­­­­­ 

Armed with these 4 primitives, we can write first round sketches for the celsius program 
described in Exercise 1.2.
Flowchart for Exercise 1.2:

                         /­­­­­ 
                ­­­­­­­­­­     |
   start ­­­>  | tempInCelsius |  <­­­­­­­­­­­­­­­­­
               ­­­­­­­­­­­­­­­­                     |
                       |                            |
                       |                            |
                      / \                           |
                    /     \                         |
           No    /           \                      |
   stop <­­­­  / tempInCelsius \                    |
               \      > 0      /                    | 
                 \           /                      | 
                   \       /                        |
                      \ /                           |
                       |                            |
                       | Yes                        |
                ­­­­­­­­­­­­­­­­­­­­­               |  
               | tempInFarenheit     |              |
               |  = f(tempInCelsius) |              |
                ­­­­­­­­­­­­­­­­­­­­­               | 
                       |                            |
                       |                            |
                ­­­­­­­­­­­­­­­­­­­­­               |
               |  tempInFarenheit    |              |
                ­­­­­­­­­­­­­­­      |              |
                       |       \­­­­­­              |
                       |                            |
                      / \                           |
                    /     \                         |
                  /         \    Yes  ­­­­­­­­­­    |
                /tempInCelsius\  ­­­ | Very cold| ­­|
                \ is VERY COLD/       ­­­­­­    |   |
                  \         /               \­­­    |
                    \     /                         |
                      \ /                           |
                       |                            |
                       | No                         |
                      / \                           |
                    /     \                         |
                  /         \    Yes  ­­­­­­­­­­    |
                /tempInCelsius\  ­­­ | Cold     | ­­|
                \ is COLD     /       ­­­­­­    |   |
                  \         /               \­­­    |
                    \     /                         |
                      \ /                           |
                       | No                         |
                       |                            |
                      / \                           |
                    /     \                         |
                  /         \    Yes  ­­­­­­­­­­    |
                /tempInCelsius\  ­­­ | Agreeable| ­­|
                \ is Agreeable/       ­­­­­­    |   |
                  \         /               \­­­    |
                    \     /                         |
                      \ /                           |
                       | No                         |
                       |                            |
                      / \                           |
                    /     \                         |
                  /         \    Yes  ­­­­­­­­­­    |
                /tempInCelsius\  ­­­ | Hot      | ­­
                \ is HOT     /       ­­­­­­     |
                  \         /               \­­­
                    \     /
                      \ /
                       

Pseudocoding

Although flowcharting has the advantage of seeing the flow of the program in a glance, 
sometimes it can be overly specific and we seek a quicker, more compact way of sketching the 
logic.  Study the following example of pseudocode:

Celsius
­­­­­­­

repeat until (tempInCelsius is out of range)
    input tempInCelsius
    calculate tempInFarenheit
    display   tempInFarenheit
    if (tempInCelsius is VERY COLD)
       display VERY COLD message
    else if (tempInCelsius is COLD)
       display COLD message
    else if (tempInCelsius is AGREEABLE)
       display AGREEABLE message
    else if (tempInCelsius is HOT)
       display HOT message
 

A pseudocode sketch gives us the program structure at a glance. 
For example, in the above sketch, we know quickly that our program will consist of an outer do 
with a series of inner if .. else if clauses.

As programs get more complex, we will find flowcharts/pseudocode or a hybrid thereof an 
invaluable tool for getting a feel for the problem before jumping into the code.
Using the string library
We have looked at one standard library: <stdio.h>, specifically the printf and scanf functions.

In order for us to manipulate C strings, we need to call functions from the string library, unless of 
course you wish to write your own.

Here are four commonly used string functions:

. int strcmp(s1,s2)
. int strlen(s)
. char *strcpy(dest,source)
. char *strcat(dest,addOn)

Let's look at how to use them:

#include <string.h>
#include <stdio.h>

#define DEFAULT_NAME "Johnny be good"
#define COMPARE_NAME "Joey"
#define MAX_CHARS    80

void main()
{
     /* string allocation */

     char name[MAX_CHARS],copyOfName[MAX_CHARS];

     /* string assignment */

     strcpy(name,DEFAULT_NAME);
     strcpy(copyOfName,name);

     printf("The buffers are %s\n%s\n",name,copyOfName);

     /* string length */

     printf("Length of 2 buffers are %d,%d\n",
          strlen(name),strlen(copyOfName));

     /* string concatenation */

     strcat(copyOfName,name);
     printf("Concatenation of copy with name is %s\n",copyOfName);
     /* string comparison */
     /* strcmp(s1,s2) returns:
         0   if s1 has same length and same chars as s2
         ­ve if s1 < s2, meaning s1 appears earlier in dictionary
         +ve if s1 > s2, meaning s1 appears later   in dictionary
     */

     if (strcmp(name,DEFAULT_NAME) == 0)
          printf("name = %s\n",DEFAULT_NAME);

     if (strcmp(copyOfName,DEFAULT_NAME) != 0)
          printf("copyOfName != %s\n",DEFAULT_NAME);

     printf("Direct comparison of %s with %s is %d\n",
          name,COMPARE_NAME,strcmp(name,COMPARE_NAME));

}
Output
­­­­­­

Length .. 15,15
Two buffers are
Johnny be good
Johnny be good
Concat .. Johnny be goodJohnny be good
name == Johnny be good
copyOfName != Johnny be good
Direct comparison of Johnny be good with Joey is ­3

Tip

Rewriting strcmp as a macro increases program readability:

#define strEqual(a,b) !strcmp(a,b)

...

if ( strEqual("Harry","Joe") )
     ...

When the compiler sees a strEqual() function call, it substitutes the text !strcmp followed by the 
parameter.
Note that we could define the macro with the parameters reversed and achieve the same results.

#define strEqual(a,b) !strcmp(b,a)
Character (byte) manipulation
We have used the major string functions: strcmp, strcat, strlen, strcpy, now let's see how we 
would implement them in low­level C, and in so doing, investigate some different syntaxes in C.

If we want to access the ith character of a string we can use the
[] subscript operator:

char str[50];
strcpy(str,"abcdefghijklmnop");
printf("4th char of str is %c\n",str[4]); /* access the 5th character of str, 'e' */
str[0] = 'A';                            /* writes an 'A' into the first character of str */
printf("str is now %s\n",str);

/* A program which counts bytes (chars) in a string */
/*  (performs same function as strlen())            */

#define EOS 0
#define MAX_STR_LENGTH 1024

void main()
{
     char str[MAX_STR_LENGTH];
     int i;

     printf("Please enter a string");
     scanf("%s",str);

     /* loop until we see a 0 char */

     for (i = 0; (str[i] != EOS) && (i < MAX_STR_LENGTH); i++);

     printf("Length computed as %d\n",i);
}
Accessing bytes via pointers
Another way to access a byte is to use pointer arithmetic

*(str+i) is the equivalent of str[i],
   both accessing the ith character of the string <str>

Let's see how we could implement the strcpy() using pointer syntax.

/* program which copies a string <source> to <dest> */

void main()
{
     char source[80],dest[80],*s,*d;

     strcpy(source,"A bunch of chars");

     s = source;
     d = dest;

     /* loop until we get a 0 as the current character */

     while (*s)
     {
          *d = *s;
          d++;
          s++;
     }

     /* put the 0 marker at the end of the dest string */

     *d = EOS;
     printf("dest=%s\n",dest);
}

Memory map:

                0  1  2   3  4  5  6  7  8       79
                ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
source ­> ­­­> |  |  |  |  |  |  |  |  |  | ... |  |
         |      ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
dest   ­>|  ­> |  |  |  |  |  |  |  |  |  | ... |  |
         | |    ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
         | | 
         | |               ­­­­
         |  ­­­­­­­­­­­ d |    | 
         |    ­­­­         ­­­­­
          ­ s|    |
              ­­­­  

<source> and <dest> are actually pointers to 80 byte buffers and have their values fixed at "safe" 
memory addresses.  For this reason they cannot be modified directly, but the pointers <s> and 
<d> can be modified directly.  To dereference a pointer and access a particular byte of data we 
can insert a '*' before the pointer.

We can alternatively write the code as:

...
while (*d++ = *s++);
...
Character-based i/o
You have probably already noticed a limitation of the scanf() function in that it cuts the input 
string after encountering the first whitespace character.  This may or may not be desirable; 
certainly not if we are trying input a person's complete name.  Let's write our own scanf function 
that reads characters one by one, using two new functions:

int getc(FILE *fp);
int putc(charc,FILE *fp);

These are called prototypes, or short descriptions about what the
functions take as arguments and what they return.

FILE *fp;  describes <fp> as a pointer to a FILE type

The standard i/o types are stdin (keyboard), stdout (terminal)

Getc(stdin) waits for characters to be entered from keyboard, and
the program continues when <CR> is pressed.

Putc(c,stdout) pipes the character <c> to the screen.

/* Ex 1: a program to read characters until a carriage return
   is encountered */

#include <stdio.h>

#define CR '\n'

void main()
{
     char c;
     printf("Please enter a name");

     /* here we do an assignment, then a test, then loop
        if necessary */

     while ( (c=getc(stdin)) != CR )
          putc(c,stdout);
}

If we want to display the ascii code of characters entered from the keyboard, we could write:
/* Ex 2: read first character */

c = getc(stdin);

/* test */

while (c != CR)
{
     printf("%c %d\n",c,c);

     /* read next character */

     c = getc(stdin);
}
Character macros
Also existing in stdio.h are convenience macros that identify
categories of characters, useful for parsing character data:

     isspace(char),isalnum(char),isalpha(char),
     isdigit(char),ispunch(char),isupper(char),
     islower(char)

and some character conversion macros:

     toupper(char),tolower(char)

For example, if we wanted to write a piece of code that skips
leading white spaces, counts the # of digits entered, and
displays alpha characters all in upper case, we could write:

void main()
{
     int nDigits = 0;
     char c;

     /* skip leading spaces/tabs/CR's */

     while (isspace(c = getc(stdin)));

     /* process rest of non­space chars */

     while (c != CR)
     {
          /* is current char a digit */

          if (isdigit(c))
               nDigits++;

          /* convert current char to upper case before printing*/

          putc(toupper(c),stdout);
     }
     putc(CR,stdout);
     printf("The # of digits is %d\n",nDigits);
}
File i/o
So far we have accessed memory, read from the keyboard and output to the screen.  How can we 
read/write data to/from disk?  Via the file i/o functions in stdio.h:

fopen,fclose,feof
fgets,fputs,
fgetc,fputc
fprintf,fscanf

To see how to make use of some of these functions, let's create a test input file of names in test.in 
and write a program to read and display the names.

nancy segan
george oldred
kim alaster
sonny boy

/* Ex 1 : program to read an ascii file record by record */

#include <stdio.h>
#define INPUT_FILE_NAME "C:\\TEST.IN"
#define MAX_CHARS 80

void main()
{
        FILE *inputFile;
        char s[MAX_CHARS];
        char c;

   /* open the file for input and test for existence */
        inputFile = fopen(INPUT_FILE_NAME,"r");

        if (!inputFile)
        {
                printf("Can't open %s\n",INPUT_FILE_NAME);
                return;
        }
        
        /* read the first record */

        fgets(s,MAX_CHARS,inputFile);
        while (!feof(inputFile))
        {
/* display it and get the next */
                printf("%s",s);
                fgets(s,MAX_CHARS,inputFile);
        }
   /* close the file after all the reading */
        fclose(inputFile);
}
Reading a file character by character

Alternatively, we can read a file character by character using the fgetc() function.  This is useful 
if we want to perform special operations on any of the record's fields.

/* Ex 2 : Read name records character by character from a file,
    capitalize the first letter of each name, first and last,
    and display the result */

#define CR '\n'
#define SPACE ' '
#define TRUE  1
#define FALSE 0

void main()
{
int capitalize = TRUE;

        ... /* open the file same as in Ex 1 */

        /* keep processing characters until we reach the end of file */

        while((c = fgetc(inputFile)) != EOF)
        {
                if (c == SPACE)
                {
                        capitalize = TRUE;
                }
                if ((c != SPACE) && capitalize)
                {
                        c = toupper(c);
                        capitalize = FALSE;
                }
                if (c == CR)
                {
                        printf("<EOS>");
                        capitalize = TRUE;
                }
                putc(c,stdout);
        }
        fclose(inputFile);
}

Output
Nancy Segan<EOS>
George Oldred<EOS>
Kim Alaster<EOS>
Sonny Boy<EOS>
Writing to a file

In Ex's 1 and 2 we used fopen() to open a file for read access.  To open a file for output we can 
again use fopen() but specify "w" (write access) as the second parameter.  To demonstrate this, 
let's consider a file translator

Input file:        Output file:

                    Name         # chars  # Upper case
 nancy              NANCY          5        0   
 Greg      ==>      GREG           4        1
 LArry              LARRY          5        2

#define INPUT_FILE  "C:\\TEST.IN"
#define OUTPUT_FILE "C:\\TEST.OUT"

void main()
{
     FILE *in,*out;
     char record[80];
     int i,totChars,nUpr;

     if (!(in = fopen(INPUT_FILE,"r"))
return;

     /* create an output file, original file is destroyed!  */

     if (!(out = fopen(OUTPUT_FILE,"w"))
return;

     fprintf(out,"%20s","%10s","%12s","Name","# chars","# Upper case");
     fgets(record,80,in);
     while(!feof(in))
     {
   totChars = strlen(record);
   nUpr     = 0;
   for (i = 0; i < totChars; i++)
   {
if (isupper(record[i]))
nUpr++;
record[i] = toupper(record[i]);
   }
          fprintf(out,"%20s,%10d,%12d\n",record,totChars,nUpr);
          fgets(record,80,in);
     }
     /* close files */

     fclose(in);
     fclose(out);
}
Using the math library

Another standard library available in C is <math.h>.  Here is a list of widely used math 
functions:

Common            Trig        Exponentiation

abs               sin            log
sqrt              cos            exp
                  tan            pow(x,y)
                  asin
                  acos
                  atan

Notes:

. The math functions work on the float and double types.

. Functions will return results in precision offered by the
  double type, 18 significant digits.

/* Ex 1 */

include <stdio.h>
#include <math.h>

void main()
{
     printf("abs(­3.6)=%lf\n",abs(­3.6));
     printf("sin(pi)=%lf\n",sin(M_PI));
     printf("acos(0.5)=%lf\n",acos(0.5));

     /* error cases */

     printf("sqrt(­5)=%lf",sqrt(­5));
     printf("log(­2)=%lf",log(­2));
     printf("5.0/0=%lf",5.0/0);
}

/* Ex 2: test code for max precision */
double d = 1.01234567890123458901234567890;
printf("sqrt of %.30lf is %.30lf\n",d,sqrt(d));

/* Ex 3: if precision is not required in the output, use %lf
   to used default output formatting */

printf("%lf,%lf\n",d,sqrt(d));

/* Ex 4: to output long double types correctly, use the %Lf
   format specifier */

long double d2 = 1.01234567890123458901234567890;
printf("%Lf,%lf\n",d2,sqrt((double)d2) );
Type conversion functions

. String to numeric

atoi <­­­ ascii to integer (floats truncated, alpha 0's result)
atof <­­­ convert ascii to float

#include <stdlib.h>

...
printf("atoi('301') = %d\n",atoi("301"));
printf("atof('31.62')=%f\n",atof("31.62");

. Numeric to string

We can use the sprintf() function in the string library.

#include <string.h>
#include <stdio.h>

...
char s[80];
sprintf(s,"Int %d Float %f",3,6.123);
printf(s);

Output

Int 3 float 6.123
 
Part II Exercises
(1) Write complete flowchart, pseudocode, C code for a utility that keeps asking the user for 
<name> and <age> pairs until the user enters DONE as the name.  The program tallies the oldest 
person and youngest person and outputs this information at the end of the program.

(2) Try implementing the strcmp and strcat functions using subscript(array) syntax and pointer 
methods.  Again, start with a flowchart, then write the pseudocode and finally the C code.

(3) You are trying to discover the approximate height of a building.  The sun is up and the 
building casts a long shadow.  You can measure the length of the shadow and the approximate 
angle, theta, the sun makes with the top of the building building.

               * Sun
           _ 
          | | 
          | |  h = ?
__________| |

<­­ sl ­­>

sl    = shadow length
theta = angle sun makes with building
what is h?

Your program asks the user for theta and sl and computes h.  Check for possible bad inputs, like 
negative sl or negative theta.

(4) Write a program which ask the user for N integer values and your program plots a bargraph of 
the values.  Ex:

Enter N: 6
­1 3 1 ­2 ­3
 
5  |
   |   ** 
   |   **
   |   ** **
0  |­­|­­|­­|­­|­­|­­|­­|­­
   |**       ** **
   |         ** **
   |            ** 
­5 |
 
State your design assumptions.  If you find horizontal output difficult, output the graph tilted 90 
degrees.
(5) Write a program which counts the number of sentences in a
file.  Assume that sentences are terminated by a '.','?' or '!'

Your function has the prototype:

// INPUTS: fp, the input file
// OUTPUTS: none
// RETURNS: the number of sentences found in <fp>

int findNumberOfSentencesIn(FILE *fp)
{...}

(6) What is the output of the following program?

#include <stdio.h>

void main()
{
     char s[20];
     int i;
     for (i = 15; i >= 0; i­­)
          s[15­i] = 'a' + i;
     for (i = 0; i < 15; i+=2)
     {
          if ((i>3) || (i < 7))
               continue;
          printf("%c.",s[i]);
     }
}

(7) What is the output of the following program?  Show a memory
map.

#include <stdio.h>

#define PERIOD '.'

void main()
{
     char *data = "abc.def.ghi.jki";
     char *p = data;
     while (*p)
     {
          if (*(++p) == PERIOD)
          {
               p += 1;
          }
          p++;
          putch(*p);
     }
}
(8) Find the errors in the following code:

#include <strings.h>

void main()
{
     printf("MAIN: start");
     char ch
     while (ch < "Z")
          ch++;
          printf("%s",ch);
     FILE *fp = fopen("c:\cppapp\test.c","r",fp);
     fscanf("%s %d",s,i,ch);
     fprintf("%s %c",&ch,ch);
     fprintf("%s %c",s,strnlen(s));
     fclose(&fp);
};

(9) What are the compile­time problem(s) with the following code?

define X 3
#include <stdioh>
void main
{
     int X = 4.1;
     fscanf(%s,X);
     while (i <> 3++)
          i++
     q = atof(­­i)
     printf(q);
}

(10) What are the run­time problem(s) with the following code?

void main()
{
     int i;
     char *s;
     printf("Enter a string");
     scanf("%s",s);
     if (s == "Joe")
     {
          printf("You typed %s\n",s[0]);
     }
     i = 0;
     while (i = 6)
     {
          printf("%c",s[i]);
          i++;
     }
}
Arrays

Arrays are useful for storing a repetition, or table of the same type of data.  For example, we may 
wish to allocate storage for the number of hours worked for an employee for each weekday.  We 
could code:

int hrsOnMon,hrsOnTue,hrsOnWed,hrsOnThurs,hrsOnFri;

This is surely one way to do it; but tedious and requires specific code for each day of the week. 
Can you imagine if we wanted to record yearly timesheets for several employees!  Rather, we 
wish to have one variable which describes all the weekdays.  We can do this by coding an array, 
or table of hours:

int hours[5];                Memory Map
                        ­­­­­­­­­­­­­­­­­­­
                    hrs|   |   |   |   |   |
                        ­­­­­­­­­­­­­­­­­­­
                        Mon Tue Wed Thur Fri
                         0   1   2   3    4

hours[0] refers to number of hours worked on Monday
hours[1] to Tuesday,
hours[2] to Wednesday,
hours[3] to Thursday,
hours[4] to Friday

In the above example, we have allocated 5 integers, representing a total of 5 x 2 = 10 bytes.  The 
array is uninitialized, so only random data appears in this block of memory upon allocation.

/* Allocate and initialize an employee's weekly hours
    and display them along with the weekly total */

void main()
{
int hours[5] = {8,4,2,6,5};
int i,totalHours;
for (i = 0; i < 5; i++)
{
printf("%d ",hours[i]);
totalHours += hours[i];
}
printf("Total hours:%d",totalHours);
}

Notes:

C does not perform subscript checking, so if we attempt to write to hours below or above the 
allowable 0..5 range your program will inevitably crash!

We have already seen another example of an array, namely a character array, or string, char  
str[80], describing a repetition of 80 characters.
Two dimensional arrays
Sometimes applications have need of two dimensional tables. 
Consider a spreadsheet for weekly hours for several employees:  

       | Mon   Tue  Wed  Thur  Fri |  Total
Joe    |  6     1    3.5  2     6  |   18.5   <­ employee
Harry  |  2     9    0    1     1  |   13       totals
Sue    |  3     6    0    3     2  |   14
Greg   |  0     4.5  0    3     1  |    8.5
Total  ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
          11    20.5 3.5  9    10
          |
          ­­­­­ day totals

From the above output, we can visualize a program that displays the data in tabular format, tallies 
totals and allows us to
edit the data and display a few statistics about who has the
highest and lowest hours/week and who has the highest and lowest hours on any particular day.

Let's consider the storage first:

int hours[4][5];       Memory map

                            0  1  2  3  4   <­ COLUMNS represent weekdays
                            ­­­­­­­­­­­­­­­­­­­
                   hours 0 |6  |1  |3.5|2  |6  |
                            ­­­­­­­­­­­­­­­­­­­
             ­­­­­­­­­>  1 |2  |9  |0  |1  |1  |
              Rows   ­>     ­­­­­­­­­­­­­­­­­­­
             represent   2 |3  |6  |0  |3  |2  |
             employees      ­­­­­­­­­­­­­­­­­­­
                         3 |0  |4.5|0  |3  |1  |
                            ­­­­­­­­­­­­­­­­­­­

. Here we would have allocated 4 x 5 x 4 = 80 bytes of data.

. To access the number of hours worked by Sue on Thursday, we
  would code hours[2][4].
. To find out which memory address this cell resides at, we could
  do a little pointer arithmetic:

Suppose the hours array resides at memory location 4000
Then the cell offset into hours
        = (SUE's row * # of days in week + THURSDAY index)
        =  2 * 5 + 4
        = 14
 
Each float takes up 4 bytes of memory, so the memory address of hours[2][4] = base address + 
cell offset * cell size
            = 4000 + 14 * 4
            = 4056
To demonstrate the use of the employee hour array, let's consider some code to read and display 
certain employee hours from disk.  We can use enumerated constants to describe the employees 
and weekdays to improve readability:

/* Program to read and display some employee hours */

#include <stdio.h>

#define NEMPLOYEES 4
#define NWEEKDAYS  5

enum {JOE,HARRY,SUE,GREG};
enum {MON,TUE,WED,THUR,FRI};

#define INPUT_FILE "C:\\EMPLOY.DAT"

void main()
{
FILE *input;
int employee,weekday;
float employeeHours,harrysTotal;
float hours[NEMPLOYEES][NWEEKDAYS];

input = fopen(INPUT_FILE,"r");
if (!input)
return;

for (employee = JOE; employee <= GREG; employee++)
{
         for (weekday = MON; weekday <= FRI; weekday++)
{
                fscanf(input,"%f",&employeeHours);
f[employee][weekday] = employeeHour;
}
        }

fclose(input);

printf("# hours Sue worked on Thursday is %*.1f\n",hours[SUE][THUR]);
printf("Harry's hours:\n");
for (weekday = MON; weekday <= FRI; weekday++)
{
printf("%*.1f ",hours[HARRY][weekday]);
harrysTotal += hours[HARRY][weekday];
}
printf("\nHarry's total:%*.1f",harrysTotal); 
}
Output

# hours Sue worked on Thursday is 3.0
Harry's hours: 2.0 9.0 0.0 1.0 1.0
Harry's total: 13.0
Pointers

A pointer is a memory location or memory address.  Pointers are used throughout C programs to 
refer to data that resides in memory at some location.

To allocate a pointer we use the '*' operator:

int i; /* describes an integer piece of data */


int *ip; /* describes a pointer to a piece of integer data */
char c; /* describes a single character */
char *c; /* describes a pointer to a character in memory */

If we wish to initialize a pointer to a particular value, we must make sure that the types match on 
either side of the assignment:

float *fp,f = 3.2;
int j  = 53;
fp = &f;     /* the address operator '&' returns the address
                 of a particular piece of data */
fp = f;      /* Syntax error! <fp> and <f>'s types don't match */
fp = &j;     /* Syntax error! &j returns a pointer to an integer type
                  which doesn't match fp, a pointer to a float type */

Memory map

          8000         8002        8006   
          ­­­­         ­­­­­       ­­­­
    8000 |8002| ­­­>  | 3.2 |     | 53 |
          ­­­­         ­­­­­       ­­­­
           fp            f          j

Once we have initialized a pointer we may use it to indirectly access the data it points to by using 
the '*' operator to dereference the pointer.  Dereference means to follow the pointer to the address 
it describes and "get" the data at that address.

                                               Output
printf("fp=%x,f=%f,*fp=%f",fp,f,*fp);    fp=8000x   f=3.2   *fp=3.2 
*fp = 56.7;
printf("fp=%x,f=%f,*fp=%f",fp,f,*fp);    fp=8000x   f=56.7  *fp=56.7

Tips
 * and & perform opposite operations:

   * means to dereference a pointer, follow it and get the data
   & means to create a pointer for a particular piece of data

 To read a pointer declaration, read the declaration backwards:

  int *ip;  /* reads <ip> is a pointer(*) to an integer */
Functions

Now that we have seen how pointers work in C, we can investigate one of their major uses in the 
language: how to pass parameters to and from functions.

A function is a subprogram or mini­program that performs a specific useful task

We have used many functions already, {strlen,printf,scanf,....} and created our own simple 
function, void main(), simple in that it takes no arguments and returns no arguments.
 
How do we create lower level functions than main() and why would we want to do so?

Why? ­ splitting code up into logical pieces promotes modularity
     ­ modular code is easier to read, easier to code, easier to debug
     ­ functional code as opposed to inline code reduces EXE size

How? ­ we prototype our function and then code the logic

Recall the general syntax for a function declaration:

<Return arg type> funcName(<arg1 type> <arg1 name>,<arg2 type> <arg2 name>...)
{
     ... body
}

/* Ex 1: A function that takes a single argument
         and returns a single argument
         specifically to convert a float to a long integer */

#define MAX_LONG_INT pow(2,4*8­1)­1

long int floatToLongInt(float f)
{
     if (f > MAX_LONG_INT)
          return(­MAX_LONG_INT);
     else
          return((long int)f);      /* the (long int) is referred to as a cast
}                                       or type conversion */

void main()
{
float f = ­3692.1;
long int i = floatToLongInt(f);
printf("%ld %ld\n",i,floatToLongInt(1e38));
}
Writing functions as macros

Equivalently, the floatToLongInt() function can be written as a macro using the test operator, '?'.

#define floatToLongInt(a,b) (a > MAX_LONG_INT) ? ­MAX_LONG_INT : (long int)f 

The syntax for the test operator is:

(<condition>) ? (result if condition is true)
                : (result if condition is false)

For example, if we were to code:

long int z;
z = floatToLongInt(543.21);

the compiler substitutes:

z = (543.21 > pow(2,4*8­1)­1) ? ­pow(2,4*8­1)­1 : (long int)543.21

which decodes to:

if (543.21 > pow(2,4*8­1))
   z = ­pow(2,4*8­1);
else
   z = 543.21;
Parameter passing

There are two types of parameter passing mechanisms in C.  The floatToLongInt() function is an 
example of pass by value or copy­in.  This means that a local copy of the value of <f> is created 
and used within the function scope but not passed back to the caller.

In order to modify the contents of a passed argument we must use our knowledge of pointers. 
This leads to the pass by reference method.
  
Consider a function that interchanges two integers parameters, a and b.  a should get b's value 
and b should get a's value.  In order for a function to modify the original arguments, the caller 
must give permission.  The caller does this by passing the address of the value to be modified.

/* Ex 1: function to interchange two integers <a> and <b> */

void swap(int *ptrToA,int *ptrToB)            Memory map
{                                                 ­­­­­
int temp;                         temp   |3    |
temp = *a;                                ­­­­­
*a = *b;                      ­­­ ptrToA |3000 |   
*b = *a;                     |            ­­­­­
}                                    |    ptrToB |3002 | ­­­
                                     |            ­­­­­     |
                                     |                      |
void main()                          |     3000  3002       |
{                                    |     ­­­­­­­­­­­­     |
int a = 3,b = 4;               ­­>|  3  |  4   | <­­
swap(&a,&b);                       ­­­­­­­­­­­­
printf("a=%d,b=%d\n",a,b);           a     b
}

Output
a=4,b=3
Passing arrays into functions

If we combine our knowledge of functions and arrays we can create some nice code.  Consider 
the employee data examplee from before.  Originally, we had embedded the input and output 
operations in main().  Let's add an edit function to the program but to preserve modularity and 
enhance readability, we will let main() own the stock data and give permission to a function to 
modify the contents of the array.

/* Program to edit SUE's and HARRY's hours */

#include <stdio.h>

#define NEMPLOYEES 4
#define NWEEKDAYS  5

enum {FALSE,TRUE};
enum {JOE,HARRY,SUE,GREG};
enum {MON,TUE,WED,THUR,FRI};

int isValidWeekday(int dayOfWeek) /* pass by value */
{
if ((dayOfWeek >= MON) && (dayOfWeek <= FRI))
return(TRUE);
else
return(FALSE);
}

void edit(float hours[NEMPLOYEES][], /* pass by reference */
          char *employees[],         /* pass by reference */
          int employeeToEdit)        /* pass by value     */
{
int dayOfWeek;
float nHours;
printf("Edit %s's hours...\n",employees[employeeToEdit]);
do {
printf("Please enter the day of the week to edit[0­Mon,..4­Friday,­1 to exit]:");
scanf("%d",&dayOfWeek);
if (isValidWeekday(dayOfWeek))
{
scanf("%f",&nHours);
hours[employeeToEdit][dayOfWeek] = nHours;

} while (isValidWeekday(dayOfWeek));
}

void main()
{
char *employees[NEMPLOYEES] = {"Joe","Harry","Sue","Greg"};
float hours[NEMPLOYEES][NWEEKDAYS];
edit(hours,SUE);
edit(hours,HARRY);
}

All arrays are passed by reference in C.  This means we don't have the option of pass by value. 
The reason for this is speed, as generally it is too­time consuming to make a copy of an array.
  
Memory map

main()
                  4000
                  ­
 employees   ­­> |.| ­> "Joe"
            |    |.| ­> "Harry"
            |    |.| ­> "Sue"
            |    |.| ­> "Greg"
            |     ­
            |
            |              4010
            |               ­­­­­­­­­­­­­­­­­­­  
 hours      |         ­­­> |   |   |   |   |   |
            |        |      ­­­­­­­­­­­­­­­­­­­
            |        |     |   |   |   |   |   |
            |        |      ­­­­­­­­­­­­­­­­­­­ 
            |        |     |   |   |   |   |   |
            |        |      ­­­­­­­­­­­­­­­­­­­ 
            |        |     |   |   |   |   |   |
            |        |      ­­­­­­­­­­­­­­­­­­­
            |        |
            |        |
            |        | 
edit()      |        |
            |       ­­­­­
 hours      |      |4010 |  
            |       ­­­­­
            |       ­­­­­
 employees   ­­­­­­|4000 |
                    ­­­­­ 

                    ­­­­­
 employeeToEdit    |     |
                    ­­­­­  
                    ­­­­­  
 dayOfWeek         |     |
                    ­­­­­
                    ­­­­­
 nHours            |     |
                    ­­­­­

isValidWeekday()

                    ­­­­­
 dayOfWeek         |     | 
                    ­­­­­
Scope of data

When we talk about the scope of data, we are referring to "which part(s) of the program can 
access the data".

There are three scopes of data in C:

LOCAL
      FILE
      GLOBAL

So far we have used only local scope, that is to say, data that is visible only in the function that it 
is declared in.  We call this local data, or local variables.

/* Ex 1 : Local scope */

int formatReply(char *s)
{
int x,y;  /* x,y,s are local to formatReply()         */
...       /* and cannot be seen outside this function */
}

        int outputReply(char *s) /* this <s> is a different s than the one
                                    in formatReply() */
{
int a,b;   
x = 3;   /* syntax error! x is not visible in outputReply() */ 
}

     

If we wish to share data between functions without passing them as arguments, we can use file or 
global scope.  File scope variables are identified by prefixing with the static keyword and 
declaring the variable outside of any function.  These variables can only be referenced in the C 
file they are declared in: 

BUFFER.C

/* Ex2, File scope: <buffer> can be seen in any function in the
                      file buffer.c */
static char buffer[30] = "Abcdefghijk";

void getBuffer()
{
scanf("%s",buffer);
}
void outputBuffer()
{
printf("%s",buffer);
}
...
Global variables are denoted by removing the static keyword and declaring the variable outside 
of any function.  These variables can be seen in all files pertaining to the program.  We can use 
the extern statement to share variables across files:

/* Ex 3, Global variables */

BUFFER.C

char buffer[30]; /* buffer.c owns <buffer> but can share
                     it with functions in other files */

void getBuffer() {...}
void outputBuffer() {...}

BUFFER2.C

extern char buffer[];  /* all functions in buffer2.c can
                          access the <buffer> variable of
                          buffer.c */   

int lengthOfBuffer()
{
return(strlen(buffer));
}
...

Tip

Avoid the use of static and global variables when possible, especially globals, as they can muddle 
readability in large systems.
Scope of functions

Like data, functions also have scope.  There are only two possible function scopes in C:

FILE
     GLOBAL

The default scope that we have been using so far is global, that functions can access each other 
across files.

EX 1: global scope

F1.C                       F2.C

extern void f1();  /* Inform the compiler that f1()
extern int  f2();     and f2() reside in another object
                                                 module */
void f1() {...}           void f3()
int  f2() {...}           {
f1();  /* invokes the code described in F1.C */
f2();
...
}

If we wish to restrict access to a functions across files, we can prefix a function's declaration with 
static thereby making it file scope only:

Ex 2: file scope

F1.C                         F2.C

                             extern void f3();
static void  f1() {...};
static int   f2() {...};     static void f1() {...};  /* different function than f1()
static float f3() {...};                                  in f1.c */

                             static void f2()
  {
                                 f1();    /* invokes the f1() in this file f2.c */
                                 f3();    /* results in error at link time */
                             } 

                      
Tip
 
Make functions file scope as often as possible to privatize functions, localize logic, and decrease 
the debugging time of programmers other than yourself who have the bad luck to have to debug 
your code.
Part III Exercises

(1) Try your hand at writing some simple functions:

int minF(int a,int b) <­ returns lower of a,b
int maxF(int a,int b) <­ returns higher of a,b

float roundF(float f,int nDecimals) <­ returns <f> rounded to
                                     <nDecimals> decimal places

Ex's:

roundF(3.6921,2)  ­> 3.68
roundF(­8.2139,3) ­> ­8.214
roundF(9.9999,3)  ­> 10.000

Tips:

. Write pseudocode first!  Test your algorithm with the above
  three cases BEFORE attempting the code.

. Suggested C functions to use: sprintf,atof
  as it is easier to manipulate individual digits of <f> as a
  string rather than working directly with <f>

(2) Write a C function which checks whether a string is a
palindrome.  Palindromes read the same when spelled backwards. 
LEVEL,ROTOR are examples.  Start with a flowchart.

(3) Write a C function which returns the median mark (middle
mark) in a float array.  Assume the array is unsorted. 
Pseudocode first!

float median(float marks[],int nMarks);

(4) Write a C function midString(...), which returns a substring
of a string.

Usage:
     enum {ERROR,NO_ERROR};
     int status = midString("This is an easy problem",
                         11,4,substr);
     printf("%s\n",substr);

Output:
     easy

Prototype:

int midString(char source[],int startChar,int nChars,char
substr[]);

Your function returns the result in <substr> and returns a status
code as a return argument.  Identify the possible errors. 
Pseudocode first!
(5) Write a function that asks the user for an answer to a multiple choice question.  Choices can 
be either entered either as a..e or 1..5, depending on what the caller passes in for the 
<acceptReplyInLetters> argument.  The argument <c> contains the user's reply.  If the user 
enters an invalid choice, a message is displayed and the user must re­enter the answer.  The 
prototype for your function is:

void getMultipleChoiceResponse(int acceptReplyInLetters,char *c);

(6) Write a function which finds the maximum and minimum value of
the function y = sin(x) * log ( |x| ) on the integer range [ A, B
], y is a real # result.  Your function has prototype:

     void findMaxMin(int a,int b,int *maxVal,int *minVal)
     
|x| = absolute value of x, use the function abs() in <math.h>

(7) Implement a function which reads the next token from a file delimited by white space or "", 
stores the function in a static buffer, and returns the pointer to the stored token.  Here is some 
sample code:

INPUT FILE

 Mary had a "brown dog" that would bark all night.

static char buffer[BUFSIZE];
void readToken(FILE *fp,char **token) {...}

void main()
{
FILE *fp;
fp = fopen(INPUT_FILE,"r");
if (!fp)
return;
readToken(fp,&token);
while (!feof(fp))
{
printf("<%s> ",token);
readToken(fp,&token);
}
fclose(fp);
}
Output

<Mary><had><a><brown dog><that><would><bark><all><night.>
Design tips

As programs get larger and more complicated, certain golden rules of programming become 
increasing important.

Flowchart style tips

(1) Use the standard flowcharting symbols.

(2) Solve one part of the problem at a time.  If you try to show
too much of the overall program on one page, your depiction will
get messy.

(3) Walk through your flow with a variety of test cases.
Pseudocode style

(1) Use the standard pseudocode building blocks.

(2) Avoid making your pseudocode too vague.  Something like:

     read a fp # 
     calculate the result

is ok for first round pseudocode, but if "calculate the result"
involves several loops and complicated logic, you should elaborate on these steps.

(3) Conversely, avoid making your pseudocode too specific. 
Something like:

     scanf(aName from the keyboard)
     c = getc(from keyboard)
     if (c == 'Q')
        do
        {
          printf(aName[i])
          i++;
        } while

This is too C­like.  Pseudocode is supposed to be language­
independent, meaning that it is a description of the solution
that any C,Fortran, Pascal, ... programmer can pick and start
writing code from.  It will be harder for a Fortran programmer to
wade through C­like pseudocode than standard pseudocode.  Also it
is easy to get too wrapped up in the syntax of the language and
miss the logic points.  Rather code:

     input aName
     input aChar
     if (aChar = QUIT)
          i = 0
          repeat
             display aName[i]
             i = i + 1
           until i > len(aName)
                                                                 
Coding style tips

(1) Don't attempt the code until you have the logic clearly in mind with a workable flowchart or 
pseudocode in front of you verified with test cases.  The exceptions to this rule are (a) if the 
problem is trivial enough to code off the top of your head or (b) you are an experienced C 
programmer.

(2) Provide a minimum amount of documentation while you are
coding.  This helps clarify the logic and lets you see possible logic errors that escaped your 
notice in the first round design.  As far as the minimum amount of documentation goes, you 
should at least have a program header, a header for each major function, and a comment line for 
every major loop or major block of logic.

Here is sample info required for a program header

/* ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
   TITLE       : 
   AUTHOR      :
   Description (1 line to 1 paragraph)
   DETAILS  ( important parts of program,
              input required by user,
              Files used,
              important variables,flags, etc)
   ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ */

void main()
{
/* Loop until the user says to quit */

for (;;)
{
   ...
}

/* Open output file */

...
}

(3) You can even go so far as to embed the pseudocode into your
code as extra documentation.

(4) Be consistent in your coding style.  If you use specific naming conventions, stick with them. 
For example, if you capitalize the first letter of every second word in a variable, do so throughout 
your program.  Don't switch half­way through to underscoring words:

Good:                                    Bad:

 get(myClient);                          Get(my_client);
 displayCursoryStats(myClient);          displaycursorystats(my_client);
Debugging tips

(1) Use printf statements liberally to trace program execution.  You can comment them out, or 
delete them later once you have the
program working.  Ex:

static int debug = TRUE;

float complicatedFunction(float f,int *x,int *y) {...};

void main()
{
float f;
scanf("%f %d %d",&f,&x,&y);
for (i = x; i > x­y; i­­)
{
if (debug)
{
printf("DBG: ­­­i=%d\n",i);
printf("DBG: before, f,x,y=%f,%d,%d\n",f,x,y);
}
f = complicatedFunction(f,&x,&y);
if (debug)
{
printf("DBG: after, f,x,y=%f,%d,%d\n",f,x,y);
}
}
}

(2) Become familiar with any of the debugging tools available on the system {set watch, set 
breakpoint, function trace, ...} as they are invaluable and can help you save a lot of time figuring 
out "why is my program doing that?". 
Structures

In many programs we wish to group data.  Consider an application that records name and mark 
information on many students.  We could record this information in two parallel arrays:

#define MAX_STUDENTS   20
#define MAX_NAME_CHARS 30

char  name[MAX_STUDENTS][MAX_NAME_CHARS];
float mark[MAX_STUDENTS];

If Susan were student 6, then we would expect to see her full name in name[5] and her average in 
mark[5].

Although this is a solution, it is a cumbersome method in that we have to allocate arrays for each 
field.  What would happen if we were required to store forty student fields?  It would be nicer to 
group the student data into one unit or structure to avoid the increasing number of array 
declarations.  This is possible in C using the struct keyword:

struct RECORD   /* type tag */
{
     char  name[MAX_CHARS];     /* structure definition (no memory allocated)   */
     float mark;
     int   age;
} record,recList[MAX_STUDENTS]; /* structure declarations (memory is allocated) */
                                /* <record> is a single record with 3 fields    */
                                /* <recList> is an array of MAX_NAME records    */
                   

Memory map

              name part      mark part     age part
             <­­­­ 80 ­­­­>     <­ 4 ­>     <­ 2 ­>
             ­­­­­­­­­­­­­­     ­­­­­        ­­­­
   record   |  |  | ... |  |   |     |      |    |       80 + 4 + 2 bytes   
             ­­­­­­­­­­­­­­     ­­­­­        ­­­­
             ­­­­­­­­­­­­­­     ­­­­­        ­­­­
   recList 0|  |  | ... |  |   |     |      |    |       "       "
             ­­­­­­­­­­­­­­     ­­­­­        ­­­­
             ­­­­­­­­­­­­­­     ­­­­­        ­­­­
   recList 1|  |  | ... |  |   |     |      |    |       "       "
             ­­­­­­­­­­­­­­     ­­­­­        ­­­­
             ­­­­­­­­­­­­­­     ­­­­­        ­­­­
   recList 2|  |  | ... |  |   |     |      |    |       "       "
             ­­­­­­­­­­­­­­     ­­­­­        ­­­­
                .                .            .
                .                .            .
             ­­­­­­­­­­­­­­     ­­­­­        ­­­­
   recList 0|  |  | ... |  |   |     |      |    |
             ­­­­­­­­­­­­­­     ­­­­­        ­­­­      Total bytes : 2666 
                                        
Reading and writing to structures

To access a particular field in a structure we use the . or ­> operators:

  .     record.name     accesses the <name> field of <record>   record is a structure
  ­>    record­>age     accesses the <age>  field of <record>   record is a pointer
                                                                 to a structure

   
Let's consider how we would initialize and display the student record data.

#include <stdio.h>

void display(struct RECORD r);
void edit(struct RECORD *r);

void main()
{
     /* initialize our structures */

     strcpy(record.name,"Bobby");
     record.mark = 61;
     record.age  = 16;

     strcpy(recList[0].name,"Karen");
     recList[0].mark = 72;
     recList[0].age  = 17;

     /* pass structure into function for read only */

     display(record);
     display(recList[0]);

     /* pass structure into function for write access */

     edit(&record);
}

/* pass record by value, means a copy of r is made and values
     {name,mark,age}  transferred into the copy */
void display(struct RECORD r)
{
     printf("%s,%d,%d\n",r.name,r.mark,r.age);
}

/* pass record by reference, this means we actually modify the 
     original data passed in */

void edit(struct RECORD *r)
{
     printf("Enter name:");
     gets(r­>name);
     printf("Enter mark:");
     scanf("%d",&r­>mark);
     printf("Enter age:");
     scanf("%d",&r­>age);
}
Run-time memory model

So far we have written programs using a static memory model, static meaning fixed, meaning 
that as we code the program, we make assumptions as to the worst case size of the data we 
expect.  Recall our storage paradigm for a series of student names:

#define MAX_CHARS    80
#define MAX_STUDENTS 20

char s[MAX_STUDENTS][MAX_CHARS];

The student name array <s> has a fixed size, 80 chars being allotted for each student for the 
duration of the program.  In memory this is called a smooth array:

      012345.....79
       ­­­­­­­­­­­
Row 0 |Joe\0      |    Total space = 20 x 80 chars = 1600 bytes
Row 1 |Mary\0     |
...   |           |    (poor memory management)
Row 19|           |
       ­­­­­­­­­­­

There is a problem here in that too much memory is being wasted on names, considering that 
most of the time they will be less than 80 characters.  Twenty strings means we need a worst case 
of all 20 * 80 = 1600 characters.

Rather we would like to allocate the worst case once, say 80 characters, and after that allocate 
exactly the number of characters for each string at run­time.  This is a dynamic memory model, or 
run­ time memory model.  To create a run­time memory model, we must make use of the run­time 
memory functions in <stdlib.h>:

     void * malloc(int nBytes); /* allocate memory while program is running   */
     void  free(void *);        /* deallocate memory while program is running */
 How do we use  malloc()
    and 
   free()
   ?
  

malloc() returns a pointer to a safe address in memory in heap, or run­time space of a buffer of 
size which you must specify.

free() gives a malloced buffer back to the system for reuse.

  
Let's consider a simple example where we wish to read one student's name from the keyboard and 
store it optimally in memory:

/* Ex 1 */

#include <stdio.h>
#include <stdlib.h>

#define MAX_CHARS 80

void main()
{
     char temp[MAX_CHARS];   /* worst case size temporary buffer  */
     char *name;             /* pointer to an exact length string */

     printf("Please enter your name: MAX %d",MAX_CHARS);

     /* put user name in temp buffer */

     scanf("%s",temp);

     /* allocate exactly enough space for name */

     name = (char*)malloc(strlen(temp)+1);

     /* put the name in the exact length buffer */

     strcpy(name,temp);
     printf("stored result is %s\n",name);

     /* deallocate name once we are done with it */
     free(name);
}

Memory map

         ­­­­­­­­­­­­­­­­­­
temp ­> | ... 80 chars ... |          Compile time allocation
         ­­­­­­­­­­­­­­­­­­
         ­­­
name    | . |
         ­­­
         ||
         \/
         ­­­­­­­­­­
        |Jonathon\0|                  Run time allocation
         ­­­­­­­­­­  
/* Ex 2 : we can extend Ex 1 to read in a series of names 
         and store them optimally */

#define MAX_CHARS 80
#define NNAMES    5

void main()
{
     char *names[NNAMES];     /* allocate 5 ptrs to exact length strings */
     char temp[MAX_CHARS];    /* temporary buffer                        */
     int i;

     /* read and allocate the names */

     for (i = 0; i < NNAMES; i++)
     {
          printf("Enter name %d:",i);
          scanf("%79s",temp);
          name[i] = (char*)malloc(strlen(temp)+1);
          strcpy(name[i],temp);         
     }

     /* print and deallocate names */

     for (i = 0; i < NNAMES; i++)
     {
          printf("Name %d: %s\n",i,name[i]);
          free(name[i]);
     }
}

Memory Map

TEXT ...
CODE ...

HEAP (works forward in memory,
      contains malloced memory
      or run­time memory)

      Greg\0                       <­­­
      Nancy\0                      <­­­|­
      Fred\0                       <­­­|­|­
      Joe\0                        <­­­|­|­|­
      Allison\0                    <­­­|­|­|­|­
                                       | | | | |
STACK  (works backwords in memory,     | | | | |
        contains local variables)      | | | | |
                                       | | | | |
       int i                           | | | | |
       temp  ­>  |...80 chars |        | | | | |
       names ­>  names[0] ­­­­­­­­­­­­­  | | | |    [ <names> is a ragged array,
                 names[1] ­­­­­­­­­­­­­­­  | | |      much more memory efficient
                 names[2] ­­­­­­­­­­­­­­­­­  | |      than the smooth array model ]
                 names[3] ­­­­­­­­­­­­­­­­­­­  |
                 names[4] ­­­­­­­­­­­­­­­­­­­­
Tips on using malloc() and free()

(1) Make sure a buffer is allocated before you store data in it!

      /* Bad */
 
char *s;
strcpy(s,"joe");    /* crashes! copies characters
                              into a random area of memory */
s = (char*)malloc(10);

/* Good */

char *s;
s = (char*)malloc(10);
strcpy(s,"joe");

(2) Every executed malloc() call in your program should have a corresponding free().  If there is a 
mismatch in your program, serious problems will occur ranging from memory leaks to crashes, 
and are often difficult to debug.  If you forget to free the data, there exists a memory leak in the 
system.  You are hogging memory and not giving it back.

The good news is that by the end of the program, all malloced
memory is freed automatically.  The bad news is that if you
allocate too much memory during the program run and forget to
free it, you may get an "out of memory error" or your program will run extremely slowly, as it 
tries to page memory in/out from the hard disk.

Try the following program and see what happens:

char *s;
while (1)
{
     s = (char*)malloc(100);
}
(3) The importance of memory maps cannot be stressed enough.  The whole secret to coming to 
a solution quickly and easily to a problem is to be able to visualize what you want to create in 
memory on paper, then to write the code which supports that which you have sketched. 
Pseudocode follows easily from an accurate and concise memory map.

 
Allocating structures at run-time

In C we can allocate any type of data at run time including programmer­defined structures. 
Suppose we wish to read student data from a file and store it in an array of structures in memory. 
Of course, we have the option of implementing a static model:

struct RECORD
{
     char *name;
     float mark;
     int   age;
};

struct RECORD recList[100];    /* allocates 100 * ( 2 + 4 + 2 ) = 800 bytes */

But this design is a memory hog.  If we have only 20 records in our file, we waste 80 * (2 + 4 + 
2) = 640 bytes of unused record space.

Rather we would like to allocate space for our structures at run­
time when we read the header of our input file and discover the exact number of records to read.

Suppose we wanted to read exactly 5 records into memory at run­time.  Then we could make our 
array of structures declaration completely general:

                              ­­­­­­­
struct RECORD *recList;      |   .   |
                              ­­­­­­­
                                 |
                                 |
and allocate the exact number of structures when necessary:         
                                 |
                                 |
recList = (struct RECORD *)malloc(sizeof(struct RECORD)*5);
                                 |
                                 |     name  mark  age      sizeof(struct RECORD)
                                 |      2     4     2         =  8 bytes/record
                                 |     ­­­­­­­­­­­­­­­­
                                  ­­> | .  |     |     |
                                       ­­­­­­­­­­­­­­­­
                                       ­­­­­­­­­­­­­­­­
                                      | .  |     |     |
                                       ­­­­­­­­­­­­­­­­
                                      ­­­­­­­­­­­­­­­­
                                      | .  |     |     |
                                       ­­­­­­­­­­­­­­­­
                                       ­­­­­­­­­­­­­­­­
                                      | .  |     |     |
                                       ­­­­­­­­­­­­­­­­
                                       ­­­­­­­­­­­­­­­­
                                      | .  |     |     |
                                       ­­­­­­­­­­­­­­­­
 
Run-time structure allocation

INPUT FILE, test.in
­­­­­­­­­­­­­­­­­­­

3
Nancy 88 16
Greg 71 22
Allison 62 41

/* ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ */
/* FUNCTION: readFile()                                             */
/*  Reads <nRecs> student records from an ascii file                */
/* INPUTS:  <in>    ­ file to read from                             */
/* OUTPUTS: <nRecs> ­ # of records read from file                   */
/* RETURNS: the pointer to the array of structures read into memory */
/* ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ */

#define MAX_CHARS 80

struct RECORD * readFile(FILE *in,int *nRecs)
{
     struct RECORD *recList;
     int i;
     char curName[MAX_CHARS];

     fscanf("%d",nRecs);
     recList = (struct RECORD*)malloc(sizeof(struct RECORD));
     for (i = 0; i < *nRecs; i++)
     {
          /* read name into temporary buffer of size <MAX_CHARS> bytes */

          fscanf(in,"%s",curName);

          /* create the EXACT number of characters for the name string */

          recList[i].name = (char*)malloc(strlen(curName)+1);

          /* copy the temp string into the safe location */

          strcpy(recList[i].name,curName);

          /* read in the non­complex fields */

          fscanf(in,"%d",&recList[i].mark);
          fscanf(in,"%d",&recList[i].age);
     }

     /* Inform the caller of this function WHERE we stored all
          these records */

     return(recList);
}
/* ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ */
/* FUNCTION: DEALLOC()                                              */
/* If we allocated all this memory, should we not free it when we   */
/* are finished?  YES!  At the end of main() we should call a       */
/* deallocate function()                                            */
/* INPUTS: <recList> array to deallocate                            */
/*         <nRecs>   number of records to deallocate                */
/* RETURNS: none                                                    */  
/* ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ */

void dealloc(struct RECORD *recList,int nRecs)
{
     int i;
     for (i = 0; i < nRecs; i++)
     {
          /* deallocate name field of each record */

          free(recList[i].name);
     }

     /* deallocate the array of structures */

     free(recList);
}

/* ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ */
/* FUNCTION: MAIN()                                                 */
/* Read the input file and load the student records into memory     */
/* Do some useful things with the data and deallocate it            */
/*   when done                                                      */
/* ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ */

void main()
{
     int nRecs;
     struct RECORD *recList;
     FILE *in;
     ...
     recList = readFile(in,&nRecs);
     ...
     search(...);
     ...
     display(recList[0]);
     ...
     dealloc(recList,nRecs);
}
Defining types

We can create our own types in C by using the typedef keyword.  Having done this, we can use 
them to declare variables as we would the standard C types like int, float and char.  Here's how 
we would do it with our student record example:

/* Inform the compiler of a type definition called <Student> */
/* No memory is allocated                                    */

typedef struct 
{
char *name;
float mark;
int   age;
} Student;

Student r[3];  /* Allocate three student records in memory */

We can even create types within types.  Consider a definition of a Class which uses the Student 
type as a subfield.

typedef struct
{
char *teacher;
char *courseDescription;
Student *s;
} Class;

And an even greater aggregate School which includes Class which includes Student: 

typedef struct
{
char *name,*address,*telephone;
Class *classes;
} School;
It is also possible to typedefine standard types into other names:

typedef char Buffer[80];     /* define Buffer as an 80 byte char space */
typedef unsigned char Byte;  /* define Byte as a value from 0­255 */

Tip

Whenever possible, type­define symbols to increase readability of code.
Include files

Usually when we define our own types, we encode it as a separate unit its own file.  We call such 
files include files and suffix them with '.H' to distinguish them from C code files:

STUDENT.H                      STUDENT.C 
­­­­­­­­­                      ­­­­­­­­­

#ifndef STUDENTH                #include <stdio.h>
#define STUDENTH                #include "student.h"

typedef struct                  void main()
{                               {
char *name;                  Student s[5];
float mark;                  s[0].name = "Barny"; 
int   age;                   s[0].mark = 67;  
} Student;                           ...
                                }
#endif

Advantages of include files:

. Promotes code reuse.  Include files be used across source (.c)
  files to avoid retyping definitions over again.
 
. Promotes modularity.  When any programmer wishes to find out what a Student is, they can 
browse the appropriate .h file rather than leafing through several source files.

Tips

. Never put declarations in an include file.  The result will inevitably be redeclaration errors at 
link time.

. Always include an #ifndef..#endif wrapper around the code in a
  .h file order to avoid multiple inclusion by the compiler.
  Ensure that your #define <symbol> is unique.

. Only put #define macro definitions and typedef statements
  in your include files.
Unions

C provides space­saving support to hold data of differing types in the same area of memory. 
There are instances when we may wish to record heterogeneous data in the same structure 
definition.  Consider the following overlay of three different types:

union threeTypesInOne              

int i;        /* 2 bytes */
float f;      /* 4 bytes */
char s[3];    /* 3 bytes */
};                                        0   1   2   3
                                         ­­­­­­­­­­­­­­­
union threeTypesInOne data;             |   |   |   |   |
                                         ­­­­­­­­­­­­­­­

This structure describes an area of memory of not 2, not 3, but 4 bytes, the byte size of the largest 
field in the structure.  In that 4 bytes we have the choice of storing an integer a float or a 3 byte 
string (but not all 3 at once).  The following 3 statements are all legal assignments of the 4 byte 
structure:

data.i = 4;           /* uses the first 2 bytes of <data> */
data.f = 56.67;       /* uses all 4 bytes of <data>       */
strcpy(data.s,"ab");  /* uses the first 3 bytes of <data> */

If we know what type of data is stored in <data>, we can decode it and display it accordingly:

printf("%d",data.i);    /* if <data> is integer data */
printf("%f",data.f);    /* if <data> is float   data */
printf("%s",data.s);    /* if <data> is string  data */
Advantages of unions

We may wonder what is the advantage of creating a union.

SPACE SAVING!

If we are unsure of the type of data we will get at run­time, we can make a general overlay or 
union definition to account for all the cases and let the program handle the cases at run­time 
using the minimum amount of storage space.

What is costly memory­wise by using a standard struct definition
instead of the union definition?
 

struct
{
int i;        /* 2 bytes */
float f;      /* 4 bytes */
char s[3];    /* 3 bytes */
};
Using unions in real life

Consider the case of a business wishing to store data on their clients of which one particular field 
is a general description of their salary.  There are 3 possiblities:

(1)   hourly rate   => designated by a floating point number
(2)   yearly salary => designated by a long int
(3)   UI            => designated by a 3 byte character field

The tragedy of the original int,float,char[3] union example is that we have no way of knowing 
which type the salary is.  We must keep a controller variable <typeOfSalary> handy to identify 
the salary category otherwise our program is useless.

client.h

#ifndef CLIENTH
#define CLIENTH

enum {HOURLY,YEARLY,OTHER};

union GeneralSalary
{                         
float    hourly; 
long int yearly; 
char     other[3];
};       
                               
typedef struct
{
char *name;
char *address;
...
char typeOfSalary;
union GeneralSalary salary;
} Client;

#endif

client.c
#include "client.h"
#include <string.h>

#define MAX_CLIENTS 40

void main()
{
Client cL[MAX_CLIENTS];
cL[0].name          = "Martha";
cL[0].address       = "231 Henning St,..";
cL[0].typeOfSalary  = HOURLY;
cL[0].salary.hourly = 15.50; 

cL[1].name          = "Bruce";
cL[1].address       = "P.O Box 23,..";
cL[1].typeOfSalary  = OTHER;
strcpy(cL[1].salary.other,"UI");
...
}
Function pointers

We can invoke functions directly or indirectly in C.  Direct access is straightforward as we have 
been doing up to this time: 

void f()
{
     printf("F is called\n");
}

void main()
{
     f();
}

f is a function in memory somewhere.  The compiler treats the
symbol f as the start address of the code.  f() means go to that
address and begin executing the code at that address.

We can call f() indirectly by creating a secondary pointer to f
and de­referencing this pointer.  Let us modify main to do this:

void main()
{
     void (*pf)() = f;  /* Declare <pf> as a pointer
                           to a function that returns void
                           and takes no arguments.
                           Assign this pointer to point to f() */
     (*pf)();           /* Invoke f() via <pf> */  
}

Output:
F is called

Anaysis of syntax:

Declare a pointer var called pf
pf points to a function that returns     Assign pf to point to f
      void                                     

<­­­­­­­­>                               <­­­>
void (*pf)  ()                           =   f ;
            /\
            ||­­specifies no args

dereferences the pointer,       Means pass no args
or follows the pointer           into the function (*pf)

<­­­>                            <­­­>
(*pf)                            ();

The syntax is awkward at first but the result is very powerful as
we will see.
Application of function pointers

Consider a menu driven application with the following items:

Load, Save, Edit, Run, Quit

Instead of hard coding the display, user select and function invocations, we can create a data 
driven interface:

/* Generalized menu driven interface */

enum {NO_ERROR,ERROR};
enum {LOAD,SAVE,EDIT,RUN,QUIT};

static int load() {...};
static int save() {...};
static int edit() {...};
static int run()  {...};
static int quit() {...};

typedef void (*PVF)();

#define NMENU_OPTIONS 5

static int doMenu(char *menuOptions[],PVF *choices);
 
void main()
{
char menuOptions[NMENU_OPTIONS] = {"Load","Save","Edit","Run","Quit"};

PVF functions[NMENU_OPTIONS]      = {load,save,edit,run,quit};
int selection;

selection = doMenu(menuOptions,function);

/* if we wanted to call a specific user function in code,
         we could code something like : */

(*function[EDIT])();
}

/* ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
   FUNCTION: doMenu
             Display a series of <menuOptions> to the user
             and invoke the corresponding <function>
   INPUTS  : <menuOptions>
             <function>
   RETURNS : index of the selected function invoked
   ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ */

static int doMenu(char *menuOptions[],PVF *function)
{
int selection;
for (i = 0; i < NMENU_OPTIONS; i++)
printf("%d %s\n",i+1,menuOptions[i]);

do
{
scanf("%d",&selection);
} while ((i < 1) || (i > NMENU_SELECTIONS));

if ((*function[i­1])() == ERROR)
printf("Error in function %s\n",menuOptions[i­1]);

return(selection);
}
Bit-wise operations

We have been accessing data of byte size or greater via the {char,int, float, double} declaration 
primitives.  To save space, sometimes we will be forced to optimize our code to crunch many 
pieces of data into one byte.  How do we do it?  Recall the format of a byte:

101011001 <­ 1 byte consists of 8 single bits

Each bit denotes an unique power of 2.  The above bit pattern
designates:

   7      6      5      4      3      2       1       0 
0*2  + 1*2  + 0*2  + 1*2  + 1*2  + 0*2   + 0*2  +  1*2

= 64 + 16 + 8 + 1

= 89

Actually 8 pieces of information were recorded in this bit
pattern:  4 FALSE or (0) conditions and 4 TRUE (1) conditions

If each bit denotes a bit flag, we can have 8 bit flags encoded
in 1 byte.

We can mask bits in and out to load/retrieve bit information using the following bitwise 
operators:

&       AND           bits
|       OR            bits
^       EXCLUSIVE OR  bits
<<      SHIFT         bits left  (multiply by 2)
>>      SHIFT         bits right (divide   by 2)
Tip

Don't mix up the logical type operators && and || with the bit­wise and/or operators & and |. 
They have different meanings!
Encoding bit fields

Suppose a banking company wishes to store several bits of information on their clients.  Then 
each bit can be reserved to encode a particular client attribute:

enum {BIT0 = 1, BIT1 = 2, BIT2 = 4,BIT3 = 8, BIT4 = 16,
      BIT5 = 32,BIT6 = 64,BIT7 = 128};

enum {HAS_A_MAJOR_CREDIT_CARD     = BIT0,
      IS_CURRENTLY_WORKING        = BIT1,
      HAS_BOUNCED_A_CHEQUE        = BIT2,
      HAS_A_CURRENT_MAJOR_BALANCE = BIT3};

By using the bit­wise and/or operations we can encode the above
information arbitrarily into a single byte:

Bit­wise & operation

"Pessimistic match" => used for TEST, OUTPUT operations

 00100000 & 11011111   =   00000000

 11011011 & 10111000   =   10011000

Bit­wise | operation

"Optimistic match" => used for INPUT operation

 00100000 | 11011111   = 11111111

 11011010 | 10111000   = 11111110
Bit fields in customer records

How can we use these &,| operations to encode/decode customer
data?

/* Customer record bit field example */

struct custRecord
{
     char *name;
     char info;
};

#define CUST_NAME "Ralph"

typedef custRecord custRec;

void main()
{
     custRec c1;

     c1.name = (char*)malloc(strlen(CUST_NAME)+1);
     strcpy(c1.name,CUST_NAME);

     /* Load bit info into <c1> */

     c1.info = 0;
     c1.info = c1.info | HAS_A_MAJOR_CREDIT_CARD;
     c1.info |= HAS_BOUNCED_A_CHEQUE;

     /* If we want to check c1's info, we should get 5 as the output */

     printf("c1.info=%d\n",c1.info);    

     /* Now to query a particular flag in <c1.info> */
     /* We are masking the <info> byte              */

     if (c1.info & IS_CURRENTLY_WORKING)
printf("%s is currently working\n",c1.name);
     else
printf("%s is not currently working\n",c1.name);
  
     if (c1.info & HAS_BOUNCED_A_CHEQUE)
          printf("%s has bounced a cheque\n",c1.name);
     else
          printf("%s has not bounced a cheque\n",c1.name);
     
}

Output

c1.info=5
Ralph is not currently working
Ralph has bounced a cheque
 
How much memory have we saved?

. If we stored an int for each flag we would need 4 x 2 = 8
  bytes/customer.

. If we stored an char for each flag we would need 4 x 1 = 4
  bytes/customer.

. If we stored a bit for each flag we would need 1 byte/customer.

If there are 3000 customers in memory, we save

   3000 * 4 = 12000
   3000 * 1 =  3000
              ­­­­­
               9000

9K saving!

Other bit operation examples

Recall:

~  <­ invert bits
^  <­ exclusive or
<< <­ left bit shift
>> <­ right bit shift

Ex's:

~10110110 = 01001001
011 ^ 110 = 101
10010010 << 1 = 00100100     <­ multiply by 2 operation
10010010 >> 2 = 00100100     <­ int divide by 4 operation
Part IV Exercises

(1) How much memory is taken up by the following C statements? 
Draw a memory map.

struct COMPUTER
{
     int mem;
     int hd;
     char make[30];
     int cpu;
} *cp;

struct COMPUTER cs[10];
struct COMPUTER *csp[10];

(2) What is the output of the program?

void f(struct COMPUTER c,struct COMPUTER *csp2)
{
     c.cpu = 100;
     strcpy(csp2­>make,"Pentium");

void main()
{
     csp[1] = &cs[0];
     f(cs[1],csp[1]);
     printf("%s %d\n",cs[0].make,cs[0].cpu);
     printf("%s %d\n",cs[1].make,cs[1].cpu);
}

(3) Draw a memory map for the following program.  There are two
run­time flaws with this program.  What are they?  Explain.

#include <stdlib.h>
#include <string.h>

void main()
{
     char *array[];
     array = (char**) malloc(sizeof(char*) * 3 );
     array[0] = "Tom";
     array[1] = "Jerry";
     strcpy(array[2],"Floyd");
     array[3] = "Quinn";
}
(4) Draw a memory map for the following program.  How much memory is allocated?

enum {LIGHT_GRAY,MEDIUM_GRAY,DARK_GRAY,BLACK};

struct ZOMBIE
{
     char *name;
     int nPastLifes;
     int color;
};

void main()
{
     struct ZOMBIE zombie[2];
     struct ZOMBIE *zp[2];
     zp[0] = &zombie[0];
     zp[1] = &zombie[1];
     zp[0]­>name = "ghoul";
     zp[0]­>nPastLifes = 1003;
     zp[0]­>color = DARK_GRAY;
     zombie[1].name = (char*) malloc(30);
     strcpy(zombie[1].name,"Troll");
     zombie[1].nPastLifes = 9567;
     zombie[1].color = BLACK; 
}

(5) A brokerage firm requires need of stock analyzer program.  Currently they have a database of 
stock data on several companies in the following format:

<Co name> <nStockTrading days> <start date> <stock $ data/day...>

XYZ Co       10  09/12/96  112.34 112.45 110.54 97.56 110.66 ..  
ABC Co       8   09/24/96  5.45   5.46    5.45   5.45 5.47 
...

They have need of a utility to pull up the data on a particular company and display it in tabular 
format and to note any anomalies in price change/day.  Here is a sample run:

Main menu:

[S] Select company
[L] List   companies
=> S XYZ

[D] Display data
[A] Review daily $ fluctuation anomalies
[S] Statistics

=> A

Enter max expected price change/day: (% or $ absolute value)
=> 8%
Anomalies for XYZ Co (over 8% change):
09/12/15 ­11.76%   from $110.54 to $ 97.56
09/12/16 +11.34%   from $ 97.56 to $110.66
...

=> S
XYZ Co highest, lowest, average price for 10 days: $112.45, $97.56, $100.55
(6) You are working for a company that creates educational software for children.  To make the 
program more fun, the team designers have decided to add a games section to the package. 
Under this heading, you have been asked to create a tic­tac­toe program where the player plays 
against the computer.  Your program runs in three selectable levels: easy, medium or difficult. 
Easy rarely wins, medium mostly draws and difficult never loses.

(7) Praxis is in need of a system to manage and monitor student attendance.  Typically, daily, the 
intstructors fill in attendance sheets which are key­entered by secretarial staff at the end of the 
day, and bi­weekly upper management effects attendance queries to see if there are any 
problematic recurring absences. 

Among the important features is certainly the last where users should be able to pull up students 
who have been away more than a certain threshold of acceptable days.  Here is a sample run:

Main menu

[D] Display student attendance
[L] List delinquents
[A] Add an absence
[E] Edit an absence

=> D henri, larry

Larry Henri: Total 1 Absence(s)
  May 24/97  0.5 day, Doctor's appointment

=> L 5

Students away 5 or more days:
   John Doe     6  April 2/97, April 5/97, May 16/97, ...
   Carol Brown  5  March 11/97, March 12/97, March 13/97, ...
   ...
PROJECT 1: Grapher

Write a program which allows functions to be graphed on the text screen.

Make a function graphFunc() that accepts the following inputs:

xrange: x1,x2 
yrange (for clipping output): y1,y2
a function to plot (if you know how to use function pointers)
(otherwise just assume that there is a function float f(float);
 defined in the code)

SPECIFICATIONS

Instead of printing directly to the screen using printf or putch
You can map the screen with a 2D array :

char screen[XSIZE][YSIZE];

graphFunc() plots '*' characters to this array wherever there is a function value.

EX, suppose f(x) = x+3   at x = 2, f = 5 so we expect a  * character to be assigned to the cell 
screen[3][5].

Once the function has been plotted to the array, it is just a matter of displaying the array to the 
terminal

graphFunc(..);
clrscr();
display(screen); /* create a function to display the array passed in */
...
PROJECT 2 : Document proof reader

You are working for a company which produces text­editing tools. 
One of the parts of the word­processor they are developing is a
proof reader utility which checks text files for obvious or
potential user typos.  Typically these documents are essays,
reports or letters where we can expect to find such errors as:

(1) A sentence is not terminated by a . ? or !
(2) The first word of a sentence is not capitalized.
(2) a '.' or '!' or '?' is found with not exactly 2 spaces after
    a '.' or '!' or '?' is found with spaces before it
(3) a ';' or ',' is found with no space after it
    a ';' or ',' is found with spaces before it 
(4) a ( or " is found with no matching " or )

Warnings should also be issued for the following scenarios:

(1) A word other than the first word in a sentence is
    capitalized.
(2) Repeated punctuation occurs
     !! ?? !? ?! ;; ,, .. .,

Your program should run in two modes.  Mode 1 displays full
error/warning diagnostics, mode 2 suppresses warnings.

Here is some sample output for what your proof reader should
issue running in Mode 1 given the following document (TEST.IN)

TEST.IN

     This first sentence has no errors.  the quick brown Fox
jumped over the moon , and beyond that into the void of the
universe.  What about the rabbit
Why o why did I ever get into "C" programming???    Because it's
fun?  Ga! Why did they ever call it the "c language?
     This is a run on sentence that never seems to end when will
it ever end maybe today, maybe tomorrow or the next or the next
or a fortnight from now; or possibly never never never never
never never never never never never never never never
never never never when will it end
PROGRAM OUTPUT

ERROR:   Line 1 Beginning word in sentence <the> not capitalized
ERROR:   Line 3 Forgotten '.' at end of sentence <rabbit>
ERROR:   Line 4 Beginning of paragraph <Why> not indented
WARNING: Line 4 Interior word of sentence capitalized <"C">
WARNING: Line 4 Extra punctuation after ? at <???>
ERROR:   Line 4 Improper # of spaces after <?>
ERROR:   Line 5 Improper # of spaces after <!>
ERROR:   Line 5 Mismatched " at <"c>
ERROR:   Line 6 Improper # of spaces after <!>
WARNING: Line 12 Possible run on sentence, 400 characters seen 
ERROR:   Line 12 Forgotten '.' at end of sentence
DESIGN

To facilitate the programming think of a document as a series of
paragraphs, each paragraph a series of sentences, each sentence a
series of words, each word a series of characters.  Then create
functions to process paragraphs, sentences, and words.  For
example a paragraph should start with a tab character '\t' and
end with a carriage return.  A sentence must begin with an upper
case character and end with a '.'.  If these conditions are not
satisfied then errors/warnings are spit out.

TESTING

To test your program, devise a number of input test files.  Each
file should separately test for each type of error.

PART II

Find an expression for the execution time of your program in
terms of the file size and any other variables you feel
necessary, like NP, # of punctuation marks in file, etc.

PART III EXTENDED OPTION

If you find this kind of programming challenging or even
interesting, write another utility which will do paragraph
formatting.  The user inputs the name of the file to format and
the left and right margin (lm,rm) settings to appear in the
output file.  The program writes the output file, wrapping words
after rm to the next line without chopping them up.  Consider the
following input document TEST.IN with the following control
settings:

LM 2, RM 20
TEST.IN

     Here is a bunch of stuff to try your word
wrap program on.  No words should be chopped up,
discarded, altered or added!
If there are then debugging is necessary 
which has the effect of ruining one's weekend.

OUTPUT:
012345678901234567890    <­ This line is not written as part of
     Here is a bunch        of the formatted text
of stuff to try your
word wrap program on.
  No words should be
chopped up,
discarded, altered or
added!
If there are then
debugging is
necessary which has
the effect of ruining
one's weekend.
PROJECT 3: Catering company ingredient manager

You are hired by Karake food catering Co. to develop an automated system that manages weekly 
shopping requirements.  Karake's scenario is the following:

     ADVANCE ORDERS             Shopping for meal
       COME IN         ===>     ingredients must be done
                                in advance so banquets can
                                be prepared on time

This means that specific food and quantities must be purchased at
the appropriate times throughout the week.  This may be complex. 
Mistakes can be made by erring/sleepy personell.  Better to let
the computer perform optimal allocation of meal ingredients.

Karake promotes the following menu:

=================================================
KARAKE CATERING CO.
    Delicacies offered:

  1 ­ Combo 1, veggie platter
  2 ­ Combo 2, fish   platter with greek salad

=================================================

The type of report they would like to be generated is:

Attention : shoppers

Report generated on Mar 09/96
You have the following shopping schedule(s):
Mar 3/96
  Item        Amount      Price/unit     Expected price
  ­­­­        ­­­­­­      ­­­­­­­­­­     ­­­­­­­­­­­­­­
  Onion        40           $0.10        $400
  Lettuce      50           $1.12        $65
  Tomato       43
  Cucumber     21            ...
  Potatoe      100
  Rice         12 kg
  Beverage     100
                                        ­­­­­­­­
                                          $312.12
Mar 5/96

  Item        Amount      Price/unit     Expected price
  ­­­­        ­­­­­­      ­­­­­­­­­­     ­­­­­­­­­­­­­­
   Halibut      5 kg         5.00           $25
   Cream        3 kg         1.50           $ 4.50
                                           ­­­­­­­­­
                                             29.50

     Warning: the following foods may spoil before next usage!!
          Cream $4.50

Mar 10/96
   
  Item        Amount      Price/unit     Expected price
  ­­­­        ­­­­­­      ­­­­­­­­­­     ­­­­­­­­­­­­­­
   Tomatoes   ....

.. etc.

     
Attention: managers, stats (for week ending Mar 10/96):

Total # combos     : 150
# Mouths to feed   : 150
Total Revenue      : $1202.56
Total Food expenses: $ 401.22
Gross profit       : $ 801.34
Sample time line

Let us look at a time line for a typical week in the life of
Karake:

(O) Mar 01/96  Karake gets two orders for catering
                  One     due Mar 04/96
                  Another due Mar 09/96
|

(S) Mar 03/96  Shopping day, ingredients purchased

(B) Mar 04/96  Banquet served

(S) Mar 05/96  Shopping day, more ingredients purchased

(0) Mar 06/96  Another order for Mar 12/96

(B) Mar 09/96  Banquet served

|
(S) Mar 10/96  Shopping day, more ingredients purchased

(B) Mar 12/96  Feast!

...
According to the codes listed above,

     O = Order received
     S = Shopping day
     B = Banquet

we will need several files to organize orders, meal combos,
specific food info and shopping times.  Let's propose them
working from high to low level.
DATA FILES:

 combo ­  details 
 
{
  "Combo 1, veggie platter"
  Salad           1
  Fries           1
  RiceDelight     1
  Beverage        1 
}
{
  "Combo 2, fish platter with greek salad"
     Halibut 1
     Fries   2
     Beverage 1
}
{
  "Combo 3, cocktail delight"
     ShrimpTray          1
     CheeseAndCrackers   1 
     Wine                1
}

 combo ­  primitives 

Salad
{
     Lettuce 0.3
     Tomato  0.5
     Cucumber 0.1
}

Fries
{
     Potato 2;
}

RiceDelight

  Tomato   0.5
  Rice     0.2   
  Onion    0.4  
}
 Basic ­  ingredients 

# NAME    Units       Expected price/unit SPOIL TIME(in days)
#­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
#          N = # of units
#          K = # of kgs

Tomato      N    0.3                     5
Cucumber    N    0.25                    3
Onion       N    0.4                   30
Potatoes    N    0.05                  20
Broccoli    N    1.25                   4
SwissChar   N    2.00                   3
Halibut     K    5.00                   4
Rice        K    0.45                  365
Spagghetti  K    0.32                  365
Milk        K    0.50                   5  
Cream       K    0.70                   3

orders

# each order has following format
#  # of combo orders, Delivery required
#    the details of the record are
#      date, # persons, meal #, delivery

1 YES
   02/03/09  10     2

2 YES
   02/03/11  25     1
   02/03/11  15     2

 regular ­  runs done on the following days 

Mon
Wed

NOTE:

By setting up the control information in various files, the program is quite general.  The user can 
restructure meal plans, adjust ingredient prices, etc by changing these control files and rerunning 
the program.
Suggested definitions:

MEAL COMBOS  (MC.IN)

struct Plate
{
     char *itemName;
     float quantity;
};

struct MealCombo
{
     char *comboName;
     struct Plate *pList;
};

ORDERS (ORDERS.IN)

struct SubCombo
{
     int comboStyle;
     int totalPeople;
};

struct Order
{
     char *name;
     char *address;
     char *phone;
     char *orderDate;
     char *banquetDate;
     int nCombos;
     struct SubCombo *scList;
     char *payment;
     int delivery;
};

MEAL COMBO INFO (MCI.IN)

struct MealIngredient
{
     char *ingredient;
     float quantity;
};

struct MealComboInfo
{
     char *mealChoice;
     struct MealIngredient *ingrList;
};
INGREDIENTS (ING.IN)

enum {PASTA,DAIRY,MEAT,VEG};  // this is for typeOfIngredient

struct Ingredient
{
     char *name;
     char unit;
     float price;
     int  keepDuration;
     int typeOfIngredient;
};

REGULAR RUNS (SHOPTIME.IN)

char* shopDays[MAX_DAYS];

shopDays[0] = "Mon";
shopDays[1] = "Wed";
shopDays[2] = 0;

The above describes two regular shopping days

You might also like