Copyright: Ici votre nom
Citation: Ici votre nom -> With ArchiDoc (Help Generator)


Introduction

Fichier d'aide créé par Jérémy Beuchot.

Cours de C et C++

Table des matières

Généralités

Structure d'un programme C

Historique

Entrées/Sorties

Introduction

E/S de caractères isolés

E/S de données mises en forme

E/S de chaînes caractères

Types, Opérateurs, Expressions

Noms de variables

Types de données et tailles

Constantes

Déclaration

Opérateurs arithmétiques

Opérateurs unaires

Opérateurs de comparaison et opérateurs logiques

Opérateurs de traitement de bits

Opérateurs d'affectation

Structures de contrôle

Instructions et blocs

if-else

switch

while

do-while

for

break

Continue

goto

Fonctions et structures de programmes

Principes fondamentaux

Variables, classes de mémorisation

Programme mono-fichier

Programme multi-fichier

Compléments sur les fonctions externes

Pointeurs et Tableaux

Introduction

Passage de paramètres par adresse

Tableau mono-dimentionnel

Opérations sur les pointeurs

Passage de tableau en paramètres

Tableaux de pointeurs - pointeurs de pointeurs

Tableaux multidimensionnels

Gestion de la mémoire

Pointeur vers une fonction

Structures, unions, énumérations

Structures

Types de données personnalisées

Unions

Enumérations

Fichiers de données

Ouverture et fermeture

Fonctions de positionnement

Création d'un fichier de données

Autres fonctionnalités

Macros

Pré-processeur C

Fonction main


Généralités
Structure d'un programme C

Historique


Historique

Créé en 1970 dans les laboratoire de Bell telephon par D.Ritchie et B.W.Verningham, il est utilisé en interne jusqu'en 1978, date à laquelle les premières spécifications sont publiées. En 1981, apparaît le C++, un langage dérivé du C dans lequel est ajouté la notion de programmation orientée objet (POO). Toutes les fonctions du C standard sont comprise dans le C++.

Le langage C est un langage procédural.


Structure d'un programme C

Un programme C est composé :

d'un ou de plusieurs fichiers

d'une ou de plusieurs modules

d'une fonction principale main(), les autres fonctions étant appelée par celle ci directement ou indirectement.

Le langage C ne connaît que les fonctions, la notion de procédure n'existe pas mais on peut créer des fonctions qui agissent comme des procédures. Si les fonctions sont reparties dans plusieurs fichiers, elles sont reliées au moment de l'édition de liens.

Une fonction est un sous-programme auquel on transmet une liste d'arguments (éventuellement vide) et qui retourne un valeur après avoir effectué une certaine tache. Contrairement au langage Ada, les arguments peuvent être en mode in, out ou in/out. De plus, la valeur retournée peut ne pas exister. Toute fonction doit comprendre :

une entête de la forme : type nom_de_fonction(type_arg1 arg1, type_arg2 arg2,...) ou type est le type de la fonction (void si la fonction  ne retourne rien). Les parenthèses sont obligatoire même si la fonction n'a pas de paramètres.

liste des déclaration des paramètre (nécessaire si on n'a pas spécifier le type des paramètre au moment de leur passage dans l'entête de la fonction)

une instruction composée (c'est à dire un bloc d'instructions)

Un bloc d'instruction est noté entre accolades ({...}) et est composé d'instructions simples ou elles-même composées.

On note les commentaire entre les symboles /* et */ ou après un //.

Attention !!! il peut y avoir des problèmes avec l'utilisation des commentaire. Par exemple, si on a la partie de programme suivant qui contient des commentaire et que l'on voudrait mettre en commentaire :

...

  bloc d'instructions /* commentaire */

...

on ferait ceci :

/*

...

  bloc d'instructions /* commentaire */

...

*/

mais en fait, il n'y aurait que cette partie qui serait mise en commentaire :

/*

...

  bloc d'instructions /* commentaire */

...

*/

Il aurait en fait mieux valu mettre les commentaires d'origine derrière un // comme ceci :

...

  bloc d'instructions // commentaire

...

Une variable en C est visible depuis sa déclaration jusqu'à la fin du bloc où elle a été déclarée. L'appel d'une fonction en C constitue un instruction. On n'est pas obligé de tenir compte de la valeur retournée par une fonction. On se sert alors que des effets produits par la fonction (affectation de variables globales, affichages, saisies…).

Ada	C
with fichier;	#include <fichier>
procedure main is   déclaration des variables globales   spécification des fonctions   définition des fonctions begin   corps de main end main;	déclaration des variables globales spécification des fonctions main() {   corps de main } définition des fonctions

La définition des fonctions en fin de fichier permet d'avoir une meilleur vue d'ensemble d'un fichier dès son ouverture. On voit directement la liste des fonctions du fichier et le corps de main.


Entrées/Sorties
Introduction

E/S de caractères isolés

E/S de données mises en forme

E/S de chaînes caractères


Types, Opérateurs, Expressions
Noms de variables

Types de données et tailles

Constantes

Déclaration

Opérateurs arithmétiques

Opérateurs unaires

Opérateurs de comparaison et opérateurs logiques

Opérateurs de traitement de bits

Opérateurs d'affectation


Structures de contrôle
Instructions et blocs

if-else

switch

while

do-while

for

break

Continue

goto


Introduction

Les fonctions d'entrées/sorties (E/S) permettent l'échange d'informations entre l'ordinateur et les périphériques standard (clavier, écran). Comme toute fonction, il faut préciser lors de l'appel, le nom de la fonction suivi d'une liste d'arguments entre parenthèses. Les parenthèses sont obligatoires même si aucun argument n'est passé à la fonction.

Les compilateurs C incluent un ensemble de fichiers entête fournissant l'information nécessaire à l'emploi des bibliothèques. Le fichier entête correspondant aux entrées/sorties doit être inclus dans tout fichier source les utilisant par la directive #include <stdio.h>. L'extension .h signifie que le fichier contient des entêtes (header), les entêtes de fonction étant leur déclaration ou spécification.


E/S de caractères isolés

getchar() : attend la saisie d'un caractère sur le périphérique d'entre standard (généralement le clavier)

putchar() : affiche un caractère sur le périphérique de sortie standard (généralement l'écran)

Exemple d'utilisation :

...

char MonCar;       //déclaration d'une variable de type caractère

MonCar=getchar();  //saisie d'un caractère stocké dans la variable MonCar

putcar(MonCar);    //affichage du contenu de la variable MonCar


E/S de données mises en forme

printf("Chaine de formatage", Arg1, Arg2, ...) : permet l'affichage de données de type caractère isolé, valeur numérique, chaîne de caractères.

scanf("Chaine de formatage", Arg1, Arg2, ...) : permet la saisie de données de type caractère isolé, valeur numérique, chaîne de caractères.

Les ArgI représentent les données à saisir ou à afficher. Pour scanf(...), ArgI est l'adresse mémoire de la variable dans laquelle la donnée saisie sera stockée. Pour stocker la saisie dans une variable Var, il faut donc passer &Var en argument à la fonction scanf(...).

La chaîne de formatage contient des groupes de caractères distincts, associés à chacun des ArgI.

La forme globale d'un ArgI est la suivante : %[[-]taille] [.précision [l]] conversion avec :

taille : taille minimum du champ d'impression, exprimée en caractères. Si le "-" est présent, l'argument sera cadré à gauche dans son champ d'impression.

précision : si l'argument est une chaîne, cette valeur indique le nombre maximum de caractères à imprimer. Si c'est un flottant, elle donne le nombre de chiffres à indiquer après le point. Si le "l" est présent, l'argument sera interprété comme un long.

conversion : peut prendre les valeurs suivantes :

d	L'argument sera écrit en base dix
o	L'argument sera écrit en octal
x	L'argument sera écrit en hexadecimal
u	L'argument sera écrit en base dix non signé
c	L'argument sera interprété comme un caractère
s	L'argument sera interprété comme l'adresse d'une chaîne se terminant par "\0"
e	L'argument sera interprété comme un flottant et écrit sous la forme [-]m.n E[-]e
f	L'argument sera interprété comme un flottant et écrit sous la forme [-]m.n
g	L'argument sera interprété comme un flottant et écrit sous la forme la plus courte des précédentes.

Exemple d'utilisation :

#include <stdio.h>

main

{

  int MonEntier;

  float MonReel;

  char MonCar;

  char MaChaine[10];

  printf("Saisir un entier, un réel, un caractère et une chaine");

  scanf("%d %f %c %s", &MonEntier, &MonReel, &MonCar, MaChaine);

  printf("L'entier vaut %d\n Le réel vaut %f\n", MonEntier, MonReel);

  printf("Le caractère vaut %d\n La chaine vaut %f\n", MonCar, MaChaine);

}

Une chaîne de caractère est un tableau de caractères. Dans notre exemple, le nombre d'élément du tableau est de 10, on pourra donc stocker une chaîne de caractères d'au plus 9 caractères. En effet, une chaîne de caractères se termine toujours par le caractère '\0', il faut donc un tableau pouvant stocker un caractère de plus que le nombre de caractères de la chaîne.

Une variable de type tableau est en fait une variable de type pointeur vers un tableau. Elle contient l'adresse du premier élément du tableau. Puisque c'est déjà une adresse, on peut la passer directement en argument à la fonction scanf().

Attention : l'utilisation de la fonction scanf() pour la saisie d'une chaîne de caractères arrête la saisie après un espace ou un retour-chariot.

Si l'utilisateur saisit "Mon nom est Paul" à la commande scanf("%s", MaChaine), la variable MaChaine contiendra "Mon". Il vaut mieux utiliser gets() pour la saisie d'une chaîne de caractères.


E/S de chaînes caractères

gets() : attend la saisie d'une chaîne de caractères sur le périphérique d'entre standard

puts() : affiche une chaîne de caractères sur le périphérique de sortie standard

Exemple d'utilisation :

...

char MaChaine[20]; //déclaration d'une variable de type chaine de caractères

gets(MaChaine);    //saisie d'un caractère stocké dans la variable MaChaine

puts(MaChaine);    //affichage du contenu de la variable MaChaine

...

Contrairement à la fonction scanf(), la saisie ne s'arrête que pour la frappe d'un retour-chariot.

Attention : la saisie d'un entier avec scanf() doit être validée par un retour-chariot. Le caractère retour chariot sera donc présent dans le buffer clavier. Une saisie de caractère ultérieure ira chercher le contenu du buffer clavier qui contient le retour-chariot et l'utilisateur n'aura pas pu entrer de caractère. Pour éviter ce problème, il convient de vider le buffer clavier avant une saisie de caractère grâce à la commande ffluch(sdtin).


Noms de variables

Un nom de variable peut être composé de caractères alphabétiques majuscules et minuscules, du symbole '_' et de chiffres mais ne peut pas commencer par un chiffre.


Types de données et tailles

Spécificateurs de types :

void : aucun type

char : caractère (un octet)

int : entier (deux ou quatre octets selon les machines)

float : réel virgule flottante simple précision

double : réel virgule flottante double précision

Quantificateurs de types :

short, long, unsigned : appliqués à int

signed, unsigned : appliqués à un char

long : appliqué à double

Exemple :

...

short int MonEntierCourt ;

...


Constantes

Il existe quatre types de constantes :

entière (Ex : 20 (décimale), o37 (octale), 0x1F (hexadécimale))

réelle (Ex : 1., 1.0, 0.2E6, 2e-3)

caractères (Ex : 'a','\n', '\0')

chaîne de caractères (Ex : "abc", "a")

 VERSION NON - ENREGISTREE


Déclaration

On déclare une variable en donnant son type suivi de son nom et éventuellement d'une valeur.

Ex : int MonEntier=3 ;

 VERSION NON - ENREGISTREE


Opérateurs arithmétiques

Il y en a cinq : +, -, /,*, % (modulo)

Les autres opérations mathématiques sont incluses dans la bibliothèque math.h.

Dans une opération, si deux opérandes sont de types différents, le résultat est exprimé dans le type ayant la plus grande précision.

 VERSION NON - ENREGISTREE


Opérateurs unaires

Le moins unaire : -

L'incrémentation : ++

A++ est équivalent à A=A+1. La pré-incrémentation (++A) est effectuée avant une affectation alors que la post-incrémentation (A++) est effectuée après.

... int A,B; A=3; B=A++ B=++A; ...	A B   3 4 5   3 5

La décrémentation : --

Idem…

L'opérateur sizeof(type ou variable)

Retourne la taille en octet d'un type ou d'une variable.

L'opérateurs de conversion : (type)

(int)A : converti la variable en entier.

 VERSION NON - ENREGISTREE


Opérateurs de comparaison et opérateurs logiques

Il y a :

4 opérateurs relationnels : <, >, <=, >=

2 opérateurs de comparaison : ==, !=

3 opérateurs logiques : &&, ||, !

	ADA	C
Comparaison	=	==
Affectation	:=	=
ET	AND	&&
OU	OR	||
NON	NOT	!

Ces opérateurs permettent de construire des expressions dont la valeur est vraie ou fausse. Le résultat est de int puisque le type booléen n'existe pas en C. Si l'entier vaut 0 le résultat est vrai et sinon, il est faux.

 VERSION NON - ENREGISTREE


Opérateurs de traitement de bits

Il existe un certain nombre d'opérateurs permettant la manipulation individuelle de bits à l'intérieur d'un mot mémoire. Ils s'appliquent aux types objets de types fondamentaux c'est à dire les entier et les caractères.

Opérateur	Fonction	Valeur en base 10	Valeur en binaire	Résultat en binaire
~	Complément à 2	5	0...000101	1...111010
&	ET logique	5 & 6	...101 & ...110	...100
|	OU logique	5 | 6	...101 | ...110	...111
^	OU exclusif logique	5 ^ 6	...101 ^ ...110	...011
<<	Décalage des bits à gauche	5 << 2	...00101	...0010100
>>	Décalage des bits à droite	5 >> 1	...00101	...0010

Exemple :

...

int MonEntier=7, SaMoitie, SonDouble;

SaMoitie=MonEntier >> 1 ;

SonDouble=MonEntier << 1

...

 VERSION NON - ENREGISTREE


Opérateurs d'affectation

Il y en a 6 : =, +=, -=, /=, *=, %=. Tous les opérateurs d'affectation associés à un opérateur arithmétique fonctionnent sur le principe suivant : A+=B; · A=A+B;

 VERSION NON - ENREGISTREE


Instructions et blocs

Chaque instruction simple se termine par un ';'. Les instructions composées (ou blocs) sont délimités par les accolades.

 VERSION NON - ENREGISTREE


if-else

... if (condition)   instruction; ...

ou :

... if (condition)   instruction1; else   instruction2; ...
Plusieurs instructions peuvent être imbriquées. Dans ce cas, et par convention, la clause else est toujours associée à l'instruction if la plus proche pour laquelle il n'y a pas encore de clause else.

 VERSION NON - ENREGISTREE


switch

Syntaxe :

...

switch(expression)

{

  case choix1 :

    intruction1;

    instuction2;

    ...

    instructionN;

    break;

  ...

  case choixN :

    intruction1;

    instuction2;

    ...

    instructionN;

    break;

  default :

    intruction1;

    instuction2;

    ...

    instructionN;

}

...

Exemple:

...

char Reponse;

switch(Reponse=getchar())

{

  case 'r' :

  case 'R' :

    printf("rouge");

    break;

  case 'v' :

  case 'V' :

    printf("vert");

    break;

  case 'b' :

  case 'B' :

    printf("bleu");

    break;

}

...

 VERSION NON - ENREGISTREE


while

Syntaxe :

...

while(expression)

  instruction;

...

Effectue instruction tant que expression est vraie. Evidement, il faut faire évoluer expression dans la boucle...

 VERSION NON - ENREGISTREE


do-while

Syntaxe :

...

do

{

  instruction;

}while(expression);

...

Dans ce cas, instruction est effectuée au moins une fois.

 VERSION NON - ENREGISTREE


for

Syntaxe :

...

for(expression1;expression2;expression3)

  instruction;

...

expression1 correspond à la partie initialisation de la boucle, expression2 à la partie test de continuité et expression3 à la partie incrémentation. Une expression peut être composée de plusieurs instructions qui sont alors séparées par des ','.

L'indice de boucle est une variable comme les autres. Il faut la déclarer avant, sa portée n'est pas limitée à la boucle, on peut modifier sa valeur dans la boucle et on peut lire sa valeur après la boucle.

 VERSION NON - ENREGISTREE


break

Met fin à une boucle (for, do...while, while) ou sort de l'instruction switch.

 VERSION NON - ENREGISTREE


Continue

Ne s'applique qu'aux boucles et permet de passer à l'itération suivante. Pour while et do...while, la condition d'arrêt est directement réévaluée. Pour for, la partie incrémentation est effectuée puis la partie test de continuation de répétition est évaluée.

 VERSION NON - ENREGISTREE


goto

Syntaxe :

...

goto Etiquette

...

Etiquette:

...

Même si l'instruction goto existe en C, l'utiliser est une très mauvaise façon de programmer.

 VERSION NON - ENREGISTREE


Fonctions et structures de programmes
Principes fondamentaux

Variables, classes de mémorisation

Programme mono-fichier

auto

extern

static

register

Programme multi-fichier

fonctions

variables

Compléments sur les fonctions externes


Principes fondamentaux

Un problème peut se décomposer en sous-problèmes qui peuvent être traités dans des fonctions séparées. Les différentes fonctions peuvent être placées dans un même fichier ou dans des fichiers différents.

Une fonction est composée de deux parties :

sa déclaration (spécification)

sa définition (corps)

Déclaration :

type nom_de_fonction(arg1, arg2, ..., argn);

type : type de la valeur retournée par la fonction(int par défaut). On utilise le type void si la fonction ne retourne rien.

La liste des arguments peut être vide mais les parenthèses sont alors quand même obligatoires.

Définition :

type nom_de_fonction(type arg1, type arg2, ..., type argn)

{

  instructions;

}

La déclaration suivante existe mais il vaut mieux lui préférer la précédente qui est conseillée par la norme ANSI.

type nom_de_fonction(arg1, arg2, ..., argn)

type arg1 ;

type arg2 ;

...

type argn ;

{

  instructions;

}

En C les paramètres sont toujours en mode IN. On effectue le passage des variables par valeur. L'équivalent du mode OUT de l'Ada n'existe pas. Lors de l'appel d'une fonction, le paramètre réel est copié dans le paramètre formel. La fonction s'exécute sur le paramètre formel (la copie du paramètre réel) et à la fin de l'exécution, la valeur du paramètre formel n'est pas recopiée dans le paramètre réel.

Exemple :

#include <stdio.h>

int Carre(int);

void Afficher(int);

main()

{

  int x, y ;

  printf("Saisir une valeur : ");

  scanf("%d", &x);

  y=Carre(x);

  Afficher(y);

}

int Carre(int Nb)

{

  return Nb*Nb;

}

void Afficher(int Nb)

{

  printf("L'entier vaut : %d\n", Nb) ;

}

Détail des allocations mémoires en pile lors de l'exécution du programme :

(1) Appel de main() :                main      x y	(2) Saisie de x :                main      x y    2
(3) Appel de Carre(x) : Carre      Nb     2    main      x y    2	(4) Fin de Carre(x) :                main      x y    2 4
(5) Appel de Afficher(y) : Afficher      Nb     4    main      x y    2 4
L'appel d'une fonction se fait en spécifiant son nom suivi de la liste des paramètres réels entre parenthèses et séparés par des virgules. L'appel d'une fonction peut constituer l'une des opérandes d'une expression plus complexe. Le nombre et le type de paramètre réels et formels doivent être identiques.

 VERSION NON - ENREGISTREE


Variables, classes de mémorisation

En C, il y a deux façons de caractériser une variable :

par son type

par sa classe de mémorisation

La classe de mémorisation fait référence à la pérennité d'une donnée et à sa portée dans le programme, c'est à dire à la partie du programme dans laquelle la variable est reconnue et peut être utilisée.

 VERSION NON - ENREGISTREE


Programme mono-fichier

Il y a 4 classes de mémorisation :

auto

extern

static

register

Si on veut spécifier la classe d'une variable :

classe type nom;

... Table des matières (suite)

auto

extern

static

register

 VERSION NON - ENREGISTREE


auto

Ces variables sont déclarées au sein d'une fonction. Ce sont des variables locales. Leur portée va de l'endroit où elles ont été déclarées jusqu'à la fin du bloc où elle est déclarée. On peut initialiser une variable de classe auto en même temps qu'on la déclare. Les variables déclarées dans une fonction sont auto par défaut.

 VERSION NON - ENREGISTREE


extern

Ces variables ont une portée qui s'étend du point où elle sont définies jusqu'à la fin du programme. Elle sont souvent appelées variables globales puisqu'elles sont visibles depuis plusieurs fonction. Leur emploi représente un moyen efficace d'échanger des informations entre fonctions sans utiliser le passage par paramètre. Une variable extern peut être initialisée dans sa déclaration mais uniquement par une valeur constante, pas par une expression. Si une variable est déclarée hors de toute fonction, elle est de classe extern par défaut.

 VERSION NON - ENREGISTREE


static

Dans un programme mono fichier, les variables de classe static sont définies dans les fonctions. Elles ont une portée locales mais conservent leur valeur tout au long du programme. Lorsqu'une fonction est appelée une deuxième fois après avoir été appelé puis quittée, ses variables statiques ont conservé leur valeurs. Une variable statique peut être initialisée lors de sa définition.

Exemple :

#include <stdio.h>

void Test();

main()

{

  Test();

  Test();

}

void Test()

{

  int j=0;

  static int i=0;

  i++;

  j++;

  printf("var auto %d - var static %d\n", j, i);

}

Execution :

var auto 1 - var static 1

var auto 1 - var static 2

 VERSION NON - ENREGISTREE


register

En C, les valeurs des variables register sont rangées dans les registres du processeur. Les classes register et automatic sont étroitement liées. Leur portée est la même. La classe register ne peur etre choisie que pour une variable qui aurait été automatic. Seules quelques variables peuvent être de type register (2 ou 3) et en général, ces variables sont de type entier. S'il n'y a pas de registre libre lors de l'execution, les variables register sont alors des variables automatic.

 VERSION NON - ENREGISTREE


Programme multi-fichier

Les fonctions et les variables sont alors définies dans plusieurs fichiers. Il existe des classes de mémorisation aussi bien pour les variables que pour les fonctions.

... Table des matières (suite)

fonctions

variables

 VERSION NON - ENREGISTREE


fonctions

extern : reconnue dans tous les fichiers du programme

static : seulement reconnue dans le fichier ou elle est définie

Par défaut, une fonction est extern. Lorsqu'une fonction est définie dans un fichier et utilisée dans un autre, ce dernier doit contenir une déclaration de fonction dont le rôle est de signifier la présence d'une fonction extern définie ailleurs.

Exemple : La partie de Crapps

But du jeu : on lance à plusieurs reprises une paire de dés. Tant qu'on gagne on joue. Ce jeu peut etre simulé sur un ordinateur par un tirage de nombres aléatoires.

sur 7 ou 11 au premier lancé : GAGNE

sur 4, 5, 6, 8, 9, 10 au premier lancé :on ne sait pas. Il faut relancer le dé jusqu'à obtenir le même total sans jamais obtenir 7 pour gagner. Si on obtient 7 : PERDU

sur 2, 3, 12 au premier lancé : PERDU

On repartira le programme dans deux fichiers :

fic1.c : contient la fonction main() dans laquelle on appelle jouer()

fic2.c : contient la fonction jouer() et quelques autres fonctions appelées par jouer

fic1.c

#include <stdio.h>

#include <ctype.h>

#include <stdlib.h>

#define RACINE 12345

extern void Jouer();

main()

{

  char Reponse;

  srand(RACINE);

  do

  {

    Jouer();

    printf("voulez-vous rejouer ? (O/N)\n");

    scanf("%c", &Reponse);

  }while(toupper(Reponse)=='O');

  system("pause");

}

fic2.c

#include <stdio.h>

#include <stdlib.h>

extern void Jouer();

static int Lancer();

extern void Jouer()

{

  char Rep;

  int Total1, Total2;

  printf("Appuyez sur une touche pour lancer les dés...\n");

  scanf("%c", &Rep);

  Total1=Lancer();

  printf("Vous avez tiré : %d\n", Total1);

  switch(Total1)

  {

    case 7 :

    case 11 : printf("Gagné !!!\n");

              break;

    case 2 :

    case 3 :

    case 12 : printf("Perdu :-((\n");

              break;

    default : do

              {

                printf("Appuyez sur une touche pour relancer les dés...\n");

                scanf("%c", &Rep);

                Total2=Lancer();

                printf("Vous avez tiré : %d\n", Total2);

              }while((Total2 != Total1) && (Total2 != 7));

if(Total2 == Total1)

                printf("Gagné !!!\n");

              else

                printf("Perdu :-(\n");

              break;

  }

}

static int Lancer()

{

  float x1,x2;

  int n1, n2;

  x1=rand()/32768.0;

  x2=rand()/32768.0;

  n1=1+(int)(6*x1);

  n2=1+(int)(6*x2);

  return n1+n2;

}

 VERSION NON - ENREGISTREE


variables

extern : reconnue dans tous les fichiers du programme

static : seulement reconnue dans le fichier ou elle est définie

Dans un programme multi-fichier, les variables extern peuvent être définies dans un fichier et utilisées dans un autre. Lors de la définition d'une variable extern, on peut l'initialiser mais ne peut pas initialiser une variable extern lors de sa (ses) déclaration(s). Le terme extern n'est pas obligatoire pour la définition. Par convention, on met le mot clé extern lors de la déclaration mais pas lors de la définition.

 VERSION NON - ENREGISTREE


Compléments sur les fonctions externes

Le programme source doit contenir les déclaration des fonctions externes des bibliothèques au même titre que les fonctions externes que le programmeur aurait lui-même placer dans d'autres fichiers. Plutôt que d'écrire explicitement en début de fichier les déclarations des fonctions externes, on peut utiliser des fichiers entête. Un fichier entête contient l'ensemble des déclarations des fonctions d'un fichier source. Lorsqu'on utilise dans un autre fichier source plusieurs de ses fonctions, il suffit d'inclure le fichier entête par la directive #include.

 VERSION NON - ENREGISTREE


Pointeurs et Tableaux
Introduction

Passage de paramètres par adresse

Tableau mono-dimentionnel

Opérations sur les pointeurs

Passage de tableau en paramètres

Tableaux de pointeurs - pointeurs de pointeurs

Tableaux multidimensionnels

Gestion de la mémoire

Pointeur vers une fonction


Structures, unions, énumérations
Structures

Types de données personnalisées

Unions

Enumérations


Fichiers de données
Ouverture et fermeture

Fonctions de positionnement

Création d'un fichier de données

E/S de caractères

E/S formatées

E/S de chaînes de caractères

E/S binaires bufferisées


Introduction

Un pointeur est une variable qui contient l'adresse et non la valeur d'une autre variable ou d'un élément de tableau. Dans un ordinateur, les cellules mémoires sont adjacentes. si un entier est stocké sur 4 octets, l'accès à la valeur entière peut se faire via l'adresse du premier des 4 octets. Cette adresse s'obtient grâce à l'opérateur unaire d'adressage &. On peut assigner l'adresse de l'entier Entier à la variable Ptr de type pointeur : Ptr=&Entier

L'opérateur d'adressage agit obligatoirement sur des opérandes associés à une adresse unique tels que des variables ou des éléments de tableaux. L'opérateur d'indirection * ne peut s'appliquer qu'à des opérandes qui sont des pointeurs.

Exemples :

int *pi;	pi est un pointeur vers un entier

*pi;	désigne la valeur pointée par pi. Puisque pi est un pointeur vers un entier, on lira les quatre octets consécutifs en commencent par celui désigné par le pointeur (un pointeur contient une adresse)

pi++;	On incrémente pi de quatre octets (car l'entier est codé sur 4 octets) de façon à ce que le pointeur pointe sur le prochain entier en mémoire.

 VERSION NON - ENREGISTREE


Passage de paramètres par adresse

Ce mode de passage des paramètres correspond au modes de passage out ou in/out de l'ada alors que le mode de passage par valeur correspond au mode in de l'ada.

Exemple :

void Echanger(int x, int y);

main()

{

  int A=5, B=3;

  printf("Valeur des variables avant l'echange :\n A : %d\n B : %d\n", A, B);

  Echange (A, B);

  printf("Valeur des variables après l'echange :\n A : %d\n B : %d\n", A, B);

}

void Echanger(int x, int y)

{

  int temp;

  temp=x;

  x=y;

  y=temp;

}

La fonction échange n'échange rien du tout ! En effet, les variables sont passées par valeur. Lors de l'appel de la fonction Echanger, le programme crée deux variables x et y dans lesquelles il recopie la valeur de A et B. La fonction Echanger travaille avec les variables x et y et à la fin de Echanger, les valeurs de x et y sont perdues.

Pour modifier la valeur de A et B par la fonction Echanger, il faut passer les adresses de ces variables à la fonction. La fonction Echanger travaillera alors directement avec les variables A et B.

Exemple :

void Echanger(int *x, int *y);

main()

{

  int A=5, B=3;

  printf("Valeur des variables avant l'echange :\n A : %d\n B : %d\n", A, B);

  Echange (&A, &B);

  printf("Valeur des variables après l'echange :\n A : %d\n B : %d\n", A, B);

}

void Echanger(int *x, int *y)

{

  int temp;

  temp=*x;

  *x=*y;

  *y=temp;

}

 VERSION NON - ENREGISTREE


Tableau mono-dimentionnel

On déclare un tableau de la façon suivante :

type Nom[taille];

Exemples :

char Chaine[10];

int Tab[50];

Les n éléments du tableau sont numérotés de 0 à n-1.

Chaine[i] désigne l'élément en position i (le i+1ième élément) de Chaine.

Tab[j] désigne l'élément en position j (le j+1ième élément) de Tab.

Une expression avec un tableau et un indice Tab[j] est équivalente à une autre expression écrite avec un pointeur et un déplacement *(Tab+j). De même, &Tab[j] est equivalent à (Tab+j).

Attention : un pointeur est une variable alors qu'un tableau n'en est pas un !!! On ne peut qu'utiliser la valeur d'un tableau.

Il est possible d'affecté un tableau lors de sa déclaration sans spécifier la taille.

Exemples :

char Chaine[]="toto";

char Chaine[]={'t','o','t','o','\0'};

int Tab[]={10,15,20};

char *pChaine="toto";

On peut modifier indépendamment chacun des caractères de la Chaine mais Chaine représente toujours le même espace mémoire. Le pointeur pChaine peut pointer ailleurs mais on ne peut pas modifier le contenu de la chaîne globalement.

 VERSION NON - ENREGISTREE


Opérations sur les pointeurs

Autorisées :

affectation entre deux pointeurs de même types

addition, soustraction d'un pointeur et d'un entier

addition, soustraction entre deux pointeurs de même types

affectation et comparaison à 0 (NULL)

Non-autorisées :

décalage ou masques de bits

addition, soustraction d'un pointeur et d'un float ou double

addition, soustraction entre deux pointeurs de types différents sans effectuer de transtypage

 VERSION NON - ENREGISTREE


Passage de tableau en paramètres

Quand on passe un tableau en argument c'est l'adresse de son premier élément qu'on lui passe. Dans la fonction, cet argument (que l'on appelle paramètre formel) est une variable locale qui est vue comme un pointeur. Ainsi, un tableau passé en paramètre correspond à un passage par adresse.

int StrlenBis (char *Chaine)

{

  int n;

  for (n=0;*Chaine != '\0';Chaine++)

    n++;

  return n;

}

ou

int StrlenBis (char Chaine[])

{

  int n;

  char * pChaine=Chaine;

  for (n=0;*pChaine != '\0';pChaine++)

    n++;

  return n;

}

Dans le premier cas, on peut incrémenter Chaine car c'est un pointeur. Par contre dans le deuxième cas, Chaine est un tableau donc l'adresse du premier élément. On ne peut donc pas le modifier.

Pour un paramètre formel de type tableau, on peut donc avoir indifféremment :

char Chaine[];

char *Chaine;

Avec la deuxième notation, Chaine est un pointeur et peut ainsi être manipulé en conséquence (plus souple donc meilleure). Cependant, lors de l'appel , on trouve toujours

main ()

{

  char Chaine[20];

  int Lg;

  scanf("%s",Chaine);

  Lg=StrlenBis(Chaine);

}

Remarque importante : Dans les fonctions StrlenBis(...), on ne connaît pas la taille allouée pour le tableau. Ici, tout va bien car on manipule une chaîne de caractères et donc le caractère '\0' est automatiquement placé en fin de chaîne lors de la saisie.

#include <stdio.h>

int Somme(int*);

main ()

{

  int Tab[10];

  int Val, i;

  for (i=0;i<10;i++)                     // on saisit les 10 éléments du tableau

  {                                      // de 0 à 9

    printf("\nEntier d'indice %d : ", i);

    scanf("%d", &Tab[i]);                // ou scanf("%d", Tab+i);

  }

  Val = Somme(Tab);

...

}

int Somme (int *Tab)

{

  int Res=0, i;

  for(i=0;i<?????;i++)

  {

    Res+=*(Tab+i);

  }

  return Res;

}

Solution : la taille (10) du tableau est une variable ou constante globale (Sol1) ou bien on passe cette taille comme deuxième paramètre (Sol2).

Sol1:

#include <stdio.h>

#define TAILLE 10                      // attention, pas de ';'

int Somme(int*);

main ()

{

  int Tab[TAILLE];

  int Val;

  for (i=0;i<TAILLE;i++)               // on saisit les 10 éléments du tableau

  {

    printf("\nElt d'indice %d : ", i);

    scanf("%d",&Tab[i]);                 // ou scanf("%d", Tab+i);

  }

  Val = Somme(Tab);

...

}

int Somme (int *Tab)

{

  int Res=0, i;

  for(i=0;i<TAILLE;i++)

  {

    Res+=*(Tab+i);

  }

  return Res;

}

Sol2:

#include <stdio.h>

int Somme(int*, int);

main()

{

  int TAILLE 10;

  int Tab[TAILLE];

  int Val;

  for (i=0;i<TAILLE-1; i++)          // on saisit les 10 éléments du tableau

  {

    printf("\nElt d'indice %d : ", i);

    scanf("%d",&Tab[i]);             // ou scanf("%d", Tab+i);

  }

  Val=Somme(Tab,TAILLE);

...

}

int Somme (int *Tab, int TAILLE)

{

  int Res=0, i;

  for(i=0;i<TAILLE;i++)

  {

    Res+=*(Tab+i);

  }

}

Remarque : scanf("%d",&Tab[i]); // ou scanf("%d", Tab+i);

Ca montre pourquoi on met un '&' dans scanf() pour saisir des entiers, réels et caractères et non pour des chaînes. Le paramètre doit être passé par adresse pour saisir et stocker la valeur. Puisqu'une chaîne de caractères est un tableau, on passe déjà l'adresse.

Remarque : L'instruction return ne pouvant retourner qu'une valeur unique, elle ne peut pas retourner un tableau. Ainsi, lorsque l'on souhaite modifier les valeurs d'un tableau dans une fonction, il y a 2 possibilités :

soit le tableau est déclaré en global (variable externe)

soit le tableau est passé en paramètre.

Remarque : On peut toujours écrire dans un programme *(Tab-2) ou Tab[-2]. Le compilateur ne dira rien même si on lit ou écrit à un endroit non alloué. Par contre, le programme plantera si il y a un problème. Donc, avec les pointeurs (et les tableaux), faire attention à l'endroit où on lit et où on écrit.

void StrCpyBis (char *Dest, char *Sour)	// Version avec tableaux

{

  int i=0;

  while ((Dest[i]=Sour[i])!='\0')

    i++;

}

void StrCpyBis (char *Dest, char *Sour)        // Ver1 avec pointeurs

{

  while ((*Dest=*Sour)!='\0')

  {

    Dest++;

    Sour++;

  }

}

void StrCpyBis (char *Dest, char *Sour)        // Ver2 avec pointeurs

{

  while((*Dest++=*Sour++)!='\0') ;             // Attention au ';'

}

void StrCpyBis (char *Dest, char *Sour)        // Ver3 avec pointeurs

{

  while (*Dest++=*Sour++);

}

 VERSION NON - ENREGISTREE


Tableaux de pointeurs - pointeurs de pointeurs

Un pointeur étant une variable, on peut créer des tableaux de pointeurs.

int *Tab[10];

Tab[i] est un pointeur vers un entier.

 VERSION NON - ENREGISTREE


Tableaux multidimensionnels

Tableau de 10 lignes par 20 colonnes :

Int Tab[10][20];

En C, un tableau à 2 dimensions est un tableau à 1 dimension dont chaque élément est un tableau. Les éléments sont rangés par ligne. Un tableau s'initialise par une liste de valeurs entre accolades. Chaque ligne d'un tableau bidimensionnel s'initialise par une sous-liste.

int Tab[2][3]={

  {1, 2, 3},

  {4, 5, 6}

};

Si on passe un tableau à deux dimensions en paramètre à une fonction, il faut préciser le nombre  de colonnes au niveau de la déclaration (paramètre formel) du paramètre. Le nombre de lignes est sans importance.

main ()

{

  int Tab[2][3];

  ...

  Fonc(Tab);

  ...

}

void Fonc (int Tab[][3]) {   ... }	Ou  void Fonc (int Tab[2][3]) {   ... }	Ou  void Fonc (int (*Tab)[3]) {   ... }

Remarque : avec la version 3, les parenthèses sont obligatoires. Sinon, *Tab[3] est un tableau de 3 pointeurs vers des entiers alors qu'on veut que Tab soit un pointeur sur un tableau de 3 entiers.

En C, lorsqu'on passe des tableaux à plusieurs dimensions, seule la première dimension est libre.

 VERSION NON - ENREGISTREE


Gestion de la mémoire

Tout d'abord, différence entre tableau de caractères à 2 dimensions et tableau de pointeurs vers des caractères.

// Tableau à 2 dimensions : char Nom1[][10]={"janvier",                  "fevrier",                  "mars",                  "avril",                  ... }	// Tableau de pointeurs : char *Nom2[]={"janvier",               "fevrier",               "mars",               "avril",               ... }

Nom1 est un vrai tableau à 2 dimensions de taille 12x10 caractères. Nom1[i][j] est le caractère à la position 10*i+j par rapport à Nom1. 120 octets contigus sont alloués.

Nom2 correspond à 12 pointeurs. Chaque pointeur pointe vers un caractère qui est le premier d'une chaîne. Le gros avantage est que la longueur de chaque chaîne peut être différente. Chaque chaîne a des caractères qui sont contigus. Mais les chaînes ne sont pas contigus entre elles. 12 cellules sont réservées en mémoire pour stocker une adresse chacune + autant de caractères que nécessaire.

Il faut allouer l'espace mémoire pour stocker les entiers. La fonction malloc() permet d'allouer dynamiquement de la mémoire.

void *malloc (size_t n) retourne un pointeur sur un espace de n octets de mémoire non initialisés.

#include <stdio.h>

#include <stdlib.h>

void main ()

{

  int *Ptr, N, i;

  printf("Taille du tableau : ");

  scanf("%d", &N);

  // allocation dynamique

  Ptr=(int *) malloc (N*sizeof(int));

  // par exemple, remplissage du tableau

  for (i=0;i<N;i++)

  {

    printf ("element %d : ", i);

    scanf("%d", &Ptr[i]);	// ou scanf("%d", Ptr+i);

  }

  // affichage du tableau

  for (i=0;i<N;i++)

    printf("Ptr[%d] : %d\n", i, Ptr[i]);

}

 VERSION NON - ENREGISTREE


Pointeur vers une fonction

Exemple :

#include <stdio.h>

float Somme (float A, float B);

float Difference (float C, float D);

void CaDepend (float (*Ptr)(float E, float F));

main()

{

  printf ("\n --premier appel-- ") ;

  CaDepend (Somme) ;

  printf ("\n --deuxieme appel-- ") ;

  CaDepend (Difference) ;

}

float Somme (float A, float B)

{

  return A+B ;

}

float Difference (float C, float D)

{

  return C-D ;

}

void CaDepend (float (*Ptr)(float E, float F))

{

  float  MonReel1 = 3.14,

         MonReel2 = 2.718,

         MonRes ;

  MonRes = (*Ptr) (MonReel1, MonReel2) ;

  printf ("resultat : %f", MonRes) ;

}

Résultat :

--premier appel-- resultat : 5.858

--deuxieme appel-- resultat : 0.422

 VERSION NON - ENREGISTREE


Structures

Les structures permettent de représenter une collection de données liées dont les types peuvent être différents. La syntaxe est la suivante :

struct Nom

{

  Membre1;

  Membre2;

  ...

  MembreN;

};

Une fois définie la composition de la structure, des variables ayant le type de cette structure peuvent être déclarées de la façon suivante :

classe_memo struct Nom variable1, variable2, ..., variableP;

Il est possible de condenser en une seule écriture la déclaration de variables de type structure et la composition de ce type de structure. L'identificateur Nom est alors facultatif :

classe_memo struct Nom

{

  Membre1;

  Membre2;

  ...

  MembreN;

} Variable1, variable2, ..., VariableP ;

Exemples :

struct Point

{

  int X, Y;

} A, B, C, D;

struct Date

{

  int Jour;

  int Mois;

  int Annee;

};

struct Date Anniv ;

On peut initialiser également une variable de type structure au cours de sa déclaration.

struct Date Anniv = {14, 7, 1989};

Remarque : Les membres d'une structure peuvent être de type quelconque, hormis une structure identique (sinon récursif à l'infini!).

struct Personne

{

  char Nom[30];

  char Prenom[30];

  struct Date Naissance;

  struct Personne *Pere, *Mere;

};

En règle générale, les membres d'une structure sont manipulés individuellement grâce aux opérateurs '.' et '->'. Le point se met entre une structure et l'un de ses champs. La flèche se met entre un pointeur de structure et l'un des champs de la structure pointée.

struct Personne Moi, *Toi ;

Moi.Naissance.Jour = 21 ;

Toi = (struct Personne *) malloc (sizeof (struct Personne));

Toi->Date.Mois = 12;

Moi.Pere=Toi;

Moi.Pere->Date.Jour = 8;

Attention : ne pas confondre *(P.X)=*P.X et (*P).X · P->X

Opérations possibles entre structures :

-	affectation et copie

-	passage en argument de fonction

-	faire retourner une structure à une fonction

-	initialiser lors de sa définition

Attention, on ne peut pas comparer deux structures.

Il est également possible de définir et manipuler des tableaux de structures.

struct Point Carre[4];

...

Carre[2].X=100;

...

Les structures sont très prisées pour la manipulation des listes chaînées :

struct Element

{

  char Nom[30];

  int Note;

  struct Element *Ptr_Suivant;

};

On peut très facilement (c'est-à-dire de façon dynamique) mettre à jour, ajouter ou supprimer des éléments à la liste. Mais attention, lorsque l'on souhaite créer un élément de la liste, il faut tout d'abord effectuer une allocation mémoire.

 VERSION NON - ENREGISTREE


Types de données personnalisées

La directive typedef permet de créer des types de données personnels équivalents à des types existants.

typedef int age;

age male, femelle;           // équivaut à int male, femelle;

Cette directive est pratique pour la déclaration de structures. Cela évite de répéter à chaque appel la séquence struct.

Exemple :

typedef struct

{

  int Jour, Mois, Annee ;

} Date;

Date Anniversaire ;

Plutôt que :

Struct Sdate

{

  int Jour, Mois, Annee ;

};

struct SDate Anniversaire;

 VERSION NON - ENREGISTREE


Unions

Une union peut être considérée comme une structure dans laquelle tous les éléments sont alignés sur la même adresse. La syntaxe est la suivante :

union Type_Union

{

  Membre1;

Membre2;

...

MembreN;

};

Des variables de ce type union peuvent ensuite être déclarées de la façon suivante :

classe_memo union type_union Variable1, Variable2, ..., VariableP;

Le symbole type_union est alors facultatif dans ce cas.

union Type_union

{

  char Carcat ;

  int Ent ;

  char Chaine[20] ;

} u1, *u2;

u1.Ent = 5;

u1 est une variable qui est soit un char, soit un int ou soit une chaîne de 20 caractères.

Lorsqu'une variable de type union est déclarée, un espace mémoire suffisant pour stocker le plus grand des éléments est alloué. On accède aux éléments d'une union de la même façon qu'aux éléments d'une structure.

 VERSION NON - ENREGISTREE


Enumérations

Une énumération est une liste symbolique. La syntaxe est la suivante :

enum Identificateur {liste de constantes symboliques};

enum TJour {Lundi, Mardi, Mercredi, ...} ;

enum TJour UnJour ;

ou

enum Jour {Lundi, Mardi, Mercredi, ...} UnJour;

UnJour peut prendre une des constantes de l'énumération.

Remarque : Les éléments de la liste sont considérés comme des constantes entières. Par défaut, elles sont numérotées à partir de 0.

Ex :

UnJour = 0; équivaut à UnJour = Lundi;

 VERSION NON - ENREGISTREE


Ouverture et fermeture

Pour utiliser un fichier de données séquentiel (un flux), il faut dans un premier temps créer une mémoire-tampon dans laquelle sont enregistrées les informations avant d'être transférées de la mémoire centrale au fichier de données. La création de cette mémoire-tampon se fait de la façon suivante :

FILE *Fich ;

où Fich est un pointeur indiquant l'adresse de départ de la mémoire-tampon. Le type structuré FILE est défini dans le fichier d'en-tête stdio.h.

L'ouverture d'un fichier de données s'effectue au moyen de l'instruction suivante :

Fich = fopen (Nom_Du_Fichier, mode) ;

où Nom_Du_Fichier et mode sont 2 chaînes de caractères désignant respectivement le nom du fichier et son mode d'utilisation. Les différents modes sont :

-	"r" : ouverture du fichier en lecture seulement

-	"w" : création et ouverture du fichier en écriture seulement ; si le fichier existe, il est détruit et remplacé par le fichier créé

-	"a" : ouverture d'un fichier existant pour lui ajouter des enregistrements  à la suite des enregistrements existants ; si le fichier n'existe pas, il est créé

-	"r+" : ouverture en lecture et écriture d'un fichier existant

-	"w+" : création d'un fichier et ouverture en lecture et écriture ; si le fichier existe, il est détruit et remplacé par le fichier créé

-	"a+" : ouverture en lecture et écriture d'un fichier auquel on peut ajouter des enregistrements ; si le fichier n'existe pas, il est créé

La valeur retournée par la fonction fopen() est NULL si le fichier n'a pus être ouvert, sinon Fich pointe en début de fichier

La fermeture d'un fichier s'effectue de la façon suivante :

fclose (Fich) ;

 VERSION NON - ENREGISTREE


E/S de caractères

Lecture d'un caractère :

caract = getc (Fich);

Ecriture d'un caractère :

putc (Caract, Fich);

 VERSION NON - ENREGISTREE


E/S de chaînes de caractères

Lecture de n-1 caractères au maximum, qui seront stockés dans une chaîne :

fgets(Chaine, n, Fich);

Ecriture d'une chaine :

fputs(Chaine, Fich);

 VERSION NON - ENREGISTREE


E/S formatées

De nombreux fichiers contiennent des structures composées de données de types différents.

fscanf (Fich, Chaîne_de_Format, arg1, arg2, ..., argn) ;

fprintf (Fich, "Chaîne_de_Format", arg1, arg2, ..., argn) ;

fscanf() et fscanf() retournent le nombre d'éléments lus ou EOF si la fin de fichier est atteinte.

Remarque : Il existe sscanf() et sprintf() qui fonctionnent de la même façon sauf que le premier argument est un nom de chaîne de caractères.

Exemple :

sprintf(Ch2, "bonjour %s, il est %d heures", Ch1, H);

 VERSION NON - ENREGISTREE


E/S binaires bufferisées

Lorsque l'on souhaite créer et manipuler des fichiers constitués de structures ayant la même composition, il est préférable de lire ou écrire les données par blocs entiers sur le fichier. Les fonctions fwrite() et fread() sont habituellement désignées sous le terme fonctions d'E/S binaires. La syntaxe est la suivante :

fwrite (&client, sizeof(enregistrement), n, Fich);

fread (&client, sizeof(enregistrement), n, Fich);

où client est une variable structure de type enregistrement et Fich le pointeur de flux associé au fichier ouvert.

fread() lit n éléments de données du type de client à partir du flot de données pointé par Fich et place le tout à partir de l'adresse &client.

fwrite() écrit n éléments de données du type de client dans le fichier pointé par Fich. Les n éléments sont pris à partir de l'adresse &client.

FILE *Fich ;

struct Personne {

  char Nom[30],

       Prenom[30];

  int Age;

};

struct Personne Moi;

...

Fich = fopen ("essai.dat", "w");

fwrite (&Moi, sizeof (struct Personne), 1, Fich);

fclose (Fich);

...

 VERSION NON - ENREGISTREE


Fonctions de positionnement

fseek (Fich, Déplacement, mode);

où Deplacement est de type long et mode de type int. Cette procédure a pour but de déplacer le pointeur de fichier Fich de Deplacement octets depuis :

-	le début du fichier si mode=0

-	la position courante si mode=1

-	la fin de fichier si mode=2.

-

rewind (Fich); est équivalent à fseek (Fich, 0L, 0);

ftell (Fich); retourne la valeur, qui est de type int, de la position courante du pointeur de fichier Fich par rapport au début du fichier.

Remarque : Lorsque l'on effectue une E/S dans un fichier de données, le pointeur de fichier se déplace d'autant de valeurs lues ou écrites.

 VERSION NON - ENREGISTREE


Autres fonctionnalités
Macros

Pré-processeur C

Fonction main


Macros

L'instruction #define est utilisée comme définition de constantes symboliques.

#define UNE_CTE_ENTIERE 10

#define UNE_CTE_REELLE 0.5

#define UNE_CTE_CHAINE "Bonjour"

On peut aller plus loin en utilisant l'instruction #define pour définir des macros. Une macro contient des expressions, des instructions ou des groupes d'instructions.

#include <stdio.h>

#define Perimetre 3.14*Diametre

main ()

{

  int Diametre;

  printf("Diametre : ");

  scanf("%d", &Diametre);

  printf("Le perimetre est %d.\n", Perimetre);

}

#include <stdio.h>

#define Boucle for(L=1;L<=N;L++)     \

               {                     \

                 printf("\n");       \

                 for (C=1;C<=N;C++)  \

                   printf(" ");      \

               }

main ()

{

  int L, C, N;

  printf("Taille : ");

  scanf("%d", &N);

  Boucle                     // le ';' est inutile

}

On peut même ajouter des arguments dans une définition de macro. La parenthèse ouvrante doit suivre immédiatement le nom de la macro (ni espace, ni tabulation).

#include <stdio.h>

#define Boucle(N) for (L=1;L<=N;L++)    \

                  {                     \

                    printf("\n");       \

                    for (C=1;C<=N;C++)  \

                      printf(" ");      \

                  }

main ()

{

  int L, C, N;

  printf("Taille : ");

  scanf("%d", &N);

  Boucle(N)                // le ';' est inutile

}

Lors de la compilation, les références à une macro sont remplacées par les instructions correspondantes. Donc gain de temps lors de l'exécution du programme mais augmentation de la taille (ce qui les différencie des fonctions).

 VERSION NON - ENREGISTREE


Pré-processeur C

Le pré-processeur C est formé d'un ensemble d'instructions particulières (#define, #include, #if, #elif, #else, #endif, #ifdef, #ifndef, #undef). Ces instructions (appelées directives) sont exécutées au début de la compilation (première passe).

#if defined (VAR1)

  #define VAR2    0

#else

  #define VAR1    1

  #define VAR2    2

#endif

ou

#ifdef VAR1

  #define VAR2    0

#else

  #define VAR1    1

  #define VAR2    2

#endif

Un cas d'utilisation est l'écriture d'un programme qui doit fonctionner sous Unix et sous Windows.

Lorsque le pg doit tourner sous Unix, il suffit d'avoir au début du pg

#define UNIX    1

Lorsque le pg doit tourner sous Windows, il suffit d'avoir au début du pg

#define UNIX    0

Ensuite, dans le programme,

#if UNIX==1

  définitions propres au système Unix

#else

  définitions propres au système Windows

#endif

 VERSION NON - ENREGISTREE


Fonction main

Il est possible de passer des arguments à la fonction main depuis le système d'exploitation, usuellement appelés argc et argv, respectivement de type entier et tableau de chaîne de caractères. La valeur de argc indique le nombre de paramètres passés et les différents paramètres sont stockés dans le tableau défini par argv.

#include <stdio.h>

main (int argc, char *argv[])

{

  int compteur ;

  printf ("argc = %d\n", argc) ;

  for (compteur=0 ; compteur<argc ; ++compteur)

    printf ("argv[%d] = %s\n", compteur, argv[compteur]) ;

}

Si ce programme s'appelle exemple et que l'on tape la ligne de commandes suivante :

> exemple bleu blanc rouge

le programme affichera :

argc = 4

argv[0] = exemple

argv[1] = bleu

argv[2] = blanc

argv[3] = rouge

 VERSION NON - ENREGISTREE


Création d'un fichier de données

Il y a 4 classes de fichier de données :

      E/S de caractères

      E/S formatées

      E/S de chaînes de caractères

      E/S binaires bufferisées

... Table des matières (suite)

E/S de caractères

E/S formatées

E/S de chaînes de caractères

E/S binaires bufferisées

 VERSION NON - ENREGISTREE


E/S de caractères

Lecture d'un caractère :

caract = getc (Fich);

Ecriture d'un caractère :

putc (Caract, Fich);

 VERSION NON - ENREGISTREE


E/S formatées

De nombreux fichiers contiennent des structures composées de données de types différents.

fscanf (Fich, Chaîne_de_Format, arg1, arg2, ..., argn) ;

fprintf (Fich, "Chaîne_de_Format", arg1, arg2, ..., argn) ;

fscanf() et fscanf() retournent le nombre d'éléments lus ou EOF si la fin de fichier est atteinte.

Remarque : Il existe sscanf() et sprintf() qui fonctionnent de la même façon sauf que le premier argument est un nom de chaîne de caractères.

Exemple :

sprintf(Ch2, "bonjour %s, il est %d heures", Ch1, H);

 VERSION NON - ENREGISTREE


E/S de chaînes de caractères

Lecture de n-1 caractères au maximum, qui seront stockés dans une chaîne :

fgets(Chaine, n, Fich);

Ecriture d'une chaine :

fputs(Chaine, Fich);

 VERSION NON - ENREGISTREE


E/S binaires bufferisées

Lorsque l'on souhaite créer et manipuler des fichiers constitués de structures ayant la même composition, il est préférable de lire ou écrire les données par blocs entiers sur le fichier. Les fonctions fwrite() et fread() sont habituellement désignées sous le terme fonctions d'E/S binaires. La syntaxe est la suivante :

fwrite (&client, sizeof(enregistrement), n, Fich);

fread (&client, sizeof(enregistrement), n, Fich);

où client est une variable structure de type enregistrement et Fich le pointeur de flux associé au fichier ouvert.

fread() lit n éléments de données du type de client à partir du flot de données pointé par Fich et place le tout à partir de l'adresse &client.

fwrite() écrit n éléments de données du type de client dans le fichier pointé par Fich. Les n éléments sont pris à partir de l'adresse &client.

FILE *Fich ;

struct Personne {

  char Nom[30],

       Prenom[30];

  int Age;

};

struct Personne Moi;

...

Fich = fopen ("essai.dat", "w");

fwrite (&Moi, sizeof (struct Personne), 1, Fich);

fclose (Fich);

...

 VERSION NON - ENREGISTREE


Cours C++
Nombre de pages : 	72

Date de création : 	17/04/2004 14:32:38

Mise à jour du : 	17/04/2004 19:29:54

Créé à l'aide de : 	ArchiDoc > http://welcome.to/archidoc

Sous licence : 	Fin d'évaluation dans 30 jour(s)