Principales notions de programmation en C


Déclaration des constantes et des variables

Langage C

Equivalent Pascal

Déclarations / affectations

  • des constantes
#define NOMBRE 1
#define CHAINE "ALPHA"
CONST NOMBRE = 1;
CONST CHAINE = 'ALPHA';

  • des variables
int i,j;
int k,l=0;

char c;
float r;
VAR i,j:INTEGER;
VAR k,l:INTEGER;
...l:=0;
VAR c:CHAR;
VAR r:REAL;

Notations

Les caractères se notent entre apostrophes : 'a'
Les chaînes de caractères se notent entre guillemets : "abcd ef"
Les commentaires se notent entre /* et */
Les blocs se notent entre { et }


Les types des variables

Nom Type Taille du codage
char caractère 8 bits
int entier 1 mot mémoire (16/32 bits)
long entier long 16/32 bits
short entier court 16 bits
float réel 32 bits
double réel + de 4 octets

Remarque :

Il est possible de redéfinir un type en utilisant la commande typedef

L'affectation d'une variable se fait à l'aide de l'opérateur =

Exemples :

char a;
int b,c = 0;
a = 'A';


L'affectation d'une valeur V à une variable x se décompose en deux phases :

  1. la variable x reçoit la valeur V ;
  2. le système retourne, comme résultat de cette affectation, la valeur V (ce qui n'est pas le cas en Pascal). Il n'est pas nécessaire d'utiliser ce résultat.

Confidentialité

Certaines variables, définies "en global" dans un fichier, ne doivent pas être référencées dans d'autres fichiers pour ne pas être confondues avec les déclarations de variables globales dans d'autres fichiers. Pour exprimer le fait que ces variables sont confidentielles à un fichier (c'est-à-dire visibles uniquement dans ce fichier), on fait précéder leur déclaration du mot-clé static

Remarque :

Il est possible de définir, de la même manière, des fonctions confidentielles à un fichier.
Les variables locales à un sous-programme peuvent être de diverses classes :

Déclaration de type

typedef est une instruction qui permet de créer de nouveaux noms de types pour la déclaration des variables. La syntaxe de cette instruction est :

typedef [type] [nom du type];

Après l'utilisation de cette instruction, [nom du type] devient le synonyme de [type]

Le mot-clé const devant la déclaration d'une variable indique que la valeur de cette variable ne doit absolument pas être modifiée dans le bloc courant (on parle de "type contraint").


Les conversions de types

La conversion d'un type vers un autre s'effectue en faisant précéder la valeur à convertir du nouveau type entre parenthèses :

char c='a';
int i=0;
float j;
...
j = (float)i;
i = (int)c; /* i reçoit le code ASCII de 'a' */


Attention, certaines conversions peuvent générer des "warnings" à la compilation et des erreurs à l'exécution.

Exemple :

char c;
int i = 257; /* un entier est codé sur 16 ou 32 bits */
c = (char)i; /* un caractère est codé sur 8 bits (1 octet), la conversion risque de produire une perte d'information */


La division entière

En C, la division (/) de deux entiers produit le quotient de la division entière.

Exemple :

int a;
a = 2 / 4;
printf("%d\n",a);


retourne à l'écran la valeur 0. En revanche :

float a;
a = 2.0 / 4.0;
printf("%f\n",a);

retourne à l'écran la valeur 0.5


La déclaration des tableaux

La syntaxe pour la déclaration d'un tableau est :

type_des_données identificateur[nombre_de_données]

On rajoute autant de [ ] que le tableau a de dimensions.

Exemple :

Langage C

Equivalent Pascal

int tab1[50];
int tab2[50][10];
Exemple : ...tab2[i][j]=6;
VAR tab1:ARRAY [0..49] OF INTEGER;
VAR tab2:ARRAY [0..49,0..9] OF INTEGER;
Exemple : ...tab2[i,j]:=6;

Initialisation d'un tableau à la déclaration

Un tableau peut être initialisé au moment de sa déclaration en indiquant la liste de ses valeurs.

Exemples :


Les opérateurs

Les opérateurs de comparaison (>, <, >=, <=), d'égalité (==, !=) et logiques (&&, ||) renvoient 0 si le résultat est faux et 1 s'il est juste. L'opérateur de négation logique (!) renvoie 0 pour toute valeur non nulle et 1 pour la valeur 0 :

!0 --> 1
!8 --> 0


Les structures de controle de type "condition"


Les structures de controle de type "boucle"

Equivalence "For" et "While"

La commande :

for (expr1;expr2;expr3) action;

est équivalente à :

expr1;
while(expr2)
{ action; expr3;
}

Boucles infinies

while(1) et for(;;) sont des boucles infinies.


Les sous-programmes

Un sous-programme en C renvoie toujours une valeur. Lorsque cette valeur est indéfinie, le sous programme est déclaré de type void. En général, les fonctions du langage C renvoient une valeur non nulle lorsque la commande a pu être correctement exécutée et 0 sinon.

La structure d'un sous-programme est généralement :

type_du_résultat nom_fonction(liste_et_types_des_paramètres)
{
corps_du_sous-programme
return(resultat);
}

Le passage des paramètres se fait toujours par valeur. Pour modifier la valeur d'une variable, il faut en transmettre l'adresse par pointeur.


Les chaînes de caractères

Une chaîne de caractères est représentée sous la forme d'un tableau de caractères. Le dernier caractère indique la fin de la chaîne et sa valeur est \0

Les deux exemples suivants permettent de déclarer un tableau de 20 caractères :

  1. char chaine[20]; /* création "statique" d'un espace mémoire de 20 caractères */
  2. char *chaine;
    chaine = (char *)malloc(20);
    /* création "dynamique" d'un espace mémoire de 20 caractères : réservation dans la mémoire d'un espace pouvant recevoir 20 caractères (tableau), et affection de l'adresse de cet espace à la variable "chaine" */

La taille maximale de la chaîne déclarée ci-dessus (par la première ou la deuxième méthode) est de 19 caractères (+ le caractère \0).

Remarques :

  1. Tout espace mémoire réservé par l'instruction malloc doit être libéré.
  2. La libération d'une zone mémoire réservée par l'instruction malloc se fait grâce à l'instruction : free(chaine);

Exemples d'initialisation d'une chaîne de caractères :

  1. while(((chaine[i++]=getchar())!='\n')&&(i < TAILLE - 1));
    chaine[i-1]='\0';
  2. scanf("%s",chaine);
    /* Attention, la chaîne ne doit contenir ni espace, ni retour-chariot */
    /* scanf place automatiquement un caractère \0 en fin de chaîne */

Remarque :

Pour afficher une chaîne de caractères, il est possible d'utiliser la commande printf de la manière suivante :

printf("%s",chaine);
chaine est un pointeur vers une zone mémoire contenant la chaîne à afficher.

Pour la manipulation des chaînes de caractères, il est possible d'utiliser les fonctions accessibles par l'inclusion de "string.h" en début de programme. Parmi celles-ci, on retiendra en particulier :

Pour convertir une chaîne de caractères contenant des chiffres sous la forme d'entiers, on peut utiliser la fonction atoi (alphanumérique to integer) de la bibliothèque standard, dont l'en-tête est le suivant :

int atoi(const char *s)


Les pointeurs

Un pointeur contient l'adresse d'un objet (i.e. d'une variable, d'une structure, ...). Lorsqu'on déclare :

int a;

le système réserve un espace suffisant dans la mémoire pour ranger un entier. L'adresse de cet espace réservé est : &a

Le passage d'un tableau en paramètre se fait toujours à partir de son adresse. Un tableau déclaré de la manière suivante :

int tab[100];

a pour adresse tab ou encore &tab[0]

& donne l'adresse d'un objet donné.

* donne la valeur d'un objet à une adresse donnée.

NULL est une valeur de pointeur ayant le plus souvent la signification d'inexistence.

Un pointeur incrémenté pointe sur l'objet adjacent suivant dans la mémoire.

Un tableau du type int TAB[10]; a pour adresse, l'adresse de son premier élément, à savoir TAB, ou &TAB[0]. Les éléments d'un tableau ont des adresses d'objets consécutifs.

void *malloc(size_t taille) : cette fonction renvoie un pointeur vers un espace mémoire dont la taille en octets est précisée en paramètre (renvoie NULL si la demande ne peut être satisfaite).
Le type size_t est défini dans la bibliothèque standard et équivaut à un unsigned int ("entier non-signé").
Au moment de l'affectation de la valeur retournée par malloc à un pointeur, il est nécessaire de forcer la conversion vers le type de l'information pointée. Dans l'exemple suivant :

char *p;
...
p = (char *)malloc(4);


on force le type du pointeur retourné par malloc à être char *

void *calloc(size_t nbobjets,size_t taille) : cette fonction renvoie un pointeur vers un espace mémoire pour le stockage d'un tableau de nbobjets objets dont chacun prend une place mémoire correspondant à taille octets. Les bits de la zone mémoire réservée sont tous initialisés à 0.

void free(void *pointeur) : cette fonction libère la zone mémoire accessible par un pointeur donné en paramètre, dans le cas oł cette zone mémoire a été allouée dynamiquement lors d'un appel à la fonction malloc.

Attention : ne jamais libérer une zone mémoire déjà libérée. Le système pourrait alors générer des erreurs de nature imprévisible.

Pour connaître la taille d'un objet afin de réserver l'espace mémoire nécessaire à son stockage, on utilise la commande sizeof(), avec l'objet en paramètre, ou son type.

L'identificateur d'un tableau est un pointeur sur une zone en mémoire contenant (ou susceptible de contenir) les informations du tableau de manière connexe.

Type d'un pointeur

La taille de la zone mémoire pointée est donnée par le type du pointeur. Par exemple, si on a la déclaration suivante :

char *c;
c est un pointeur vers une zone de 8 bits en mémoire. Dès lors, l'adresse de l'octet situé immédiatement après est c+1. En incrémentant c, il est ainsi possible de parcourir la mémoire d'octet en octet.

Un pointeur du type short *a; pointe sur une zone pouvant contenir un entier court (codé sur 16 bits). En incrémentant a, on balaye la mémoire de 16 bits en 16 bits.

On a donc les équivalences suivantes :

  1. tab+i est équivalent à &tab[i]
  2. *(tab+i) est équivalent à tab[i]

Les structures

Une structure se déclare de la manière suivante :

struct nom_structure
{
liste_des_champs
}


Exemple :

struct date
{
int jour;
int mois;
int an;

}

Initialisation d'une structure

On accède aux champs d'une structure par l'opérateur : .

struct date d; /* déclaration d'une variable de type "struct date" */
d.jour=1; /* initialisation du champ "jour" de la variable "d" */


La notation pdate->jour est équivalente à (*pdate).jour (champ jour de l'objet pointé par pdate).

Il est possible d'initialiser une structure au moment de sa déclaration, en donnant la liste des valeurs de ses champs, de la même manière que pour un tableau. En reprenant l'exemple précédent, au moment de déclarer la variable d, on peut écrire :

struct date d={30,02,1998};

ATTENTION : ce type d'initialisation n'est correct qu'au moment de la déclaration d'une variable. Il ne peut pas être utilisé ailleurs dans un programme.


La représentation des données en mémoire

Afin de donner une représentation de la manière dont les structures sont liées les unes aux autres dans la mémoire centrale d'un ordinateur lors de l'exécution d'un programme, on produit un graphique respectant les conventions suivantes :

Exemples :

représentation de structures chaînées


Les entrées / sorties

Entrée / Sortie standard

stdin, stdout, stderr : entrée standard (clavier), sortie standard (écran) et sortie standard des erreurs (écran).

Pour forcer un affichage sur la sortie standard :
printf("affichage"); ou fprintf(stdout,"affichage");

Pour forcer une lecture sur l'entrée standard :
scanf("%d",&v); ou fscanf(stdin,"%d",&v);

Pour forcer les affichages, il peut être nécessaire de vider le buffer de sortie :
fflush(stdout);

Pour vider le buffer d'entrée et éviter la lecture d'une valeur obsolète, il peut être nécessaire de vider le buffer d'entrée :
fflush(stdin);

Spécificateurs de format

Les spécificateurs de formats pour les instructions printf et scanf sont :


Les fichiers


Le préprocesseur

Le préprocesseur permet entre autres :

Exemples :

#define A 1
#define max(A,B) ((A) > (B) ? (A) : (B))
...
x = max(a+b,c+d);
y = A;
...
Attention : x = max(i++,j++); incrémente deux fois i et j...

L 'opérateur # permet, dans une macro, de substituer un paramètre par sa valeur convertie en chaîne de caractères.

L'opérateur ## effectue la concaténation de deux symboles.

Il existe un ensemble de symboles prédéfinis permettant de récupérer dans un programme C des valeurs renvoyées par des "appels système" :


Les pointeurs de fonctions

Pour transmettre de programme à programme l'adresse d'un sous-programme gérant une tâche particulière, on peut utiliser un pointeur de fonction. Un sous-programme n'est alors plus appelé à travers son nom, mais à travers l'adresse du code de la tâche qu'il effectue...

Exemple :

int f(...)
{
...
}

main()
{
int (*p)(...);
int i;
p=f; /* ou p=&f; */
i=f(...);
i=p(...);
i=(*p)(...);
}


Retour d'une valeur

L'arrêt de l'exécution d'un programme s'effectue à l'aide de l'instruction :
exit(valeur);

Par convention (avec UNIX), il est conseillé de retourner la valeur 0 lorsque tout s'est bien passé, et une valeur non nulle lorsqu'il y a eu un problème.
La fonction exit provoque l'arrêt du programme. Il ne faut pas confondre cette fonction ni avec break (qui sort d'un bloc), ni avec return (qui sort d'un sous-programme en renvoyant une valeur, sans arrêt de l'exécution).


Gestion des erreurs

La fonction perror, d'en-tête :
void perror(const char *s)

a pour rôle de faire afficher, sur le fichier standard d'erreurs (qui est l'écran, par défaut), un message décrivant la dernière erreur rencontrée. Si le paramètre s pointe sur une chaîne de caractères non vide, alors cette chaîne est affichée juste avant le message décrivant le type de l'erreur. Sinon, le message d'erreur est affiché directement. Dans les deux cas, l'affichage est terminé par un retour à la ligne.


Appels système

Le langage C permet de lancer des commandes système grâce à l'utilisation de la fonction system. Cette fonction doit être appelée avec un seul paramètre effectif, qui est une chaîne de caractères représentant la commande système requise.


Paramètres d'une commande C

Pour récupérer les paramètres passés au moment de l'appel à un programme en C, il faut déclarer le programme principal avec deux arguments :
main(int argc,char **argv)

ou encore :

main(int argc,char *argv[])

argc sera automatiquement initialisé par le système au moment de l'appel au nombre de termes apparaissant sur la ligne d'appel du programme (i.e. le nombre de paramètres + 1 pour le nom du programme exécutable).

argv désignera un tableau de pointeurs vers des chaînes de caractères. Chaque chaîne contient un des termes (dans l'ordre) apparaissant sur la ligne d'appel du programme.


Ces pages ont été adaptées à partir de celles réalisées par J.D. Durou et Ph. Joly avec leur aimable autorisation.