CS 2213 Advanced Programming
Introduction to Program Development in C


Read Chapter 1 of the text.
Next Topic: Types, Operators and Expressions
Start of Class Tuesday, Sept. 5, 2000
Start of Class Thursday, Sept. 7, 2000

Some similarities between Java and C


Some differences between Java and C


A simple C program

#include <stdio.h>

int main() {
printf("hello world\n");
}

Compiling a C program

cc -o executablename sourcename.c
Usually executablename and sourcename are the same.

Suppose the above program were in a file called myhello.c

cc -o myhello myhello.c would create a file called myhello.
You could run the program just by invoking its name:

pandora# myhello
hello world
pandora#

Using lint

The C compiler is not very picky about checking for correctness.
The lint program is used to pick out the fuzzy parts of your code.

To run this just execute:
lint sourcefilename.c

For example:
lint myhello.c produces output like the following:

pandora% lint myhello.c
(5) warning: Function has no return statement : main

function falls off bottom without returning value
(5) main        

function returns value which is always ignored
printf          
pandora% 
Comments about the lint output: We can fix this program as follows:
#include <stdio.h>

int main() {
printf("hello world\n");
return 0;
}
We can run lint on this:
pandora% lint myhello.c

function returns value which is always ignored
printf  
pandora% 

Using make

make is a utility that can help you avoid doing a lot of typing when you want to compile your programs.

It may not seem to useful at this point, but when your programs become more complicated, you will want to use it.

In this course, you must use it!

Only the very simplest aspects of the make utility will be discussed here.

When you execute make, it looks for a file called makefile in the current directory. This makefile is a description file which described the dependency relationships that exist between various program modules.

The dependencies have the following form:

target:     components
TAB         rule
The first line is called a dependency and the second line is called a rule. Rule lines must start with a tab character. There may be more than one rule line for each dependency.

The rules are invoked if any of the components are newer than the target, or if the target does not exist. Here is a simple makefile for myhello

myhello:   myhello.c
cc -o myhello myhello.c
If myhello does not exist, then running make will compile myhello.c
pandora% make
cc -o myhello myhello.c
pandora% 
If we do it again:
pandora% make
`myhello' is up to date.
pandora% 
By default, make will look only at the first target in the makefile. If you give it an argument, it will look at the corresponding target.

Consider the following makefile:

myhello:   myhello.c
cc -o myhello myhello.c

lint:
lint myhello.c
Now typing make will do the same thing as before, but typing
make lint
will cause the rule for lint to be used. Since the file lint does not exist, the rule will always be executed.
pandora% rm myhello
pandora% make 
cc -o myhello myhello.c
pandora% make lint
lint myhello.c

function returns value which is always ignored
printf          
pandora% make
`myhello' is up to date.
pandora% 
Sometimes you want to remove the executable and start again.
Here is another useful makefile:
myhello:   myhello.c
cc -o myhello myhello.c

lint:
lint myhello.c

clean:
rm myhello
Now we can run make some more:
pandora% ls -la
total 8
drwxr-xr-x   2 srobbins staff        512 Aug 30 15:38 .
drwxr-xr-x   4 srobbins staff        512 Aug 30 15:25 ..
-rw-r--r--   1 srobbins staff         89 Aug 30 15:36 makefile
-rw-r--r--   1 srobbins staff         76 Aug 30 15:25 myhello.c
pandora% make
cc -o myhello myhello.c
pandora% make
`myhello' is up to date.
pandora% ls -la
total 22
drwxr-xr-x   2 srobbins staff        512 Aug 30 15:38 .
drwxr-xr-x   4 srobbins staff        512 Aug 30 15:25 ..
-rw-r--r--   1 srobbins staff         89 Aug 30 15:36 makefile
-rwxr-xr-x   1 srobbins staff       6212 Aug 30 15:38 myhello
-rw-r--r--   1 srobbins staff         76 Aug 30 15:25 myhello.c
pandora% make clean
rm myhello
pandora% ls -la
total 8
drwxr-xr-x   2 srobbins staff        512 Aug 30 15:38 .
drwxr-xr-x   4 srobbins staff        512 Aug 30 15:25 ..
-rw-r--r--   1 srobbins staff         89 Aug 30 15:36 makefile
-rw-r--r--   1 srobbins staff         76 Aug 30 15:25 myhello.c
pandora% make
cc -o myhello myhello.c
pandora% 

Separate Compilation

In Java

In C

When we run the compiler with
cc -o myhello myhello.c
It creates myhello.o and then deletes it when the executable, myhello, is created.
You can do this in two steps as follows:
cc -c myhello.c
cc -o myhello myhello.o

The first line creates myhello.o and the second line creates myhello from myhello.o.

If you have several functions, you can put some of them in another file and compile them separately.


Example: Calculating Primes

In CS 1713 you may have done a problem of calculating primes using a sieve.
The idea is as follows for calculating the primes less than 1000: Here are the prototypes of some functions we might use for this.
A prototype of a function gives the return value, name, and parameter types of a function.
This is similar to what you put in an interface in Java.

void initializeSieve(int primeList[], int primeListSize);
    fill the array primeList of size printListSize with 1's
void makeSieveOne(int primeList[], int primeListSize, int prime);
    set the multiples of prime to 0.
void makeSieve(int primeList[], int primeListSize);
    set the multiples of all primes to 0.
void printSieve(int primeList[], int primeListSize);
    print out the array (for debugging)
void printPrimes(int primeList[], int primeListSize);
    print out the primes assuming the array has been made

We will put these in a file called sieve.c.

Notice that the first two of these will be called by makeSieve and do not have to be accessible outside the file.
In Java we declare these private.
In C we do something similar by declaring them to be static. This means that they are not accessible outside the file and their names do not appear in the sieve.o file.

We create a separate file called sievemain.c which contains the main program.

#include <stdio.h>
#include "sieve.h"

#define SIZE 2000

int main() {
   int myList[SIZE];
   int myListSize;

   printf("This program was written by Steven Robbins\n");
   myListSize = SIZE;
   makeSieve(myList,myListSize);
   printSieve(myList,myListSize);
   printPrimes(myList,myListSize);
   return 0;
}
Here is the sieve.h file:
void makeSieve(int primeList[], int primeListSize);
void printSieve(int primeList[], int primeListSize);
void printPrimes(int primeList[], int primeListSize);
Here is a makefile for this program:
all: sieve.o sievemain

sieve.o:   sieve.c
        cc -c sieve.c

sievemain: sieve.h sieve.o sievemain.c
        cc -o sievemain sievemain.c sieve.o

lint:
        lint sievemain.c sieve.c
run:
        sievemain
clean:
        rm -f sievemain *.o
Here is the sieve.c file:
#include <stdio.h>

static void initializeSieve(int primeList[], int primeListSize) {
   int i;

   primeList[0] = 0;
   primeList[1] = 0;
   for (i=2;i<primeListSize;i++)
   primeList[i] = 1;
}

static void makeSieveOne(int primeList[], int primeListSize, int prime) {
   int i;

   for (i=2*prime;i<primeListSize;i=i+prime)
      primeList[i] = 0;
}

void makeSieve(int primeList[], int primeListSize) {
   int i;

   initializeSieve(primeList,primeListSize);
   for (i=2;i*i<primeListSize;i++)
      if (primeList[i] == 1)
         makeSieveOne(primeList,primeListSize,i);
}

void printSieve(int primeList[], int primeListSize) {
   int i;

   printf("Sieve follows:");
   for (i=0;i<primeListSize;i++) {
      if (i%25 == 0)
         printf("\n");
      printf("%d ",primeList[i]);
   }
   printf("\n");
}

void printPrimes(int primeList[], int primeListSize) {
   int i;
   int count;

   count = 0;
   printf("Primes follow:");
   for (i=0;i<primeListSize;i++) {
      if (primeList[i] == 1) {
         if (count%10 == 0)
             printf("\n");
         printf("%6d ",i);
         count++;
      }
   }
   printf("\n");
}
Running the program gives the following output:
0 0 1 1 0 1 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 1 0 
0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 
0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 
0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 
0 1 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 
0 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 
0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 
0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 1 
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 
0 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 
0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 
0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 
0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 
0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 
0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 
0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 
0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 
0 0 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 
0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 
0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 
0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 
0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 
0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 
0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0 
0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 
0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 
0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 
0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 
0 1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 
0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 
0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 
0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 
0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 
0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 
0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 
0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 
0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 
0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 
0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 
0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 
0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 
0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 
0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 
0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 
0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 
0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 
0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 
0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 
0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 
0 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 
0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 
0 0 0 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 1 
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 
0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 
0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 
0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 
0 1 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 
0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 1 
0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 
0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 
0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 
0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 
0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 
0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 
0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 
0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 
Primes follow:
     2      3      5      7     11     13     17     19     23     29 
    31     37     41     43     47     53     59     61     67     71 
    73     79     83     89     97    101    103    107    109    113 
   127    131    137    139    149    151    157    163    167    173 
   179    181    191    193    197    199    211    223    227    229 
   233    239    241    251    257    263    269    271    277    281 
   283    293    307    311    313    317    331    337    347    349 
   353    359    367    373    379    383    389    397    401    409 
   419    421    431    433    439    443    449    457    461    463 
   467    479    487    491    499    503    509    521    523    541 
   547    557    563    569    571    577    587    593    599    601 
   607    613    617    619    631    641    643    647    653    659 
   661    673    677    683    691    701    709    719    727    733 
   739    743    751    757    761    769    773    787    797    809 
   811    821    823    827    829    839    853    857    859    863 
   877    881    883    887    907    911    919    929    937    941 
   947    953    967    971    977    983    991    997   1009   1013 
  1019   1021   1031   1033   1039   1049   1051   1061   1063   1069 
  1087   1091   1093   1097   1103   1109   1117   1123   1129   1151 
  1153   1163   1171   1181   1187   1193   1201   1213   1217   1223 
  1229   1231   1237   1249   1259   1277   1279   1283   1289   1291 
  1297   1301   1303   1307   1319   1321   1327   1361   1367   1373 
  1381   1399   1409   1423   1427   1429   1433   1439   1447   1451 
  1453   1459   1471   1481   1483   1487   1489   1493   1499   1511 
  1523   1531   1543   1549   1553   1559   1567   1571   1579   1583 
  1597   1601   1607   1609   1613   1619   1621   1627   1637   1657 
  1663   1667   1669   1693   1697   1699   1709   1721   1723   1733 
  1741   1747   1753   1759   1777   1783   1787   1789   1801   1811 
  1823   1831   1847   1861   1867   1871   1873   1877   1879   1889 
  1901   1907   1913   1931   1933   1949   1951   1973   1979   1987 
  1993   1997   1999 
We can check some of these:
pandora% factor 1999
1999
1999

pandora% factor 1621
1621
1621

pandora% factor 1623
1623
3
541

pandora% factor 12345678901234
12345678901234
2
7
73
12079920647

pandora% 
The last one was just for fun.

Primitive Data Types

C supports the following data types:

Output with printf

printf writes formatted output. For more details, do man -s 3S printf

printf takes one or more arguments. The first argument is a string, usually a constant string in quotes. The additional arguments are values to output.

Example:
int a,b,c; 
double r,s;
char w;

a = 1; 
b = 2; 
c = 3001; 
r = 2.0/3; 
s = 5.01; 
w = 'X';
printf("a is %d, b is %8d, c is %2d, r is %6.2f, s is %6.4f and w is %c\n",
   a,b,c,r,s,w); 

Produces the output:
a is 1, b is        2, c is 3001, r is   0.67, s is 5.0100 and w is X

for Statement

There are the same as in Java, except that you cannot declare variables inside the for statement.

incorrect:

for (int i=0;i<10;i++) 
   printf("i=%d\n",i);
correct:
int i;
for (i=0;i<10;i++) 
   printf("i=%d\n",i);

Symbolic Constants

In Java you might use:
final int MAX = 1000;
This defines a variable whose value cannot change.

In C you would use:
#define MAX 1000

This is handled by the preprocessor which replaces all occurrences of MAX with 1000.

This constant is only available in the file in which it is defined.


Standard Input, Standard Output and Redirection (UNIX)

When you start up a program there is a standard input device and a standard output device. These typically default to the keyboard in screen.

printf outputs to the standard output device.

When you start up a program, you can change the standard output device using redirection.

myhello > t.1

Will send all output bound for standard output to the file t.1. The file will be created if it did not previously exist.

In a similar way

myprog <t.2

would take input which normally would come from the keyboard from the file t.2 instead.


Character input and output

The getchar function can read one character from the standard input device. The prototype for getchar is:
int getchar(void);
Notice that it returns an int rather than a char. When successful, the value returned can be converted to a char.

Consider the following example:

#include <stdio.h>

int main() {

   int i;
   int c;

   for (i=0;i<10;i++) {
      c = getchar();
      printf("c = %d\n",c);
   }
   return 0;
}
Here is what the output would look like if the input were ABCDEFGHIJ:
c = 65
c = 66
c = 67
c = 68
c = 69
c = 70
c = 71
c = 72
c = 73
c = 74
But if we changed the printf line to
printf("c = %c\n",(char)c);
The output would be:
c = A
c = B
c = C
c = D
c = E
c = F
c = G
c = H
c = I
c = J
When getchar tries to read past the end of a file, it returns a special value EOF defined in stdio.h. We could modify our program as follows:

#include <stdio.h>

int main() {

   int i; 
   int c; 

   c = getchar();
   while (c != EOF) {
      printf("c = %d\n",c);
      c = getchar(); 
   } 
   return 0; 
} 
If we create a file, t.in containing: ABCDEFGHIJ and called this program copychars, we could run it with:
copychars < t.in
the output generated would be:
c = 65
c = 66
c = 67
c = 68
c = 69
c = 70
c = 71
c = 72
c = 73
c = 10
What is the meaning of the last line of output?

Another way of doing character output is with putchar.

Here is another program:

#include <stdio.h>
 
int main() {
   int i;
   int c;
 
   c = getchar();
   while (c != EOF) {
      putchar(c);
      c = getchar();
   }
   return 0;
}
Here is the output generated by copychars1<t.in
ABCDEFGHI

This program just copies from standard input to standard output.


Filters and pipes

A filter is a program that reads from standard input, and writes to standard output. Typically its job is to process the data in some way.

Let us assume that we are using the ASCII character set. This has the property that the codes for upper and lower case characters differ by a fixed amount. Consider the following program which is a slight modification of the previous one:

#include <stdio.h>
 
int main() {
   int i;
   int c;
 
   c = getchar();
   while (c != EOF) {
      if ( (c >= 'A') && (c <= 'Z') )
         c = c + 'a' - 'A';
      putchar(c);
      c = getchar();
   }
   return 0;
}
Suppose this is called tolower and we execute
tolower < t.in
The output would be
abcdefghi

If we just typed tolower

Whatever we typed would come back with the upper case letters converted to lower case.

How would you convert tolower into toupper?

Unix allows you to easily send the output of one program into the input of another with a pipe. To do this from the command line, just connect the two programs with a | symbol.

Example:

ls -l | toupper

would show your directory converting upper case letters to lower case.


Counting Words

Idea: The program is in one of two states, in a word or not (in delimiters)
#include <stdio.h>

#define OUT 0
#define IN 1

static int delimiter(int c) {
   if (c == ' ')
      return 1;
   return 0;
}

int main() {
   int c, wordCount, state;

   state = OUT;
   wordCount = 0;

   while ( (c=getchar()) != EOF) {
      if (state == OUT) {
         if (!delimiter(c)) {
            wordCount++;
            state = IN;
         }
      }   
      else                     /* state == IN */
         if (delimiter(c))
            state = OUT;
   }
   printf("The number of words was %d\n",wordCount);
   return 0;
}
What would happen if there were no braces around the first if?

How would we allow other characters to be delimiters? Change the delimiter function.

static int delimiter(int c) {
   if ( (c == ' ') || (c == '\n') || (c == '\t') )
      return 1;
   return 0;
}

Arrays

Arrays in C are very much like arrays in Java, except for the way they are declared and the restrictions this implies.

When you declare an array in C, you specify the size of the array, and the size must be known at compile time. This means that the size must be a constant.

In Java:

   int letterCount[];
   letterCount = new int[26];
In C:
   int letterCount[26];
Here is a simple program that gives a letter count for its input:
#include <stdio.h>

int main() {
   int count[26];
   int i;
   int c;

   for (i=0;i<26;i++)
      count[i] = 0;
   while ((c = getchar()) != EOF) {
      if ( (c >= 'A') && (c <= 'Z'))
         count[c-'A']++;
      else if ( (c >= 'a') && (c <= 'z'))
         count[c-'a']++;
   }
   printf("Letter Counts Follow:\n");
   for (i=0;i<26;i++) 
      printf("%c: %4d\n",(char)(i+'A'),count[i]);
   return 0;
}

functions

Functions in C which take primitive arguments (int,char, float, double, etc.) behave in a way similar Java methods.

Recall that the arguments are passed by value.
This means the the called function gets a copy of the value of the parameters and so any changes it makes to these values is not seen by the calling program.

The function can affect the calling program only through its return value.

Unlike in Java, C does provide a mechanism for changing primitive types that are passed as parameters. This will not be discussed for a while.


Character Arrays and strings

C does not have a String data type.

In C, a string is just an array of type char. The length of the string is marked by a special character, sometimes referred to as the null character.
Here, we will call it the string terminator.
It is the character with ASCII code 0. There are two ways to set a character to have this value:
    char c;
    c = 0;
    c = '\0';

The last two lines do exactly the same thing.

The length of the string is the number of characters in the string up to but not including the string terminator.

Here is an example of a function that will read in a line from standard input.


Constant strings and string storage

The simplest way to specify a string is with a string constant.
This is done just as in Java:
"hello" is represented as
h e l l o \0
Note that this takes up 6 bytes, not 5

"hello\n" is represented as

h e l l o \n \0
which takes up 7 bytes.

You cannot do:

char hello[6];
hello = "hello";
but you can do:
char hello[6];
hell0[0] = 'h';
hell0[1] = 'e';
hell0[2] = 'l';
hell0[3] = 'l';
hell0[4] = 'o';
hell0[5] = '\0';
And nothing will prevent you from doing:
char hello[5];
hell0[0] = 'h';
hell0[1] = 'e';
hell0[2] = 'l';
hell0[3] = 'l';
hell0[4] = 'o';
hell0[5] = '\0';
But the consequences would be unpredictable.

Example:

#include <stdio.h>
 
int main() {

   int d;
   int A[4];
   int c;
 
   c = -1;
   d = -2;
   A[0] = 1;
   A[1] = 2;
   A[2] = 3;
   A[3] = 4;
   A[4] = 5;
   printf("c=%d, d=%d, A=%d %d %d %d %d\n",c,d,A[0],A[1],A[2],A[3],A[4]);
   return 0;
}
This compiles without error and when run gives:
c=-1, d=5, A=1 2 3 4 5

lint will catch this:

(15) warning: array subscript cannot be > 3: 4
(16) warning: array subscript cannot be > 3: 4

function returns value which is always ignored
    printf          
However, lint will not complain about:
int A[4];
int i;
for (i=0;i<5;i++)
    A[i] = a+1;

The array can be longer than the string:

char hello[10];
hell0[0] = 'h';
hell0[1] = 'e';
hell0[2] = 'l';
hell0[3] = 'l';
hell0[4] = 'o';
hell0[5] = '\0';
This works fine as long as you don't access anyhting past hello[5].

The storage looks like:

h e l l o \0 ? ? ? ?

What is stored in the spaces with a ??


A string function example

Here is a function which reads in a string from standard input.
The idea is to read in one character at a time until a newline is found, and then put in the string terminator character.
/* getline: read a line into s and return its length*/
int getline(char s[], int lim) {
   int c, i;

   c = getchar();
   for (i=0; c != '\n'; i++) {
      if (c == EOF) {
         s[i] = '\0';
         return i;
      }
      if (i == lim-1) {
         s[i] = '\0';
         return i;
      }
      s[i] = c;
      c = getchar();
   }
   s[i] = '\n';
   if (i != lim-1)    /* handle case of no room for string terminator */
      i++;
   s[i] = '\0';
   return i;
}
Notes: Here is a test program for getline:
#include <stdio.h>
#define MAXLINE 10

int getline(char line[], int maxline);

int main() {
   int len;
   int i;
   char line[MAXLINE];

   len = getline(line,MAXLINE);
   printf("The line has length %d and the array is\n",len);
   for (i=0; i<MAXLINE; i++) {
      printf("%3d: %c = %d\n",i,line[i],(int)line[i]);
      if (line[i] == '\0')
         break;
   }
   return 0;
}
Some sample output:


Test 1:
pandora% getlinetest1
abc
The line has length 4 and the array is
  0: a = 97
  1: b = 98
  2: c = 99
  3: 
 = 10
  4:  = 0
pandora%

Test 2:
pandora% getlinetest1
abcdefghijklmnop
The line has length 9 and the array is
  0: a = 97
  1: b = 98
  2: c = 99
  3: d = 100
  4: e = 101
  5: f = 102
  6: g = 103
  7: h = 104
  8: i = 105
  9:  = 0
pandora% 

Test 3:
pandora% getlinetest1
abc^D^DThe line has length 3 and the array is
  0: a = 97
  1: b = 98
  2: c = 99
  3:  = 0
pandora% 
Note: The two ^D^D indicate pushing the D-key while holding down Control.
They do not appear on the screen.
Why must it be pushed twice?
A complete set of test would consist of: Why didn't we write the test program with MAXLINE 1000?

Creating test files:

pandora% wc test*
wc test*
       0       0       0 test00
       0       1       1 test01
       0       1       8 test08
       0       1       9 test09
       0       1      10 test10
       1       0       1 testline00
       1       1       2 testline01
       1       1       9 testline08
       1       1      10 testline09
       1       1      11 testline10
       1       1       2 testlines02
       1       2       8 testlines08
       1       2       9 testlines09
       1       2      10 testlines10
       9      15      90 total
pandora% 
The wc utility lists number of newlines, words and bytes in a file.

Here is a batch file (shell script) to for testing getlinetest1.c

#!/bin/sh
getlinetest1 < test00
getlinetest1 < test01
getlinetest1 < test08
getlinetest1 < test09
getlinetest1 < test10
getlinetest1 < testline00
getlinetest1 < testline01
getlinetest1 < testline08
getlinetest1 < testline09
getlinetest1 < testline10
getlinetest1 < testlines02
getlinetest1 < testlines08
getlinetest1 < testlines09
getlinetest1 < testlines10
and we can run this with:
getlinerun1 > run1.out
to create an output file.

Here is the version of getline from the book (page 29):

int getline(char s[], int lim) {
   int c, i;

   for (i=0; i<lim-1 && (c=getchar())!=EOF && c!='\n'; ++i)
      s[i] = c;
   if (c == '\n') {
      s[i] = c;
      ++i;
   }
   s[i] = '\0';
   return i;
}
Notes: There is a library version of getline called gets but it takes only one parameter, an array of characters, and does not check for overflow.
This is very bad!


A copy string function (page 29)

/* copy: copy 'from' into 'to'; assume big enough */
void copy(char to[], char from[]) {
   int i;

   i = 0;
   while( (to[i] = from[i]) != '\0')
      ++i;
}
Notes:

External variables and scope

This is from section 1.10 of the text.

In a Java class you can declare variables outside of any method and these variables are available to any method in the class.

If they are declared public, they are available to any object which can access the object in which they appear.

In a similar way, you can declare a variable in a C source file outside of any function, and this variable is available to all functions in that file.

It is also available to functions outside of that file unless it is declared to be static.

We make a distinction between the definition and the declaration of a variable. Definition refers to the place where the variable is created or assigned storage.

Declaration refers to the place where the nature of the variable is stated, but no storage is allocated.

We have already seen an example of this with functions.
The name of a function is a variable of a certain type.
(The nature of this type will be described later.)

The definition of a function occurs where the body of the function appears. A prototype of the function is its declaration.

A function or variable in C must be defined or declared before it is used.
If it is not possible or convenient to define it before it is used, a declaration is necessary.

If a variable that is defined in another file is to be used, it must be declared in the file in which it is used before it is used. This is done by putting the key word extern in front of what would be its definition.

Example:
The following is a definition:
     int count
The following is a declaration:
     extern int count
Normally, there would be one file that contains the definition and any number of other files that contain the declaration.

If two files contain the definition of count they would be referencing different variables.

If a prototype describes a function which is defined in another file then extern should be used.
If the function is defined later in the same file, do not use extern

Definitions are usually placed in separate files, traditionally called header files, and included in the source file when needed.

An example of this is stdio.h which contains declarations of functions such as printf and getchar.

Header files may also contain constants defined by #define such as EOF.

Note: There is a difference between the declarations:
    int myfun(void);
and
    int myfun();
The first one indicates that the function has no parameters.
The second one indicates that the parameters are not specified and agreement with the parameters should not be checked by the compiler.