Here we try out a small program that computes the value of Pi using the
following formula:
Pi = 4 * (1 - 1/3 + 1/5 - 1/7 + 1/9 - ... )
Here is the code:
1 #include <stdio.h>
2 #include <math.h>
3 int main(int argc, char *argv[]){
4 double t, k = 3.0, l = -1.0;
5 int i, s;
6 if(argc < 2){
7 fprintf(stderr, "%s <number of iterations>\n", argv[0]);
8 exit(1);
9 }
10 t = 1.0;
11 for(i = 0, s = atoi(argv[1]); i < s; i++){
12 t += l/k;
13 k += 2.0;
14 l *= -1.0;
15 }
16 t *= 4;
17 printf("My value of Pi: %1.6f, math.h's value of Pi: %.16f\n", t, M_PI);
18 printf("Absolute difference: %.16f\n", fabs(M_PI - t));
19 return 0;
20 }
To compile the program, remove all lines numberings, those are just there to
make it easier to go through the code. Then type in your shell, provided that
you save the code in a file called pi.c:
% gcc -o pi pi.c -lm
We compile pi.c to a binary called pi. -lm tells the linker (a part of the
compiler) that we want to use math libraries. Using the -lm flag is not always
neseccary though. Anyway, to run the program type the following in your shell:
% ./pi 1000
The number 1000 is the number of iterations we would like to do in the main
loop, i.e. how many items we want to use in the formula above. You should get
something like this:
$ ./pi 1000
My value of Pi: 3.142592, math.h's value of Pi: 3.1415926535897931
Absolute difference: 0.0009990007497471
Lets dissect the code. On line 1 and 2 we include some standard libraries.
1 #include <stdio.h>
2 #include <math.h>
stdio.h contains standard input/output functions such as printf and alike.
math.h contains math functions such as sin, fabs, etc. What are those .h files
anyway? It demands some explanation. Assume you would like to use some
function like printf in your program. This function is nothing you have written
yourself, the function comes in compiled form in your standard libraries. When
the program is compiled a program called the linker (often ld, try man ld)
tries to figure out how to call functions in these standard libraries. This is
something that more or less just happens, as a new C programmer this is nothing
you have to worry about. What is important here is that the compiler wants to
know what printf looks like, i.e. how many arguments it wants, return type etc.
Information about this is included in the .h files, which often is referred to
as a header file (.h for header). We often talk about function prototypes, i.e.
a description of the function. But why? So that the compiler can complain when
you try to use a function is some way that is considerer incorrect, using the
wrong number of arguments for instance. Hope that sorts it out.
What about this like then?
3 int main(int argc, char *argv[]){
All C programs are composed of functions more or less. The compiler looks for
a function called main. When the program is executed, that is by your shell,
control is transfered from the shell to the place where main starts. I will
later try (supposed I have some spare time) to explain how to write a shell,
something that is easier than it might seem. Also there are two arguments to
this function. int argc, char *argv[]. int argc is the number of arguments to
the program and char *argv[] are those arguments. Arguments? These are the ones
you provided via the command line when you started the program, "1000" as an
example. However the programs name is always the first argument. If we look at
the example where we did:
% ./pi 1000
Then *argv[] looks like {"./pi", "1000"}. That also means that argc = 2, NOT 1
as you might expect. That means that the first argument that you are likely to
be interested in is argv[1]. As an exercise you could try to write a program
that prints it's own name.
Here we declare some variables that we use later on:
4 double t, k = 3.0, l = -1.0;
5 int i, s;
Nothing much about these really, just keep in mind that variables should be
declared in the beginning of each code block. That is the beginning of
functions, loops, if statements etc. Quiet frankly everything that looks like
this is valid syntax:
{
int a, ...
}
Newer versions of gcc allows you to declare variables everywhere, but try to
avoid that. You probably want your program to be compatible with older versions
of gcc and other compilers.
Here we check that the user have supplied the correct number of arguments to
the program:
6 if(argc < 2){
7 fprintf(stderr, "%s <number of iterations>\n", argv[0]);
8 exit(1);
9 }
Recall that argc is the number of arguments to the program. If these are less
than 2 we have a problem. We print out a message, on line 7, that tells the
user that he/she tried to use the program in an incorrect way. Then we exit the
program with the value 1. Why 1? I'll explain this at the end of this example,
bare with me for a while. We use the function fprintf which could be
interpreted as: File-PRINT-Format. FILE here refers to the file stderr, which
is a standard file of type FILE*. See the example ConnectingASocketInC for a
better explanation about this. PRINT simply refers to printing, that is writing
something to the shell. Format means that want to format the output in some
sense. The %s in "%s <number of iterations>" refers to a string, that is the
second argument given to fprintf, namely argv[0]. Check your manpages for
fprintf (that is man fprintf) for a lot more details.
Simply set t to 1.0.
10 t = 1.0;
This for-loop can be interpreted as follows. First set i to 0, then s to
atoi(argv[0]). Observe that we do both these things before the first ;. This
part is just done once, that is when we enter the loop. What is this atoi? The
argument "1000" in the example above, i.e. the number of iterations we want to
do, comes as a string, not a number. Therefor we have to convert this string
to a number. atoi does this, (man atoi).
This loop will loop as long as i is less than s. That is i < s. This test is
done at the beginning of the loop. In every iteration of the loop i will be
incremented by one. This is done at the end of the loop.
11 for(i = 0, s = atoi(argv[1]); i < s; i++){
Here, compute t += l/k. This corresponds to some random part of the formula,
for example: -1/3 or 1/5.
12 t += l/k;
Increment k by two, so if k = 3 before this, then k = 5 after this.
13 k += 2.0;
Multiply l by -1.0, something that simply changes the sign of l. -1*-1 = 1,
math you know.
14 l *= -1.0;
This loop is as said repeated say 1000 times, after that we multiply the result
by 4, according to the formula.
16 t *= 4;
Now lets print the result. Here again we use formated printing. But where is
the stderr thing? Shouldn't we write to a file? Well this is done implicitly.
Since C programmers like to print a lot, there is a standard function printf
that is the equivalent of fprintf(stdout, ..., ...). We could as well have
written fprintf(stdout, "My value of Pi ... ", ...);
17 printf("My value of Pi: %.16f, math.h's value of Pi: %.16f\n", t, M_PI);
18 printf("Absolute difference: %.16f\n", fabs(M_PI - t));
What is this format %.16f? f is for float, we want to print a floating point
number, also we want 16 significant decimals. This .16 thing is just a way to
tell the how many decimals se want, nice huh? M_PI is a constant from math.h
that simply is set to the value of Pi, using as high accuracy as the computer
allows. We also use the function fabs that calculates the absolute value of a
value. fabs could well be something like:
double fabs(double a){
if(a >= 0.0){
return a;
}
return -a;
}
At last we return 0. What is the deal with returning things? Is there any
function that checks the value? I haven't written anyone anyway. Simply put, if
you return anything from main or use the function exit(value); This value is
returned to the shell. The return value is normally interpreted in fashion that
0 means, "everything is alright, program did NOT crash", everything else means
"The program crashed.". The return value is a way to tell the shell how things
went wrong. What a value of 23 would mean is quiet undefined, it's more or less
up to the program to decide that it means. But it's quiet useful when a
shellscript executes a program. For example, shellscript start_daemon.sh tries
to start a random daemon, like a mail server, if something goes wrong the error
code can be used to print a message "/var/tmp is full, free at least 10 Mb
space.".
19 return 0;
Last modified on 20 June 2007, at 18:29