Cliquez ici pour retirer la feuille de style si vous souhaitez imprimer ce document (ou en cas de problème d'affichage).

Variables, expressions, types

An instruction is an operation the CPU can perform and that can modify the memory For example, to add 2 and 3, four informations must be given to the CPU:
  • where the value 2 is store ?
  • where is the value 3 is store ?
  • where we want to store the result ?
  • which operation we want to perform (here +)

It exists instructions with 0, 1, 2, 3... operands. As C is a portable language, a C instruction does not necessarily corresponds to a single CPU instruction. For the remaining, "instruction" will be use to designate a C instruction ie a list of words that terminates with a ;

C instructions are grouped into blocks, delimited by { }

To begin, let us consider that some of these blocks can be named. A named block is a function. The first block to be executed is the block named main : main(){inst1; inst2; ...;}

To designate the operations, C uses key words: (+,-,/,+=,=,==,...). To designates operands (that can be either values or memory adresses) C uses the notions of variable and type. Declaring a variable means reserving a name to designate a specific memory address, while specifying in the same time the type of the data that will be store there, that is to say, its size in byte (example integer = 32 or 64 bits on most of the CPUs) and the operation to use for some of them that are written the same way (example: the integer division is different from the division on real numbers, but both are written in C with /).

Back with the memory

For the program, the memory is an infinite array of bytes. This array is not use randomly. The beginning contains reserved data for the process (pid, ppid, file descriptors, environment...). Then came the program instructions. After came an area called the stack. Finally, the last address (the biggest that can be encoded with an integer value) is the beginning of the heap, that grows from the right to the left.

On the following, we will just represent the stack part, and vertically (the bottom being the first address of the stack). Let get back to the add example and look at what happens on the stack:

The first step consist in memory reservation, giving names to reserved area (in order to be able to use them late): int a; int b; int res; The type must be written in order to know the size of the area to reserve. This is called variable declarions. Then, the reserved area can be fill: a=2; b=3; This is called variable initializations.

The "a+b;" instruction is correct. However there is no side effect on the stack. In order to be usefull, the result of this instruction must be affected to a variable: res=a+b;

Some C specific rules

  • Variables must be declared only at a block beginning (not mixed with other instructions)
  • The "existence" of the variable, ie the code from where its name exists, is limited to this block

Types

C is a strongly typed language. Every expressions have a type. Allowed type are structures, alias (with typedef) and primitive types : int, char, float,... A type is characterized by its size in bytes. The special function sizeof() permits to obtain the size of a type. The instruction "sizeof(char);" for example is equal to 1. The C specification only says:

  • char type size is 1 (in fact 1 octet, which is use to be equal to 1 byte)
  • 1 = sizeof(char) = sizeof(unsigned char) ≤ sizeof(short int) = sizeof(unsigned short int) ≤ sizeof(int) = sizeof(unsigned int) ≤ sizeof(long) = sizeof(unsigned long) ≤ sizeof(float) ≤ sizeof(double) ≤ sizeof(long double)
The remainder, listed in the following array, is just indicative (it even already exists systems where 1octet=7bits...)

data typemeanssyze (octet/byte)Range of value
charCharacter1-128 à 127
unsigned charunsigned character10 à 255
short intshort integer2-32 768 à 32 767
unsigned short intunsigned short integer20 à 65 535
intinteger
2 (on 16 bits CPU)
4 (on 32 bits and 64 bits CPU)
-32 768 à 32 767
-2 147 483 648 à 2 147 483 647
unsigned intunsigned integer
2 (on 16 bits CPU)
4 (on 32 bits and 64 bits CPU)
0 à 65 535
0 à 4 294 967 295
long intlong integer
2 (on 16 bits CPU)
4 (on 32 bits CPU)
8 (on 64 bits CPU)
-32 768 à 32 767
-2 147 483 648 à 2 147 483 647
-2^63 à 2^63
unsigned long intunsigned long integer
2 (on 16 bits CPU)
4 (on 32 bits CPU)
8 (on 64 bits CPU)
0 to 65 535
0 to 4 294 967 295
0 to 2^64-1
floatFloating point number (real)43.4*10^-38 à 3.4*10^38
doubleFloating point number double precision81.7*10^-308 à 1.7*10^308
long doublelong double103.4*10^-4932 à 3.4*10^4932

Les expressions

Formally, an expression is a part of code which begins after a ; and terminates at the next ; Every expressions have a value and can have a side effect (ie modify the stack or more generally the memory). As every expression have value, it is possible to compare them one by one this operators ==, <, > <=, >=; or to assign their value to a variable with =. The combination then is a bigger expression, only the final ; is kept

example

  • int a; is an expression. Its value depends on the state of the memory before its execution. Its side effect is to move the stack pointer by sizeof(int) bytes to the right (or the top) and to reserve the name "a" for the block duration;
  • a=3; is also an expression. Its value is 3. Its effect is to fill the memory area designated byt "a" with the bits encoding the value 3 for the target machine.
  • These two expressions can be combined in one:int a = 3; The resulting expression value is 3. It's side effect is the combination of the two previous effects.
  • a==3; is an expression. It s value is 1 if a value is 3, else it is 0. It has no side effect.

Questions :

  1. Le programme suivant est-il correct au sens de la norme ISO C90 ?
    1. #include <stdio.h>
    2. int main(void){
    3. int a = 5;
    4. printf("%d\n",a);
    5. int b = 5;
    6. printf("%d\n",b);
    7. return 0;
    8. }
    9.  
  2. Le programme suivant est correct. Qu'affiche-t-il ?
    1. #include <stdio.h>
    2. int main(void){
    3. int a = 5;
    4. if(a==5){
    5. int a = 3;
    6. printf("%d\n",a);
    7. }
    8. printf("%d\n",a);
    9. return 0;
    10. }
    11.  
  3. Celui-ci est correct également, qu'affiche-t-il ?
    1. #include <stdio.h>
    2. int main(void){
    3. int a = 5;
    4. if(a==5){
    5. a = 3;
    6. printf("%d\n",a);
    7. }
    8. printf("%d\n",a);
    9. return 0;
    10. }
    11.  
  4. Dessiner l'évolution de la partie de pile correspondant aux codes précédants.
  5. Qu'affiche le code suivant ? :
    1. int a=3; a=a==3 ; printf("%d\n",a);
  6. Qu'affiche le code suivant ? :
    1. int a=sizeof(int); printf("%d\n",a==sizeof(a)); printf("%d\n",sizeof(char)>sizeof(int));