This section contains a number of sample C programs, progressing from quite simple programs to more useful and realistic programs. Each is presented as a it would appear in a C program file suitable for compilation by a C compiler, such as cc or gcc.
The first program is intended to print back to the user just the first word of whatever words the user gave on the command line. It complains, using the function scream defined in the program, and uses the function die to exit. While these are rather trivial functions, this program illustrates how user-defined functions are invoked. One point to note is that here we have chosen to define the functions scream and die completely before they are called, so that the last function to be defined is main. Note that main uses the value of argc to determine if there is indeed a first word to print.
/* FILE = say.c
This simple program prints on the screen the first word of whatever was
typed on the command line -- for example:
say first second third
will respond with:
first
*/
#include <stdio.h>
void scream(str) /* Just scream a line of text to stderr stream */
char *str; /* What to scream! */
{
fprintf(stderr,"%s\n",str); /* print the string, and put new-line */
}
void die() /* Just die - ie exit now, with a status flag */
{ /* of 1, telling the operating system that */
exit(1); /* was not well. */
}
main(argc,argv)
int argc; /* Number of words on command line - */
char **argv; /* includes program name - always there */
{
if( argc < 2 ) { /* nothing to say - scream and die */
scream("Well, at least give me something to say!");
die();
}
printf("%s\n",argv[1]); /* prints only the first word */
}
The second program closely resembles the first, except that it is intended to print the last word typed by the user. One important point to note is the last elemwnt in the argv array has index argc - 1 since argc is the number of elements, and these elements number from 0.
/* FILE = say_last.c
This simple program prints on the screen the last word of whatever was
typed on the command line -- for example:
say first second third final
will respond with:
final
*/
#include <stdio.h>
void scream(str) /* Just scream a line of text to stderr stream */
char *str; /* What to scream! */
{
fprintf(stderr,"%s\n",str); /* print the string, and put new-line */
}
void die() /* Just die - ie exit now, with a status flag */
{ /* of 1, telling the operating system that */
exit(1); /* was not well. */
}
main(argc,argv)
int argc; /* Number of words on command line - */
char **argv; /* includes program name - always there */
{
if( argc < 2 ) { /* nothing to say - scream and die */
scream("Too few arguments! Exit!");
die();
}
printf("%s\n",argv[argc - 1]); /* prints only the last word */
}
The program say_all simply prints all the words typed by the user, in the order they appeared. Since we do not the name of the program (in this case say_all) printed, the for loop's initialization statement is i = 1 rather than i = 0. To walk through the array of user-types command line line arguments, the index i is incremented in the for statement, using the short form ++i which here is equivalent to the statement i = i + 1.
The format of the output is such that the command line words are printed on one line, and separated by exactly one space, with the last one being followed by the newline character (in fact there is actually a space character before the newline character).
/* FILE = say_all.c
This simple program prints on the screen all the words which were
typed on the command line -- for example:
say first second third and finally the end.
will respond with:
first second third and finally the end.
*/
#include <stdio.h>
void scream(str) /* Just scream a line of text to stderr stream */
char *str; /* What to scream! */
{
fprintf(stderr,"%s\n",str); /* print the string, and put new-line */
}
void die() /* Just die - ie exit now, with a status flag */
{ /* of 1, telling the operating system that */
exit(1); /* was not well. */
}
main(argc,argv)
int argc; /* Number of words on command line - */
char **argv; /* includes program name - always there */
{
int i;
if( argc < 2 ) { /* nothing to say - scream and die */
scream("Too few arguments! Exit!");
die();
}
for( i = 1 ; i < argc ; ++i ) {
printf("%s ",argv[i]); /* print each word, followed by a blank */
}
printf("\n"); /* print a new-line at end of line */
}
The next program computes the sum of all its arguments which can be either integers or decimal numbers. The actual sum is computed to double precision. After the initialization of the variable sum to 0, an integer i counter is initialized to 1 and incremented in the for loop. This loop continues as long as i is less than argc. If the user entered no numbers to add, so that argc is 1, then the body of the loop is never executed, since its continue condition, ie. i < argc, fails in the first step.
It is important to note that the argv[i] are character strings and not numbers as far as C is concerned. Hence it is necessary to use a library function like atof to determine the actual number which that character string represents. Then the numeric vale, x, is added to the already computed partial sum.
/* FILE = sum.c
This simple C program computes the sum of the numbers on the command line.
sum 1 2 3 4 5 6 7 8 9 10
which will return the output:
55
*/
#include <stdio.h> /* Include lots of standard definitions */
#include <stdlib.h> /* Get more useful definitions, like atof() */
main(argc,argv) /* Operating System supplies argc & argv */
int argc; /* The number of words in command line */
char *argv[]; /* The sequence of words in command line */
{ /* argv[0] is the program name */
int i; /* a loop counter - always an integer */
double x, sum; /* working variables, double precision here */
sum = 0; /* Initialize sum to 0 */
for( i = 1 ; i < argc ; ++i ) { /* loop over args[1,...] */
x = atof(argv[i]); /* convert a string to a number */
sum = sum + x; /* build the sum from arguments */
}
printf("%g\n",sum); /* print sum after the loop completes */
}
The next program uses C code similar to the sum program, except that instead of returning the sum of its arguments, it computes the square root of each argument. This program requires the -lm switch when it is compiled, since unlike the function printf, for example, mathematical functions like sqrt, sin, or cos are not in the standard libraries of functions that are always included in a C program compilation. The compile command for this program is:
cc -o sqrt sqrt.c -lmThe -lm switch instructs the C compiler to search for any functions it needs but cannot find in its standard libraries in the mathematical library.
In adition to needing the actual code to run to compute the square root of a number, the C compiler also needs to know certain about the function called sqrt. In particular it needs to know what the data type of the returned value will be, such as float, double, or int. In addition, while it is not absolutely necessary in most C implementations, the C compiler can make use of any knowledge about the arguments a function like sqrt or atof expects. This is particularly useful for the compiler to do some analysis of the program that can be useful in ensuring that the intended operation is what really happens. This kind of information, called the prototype of the function, is stored in the files like stdio.h, stdlib.h, or math.h, usually called include files, for fairly obvious reasons. It is most important that one notes that these include files do not contain the actual definition of the C instructions required to execute - they are only prototype definitions.
/* FILE = sqrt.c
This simple C program computes the square root of the number given on
the command line. The program is invoked as in the example:
sqrt 5
which returns the output:
SQRT(5) = 2.23607
The program can also be called with multiple arguments, so that each
will have its square root calculated, as in:
sqrt 2 3 4 5
which will return the output:
SQRT(2) = 1.41421
SQRT(3) = 1.73205
SQRT(4) = 2
SQRT(5) = 2.23607
*/
#include <stdio.h> /* Include lots of standard definitions */
#include <stdlib.h> /* Get more useful definitions, like atof() */
#include <math.h> /* Get math-type definitions, like sqrt() */
main(argc,argv) /* Operating System supplies argc & argv */
int argc; /* The number of words in command line */
char *argv[]; /* The sequence of words in command line */
{ /* argv[0] is the program name */
int i; /* a loop counter - always an integer */
double x, y; /* working variables, double precision here */
for( i = 1 ; i < argc ; ++i ) { /* loop over argv[1,...] */
x = atof(argv[i]); /* convert a string to a number */
y = sqrt(x); /* set y to the square root of x */
printf("SQRT(%g) = %g\n",x,y); /* print x and y with new-line */
}
}
The next two programs compute the area of a small class of planar geometrical figures. The two versions provide the same functionality, and use the same algorithms to compute the area in each case, and only differ in the manner in which they store the intermediate information, which we discuss below.
The use of the program is described in the initial comments in the C file. For each of the six different geometrical figures, there is a most appropriate way to compute the area. Of course there is some degeneracy in these methods, since the cricle is a special case of an ellipse, and each of the square, rectangle and triangle are special cases of a polygon. It is still useful to handle these cases individually.
The first argument to the program is the type of geometrical object, and this determines the meaning of subsequent arguments, and their required number. Clearly a circle or a square requires only one further argument, the radius or the side, respectively. The rectangle, the triangle and the ellipse each require two length arguments rather than one. The polygon is complicated by the fact that it requires further arguments in pairs, the X and Y coordinates,, and to form an area at all, there must be at least three such pairs of arguments. In each case, a test ensures that the correct number of arguments have been given, keeping in mind that the polygon can have n arbitrary number of vertices.
The decision on the geometrical type is made through a series of tests that compare the first user argument string (in arg[1]) with each of the known types, using the strcmp function. The full details of this function can be obtained on a UNIX system using the command:
man strcmpwhere one will find that strcmp takes two character string arguments. It returns 0 if the two strings are identical, and
Once the geometrical type has been determined, the appropriate data is collected from the argv strings and the area computed. Note that in the polygon case, the program tries to catch the possible error of trying to specify enough vertices, but loosing the final value.
It may be puzzling as to why one has included the math.h file, when
we are not using any mathematical functions like sin or sqrt.
Along with prototypes of the mathematical common functions math.h also
contains the accurate definitions of many fundamental constants, such as
= M_PI or
= M_PI_2. It is usually having a look
at the file /usr/include/math.h (the usual location on a UNIX system)
before getting out tables and a calculator to evaluate such constants.
/* FILE = area.c
This program computes the area of simple geometric figures. The figure
type and the required number of parameters to be supplied are:
Figure Type Parameters
=========== ==========
circle radius
square side
rectangle side_1 side_2
triangle base height
ellipse semi-major semi-minor
polygon X Y pairs of vertices
Example of usage are:
area circle 2 --> Area = 12.5664
area rectangle 3 --> Area = 9
area ellipse 2 3 --> Area = 18.8496
area polygon 0 0 1 0 1 1 0 1 --> Area = 1
area polygon 1 1 1 2 2 3 3 4 4 5 5 2 --> Area = -8
Note that a polygon with negative area just indicates the left or right
hand nature of the traversal of the list of vertices.
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h> /* just to get the value of M_PI */
main(argc,argv)
int argc;
char **argv;
{
int npts, i, j;
double a, b, area;
double s_xy, s_yx; /* for the polygon sums */
double x, y, x_first, y_first, x_prev, y_prev;
if( argc < 3 ) { /* need at least figure-type and one size */
fprintf(stderr,"Too few arguments - Exit!\n");
exit(1);
}
if( strcmp(argv[1],"circle") == 0 ) { /* which geometric figure? */
a = atof(argv[2]);
area = M_PI*a*a;
}
else
if( strcmp(argv[1],"square") == 0 ) {
a = atof(argv[2]);
area = a*a;
}
else
if( strcmp(argv[1],"rectangle") == 0 ) {
a = atof(argv[2]);
if( argc < 4 ) { /* we need one more parameter */
fprintf(stderr,"Missing last argument - Exit!\n");
exit(1);
}
b = atof(argv[3]);
area = a*b;
}
else
if( strcmp(argv[1],"triangle") == 0 ) {
a = atof(argv[2]);
if( argc < 4 ) {
fprintf(stderr,"Missing last argument - Exit!\n");
exit(1);
}
b = atof(argv[3]);
area = 0.5*a*b;
}
else
if( strcmp(argv[1],"ellipse") == 0 ) {
a = atof(argv[2]);
if( argc < 4 ) {
fprintf(stderr,"Missing last argument - Exit!\n");
exit(1);
}
b = atof(argv[3]);
area = M_PI*a*b;
}
else
if( strcmp(argv[1],"polygon") == 0 ) {
if( argc % 2 == 1 ) {
fprintf(stderr,"X Y pairs end with straggling X value. Exit!\n");
exit(1);
}
if( argc < 7 ) {
fprintf(stderr,"At least 3 vertices required to have area!\n");
exit(1);
}
s_xy = 0; s_yx = 0; /* init the sums to 0 at start */
x_prev = x_first = atof(argv[2]); /* save first pair */
y_prev = y_first = atof(argv[3]); /* and init the "prev" pair */
for( i = 4 ; i < argc ; i += 2 ) {
x = atof(argv[i]); /* get x value of next point */
y = atof(argv[i+1]); /* get y value of next point */
s_xy += x_prev*y; /* the down-right sum */
s_yx += y_prev*x; /* the down-left sum */
x_prev = x; y_prev = y; /* update "previous" pair values */
}
s_xy += x_prev*y_first; /* wrap back to close area */
s_yx += y_prev*x_first; /* using first pair again */
area = 0.5*(s_xy - s_yx); /* half the difference of sums */
}
else {
fprintf(stderr,"Figure type is not a known type - exit!\n");
exit(1);
}
printf("Area = %g\n",area); /* printf the useful result */
}
This second version of the area program uses the same algorithms as the previous version but instead of very carefully walking through the arguments in the polygon case, it uses eplicit arrays to store the x and y coordinates of the polygon. This makes it more straightforward to apply the simple area calculation procedure for a polygon, given it vertices, using the familiar high school down-right - down-left algorithm. The advantage is that the correctness of the implementation of the algorithm is somewhat easier to verify, but the disadvantage is that one has to choose some maximum number of vertices to consider. In this particular code, the maximum is 100, which is probably above the upper limit to what one would want to type, so it is probably not too serious a limitation. In a typical UNIX system, the number of command line arguments is usually limited to someting like 10000.
The difference between these two programs is thus primarily in the convenience of not having to play with the internal sizes of program variables once written, so that the program can be put away and just used. It is worth pointing out that the use of dynamic memory allocation, where the size of an array can be established at the time a program is run rather than when it is written and compiled, would remove any memory limitation disadvantage of this second version of the area program.
/* FILE = area_arr.c
/****** NOTE: Still compute area, but using arrays for polygon. ******/
This program computes the area of simple geometric figures. The figure
type and the required number of parameters to be supplied are:
Figure Type Parameters
=========== ==========
circle radius
square side
rectangle side_1 side_2
triangle base height
ellipse semi-major semi-minor
polygon X Y pairs of vertices
Example of usage are:
area circle 2 --> Area = 12.5664
area rectangle 3 --> Area = 9
area ellipse 2 3 --> Area = 18.8496
area polygon 0 0 1 0 1 1 0 1 --> Area = 1
area polygon 1 1 1 2 2 3 3 4 4 5 5 2 --> Area = -8
Note that a polygon with negative area just indicates the left or right
hand nature of the traversal of the list of vertices.
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h> /* just to get the value of M_PI */
main(argc,argv)
int argc;
char **argv;
{
int npts, i, j;
double a, b, area;
double s_xy, s_yx; /* for the polygon sums */
static double x[100], y[100];
if( argc < 3 ) { /* need at least figure-type and one size */
fprintf(stderr,"Too few arguments - Exit!\n");
exit(1);
}
if( strcmp(argv[1],"circle") == 0 ) { /* which geometric figure? */
a = atof(argv[2]);
area = M_PI*a*a;
}
else
if( strcmp(argv[1],"square") == 0 ) {
a = atof(argv[2]);
area = a*a;
}
else
if( strcmp(argv[1],"rectangle") == 0 ) {
a = atof(argv[2]);
if( argc < 4 ) { /* we need one more parameter */
fprintf(stderr,"Missing last argument - Exit!\n");
exit(1);
}
b = atof(argv[3]);
area = a*b;
}
else
if( strcmp(argv[1],"triangle") == 0 ) {
a = atof(argv[2]);
if( argc < 4 ) {
fprintf(stderr,"Missing last argument - Exit!\n");
exit(1);
}
b = atof(argv[3]);
area = 0.5*a*b;
}
else
if( strcmp(argv[1],"ellipse") == 0 ) {
a = atof(argv[2]);
if( argc < 4 ) {
fprintf(stderr,"Missing last argument - Exit!\n");
exit(1);
}
b = atof(argv[3]);
area = M_PI*a*b;
}
else
if( strcmp(argv[1],"polygon") == 0 ) {
if( argc % 2 == 1 ) {
fprintf(stderr,"X Y pairs end with straggling X value. Exit!\n");
exit(1);
}
npts = (argc - 2)/2; /* number of X,Y pairs to expect */
if( npts < 3 ) {
fprintf(stderr,"At least 3 vertices required to have area!\n");
exit(1);
}
for( i = 0 , j = 2 ; i < npts ; ++i ) {
x[i] = atof(argv[j++]); /* gather the X and Y values */
y[i] = atof(argv[j++]);
}
x[npts] = x[0]; y[npts] = y[0]; /* Copy first to end of list */
s_xy = 0; s_yx = 0; /* init the sums to 0 at start */
for( i = 0 ; i < npts ; ++i ) {
s_xy += x[i]*y[i+1]; /* the down-right sum */
s_yx += y[i]*x[i+1]; /* the down-left sum */
}
area = 0.5*(s_xy - s_yx); /* half the difference of sums */
}
else {
fprintf(stderr,"Figure type is not a known type - exit!\n");
exit(1);
}
printf("Area = %g\n",area); /* printf the useful result */
}
Cengiz Şeker
2000-12-15