Basic Example
Difficulty: EasyWrite with the editor of your choice the following program (name it base.c):
#include <stdio.h>
int main ()
{
printf ("Hello World !\n");
return 0;
}
In a terminal, type the following command:
gcc base.c
Normally you should get no message (this means everything went well); the compilation generated a program named a.out (this is the default name). You can then execute the compiled program via the command which gives:
Hello World !
From this first example, a number of things are important to remember. The usual workflow is:
- write the
Ccode in an editor - compile it (
gcc); if there are errors, go back to step 1 - execute it (
./a.out)
The C Code
Without going into too much detail about C programming, here are some explanations: #include <stdio.h> is a preprocessor directive (they all start with #). Here it means that we include in our source file the content of the file <stdio.h>: this is a file that contains the standard input-output functions (keyboard input, console display…). Without it, the printf function is not usable.
In your file base.c, remove the first line. Recompile the file. What error do you get? Put the directive #include <stdio.h> back at the beginning of the file and notice by typing gcc -E base.c that your file has been enriched with the file stdio.h.
int main()
is the entry point of your program; it is the first function that will be executed by your program (even if it is located at the end of the file). A program must necessarily contain a main function.
printf("Hello World !\n");
printf is the display function of the C language.
return 0; means that the main function returns the value 0.
It is possible to get the return value of your program by typing
echo $?
in a terminal. Modify the integer after return in your C program, compile, execute, and verify that the return value is indeed changed.
Compilation
There are many C compilers: those available at Polytech Lille are gcc and clang. The differences between gcc and clang are detailed at https://clang.llvm.org/comparison.html. For an IMA3 student, it is interesting to note that the error messages are more or less easy to read, for example:
int main ()
{
printf ("Hello World !\n");
return 0;
}
gives with gcc:
base.c: In function 'main':
base.c:4:5: warning: implicit declaration of function 'printf' [-Wimplicit-function-declaration]
printf("Hello World ! \n");
^~~~~~
base.c:4:5: warning: incompatible implicit declaration of built-in function 'printf'
base.c:4:5: note: include '<stdio.h>' or provide a declaration of 'printf'
base.c:1:1:
+#include <stdio.h>
base_e.c:4:5:
printf("Hello World ! \n");
^~~~~~
with clang:
base.c:4:5: warning: implicitly declaring library function 'printf' with type 'int (const char *, ...)' [-Wimplicit-function-declaration]
printf("Hello World ! \n");
^
base.c:4:5: note: include the header <stdio.h> or explicitly provide a declaration for 'printf'
1 warning generated.
even though both give the same important information (include stdio.h), one is more verbose than the other. However the differences between compilers can be much more significant. Consider for example the following file (compil.c):
#include <stdio.h>
void loop_example ()
{
int i;
while (i < 10)
{
printf ("%s(%d) -- Hello World (%i)! \n", __func__, __LINE__, i);
i++;
}
printf ("i == %i\n", i);
}
int main ()
{
int i;
while (i < 10)
{
printf ("%s(%d) -- Hello World (%i)! \n", __func__, __LINE__, i);
i++;
}
printf ("i == %i\n", i);
loop_example ();
return 0;
}
Without going into too much detail, you can notice that the following sequence of instructions is repeated twice:
int i;
while (i < 10)
{
printf("%s(%d) -- Hello World (%i)! \n", __func__, __LINE__, i);
i++;
}
printf("i == %i\n", i);
the first time in main and the second time in a function called loop_example. Compile and execute the program. You should normally notice that the output is not what was expected:
main(19) -- Hello World (0)!
main(19) -- Hello World (1)!
main(19) -- Hello World (2)!
main(19) -- Hello World (3)!
main(19) -- Hello World (4)!
main(19) -- Hello World (5)!
main(19) -- Hello World (6)!
main(19) -- Hello World (7)!
main(19) -- Hello World (8)!
main(19) -- Hello World (9)!
i == 10
i == 32752
On one hand because the Hello World messages from the function are not displayed, and on the other hand because the value of i is inconsistent… and also it changes with each execution (try it to verify). This is because the variable i is not initialized and therefore can take any value1. Yet this error is detected neither by gcc nor by clang (verify this yourself). However if we add the warning detection options -W -Wall to clang via the command:
clang -W -Wall compil.c
we get the following messages:
compil.c:6:12: warning: variable 'i' is uninitialized when used here [-Wuninitialized]
while (i < 10)
^
compil.c:5:10: note: initialize the variable 'i' to silence this warning
int i;
^
= 0
compil.c:17:12: warning: variable 'i' is uninitialized when used here [-Wuninitialized]
while (i < 10)
^
compil.c:16:10: note: initialize the variable 'i' to silence this warning
int i;
^
= 0
2 warnings generated.
The fact that i is not initialized is this time clearly indicated (and for both affected lines). Verify that despite the options -W -Wall, gcc does not detect these warnings. Fix the program and verify that execution is as expected:
main(19) -- Hello World (0)!
main(19) -- Hello World (1)!
main(19) -- Hello World (2)!
main(19) -- Hello World (3)!
main(19) -- Hello World (4)!
main(19) -- Hello World (5)!
main(19) -- Hello World (6)!
main(19) -- Hello World (7)!
main(19) -- Hello World (8)!
main(19) -- Hello World (9)!
i == 10
loop_example(8) -- Hello World (0)!
loop_example(8) -- Hello World (1)!
loop_example(8) -- Hello World (2)!
loop_example(8) -- Hello World (3)!
loop_example(8) -- Hello World (4)!
loop_example(8) -- Hello World (5)!
loop_example(8) -- Hello World (6)!
loop_example(8) -- Hello World (7)!
loop_example(8) -- Hello World (8)!
loop_example(8) -- Hello World (9)!
i == 10
You can notice that it is possible to display the current function of a program via __func__ and the current line number via __LINE__.
Finally, regardless of the chosen compiler, it is possible to change the name of the generated program using the -o option. For example if you want your program to be called edinfo, simply type (this also works with gcc):
clang -W -Wall compil.c -o edinfo
:::warning What to Remember
- Always compile your programs with the detection options
-W -Wall - You must always initialize your variables before using them :::
Redirections
Difficulty: EasyJust like for Linux commands, you can redirect inputs/outputs. For example by using > followed by a filename, all messages displayed by your program will be written to the file rather than the console. Try it with the file compil.c.
For input redirection, write/compile/execute the following program:
#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;
}
Now write to a file (test_scanf.txt for example) the values 5 a 3.14. Execute the program via ./a.out < test_scanf.txt. Notice that you no longer need to enter values at the keyboard but everything is read directly from the file.
Your First Program
Difficulty: RxBased on the previous programs and the optional help on printf (man 3 printf) and on scanf (man 3 scanf), write a program that reads 10 numbers from the keyboard (or from a file via redirection) and displays their average on the screen (or in a file via redirection). Look at the errors generated when the format is wrong (for example a character instead of a number) or when the number of expected elements is wrong (fewer than 10 values).
Footnotes
-
the first one to figure out why
iis correctly initialized to 0 inmaingets a bonus ↩