C Programs
General Syntax of a Program
A simple program includes:
- an optional list of file inclusions (which contain function declarations)
- an optional list of type, structure, variable, and function declarations
- a
mainfunction, unique and required, which is the entry point of the program (i.e., regardless of the program, execution always begins at themainfunction).
In C, an example of a simple program:
#include <stdio.h> // Nécessaire pour imprimer quelque chose à l'écran
int main ()
{
printf ("bonjour !\n");
return 0;
}
In C, main is a function that provides a return code:
- which is of type
int(integer) - which, by convention, equals 0 if everything went well
- which is exploitable in the terminal: its value can be displayed using the command
echo $?
jdequidt@weppes:~$ clang -Wall -o simple simple.c
jdequidt@weppes:~$ ./a.out
hello!
jdequidt@weppes:~$ echo $?
0
jdequidt@weppes:~$
Anatomy of an Example
Here is an example of a simple program:
#include <stdio.h>
#define N 125
int main ()
{
int somme = 0;
int i = N;
while (0 >= i)
{
somme += i;
i--;
}
printf ("La somme des entiers de 1 à %d est %d\n", N, somme);
return 0;
}
A number of things are important to note about this program:
main: see above, it is required- a constant
Nis defined beforemain - the
printffunction which allows writing to the screen. However it is not a base function ofCand therefore its definition must be included so that the compiler can compile the program. The inclusion is done via#include <stdio.h>,stdio.h(standard input-output) being a file that centralizes the functions / types / variables useful for reading data from the keyboard and displaying to the screen. Theprintffunction is detailed in the next section.
Input - Output
printf and scanf are two functions you will use extensively. The first allows displaying messages and data on the screen, while the second allows requesting information from the user (and therefore reading what is entered at the keyboard).
printf
The general syntax of printf is somewhat complex and therefore we will not detail it right away. Its use in a simple program is the following:
#include <stdio.h> // Obligatoire !
int main ()
{
// Format simple
printf ("Ceci est un message simple.");
printf ("Ceci est un message avec retour à la ligne.\n");
printf ("\tCeci est un message avec une tabulation.");
// Format avec arguments entiers
int x = 2, y = 3, z;
printf ("\nMessage: (x = %d).\n", x);
printf ("Message: (x = %d, y = %d).\n", x, y);
printf ("Message: (x = %d, y = %d, somme = %d).\n", x, y, x + y);
printf ("\nMessage: (z = %d).\n", z); // ERREUR classique ;)
// Format avec autres arguments
float f = 0.1f;
double d = 0.1;
char c = 'a';
printf ("%c - %s - %f - %lf\n", c, "bonjour !", f, d);
return 0;
}
This program is compiled and then executed:
jdequidt@weppes:~$ clang -Wall -c print.c
jdequidt@weppes:~$ ./a.out
This is a simple message.This is a message with a newline.
This is a message with a tab.
Message: (x = 2).
Message: (x = 2, y = 3).
Message: (x = 2, y = 3, sum = 5).
Message: (z = 1570477600).
a - hello! - 0.100000 - 0.100000
jdequidt@weppes:~$
The %d, %c, %f… are format strings and allow displaying the values of your variables; they necessarily start with the character %. The complete list of format strings is shown below:
%d /* signed decimal integer */
%i /* signed decimal integer */
%u /* unsigned decimal integer */
%o /* unsigned octal integer, without prefix 0 */
%x /* unsigned hexadecimal integer without prefix 0x, 013245679abcdef */
%X /* unsigned hexadecimal integer without prefix 0x, 0123456789ABCDEF */
%c /* character */
%s /* null-terminated string */
%f /* double notation [-]mm.dd */
%e /* double scientific notation [-]m.dde[+/-]xx */
%E /* double scientific notation [-]m.ddE[+/-]xx */
%g /* double automatic notation %e (if exponent < -4) or %f */
%G /* double automatic notation %E (if exponent < -4) or %f */
%p /* pointer */
%% /* to display the % character */
:::info Note It is not necessary to memorize all format strings. However it is important to remember two things:
- there must be as many format strings as variables you want to display (even if they all have the same type).
- All data is stored in binary and operations are performed in binary; the conversion is only done for display using the format strings. :::
Modifiers
:::info Note It is absolutely not necessary to memorize this part of the course. You just need to remember it exists so you can quickly find the information if needed. :::
It is also possible to modify the display formatting with a number of modifiers and the general syntax becomes: %(fl)(ent)(.)(pre)(long)type where type is one of the values indicated previously (d, i, u…). The modifiers are shown in parentheses because they are optional.
fl: allows modifying alignment:-imposes left justification;+adds a+sign for positive numbers, a space for unsigned numbers;0allows adding leading zeros;#activates the secondary form for display (prefix on octal and hexadecimal forms…)entis an integer that indicates in characters the minimum size of the formattingpreis an integer that indicates precision (i.e., the number of digits after the decimal point)longis a length modifier, eitherh(forshorttypes) orl(forlongordoubletypes).
Here are some examples:
#include <stdio.h>
int main ()
{
printf ("%5d\n", 123); // 5 chiffres mini
printf ("%*d\n", 8, 123); // 8 chiffres mini
printf ("-------------------\n");
printf ("%+4d\n", 123); // 4 chiffres mini et signe
printf ("%+4d\n", -456); // 4 chiffres mini et signe
printf ("%+4d\n", 0); // 4 chiffres mini et signe
printf ("%04d\n", 0); // 4 chiffres mini et remplissage avec 0
printf ("-------------------\n");
printf ("%x\n", 123); // affichage octal
printf ("%#x\n", 123); // affichage octal, prefixe 0
printf ("%o\n", 123); // affichage hexa
printf ("%#o\n", 123); // affichage hexa, prefixe 0x
printf ("-------------------\n");
printf ("%f\n", 123.456789);
printf ("%5.2f\n", 123.456789); // 2 chiffres
// après la virgule (5 chiffres mini)
return 0;
}
Program that is compiled and then executed.
jdequidt@weppes:~$ clang -Wall -c print2.c
jdequidt@weppes:~$ ./a.out
123
123
***
+123
-456
+0
0000
***
7b
0x7b
173
0173
***
123.456789
123.46
jdequidt@weppes:~$
scanf
The syntax is close to printf since it also uses format strings. Below, a program that illustrates its use:
#include <stdio.h> // également obligatoire pour scanf
int main ()
{
int x;
char c;
float f;
printf ("Donnez un entier, un caractère et un réel :\n");
scanf ("%d %c %f", &x, &c, &f); // x vaut la valeur entrée au clavier
printf ("Valeurs entrées: %d %c %f\n", x, c, f);
return 0;
}
This program is compiled and then executed:
jdequidt@weppes:~$ clang -c scan.c -Wall
jdequidt@weppes:~$ ./a.out
Enter an integer, a character and a real number:
4 f 8.956
Values entered: 4 f 8.956000
jdequidt@weppes:~$
Several important things to remember:
:::warning Important
Each variable is preceded by a &. This syntax corresponds to a pointer and will be covered later. Using scanf does not require knowing about pointers but you must remember to add this character.
:::
In general, the compiler indicates that there is a possible error via a warning. In the previous example, if you remove the & before the f in the scanf call, the compiler displays the following message:
jdequidt@weppes:~$ clang -c scan.c -Wall
scan.c:10:16: warning: format specifies type 'float *' but the
argument has type 'double' [-Wformat]
scanf("%d %c %f", &x, &c, f); // x has the value entered at keyboard
~^ ~
1 warning generated.
jdequidt@weppes:~$
:::warning Important
The format string (between the ") dictates how the user must provide data. Here, they are simply separated by a space. Any other format given by the user produces a non-conforming result.
:::
For example, if the user uses a comma , as a separator then the program does not work as expected:
jdequidt@weppes:~$ ./a.out
Enter an integer, a character and a real number:
0,c,4.6
Values entered: 0 , 8234257612800.000000
jdequidt@weppes:~$
Characters and scanf
Consider the following program which asks the user for 3 characters:
#include <stdio.h>
int main ()
{
char x, y, z;
printf ("Donnez 1 caractère :\n");
scanf ("%c", &x);
printf ("Donnez 1 caractère :\n");
scanf ("%c", &y);
printf ("Donnez 1 caractère :\n");
scanf ("%c", &z);
printf ("Valeurs entrées: x = %c, y = %c, z = %c\n", x, y, z);
return 0;
}
compiled and then executed:
jdequidt@weppes:~$ clang -Wall 007_scanf_buffer.c
jdequidt@weppes:~$ ./a.out
Enter 1 character:
r
Enter 1 character:
Enter 1 character:
y
Values entered: x = r, y =
, z = y
jdequidt@weppes:~$
We observe that the program does not work correctly: the request for the second character is skipped (even though the printf message is displayed on screen) and as a result only two values can be entered. This is explained by the fact that the newline \n is a character (ASCII code equal to 10) and it is consequently scanned by the second scanf call, which is confirmed by the fact that in the terminal we see y = followed by a newline. Therefore you must be particularly careful when scanning characters. One solution (not very elegant) is to modify the last two scanf calls by adding a newline (i.e., scanf("\n%c", &y);).
This problem only occurs when scanning characters. Spaces and newlines are automatically skipped by scanf when scanning data of other types.
Redirections
Redirections are simple ways to redirect data from/to files. The 3 standard inputs/outputs are:
stdin(0): standard input which corresponds to what is typed at the keyboardstdout(1): standard output which corresponds to what is displayed on the screenstderr(2): standard error which displays error messages (by default on screen)
The general syntax of a redirection is as follows: suppose the existence of command which is a system command or a program.
command < fileallows redirecting standard input. This means that the values the user would normally type at the keyboard will be read directly fromfile.command > fileallows redirecting standard output. All messages normally displayed on the screen will be written tofile. Warning: if file already existed, its content is overwritten.command >> fileallows redirecting standard output. All messages normally displayed on the screen will be written tofile. Warning: if file already existed, the messages fromcommandare appended to the end of the file.command 2> fileallows redirecting standard error; its operation is the same as for standard output.
Of course these commands can be combined. Let us revisit the program from the previous section. Let file1 contain 9 z 3.14156, it is possible to do:
jdequidt@weppes:~$ clang -c scan.c -Wall
jdequidt@weppes:~$ ./a.out < fichier1 > fichier2
jdequidt@weppes:~$
We notice that executing our program with redirections displays absolutely nothing on the screen. We can look at the content of fichier2 to verify that the redirection worked correctly:
jdequidt@weppes:~$ cat fichier2
Enter an integer, a character and a real number:
Values entered: 9 z 3.14156
jdequidt@weppes:~$
:::info Note
The cat command allows, among other things, displaying the content of a file. You can also use the less command.
:::
Notes on Input/Output
:::info Note The case of input/output will be covered more completely in the Advanced Programming course (S6) and in Systems (S7). Here, a common problem is highlighted and a simple solution is proposed. :::
Input/output operations are not immediate but are stored in a buffer. Indeed, operations that send data from/to a device from/to computer memory are very costly in computation time. Therefore, instructions like printf and scanf are executed and their results are stored in the buffer until it is full. Once full, the buffer is flushed to the screen (in the case of printf) or from the keyboard (in the case of scanf). This minimizes exchanges between memory and devices.
#include <stdio.h>
#include <unistd.h> // pour utiliser sleep
int main ()
{
int i = 0;
while (i < 10)
{
printf ("%c", '.');
fflush (stdout);
sleep (1); // le programme s'endort 1 seconde
i++;
}
printf ("\n");
return 0;
}
A simple way to observe the presence of buffers is to compile and execute the program above whose principle is very simple. We want to display a dot '.' on the screen every second. However at runtime we observe that the program does not behave as expected: nothing is displayed on screen for 10 seconds and then all 10 '.' are displayed at once. This is explained by the fact that the characters are stored in the buffer and since it is not full, it is not transferred to the screen. When the program terminates, all buffers are flushed and therefore the display to the screen is performed. It is possible to force flushing the input/output buffers with the fflush function. For example fflush(stdout) allows flushing the standard output buffer and fflush(stdin) allows flushing the standard input buffer.