Le langage C

Le C est un langage de programmation impératif, proche du langage machine, inventé par Dennis Ritchie en 1972. Ce langage révolutionna l'informatique.

Dennis MacAlistair Ritchie (né en 1941 à Bronxville dans l'Etat de NewYork, décédé en 2011 à Berkeley Heights dans le New Jersey) est un des pionniers de l'informatique moderne, inventeur du langage C et co-développeur de Unix. Il est parfois désigné par dmr, son adresse e-mail aux Laboratoires Bell.

1) Type élémentaire

1.1) Type entier

Selon la norme ANSI C99 :

Type
Taille en octet
Intervalle de valeur
_Bool
1
0 et 1
char
1
-128 à 127
0 à 255
signed char
1
-128 à 127
unsigned char
1
0 à 255
int
2 ou 4
-32 768 à 32 767
-2 147 483 648 à 2 147 483 647
unsigned int
2 ou 4
0 à 65535
0 à 4 294 967 295
short
2
-32 768 à 32 767
unsigned short
2
0 à 65535
long
4
-2 147 483 648 à 2 147 483 647
unsigned long
4
0 à 4 294 967 295
long long
8
-9 223 372 036 854 775 808 à
9 223 372 036 854 775 807
unsigned long long
8
0 à 18 446 744 073 709 551 615

Bornes déclarées dans limit.h :

Type
Minimum
Maximum
_Bool
0
1
char
CHAR_MIN
CHAR_MAX
signed char
SCHAR_MIN
SCHAR_MAX
unsigned char
0
UCHAR_MAX
int
INT_MIN
INT_MAX
unsigned int
0
UINT_MAX
short
SHRT_MIN
SHRT_MAX
unsigned short
0
USHRT_MAX
long
LONG_MIN
LONG_MAX
unsigned long
0
ULONG_MAX
long long
LLONG_MIN
LLONG_MAX
unsigned long long
0
ULLONG_MAX

Représentations des constantes entières :

Préfixe
Représentation
Exemple
Base
Décimale 1234
10
0
Octale 01234
8
0x
Hexadécimale 0x1234
16

Constante entières :

Suffixe
Exemple
Type
  12 int
L 12L long
UL 12UL unsigned long
LL 12LL long long
ULL 12ULL unsigned long long

 

1.2) Type flottant

Selon la norme IEEE 754-1985 :

Type
Taille en octet
Intervalle de valeur
Nombre de chiffres
après la virgule
float
4
1.2E-38 à 3.4E+38
6
double
8
2.3E-308 à 1.7E+308
15
long double
12
3.4E-4932 à 1.1E+4932
19

Types déclaré dans complex.h :

Type
Taille en octet
float complex
8
double complex
16
long double complex
24

Bornes déclarées dans float.h :

 
float
double
double float
Nombre de chiffres après la virgule FLT_DIG DBL_DIG LDBL_DIG
Exposant négatif minimal FLT_MIN_10_EXP DBL_MIN_10_EXP LDBL_MIN_10_EXP
Exposant positif maximal FLT_MAX_10_EXP DBL_MAX_10_EXP LDBL_MAX_10_EXP
Minimum strictement positif FLT_MIN DBL_MIN LDBL_MIN
Maximum FLT_MAX DBL_MAX LDBL_MAX
Minimum strictement positif x tel que 1.0+x ≠ 1.0 FLT_EPSILON DBL_EPSILON LDBL_EPSILON

Constantes réelles avec exposant : 1.2E+3, 1.2e+3, 1.2E3, 1.2e3

Constantes réelles et complexes :

Suffixe
Exemple
Type
F
1.2F float
iF
1.2iF float complex
  1.2 double
i
1.2i double complex
L
1.2L long double
iL
1.2IL long double complex

 

2) Type pointeur

Etant donnée une variable x d'un type T élémentaire ou structuré, la variable x contient une valeur de type T. On dira que x est un T.

La variable x contient sa valeur dans un block mémoire contigü dont la taille est égale à sizeof(T), la taille des éléments de type T. L'adresse de ce block mémoire, appelée adresse de la variable x, appelé pointeur sur x, est donné par &x. Le type de cette valeur &x est T*. On dira que c'est un pointeur de T.

On définie plusieurs variables a, b, c contenant des pointeurs de T comme suit :

T *a, *b, *c;

On a ainsi définie 3 pointeurs a,b,c. Ces pointeurs ne sont pas initialisés. Ils peuvent pointer n'importe où, et les blocks mémoires sur lesquels ils pointent ne sont pas alloués.

Les pointeurs ont des propriétés arithmétiques. On peut les incrémenter :

Pour accéder aux valeurs qu'elles pointent on utilise l'opérateur unaire * :

Considérons une variable x de type T et une variable a de type T* que l'on initialise à la valeur &x :

T x;
T *a;
a = &x;

a est un pointeur de T. Après ces instructions a pointe sur x. Délors *a désigne la même valeur que x. Mais plus que cela, il désigne le même emplacement mémoire. Toutes modification de *a se répercute instantanément sur x et inversement car *a représente la même variable que x.

La déclaration d'un tableau x comme suit T x[50] correspond à la déclaration d'un pointeur x comme suit T *x avec une information supplémentaire qui est le nombre de blocks utilisés (ici 50). Mais cette information n'est utilisée que pour l'allocation initiale de mémoire. Aucun teste n'est fait pour vérifier si l'indice d'un appel sort des limites du tableau.

On définie plusieurs variables a, b, c contenant des tableau de T comme suit :

T a[5], b[30], c[10];

La taille d'une addresse mémoire est généralement égale à 4 octects. Tout pointeur quelque soit le type qu'il pointe occupe cette même taille mémoire.

3) Type structuré

Un type structuré représente un block mémoire contigü découpé en une succession de sous-block, appelés champs, correspondant à différents types élémentaires ou eux-même structurés.

Le tableau T x[50] est un type structuré implicite, un block constitué de 50 sous-blocks ou champs de type T. Pour rendre la structure explicite, on l'enrobe dans une instruction struct comme suit :

struct {T x[50];} m;

Dans cette exemple le type est struct {T x[50];} et la variable est m. Cette instruction déclare un tableau m de 50 éléments de type T. Les indices sont compris entre 0 et 49. Pour accéder à l'élément d'indice 3, il faut écrire le terme m.x[3]. Ce terme semble plus complexe que le terme x[3] d'une structure de tableau implicite, mais il n'en est rien. Le compilateur traduit ces deux instructions exactement de la même façon.

Les champs des structures subissent des contraintes d'alignement dépendant du système sous-jacent, qui engendre des trous anonymes entre les champs. Certain champs doivent commencer à des adresses multiples de leur taille ou de 4. Souvent la taille d'une structure correspond à un nombre d'octets multiple de 4.

On peut nommer les nouveaux types que l'on définie en insérant un nom juste après le mot clef struct.

struct tab {T x[50];}

Dans cette instruction, le type défini se nomme struct tab. Et il est possible de l'utiliser pour définir des variables ainsi que d'autres types, et souvent les deux à la fois :

struct tab m, *a, b[50];
struct toto {struct tab x; int y; char c;};
struct titi {struct tab v[5];} w;

Une variable de type structurée peut être initialisée au moment de sa déclaration par un emboitement d'ensembles. Si on indique moins de valeurs que la structure ne comporte de champs, alors les champs restants sont initialisés par des zéros. Par contre, donner plus de valeurs qu'il n'y a de champs constitue une erreur.

struct { int x[3], struct {int x; long y;} h[2]} m = {{1,2,3}, {{4,5}, {6,7}}};

La variable m est initialisée, et nous avons m.h[1].y = 7.

L'initialisation d'une chaine de caractère peut se faire ainsi :

char *x = {'B', 'o', 'n', 'j', 'o', 'u', 'r', 0}
char *y = "Bonjour"
char z[8] = "Bonjour\0"

Pour les autres types de tableau il est nécessaire d'employer la notation suivante :

int x[ ] = {1,2,3,4,5}

La taille de x sera alors d'exactement 5*sizeof(int). Noter que x est de type int*. c'est un pointeur de int. Nous avons :

*x = x[0]
*(x+1) = x[1]
*(x+2) = x[2]

 

---- 4 Janvier 2013 ----

 


Dominique Mabboux-Stromberg