« INFO916 : Cours de C » : différence entre les versions

De Wiki du LAMA (UMR 5127)
Aller à la navigation Aller à la recherche
Ligne 62 : Ligne 62 :
=== Types de base ===
=== Types de base ===


On n'utilise pas la mémoire octet par octet. Les données que l'on place dans la mémoire ou un ''type'' (on parle de ''type de donnée''). Un type possède trois caractéristiques essentielles:
On n'utilise pas la mémoire octet par octet. Les données que l'on place dans la mémoire ont un ''type'' (on parle de ''type de donnée''). Un type possède trois caractéristiques essentielles:
# Son nom que l'on utilisera dans les programmes C.
# Son nom que l'on utilisera dans les programmes C.
# Sa taille, c'est à dire le nombre d'octets nécessire pour stocker un objet de ce type dans la mémoire. Une caractéristique étonnante du C est que tous les types ont une taille fixe ... et pourtant ceci ne nous empêchera pas de manipuler des objets de taille variable tels que les tableaux ou les chaînes de caractères. Si ''type'' est un type C, alors on peux utiliser <code>sizeof(</code>''type''<code>)</code> pour obtenir la taille du type.
# Sa taille, c'est à dire le nombre d'octets nécessaire pour stocker un objet de ce type dans la mémoire. Une caractéristique étonnante du C est que tous les types ont une taille fixe ... et pourtant ceci ne nous empêchera pas de manipuler des objets de taille variable tels que les tableaux ou les chaînes de caractères. Si ''type'' est un type C, alors on peux utiliser <code>sizeof(</code>''type''<code>)</code> pour obtenir la taille du type.
# Son ''sens'' : cet aspect est totalement ignoré par le compilateur C, mais il facilite la lecture des programmes. Si on ne mettait pas un sens.
# Son ''sens'' : cet aspect est totalement ignoré par le compilateur C, mais il facilite la lecture des programmes. Si on ne mettait pas un sens.
derrière les noms des types, on utiliserait directement leur taille comme nom, car c'est la seule chose qui compte en C.
derrière les noms des types, on utiliserait directement leur taille comme nom, car c'est la seule chose qui compte pour le compilateur C.


Remarque: on vient pour la première fois d'utiliser une convention d'écriture pour le code C qui prévaudra dans tout le cours.
Remarque: on vient pour la première fois d'utiliser une convention d'écriture pour le code C qui prévaudra dans tout le cours.
On écrira me code C en utilisant une police style machine à écrire (pour le mot <code>sizeof</code> si dessus et les parenthèses.
On écrira le code C en utilisant une police style machine à écrire (coome pour le mot <code>sizeof</code> si dessus et les parenthèses).
Toutefois, ''type'' n'est pas du C, mais une ''meta-variable'' qui devra être remplacée par du C valide (ici le nom d'un type). Par exemple: <code>sizeof(int)</code> est du C valide.
Toutefois, ''type'' n'est pas du C, mais une ''meta-variable'' qui devra être remplacée par du C valide (ici le nom d'un type). Par exemple: <code>sizeof(int)</code> est du C valide.


Voici les types de base du C:
Voici les types de base du C:


;<code>void</code> : (taille 0/1) : par exemple type de retour des procédures. Nous reviendrons plus tard sur les usages très particulier de ce type.
;<code>void</code> : (taille 0/1) : par exemple type de retour des procédures. Nous reviendrons plus tard sur les usages très particulier de ce type;
;<code>char</code> : (taille 1): un caractère ASCII ou parfois un très petit entier relatif
;<code>char</code> : (taille 1): un caractère ASCII ou parfois un très petit entier relatif;
;<code>short int</code> : (taille ?) : un entier relatif de petite taille
;<code>short int</code> : (taille ?) : un entier relatif de petite taille;
;<code>int</code> : (taille ?) : un entier relatif de taille moyenne
;<code>int</code> : (taille ?) : un entier relatif de taille moyenne;
;<code>long int</code> : (taille ?) : un entier relatif de grande taille
;<code>long int</code> : (taille ?) : un entier relatif de grande taille;
;<code>long long int</code> : (taille ?) : un entier relatif de très grande taille
;<code>long long int</code> : (taille ?) : un entier relatif de très grande taille;
;<code>unsigned char</code> : (taille 1): un caractère ASCII ou parfois un très petit entier naturel
;<code>unsigned char</code> : (taille 1): un caractère ASCII ou parfois un très petit entier naturel;
;<code>short unsigned int</code> : (taille ?) : un entier naturel de petite taille
;<code>short unsigned int</code> : (taille ?) : un entier naturel de petite taille;
;<code>unsigned int</code> : (taille ?) : un entier naturel de taille moyenne
;<code>unsigned int</code> : (taille ?) : un entier naturel de taille moyenne;
;<code>long unsigned int</code> : (taille ?) : un entier naturel de grande taille
;<code>long unsigned int</code> : (taille ?) : un entier naturel de grande taille;
;<code>long long unsigned int</code> : (taille ?) : un entier naturel de très grande taille
;<code>long long unsigned int</code> : (taille ?) : un entier naturel de très grande taille;
;<code>float</code> : (taille 4) : un nombre floattant 32 bits
;<code>float</code> : (taille 4) : un nombre en virgule flottante 32 bits;
;<code>double</code> : (taille 8) : un nombre floattant 64 bits
;<code>double</code> : (taille 8) : un nombre en virgule flottante 64 bits;
;<code>long double</code> : (taille ?) : un nombre floattant 64 bits ou plus (taille 16, mais seulement 86 bits de précision en réalité sur intel)
;<code>long double</code> : (taille ?) : un nombre en virgule flottante 64 bits ou plus (taille 16, mais seulement 86 bits de précision en réalité sur intel).


Voici donc notre premier programme C qui affiche la taille des types ci-dessus (il faudra aller un peu plus loin dans le cours pour comprendre
Voici donc notre premier programme C qui affiche la taille des types ci-dessus (il faudra aller un peu plus loin dans le cours pour comprendre
Ligne 113 : Ligne 113 :
</source>
</source>


<u>Exercice</u>: compilez le programme précédent, exécutez le et analysez le résultat.
<u>Exercice</u>: compilez le programme précédent, exécutez-le et analysez le résultat.


=== Allocation dans la pile ===
=== Allocation dans la pile ===

Version du 18 septembre 2007 à 07:38

Introduction

Qu'est ce qu'un langage de programmation :

Ce sont des intructions indiquant à un ordinateur ce qu'il doit faire.

Un langage est caractérisé par trois éléments :

  • syntaxe : quels sont les programmes valides ?
  • sémantique : que font faire ces programmes à la machine ?
  • des usages et conventions : des pratiques usuelles pour mieux programmer.

Ce cours portera sur les trois aspects, notemment, on devra être capable d'évaluer un programme C à la main.

Avertissement : on ne va pas tenter de décrire le langage C dans tous ses détails, on en décrira un sous-ensemble plus simple et donc pédagogiquement plus pertinent. De plus, la description de la syntaxe de C sous forme de BNF (voir plus loin) ne sera pas tout à fait exacte, mais sera complété par des remarques (donner la syntaxe exacte du C est difficile et nuirait à la clarté).

Historique et classification des languages

Le langage C est un

  • langage impératif et procédural
  • "assembleur" portable

Modèle mémoire et type de données

Modèle mémoire

Afin de pouvoir donner la sémantique du langage, il nous faut un "modèle" simplifié de machine. Essentiellement, un programme C agit sur deux choses:

  • La mémoire de l'ordinateur,
  • Les entrées/sorties (fichier).

On s'intéressera aux entrées/sorties en fin de cours.

La mémoire est en fait un tableau (= vecteur) d'octets (byte en anglais). Un octet est un entier sur 8 bits (bits aussi en anglais) donc compris entre 0 et 255 = 28 - 1. Donc mathématiquement un élément de {0;1;2;...;255}N où N dépend de l'architecture de la machine (N = 230, N = 231 ou N = 232 sur une machine 32 bits et N = 248 ou plus sur une machine 64 bits).

Attention: N ne dépend pas de la quantité de mémoire installée dans la machine !

Puisque la pile est un tableau, on peut parler de son i-ème élément . L'index d'un élément dans la mémoire s'appelle l'adresse de cet élément.

Seul une partie de la mémoire est accessible en lecture écriture pour le programme C (on peut aussi avoir des zones mémoire accessibles en lecture seule). Lorsque l'on essaye d'accéder au contenu d'une adresse qui n'est pas accessible le programme s'arrête brusquement sur une erreur de type bus error ou segmentation fault.

De plus, la mémoire accessible est divisée en deux zones:

  • La pile
  • Le tas

La différence principale en ces zones mémoire est que la pile est une zone continue (connexe) de la mémoire tandis que le tas est en général fragmenté. L'utilisation de ces deux zones en C est aussi très différente. La pile sera examinée dans ce chapitre tandis que le tas fait l'objet d'un chapitre à lui seul.

Puisque la pile est une zone mémoire connexe, on ne peut l'agrandir ou la diminuer qu'en changeant la position (l'adresse) de ces extrémitées. En fait les extrémitées de la pile s'appelle le fond de pile (bottom of the stack) et le sommet de la pile (top of the stack) et seul le sommet de la pile change. Donc pour allouer ou libérer de la mémoire dans la pile, one se contente de déplacer le sommet de la pile.

Remarques:

  • On n'a pas acces directement en C au sommet de la pile (on ne connait pas son adresse). Néanmoins on ne peut pas donner la sémantique du langage C sans en parler.
  • Attention, le fait que l'adresse du sommet de la pile suit supérieure ou inférieure à celle du fond de la pile dépend de l'architecture de la machine.

Types de base

On n'utilise pas la mémoire octet par octet. Les données que l'on place dans la mémoire ont un type (on parle de type de donnée). Un type possède trois caractéristiques essentielles:

  1. Son nom que l'on utilisera dans les programmes C.
  2. Sa taille, c'est à dire le nombre d'octets nécessaire pour stocker un objet de ce type dans la mémoire. Une caractéristique étonnante du C est que tous les types ont une taille fixe ... et pourtant ceci ne nous empêchera pas de manipuler des objets de taille variable tels que les tableaux ou les chaînes de caractères. Si type est un type C, alors on peux utiliser sizeof(type) pour obtenir la taille du type.
  3. Son sens : cet aspect est totalement ignoré par le compilateur C, mais il facilite la lecture des programmes. Si on ne mettait pas un sens.

derrière les noms des types, on utiliserait directement leur taille comme nom, car c'est la seule chose qui compte pour le compilateur C.

Remarque: on vient pour la première fois d'utiliser une convention d'écriture pour le code C qui prévaudra dans tout le cours. On écrira le code C en utilisant une police style machine à écrire (coome pour le mot sizeof si dessus et les parenthèses). Toutefois, type n'est pas du C, mais une meta-variable qui devra être remplacée par du C valide (ici le nom d'un type). Par exemple: sizeof(int) est du C valide.

Voici les types de base du C:

void
(taille 0/1) : par exemple type de retour des procédures. Nous reviendrons plus tard sur les usages très particulier de ce type;
char
(taille 1): un caractère ASCII ou parfois un très petit entier relatif;
short int
(taille ?) : un entier relatif de petite taille;
int
(taille ?) : un entier relatif de taille moyenne;
long int
(taille ?) : un entier relatif de grande taille;
long long int
(taille ?) : un entier relatif de très grande taille;
unsigned char
(taille 1): un caractère ASCII ou parfois un très petit entier naturel;
short unsigned int
(taille ?) : un entier naturel de petite taille;
unsigned int
(taille ?) : un entier naturel de taille moyenne;
long unsigned int
(taille ?) : un entier naturel de grande taille;
long long unsigned int
(taille ?) : un entier naturel de très grande taille;
float
(taille 4) : un nombre en virgule flottante 32 bits;
double
(taille 8) : un nombre en virgule flottante 64 bits;
long double
(taille ?) : un nombre en virgule flottante 64 bits ou plus (taille 16, mais seulement 86 bits de précision en réalité sur intel).

Voici donc notre premier programme C qui affiche la taille des types ci-dessus (il faudra aller un peu plus loin dans le cours pour comprendre réellement ce programme) : <source lang="c">

  1. include<stdio.h>

main(int argc, char **argv) {

 printf("void: %d\n", sizeof(void));
 printf("void*: %d\n", sizeof(void*));
 printf("char: %d\n", sizeof(char));
 printf("short int: %d\n", sizeof(short int));
 printf("int: %d\n", sizeof(int));
 printf("long int: %d\n", sizeof(long int));
 printf("long long int: %d\n", sizeof(long long int));
 printf("unsigned char: %d\n", sizeof(unsigned char));
 printf("short unsigned int: %d\n", sizeof(short unsigned int));
 printf("unsigned int: %d\n", sizeof(unsigned int));
 printf("long unsigned int: %d\n", sizeof(long unsigned int));
 printf("long long unsigned int: %d\n", sizeof(long long  unsigned int));
 printf("float: %d\n", sizeof(float));
 printf("double: %d\n", sizeof(double));
 printf("long double: %d\n", sizeof(long double));

} </source>

Exercice: compilez le programme précédent, exécutez-le et analysez le résultat.

Allocation dans la pile

On utilise la pile en déclarant des variables avec la syntaxe suivante:

type ident[,ident]*;

Ici on utilise la convention précédente: type doit être remplacé par un type et ident par un nom de variable. Les crochets indiquent qu'il est possible de mettre un second identificateur et l'asterisque indique que l'on peut en fait en mettre autant que l'on veut.

L'effet d'une déclaration de variables est de déplacer le sommet de la pile du nombre d'octets nécessaire pour stocker les objets dont les types sont donnés. Le sommet de la pile est donc déplacé d'un nombre d'octets égal à sizeof(type) si on alloue variables de type type.

Exercice: décrire l'état de la pile lorsque l'on execute les instructions suivantes: <source lang="c"> int x,y; char c; x = 5; int z; y=2; z=x+y; </source>

Important : on peut donner la valeur d'une variable en même temps que la déclare en utilisant la syntaxe suivante:

type ident[=expr][,ident[=expr]]*;

Dans la ligne précédente: [=expr] indique que le nom du type peut être suivi d'une expression donnant la valeur initiale de la variable.

Important : il faut initialiser les variables lors de la déclaration ou tout de suite après. Il est dommage que C n'impose pas cette contrainte, car les variables non initialisées sont la source de nombreux bugs.

Pointeurs et adresses

Malloc et le tas

Fonctions et prototypes

Opérateurs

Structures de contrôles

Organisation des programmes C (les .h et les .c)

Le préprocesseur

Les types définis par le programmeur

Les entrées/sorties