Un programme en C est composé de différents blocs, chacun d'eux ayant un rôle dans la compilation, dans l’édition des liens ou dans l'exécution. Certains de ces blocs sont facultatifs .
· directives de compilation ,
· définition des types ,
· déclaration des prototypes des fonctions ,
· déclarations des variables ,
· définition des fonctions.
Une fonction obligatoire que vous retrouverez dans un programme C est la fonction main() qui est le corps principal de votre programme. C'est cette fonction qui est appelé lors de l'exécution de votre programme avec d'éventuels passages de paramètres possibles.
Pour pouvoir insérer des commentaires dans votre programme, la syntaxe suivante est utilisée:
|
début du commentaire |
commentaire |
fin du commentaire |
|
/* |
ceci est un commentaire |
/* |
Un commentaire peut s'étaler sur plusieurs lignes sans devoir nécessairement fermer et rouvrir le commentaire à la ligne suivante.
Les variables permettent le stockage de données modifiables lors de l'exécution de votre programme.
Toute utilisation d'une de ces variables n'est possible que si elle est déclarée précédemment. En C, les déclarations peuvent s'effectuer avant la fonction min() ou au début de la définition des fonctions avant toute ligne de code.
Il va de soit que pour permettre une lisibilité correcte du programme, les déclarations avant le main() doivent s'effectuer en bloc.
Permanence classe de stockage modifieur type identificateur = valeur
à La permanence permet de définir si cette variable sera ou non sujette à des modifications dans le programme ou autrement dit si c'est une constante. Elle peut prendre deux valeurs, à savoir:
· constante
· volatile (par défaut)
à La classe de stockage permet d'identifier l'endroit ou est stockée la variable et en découlant si cette variable est visible dans l’entièreté du programme. On retrouve les valeurs suivantes possibles:
· auto
· static
· extern
· register
à Les modifieurs permettent d'agir sur le type de donnée utilisée. Ils sont au nombre de quatre:
· unsigned (non signé)
· signed (signé)
· long
· short
à Les types de données utilisés en C peuvent être classés en quatre catégories:
· les variables simples
· les pointeurs (adresses)
· les tableaux
· les types définis par l'utilisateur
Pour les trois premières catégories, on retrouve les types de base prévus en C:
· char (caractère)
· int (entier)
· float (flottants)
· double (flottants en double précision)
· void (pas de type)
Pour la dernière catégorie, on retrouve les types définissables suivants:
· struct (type structuré)
· union (type union)
· enum (type énuméré)
· typedef
à L'identificateur est le nom de la variable vous permettant d'y accéder dans votre programme.
à La valeur vous permet d'initialiser la variable avant toute utilisation. Il va de soit que la valeur assignée doit être conforme au type de la variable utilisée
Une variable de type char permet de stocker une donnée sous forme d'un caractère codé en ASCII sur 8 bits.
Le type int permet le stockage de donnée sous forme entière sans partie décimale. Ces variables sont stockées dans 2 ou 4 octets et dépendent généralement de la taille du bus de données du microprocesseur ou de ses registres internes.
La norme ANSI prévoit pour les entiers une taille de 2 octets.
Le type float permet le stockage de variables sous forme de réels en virgule flottante positifs ou négatifs. Ces variables sont stockées dans 4 octets et permettent de représenter des nombres entre 3.4e38 et 3.4e-38 avec une précision de 7 chiffres.
Comme son nom l'indique, le type float permet une précision double par rapport au type float. Les variables seront stockées sur 8 octets et permettront de représenter des nombres absolus compris entre 1.7e308 et 1.7e-308 avec une précision de 15 chiffres.
On le rencontre principalement dans les fonctions pour préciser que ces fonctions ne retournent aucune valeur ou qu'elles ne reçoivent aucun paramètre.
Dans certains cas, on peut définir des pointeurs de type void qui peuvent alors pointer vers tous les types de variables connus; on appelle alors ces pointeurs des pointeurs génériques.
Ces modifieurs permettent de définir si on travaille en mode signé ou en mode non signé avec les variables de type int ou char. Il faut savoir qu'un nombre signé réserve son bit de point fort pour coder le signe et qu'un nombre non signé ne peut que représenter des nombres positifs; le bit de poids fort est utilisé pour coder le nombre.
|
Type |
valeur minimale et maximale |
|
signed int |
-32768 et 32767 (si 2 octets) |
|
unsigned int |
0 et 65535 |
|
signed char |
-128 et +127 |
|
unsigned char |
0 et 255 |
Les modifieurs short et long permettent de forcer le nombre d'octets sur lesquels les variables de type int char et float sont codées.
|
Type |
nombre d'octets pour le stockage |
|
short int |
2 octets |
|
long int |
4 octets |
|
short char |
codé sur 7bits en mode non signé |
|
long float |
équivalent au double et codé sur 8 octets |
Les modifieurs signed, unsigned, short et long peuvent être combinés. On peut par exemple retrouvé la syntaxe suivante:
unsigned long int à savoir un entier codé sur 8 octets en mode non signé et permettant un intervalle de valeurs compris entre 0 et 4 294 967 295.
On appelle visibilité des variables, la possibilité d'y faire référence dans tout le code ou uniquement dans une partie du code.
Une variable déclarée dans une fonction n'est accessible que dans le corps de la fonction uniquement. Ces variables sont stockées dans le segment de pile; elles sont appelées variables locales.
Une variable déclarée avant la fonction principale appelée main() est visible par tout le programme. Ces variables sont stockées dans le segment de données; elles sont appelées variables globales.
Une variable locale n'est pas initialisée à une valeur déterminée tandis que la variable globale est initialisée à 0.
La classe de stockage auto est la classe par défaut des variables locales aux fonctions. Une variable globale ne peut pas être définie en auto. Ce mot clef n'aura donc comme utilité que de rappeler la classe de la variable au programmeur.
Cette classe de stockage ne sera donc que très peu utilisée.
C'est la classe de stockage par défaut des variables globales. Lorsque cette classe est utilisée, la variable affectée n'est visible uniquement que par le fichier source ou elle est définie.
Cette classe peut être utilisée pour une variable locale.
Il est intéressant lorsqu'une application contient plusieurs sources de pouvoir déclarer une variable dans une des sources et de pouvoir l'utiliser dans une autre source en conservant le même emplacement mémoire. C'est possible en utilisant la classe extern. Le compilateur qui rencontre cette classe n'alloue pas d'emplacement mémoire pour la variable mais c'est l'édition de lien qui déclenchera la recherche de la déclaration de la variable. Si aucune déclaration n'est trouvée, l'éditeur de lien génère une erreur.
Lorsque cette classe de stockage est utilisée, le compilateur ne réserve pas d'emplacement mémoire mais utilise un registre interne du microprocesseur s'il y en a de disponible. Il est clair que dans des boucles par exemple, cette possibilité permet une vitesse d’exécution du programme plus rapide.
Certains compilateurs permettant d'optimiser la vitesse d’exécution des programmes utilisent cet artifice sans que le programmeur doivent utiliser ce mot clef.
Comme la taille des registres est limitée, la possibilité d'utilisation des registres n'est possible que pour les variables et pointeurs ayant une taille inférieure ou égale à la taille des registres.
Il est bon à ce sujet de vérifier les possibilités du compilateur utilisé.
Les variables de type structuré permettent de regrouper sous une même entité des données de type différent. Ce type est utile lorsque l'on veut gérer des bases de données, des index, des catalogues ou des chaînes liées.
· déclaration du type: struct identificateur {
type 1 champ 1;
type 2 champ 2;
type 3 champ 3;
.
.
type N champ N;
};
· déclaration de la variable de type structuré:
struct identificateur ident_variable;
On peut également retrouver deux autres syntaxes moins usitées:
1. On déclare les variables en même temps que la structure en faisant suivre l'accolade fermée des noms des variables.
struct identificateur {
type 1 champ 1;
type 2 champ 2;
type 3 champ 3;
.
.
type N champ N;
}variable1, variable 2...
2. Si l'intérêt d'utiliser un identificateur de type ne se fait pas sentir car on ne désire plus par la suite déclarer de variables de ce type, on peut omettre celui ci. On retrouvera alors la structure suivante:
struct {
type 1 champ1;
type2 champ2;
type3 champ 3;
.
.
type N champ N;
}variable1, variable2...;
La syntaxe de l'accès dépend si l'on travail avec un pointeur ou une variable du type de la structure. Voici les deux cas possibles:
· variable.nom_champ
· pointeur®nom_champ
Lorsqu'on travaille avec des variables logiques, il serait intéressant pour économiser le nombre de variables utilisées, de pouvoir associer une variable logique par bit dans un octet. En C, ceci est possible en utilisant les structures de champs de bits. La syntaxe est la suivante:
struct identificateur
{
type1 champ1:longueur;
type2 champ2:longueur;
.
.
typeN champN:longueur;
}
longueur représente le nombre de bits associés aux champs correspondants.
Remarque: on peut mélanger une structure classique avec des champs de bits.
Le type union permet à plusieurs variables regroupées sous une même entité mais de types différents d'occuper le même emplacement mémoire. La taille mémoire occupée par cette entité correspond, suivant la norme ANSI, à la taille de la plus grande variable (en adresse et non en valeur). Certains compilateurs ne respectant pas la norme ANSI reserve la taille de la première variable et il donc souhaité alors de placer en tête de l'entité la variable la plus grande.
Pour la déclaration d'un type union, nous utiliserons la syntaxe suivante:
union identificateur{
type1 champ1;
type2 champ2;
.
.
typeN champN;
};
Pour la déclaration d'une variable du type union identificateur, la syntaxe est la suivante: union identificateur variable;
Les autres syntaxes possibles peuvent être obtenues en se référant aux informations reprise dans les structures.
L'accès aux champs d'une union se fait identiquement à ceux d'une structure à savoir qu'il faut distinguer l'accès à partir d'une variable ou d'un pointeur.
· variable.nom_champ;
· pointeur®nom_champ;
Le type énuméré reprend sous une même entité une liste ordonnée d'identificateurs qui sont initialisés lors de la création d'une variable de ce type.
· La déclaration du type: enum identificateur{
ident1,
ident2,
ident3,
.
.
identN
}
· La déclaration d'une variable du type identificateur:
enum identificateur variable1,variable2;
D'autres syntaxes sont possibles et pour ce, il faut se référer aux paragraphe traitant des structures.
Sauf indication contraire du programmeur, l'initialisation des identN se fait en commençant par le premier dans la liste et en lui assignant la valeur entière 0. Les ident suivants se voient attribuer la valeur juste supérieure à la valeur de l'ident précédent.
Le programmeur peut ne pas se plier à cette règle et forcer une valeur de départ à cette incrémentation. Il suffit pour cela de placer la valeur à droite de l'ident juste avant la virgule.
Il est à remarquer que plusieurs ident peuvent avoir la même valeur mais pas le même nom.
Dans les syntaxes précédents de la structure de l'union et de l'énumération, on remarque que la déclaration d'une variable de ce type nécessite l'emploi du mot clef correspondant à savoir struct, union ou enum.
Il serait intéressant de pouvoir définir pour ces cas un nouveau type. C'est possible en utilisant le mot clef typedef à la différence que ce mot clef n'est pas uniquement réservé aux types précités.
typedef type identificateur.
typedef unsigned int UINT;
UINT a; /*ce code permet de déclarer une variable de type unsigned int*/
Le concept de pointeur en C est très puissants et ceux ci sont très utilisés en programmation. En assembleur, on peut comparer les pointeurs à de l'adressage indirecte. Les pointeurs sont destinés à contenir non pas une valeur comme pour les variables classiques mais une adresse qui pointe vers une variable contenant une valeur.
Les pointeurs vont être utilisés dans les tableaux, les variables simples, les fonctions et les types particuliers précités.
Il est à remarquer que la puissance de programmation que représentent ces pointeurs peut se retourner contre le mauvais programmeur en bloquant régulièrement son application .
On retrouve des pointeurs pour tous les types de variables existantes.
type * identificateur;
On peut don déclarer un pointeur de type char de la façon suivante: char * pointeur;
Avant de pouvoir travailler avec les pointeurs, il faut obligatoirement les initialiser. Ceci peut se faire en associant au pointeur l'adresse d'une variable déjà déclarer. Vous devez pour cela utiliser l'opérateur unaire &. Vous obtiendrez alors la syntaxe suivante:
int a, * b;
b = &a; /* on associe au pointeur la valeur de l'adresse de a */
L'initialisation des pointeurs peut aussi se faire par l'allocation dynamique qui renvoit alors une adresse vers le premier élément de l'espace mémoire réservé. L'allocation dynamique permet d’allouer momentanément dans un programme assez bien de mémoire qu'on peut désallouer ensuite lorsque l'on en a plus besoin. Je vous renvoie pour ce aux fonctions de type alloc, malloc, réalloc...
La déclaration d'un pointeur réserve un emplacement mémoire pour contenir une adresse mémoire. On peut dès lors se poser la question de savoir quelle est la place occupée par un tel pointeur.
La place occupée ne dépend pas du type de la variable mais dépend de l'emplacement où la mémoire va être réservée. On retrouve suivant l'emplacement mémoire utilisé les pointeurs near, les pointeurs far et les pointeurs huge. Pour bien comprendre ces différentes catégories, il est indispensable de connaître la structure mémoire interne des microprocesseurs de la famille 80x86. Une adresse physique est décomposée en deux parties que l'on appelle segment et offset. Le calcul complet de l'adresse physique s'obtient par la formule suivante:
adresse physique = (segment *16) + offset. L'adresse physique est codée sur 20 bits et permet d'accéder à 1Mo de mémoire.
La partie offset est codée sur 16 bits et permet d'accéder à des segments dont la taille n’excède pas 64Koctets.
Lorsque vous exécutez un programme, l'ensemble des données, du code et de la pile ne se mettent pas n'importe comment. On va retrouver un segment pour le code appelé Code Segment (CS), un segment pour les données appelé Data Segment (DS) et un segment pour la pile que l'on appelle Stack Segment (SS).
Dans certains cas de configuration, certains de ces segments peuvent fusionner avec d'autres et c'est le cas de la pile qui est par défaut confondue avec les données.
Le microprocesseur possède des registres internes permettant de mémoriser notamment l'adresse du segment de données, l'adresse du segment de pile et l'adresse du segment de code avec leur pointeur associé.
Si on travaille avec un seul segment de données, il n'est pas nécessaire que les pointeurs des variables contiennent à la fois la valeur du segment et celle de l'offset. Cette dernière suffit car la partie segment est toujours la même et ne doit pas être changée. C'est la raison pour laquelle on retrouve des pointeurs sur 16 bits codant uniquement l'offset; on parle alors de pointeur NEAR.
· Les pointeurs NEAR sont des pointeurs codés sur 16 bits contenant uniquement l'offset.
· Les pointeurs FAR sont nécessaire si on travaille sur plusieurs segments de données; ils doivent contenir la partie segment et la partie offset. Ces pointeurs seront codés sur 32 bits (16 pour le segment et 16 pour l'offset). On parle alors de pointeurs long ou pointeur FAR. Il est à remarquer que dans l'adressage des tableaux, ces pointeurs ne permettent pas des tailles supérieures à 64K.
· Les pointeurs HUGE sont eux aussi codés sur 32 bits mais permettent de travailler sur des tableaux dont la taille est comprise entre 64K et 1M.
Chaques codes et données de programme sont stockées dans des segments. Le modèle mémoire va déterminer l'organisation des segments. On retrouve chez Microsoft les modèles tiny, small, médium, compact, large et huge.
|
Modèles mémoires |
le(s) segment(s) de codes |
le(s) segment(s) de données |
|
Tiny |
Un segment pour le code et les données (<64K) |
Un segment pour le code et les données (<64K) |
|
Small |
Un segment (<64K) |
Un segment (<64K) |
|
Médium |
Un segment de code par module (pas de limite) |
Un segment (< 64K) |
|
Compact |
Un segment (<64K) |
Plusieurs segments mais les tableaux doivent être < 64K |
|
Large |
Un segment de code par module (pas de limite) |
Plusieurs segments mais les tableaux doivent être < 64K |
|
Huge |
Un segment de code par module (pas de limite) |
Plusieurs segments de données avec des tableaux qui peuvent être > 64K |
Remarques:
· pour construire un fichier portant l'extension .COM, il faut compiler votre programme avec l'option TINY.
· pour les modèles mémoires TINY, SMALL et MEDIUM, tous les pointeurs sont considérés de type NEAR. Pour les modèles COMPACT ou LARGE, les pointeurs sont de type FAR et pour le modèle HUGE de type HUGE.
· l'utilisation de pointeurs NEAR est beaucoup plus rapide que celle des pointeurs far et huge car toute lecture ou modification ou calcul sur un pointeurs de type near ne demande que de considérer l'offset. Les pointeurs HUGE sont encore plus pénalisant du fait que l'on peut passer pour un même tableau d'un segment à l'autre.
Le C permet un certain nombre d'opérations arithmétiques sur les pointeurs. Ceci est particulièrement intéressant dans la gestion des tableaux et dans les passages de paramètres et retour de valeur pour les fonctions.
Les opérations d’addition et de soustraction sur les pointeurs sont possibles pour autant qu'elles soient effectuées avec des entiers. On pourra donc retrouver les syntaxes suivantes:
float * fl_var, tmp, k[100]; /* réservation d'un tableau de 100 flottants*/
int i=10, j=2;
fl_var = &k;
tmp = *( fl_var + 1 ); /* le compilateur sait qu'une variable de type float est codée sur 4 octets et de ce fait, il va donc pouvoir calculer l'adresse final qui vaut fl_var + 1 * 4 octets. */
tmp = *( fl_var + j ); /* Comme j est un entier, le calcul de l'adresse est possible par le compilateur */
tmp = *( fl_var + ( i*j ) ); /* Comme ( i*j ) donne comme résultat un entier, la syntaxe est possible */
fl_var = fl_var +1; /* incrémentation du pointeur de 1 * 4 octets*/
fl_var ++; /* même action que pour la ligne précédente*/
fl_var=fl_var - 1; /* décrémentation du pointeur de 1 * 4 octets*/
fl_var --; /* même action que pour la ligne précédente*/
Le tableau en lui même ne représente pas un type en particulier mais il permet de regrouper plusieurs variables ou pointeurs d'un même type dans un ensemble. En C, les tableaux peuvent être multidimensionnels permettant ainsi des tableaux à N dimensions.
déclaration d'un tableau : déclaration identique à une variable simple excepté que l'identificateur est suivi ces opérateurs [ et ].
...type identificateur [indice] pour un tableau à une dimension,
...type identificateur [indice1][indice2] pour un tableau à deux dimensions,
...type identificateur [indice1][indice2]...[indiceN] pour un tableau à N dimensions.
L'accès à un des éléments du tableau se fait en précisant les indices de l'élément recherché entre les mêmes opérateurs.
Exemple:
char tab[10]; /* permet de réserver un tableau à 1 dimension avec 10 éléments*/
char temp;
temp=tab[0];/*le premier élément du tableau est recopié dans la variable temp*/
char tab[5][2]; /* permet de réserver un tableau à deux dimensions contenant ( 5*2 ) variables */;
Remarque: les constantes entre opérateurs peuvent être remplacées par des variables de type entier.
int i=1,j=2;
char tab[5][6], temp;
temp=tab[i][j];/*l'élément [1][2] du tableau est recopié dans la variable temp*/
· Pour un tableau à une dimension:
int tab[3]={2,7,5};/* initialisation complète du tableau*/
int tab[4]={1,2}; /* initialisation des deux premiers éléments du tableau*/
· Pour un tableau à deux dimensions:
int tab[3][3]={ {1,5,6},{1,3},{5,8,9}};/*l'initialisation de l'élément tab[1][3] n'a pas été effectuée*/
Remarque: lorsque un tableau est initialisé, sa dimension peut être omise et c'est le compilateur qui réserve la taille du tableau en fonction du nombre d'éléments initialisés.
int tab[ ] = {2,5,8,9}; /* le compilateur va réservé un tableau de 4 entiers*/
Prenons comme exemple un tableau de caractères tel que char tab[10].
char tab[]={'I','N','F','O','3'};
En supposant que l'adresse du premier élément du tableau soit 100, nous aurons la configuration suivante:
|
100 |
101 |
102 |
103 |
104 |
|
'I' |
'N' |
'F' |
'O' |
'3' |
|
indice 0 |
indice 1 |
indice2 |
indice 3 |
indice 4 |
Les octets réservés sont contigus en mémoire et respectent l'ordre des indices.
Prenons comme exemple un tableau de caractères tel que char tab[2][2].
char tab[2][2]={ {'a','b'},{'c','d'}};
En supposant due l'adresse du premier élément du tableau soit 100, nous aurons la configuration suivante:
|
100 |
101 |
102 |
103 |
|
'a' |
'b' |
'c' |
'd' |
|
indice 0,0 |
indice 0,1 |
indice 1,0 |
indice 1,1 |
Pour accéder à un élément du tableau en connaissant son adresse, vous devez prendre l'adresse de base du premier élément auquel vous ajoutez d'une part la taille du type multipliée par le premier indice et la valeur maximum du second indice et d'autre part le second indice. Si on veut connaître l'adresse de l'élément [1][1], on aura 100+( 1octet * 1 * 1) + 1 = 103.
Si l'on prend le tableau d'entiers int tab[4][4] et que l'on veut accéder à l'élément d'indices 3,2 on pratiquera de la sorte:
en supposant que l'adresse du premier élément soit 100, on aura 100 + (2 octet * 3 * 3) + 2 = 120.
Lorsqu'on travail avec les tableaux, il est parfois utile d'utiliser les pointeurs pour accéder à des éléments en particulier sans devoir gérer les indices. En premier lieu, il faut savoir que le nom du tableau est lui même un pointeur contenant l'adresse du premier élément.
int *temp;
int tmp;
int *tempo;
int tab[5]; /* réservation d'un tableau de 5 entiers*/
temp=tab; /* on initialise le pointeur temp avec l'adresse du premier élément du tableau tab*/
tempo=&tab[3]; /* on initialise le pointeur tempo avec l'adresse de l'élément d'indice 3*/
tmp=*( tab + 2); /* cette syntaxe revient à initialiser la variable tmp avec l'élément d'indice 2 du tableau tab*/
Il est inutile dans le dernier cas de faire entrer en ligne de compte la taille du type car c'est le compilateur qui s'en charge (voir calcul sur les pointeurs).
On peut également retrouver la syntaxe suivante :
tmp=*(&tab[2]+1)
/* on initialise la variable tmp avec l'élément pointé par l'adresse &tab[2] + 1 * la taille d'un entier.
Pour réserver un tableau de pointeurs pour un type donné, il faut utiliser la syntaxe suivante:
type * identificateur[dimension];
type est un type quelconque du langage C, prédéfini ou construit par l'utilisateur sous forme d'un type structuré, énuméré et union.
Exemple:
char *tab[10]; /* réservation d'un tableau de 10 pointeurs de type char*/
int a,b;
tab[0]=&a;/*initialisation du pointeur tab[0] avec l'adresse de a*/
b=*tab[5]; /* initialisation de la variable b avec l'élément pointé par le pointeur tab[5]*/
La programmation ne consiste pas à réaliser des programmes monolithiques dans lesquels toute modification ou toute adaptation est très difficile. Il est clair pour tout bon programmeur que les avantages offerts par la modularité sont multiples.
La modularité consiste à réaliser des modules sous formes de fonctions ou procédures qui soient indépendants les uns des autres. Il facile de comprendre qu'une modification d'une de ces fonctions ne doit pas nous obliger à des modifications dans d'autres parties du programme.
Le fait que les fonctions aient souvent leurs sources dans des modules séparés du corps du programme principal, la compilation est plus rapide car uniquement effectuée pour les codes modifiés.
Le gain en taille du code final est également appréciable car l'utilisation de fonctions évite la répétition de parties équivalentes.
Des fonctions souvent utilisées par le programmeur peuvent donner lieu à la création de librairies personnelles qui contribuent à une diminution du codage et peuvent amener une homogénéisation dans le développement d'applications semblables.
La modularité est un critère essentiel dans l'analyse d'un projet: un bon découpage en fonctions et sous fonctions est essentiel. Ce découpage commencera par la création des fonctions de bas niveau avec leur test pour ensuite remonter vers les fonctions de haut niveau intégrées à leurs tours dans la fonction principale du programme qui est la fonction main().Dans l'objectif de pouvoir aborder le C++ et la programmation sous windows, il est impératif que l'analyse modulaire d'un projet soit prédominant dans votre travail.
Le prototype de la fonction est déclarée avant la fonction principale main(). Elle a pour objectif de définir le type de la valeur retournée par la fonction ainsi que les types et les noms des paramètres de la fonction.
Le but de cette déclaration est double:
1. elle va permettre au compilateur de pouvoir vérifier les appels à ces fonctions dans le code de votre programme et de pouvoir détecter tout problème lors de passages de paramètres ou les retour de valeurs.
2. elle permet au programmeur de pouvoir rapidement retrouver une liste de toutes les fonctions définies et d'en retrouver les paramètres dont les noms sont souvent évocateurs ainsi que les types .
La déclaration est facultative en C si la définition de la fonction se trouve dans la même source mais je la conseille fortement pour les raisons données précédemment.
Les syntaxes en C sont les suivantes:
· identificateur();
· type identificateur();
· type identificateur(paramètre1,paramètre2,...);
· type identificateur(type1,type2,...); /*norme ANSI*/
· type identificateur(type1 paramètre1, type2 paramètre 2,...);/*norme ANSI*/
Je conseille d'utiliser la dernière syntaxe.
Exemple:
int fonction (int min, int max);
On entend par définitions des fonctions, la définition des codes faisant partie de cette fonction.
On va retrouver la syntaxe suivante pour les définitions:
type identificateur ( type1 paramètre1, type2 paramètre 2,...)
{ /*début du corps de la fonction*/
code;
} /* fin du corps de la fonction*/
On peut retrouver également la syntaxe suivante qui est moins utilisée:
type identificateur (paramètre1,paramètre 2,...)
type1 paramètre1;
type2 paramètre2;
.
.
.
{ /*début du corps de la fonction*/
code;
} /* fin du corps de la fonction*/
Les paramètres formels d'une fonction sont créés sur la pile lors de l'appel de la fonction et détruits à la fin de son exécution.
On retrouve deux façon de passer des paramètres:
· par valeur
· par pointeurs.
Par valeur, les le paramètre formel reçoit une copie de la valeur contenue dans la variable transmise lors de l'appel de la fonction. Cela signifie que toute modification du paramètre formel dans le corps de la fonction n'est pas répercutée sur la valeur de la variable transmise. Cette technique est intéressante car elle permet une encapsulation de la fonction sans débordement possible.
Comme le rôle de la fonction peut être de fournir des informations aux programme appelant, il est possible de retourner une valeur ou un pointeur par la fonction. Il est clair que cette méthode ne permet le retour que d'un seul pointeur ou d'une seule valeur. Pour étendre les possibilités, le passage de paramètres par pointeurs est autorisé.
· Exemple de passage par valeur:
/* déclaration des prototypes*/
void main(void);
void fonction(int a, int b);
/* coprs principal de la fonction*/
void main(void)
{
int x=1,y=2;
fonction (x,y); /* appel de la fonction*/
printf("valeurs initiales: %d %d \n",x,y);
}
/* définition de la fonction*/
void fonction(int a,int b)
{
printf("valeurs de a et b dans la fonction:%d %d\n",a,b);
a=10;
b=20;
printf("valeurs de a et b modifiés: %d %d\n",a,b);
}
L'exécution du programme permettra l'affichage suivant:
valeurs de a et b dans la fonction: 1 2
valeurs de a et b modifiés: 10 20
valeurs initiales: 1 2
· Exemple de passage par pointeurs:
/* déclaration des prototypes*/
void main(void);
void fonction(int a, int *b);
/* coprs principal de la fonction*/
void main(void)
{
int x=1,y=2;
fonction (x,&y); /* appel de la fonction*/
printf("valeurs initiales: %d %d \n",x,y);
}
/* définition de la fonction*/
void fonction(int a,int *b)
{
printf("valeurs de a et b dans la fonction:%d %d\n",a,*b);
a=10;
*b=20; /* comme b a la même adresse que y, toute modification de *b provoque une modification de y*/
printf("valeurs de a et b modifiés: %d %d\n",a,*b);
}
L'exécution du programme permettra l'affichage suivant:
valeurs de a et b dans la fonction: 1 2
valeurs de a et b modifiés: 10 20
valeurs initiales: 1 20
Remarque: la valeur de a n'a pas été modifiée car le passage de paramètre s'est fait par valeur tandis que la variable y a eu son contenu modifié car le passage de paramètre s'est fait par pointeur.
Cette technique permet de retourner une valeur ou un pointeur d'un type quelconque de la fonction appelée vers la fonction appelante. Dans le coprs de la fonction appelée, le retour est possible par le mot clef RETURN en utilisant les syntaxes suivantes:
return (valeur);
return (identificateur);
return (&identificateur);
Le mot clef RETURN peut être utilisé plusieurs fois dans une fonction pour retourner des valeurs différentes mais la fonction se termine lors de l'exécution du premier return rencontré.
Exemple de retour par valeur et par pointeur:
/* déclaration des prototypes*/
void main(void);
int maximum(int a, int b);
int *minimum(int a, int b);
/* coprs principal de la fonction*/
void main(void)
{
int x=1,y=2,*tmp;
printf( "valeur maximum:%d\n",maximum (x,y));
tmp=minimum(x,y);
printf("valeur minimum: %d\n,*tmp);
}
/* définition de la fonction*/
int maximum (int a,int b) /* fonction avec retour par valeur*/
{
if (a > b) return a;
else return b;
}
int *minimum(int a,intb) /* fonction avec retour par pointeur*/
{
if (a<b) return(&a);
else return(&b);
}
L'exécution du programme permettra l'affichage suivant:
valeur maximum: 2
valeur minimum: 1
Comme les variables globales sont visibles dans tout le source, il est clair que l'on peut les utiliser pour passer des paramètres dans les fonctions et vis versa. Naturellement, un programme utilisant cette technique serait très peu lisible car ne sachant à priori quelle sont les variables qui sont utilisées dans les fonctions.
Je déconseille vivement cette méthode.
Les pointeurs sur les fonctions sont une des particularités du langage C. Lors de leur emploi, il ne faudra pas confondre un pointeur de fonction et une fonction retournant un pointeur.
La syntaxe est la suivante:
type (*identificateur)();
Exemple d'utilisation:
int maximum();
int (*ptr_maximum)();
ptr_maximum = maximum;
On peut également initialiser les pointeurs lors de leur déclaration:
int a;
int maximum();
int (*ptr_maximum)()=maximum;
a=*ptr_maximum(); /* appel à une fonction par pointeur*/
L'appel à une fonction peut se faire par l' identificateur de la fonction suivie par des parenthèse (). Elle peut se faire aussi, comme renseigné au paragraphe précédent, par l'utilisation d'un pointeur.
La syntaxe d'appel à une fonction est la suivante:
identificateur de la fonction (arg1,arg2,...argn);
pointeur de la fonction (arg1,arg2,...argn);
Les bibliothèques standard contiennent de fonctions couramment utilisées en C. On retrouvera notamment toutes des fonctions d'entrées/sorties, les fonctions de manipulations de données telles que des chaines de caractères, des fonctions graphiques, des accès aux fonctions du BDOS (int 21) permettant la gestion directe du matériel.
Ces fonctions sont reprises dans des fichiers portant l'extension .LIB et se trouvant généralement dans un répertoire tel que LIB.
L'ensemble des prototypes de ces fonctions sont repris dans des fichiers d'en tête portant l'extension .h et que l'on retrouve généralement dans un répertoire portant le non INCLUDE.
Exemple:
les fonctions gérant les chaines de caractères ont leur prototype dans le fichier d'en tête STRING.H.
L'utilisation de bibliothèques "faites maisons" est possible mais cela fera l'objet d'un chapitre distinct.
Le C offre au programmeur un jeu d'opérateurs lui permettant d'effectuer un bon nombre d'opérations sur un ensemble de données. Suivant les types de données ou leurs fonctions, on pourra les classer dans les catégories suivantes:
· les opérateurs arithmétiques,
· les opérateurs logiques,
· les opérateurs conditionnels,
· les opérateurs d'adressage indirect (*) et adresse de (&),
· les opérateurs de décalage et de masquage,
· les opérateurs d'accès aux champs
· les opérateurs d'affectation.
Certains de ces opérateurs ont déjà été abordés dans les paragraphes précédents et ne seront plus que cités.
Ces opérateurs mettent uniquement en jeu des opérandes numériques réels ou entiers. On retrouve les opérations classiques comme l'addition, la soustraction, la multiplication et le reste de division entière (modulo). La difficulté ne réside pas dans la syntaxe de ces opérateurs mais dans le type des calculs intermédiaires qui peut bien souvent conduire à des erreurs.
Cet opérateur '+' effectue la somme de deux opérandes. Parmi les opérations d'additions valides, on retrouve les cas suivants:
|
Opérande 1 |
Opérande 2 |
Commentaires |
|
Pointeur |
Entier |
On ajoute au pointeur l'entier multiplié par la taille du type du pointeur. Seul un entier peut être ajouté à un pointeur. |
|
Entier |
Entier |
Entier |
|
Entier Flottant |
Flottant Entier |
Le résultat est du type flottant après conversion. |
|
Flottant |
Flottant |
Flottant |
Cet opérateur '-' effectue la soustraction entre deux opérandes. Parmi les opérations de soustractions valides, on retrouve les cas suivants:
|
Opérande 1 |
Opérande 2 |
Commentaires |
|
Pointeur |
Entier |
On soustrait de l'adresse l'entier multiplié par la taille du type du pointeur |
|
Entier Flottant |
Flottant Entier |
le résultat est de type flottant après conversion. |
|
Entier |
Entier |
Entier |
|
Flottant |
Flottant |
Flottant |
Cet opérateur '*' effectue la multiplication entre deux opérandes. Parmi les opérations de multiplications valides, on retrouve les cas suivants:
|
Opérande 1 |
Opérande 2 |
Commentaires |
|
Entier |
Entier |
Entier |
|
Entier |
Flottant |
Le résultat est de type flottant après conversion. |
|
Flottant |
Flottant |
Flottant |
|
Pointeur |
Entier |
On ajoute au pointeur la taille du type multipliée par la valeur de l'entier. |
Cet opérateur effectue la division de la première opérande par la suivante. Parmi les opérations de divisions valides, on retrouve les cas suivants:
|
Opérande 1 |
Opérande 2 |
Commentaires |
|
Entier |
Entier |
Entier. Le résultat du flottant correspondant à l'opération réelle est tronqué. |
|
Flottant |
Entier |
Le résultat est de type flottant après conversion. |
|
Entier |
Flottant |
Le résultat est de de flottant après conversion. |
|
Flottant |
Flottant |
Flottant. |
L'opérateur % fournit le reste de la division entière de la première opérande par la seconde.
|
Opérande 1 |
Opérande 2 |
Commentaires |
|
Entier |
Entier |
Donne un entier comme résultat |
Le compilateur microsoft ne permet que d'utiliser des opérandes des type entier.
En C, il n'existe pas de variables booléennes par défaut. On retrouve un type énuméré déclaré sous l'identificateur BOOL. Ce type énuméré reprend les deux champs suivants:
· FALSE qui est initialisé avec la valeur entière 0 et
· TRUE qui est initialisé avec la valeur entière 1.
Il faut signaler que l'utilisation de ces valeurs TRUE et FALSE n'est pas tout à fait conforme aux règles utilisées dans les structures de test et de boucle à savoir que:
· les expressions considérées comme FAUSSES se voient attribuer une valeur nulle,
· les expressions considérées comme VRAIES se voient attribuer une valeur différentes de 0.
Prenons l'exemple suivant:
# include <stdlib.h>
# include <conio.h>
void main (void)
{
BOOL a,b;
a=FALSE;
b=!a; /* je prends le complément de a*/
if (b==TRUE)
printf("la variable b est à TRUE\n");
else
printf("la variable b est à FALSE\n");
}
Commentaire:
la variable a est initialisée à 0. Lorsque je prend le complément de la variable pour l'affecter à la variable b, je sais que b est VRAIE c-à-d que la valeur qui lui est affectée est différente de 0. Mon test n'est donc pas correct car je le limite à tester si la variable vaut 1. Pour que mon test soit correct, je dois utiliser une des syntaxes suivantes:
if (b) /* le test est correct si la condition est vraie*/
printf("la variable est à TRUE\n");
else
printf("la variable est à FALSE\n");
ou encore:
if (b != FALSE) /* se traduit par b différent de FALSE*/
printf("la variable b est à TRUE\n");
else
printf("la variable b est à FALSE\n");
Généralement nous utiliserons des entiers pour caractériser une variable logique mais il est bon de signaler que les opérateurs logiques peuvent être appliqués à n'importe quel type d'opérande entier, flottant ou pointeur. Attention que le résultat de l'opération est toujours entier.
|
A |
!A |
|
Vrai |
Faux |
|
Faux |
Vrai |
Exemple:
int b;
int a=10; /* variable qui d'un point de vue logique est une variable booléenne à l'état "VRAI"*/
b=!a;
|
A |
B |
A && B |
|
Faux |
Faux |
Faux |
|
Faux |
Vrai |
Faux |
|
Vrai |
Faux |
Faux |
|
Vrai |
Vrai |
Vrai |
L'opérateurs peut être utilisé avec des variables booléennes ou avec tout opérateur renvoyant une information de type booléen.
Exemple:
int a=10,b=0;
if (a && b) printf ("a et b sont vraies\n");
if ((a >100) && (b != 0)) printf ("a est plus grand que 100 et b est différent de 0\n");
if (a && ( b != 0 ) printf(" a est vrai et b est différent de 0\n");
Attention: il faut remarquer que si l'un des opérandes est faux, l'autre opérande n'est pas évalué, l'expression logique résultante étant fausse.
|
A |
B |
A || B |
|
Faux |
Faux |
Faux |
|
Faux |
Vrai |
Vrai |
|
Vrai |
Faux |
Vrai |
|
Vrai |
Vrai |
Vrai |
Exemple:
int a=10,b=0;
if (a || b) printf ("a ou b est vraie\n");
if ((a >100) || (b != 0)) printf ("a est plus grand que 100 ou b est différent de 0\n");
if (a || ( b != 0 ) printf(" a est vrai ou b est différent de 0\n");
Attention: il faut remarquer que si l'un des opérandes est vrai, l'autre opérande n'est pas évalué, l'expression logique résultante étant vraie.
Exemple:
#include <stdio.h>
#include <stdlib.h>
int fonction(int a);
void main (void)
{
int a=0,c=10;
if (c || fonction(a))
printf("expression vraie\n");
else
printf("expression fausse\n");
}
int fonction(int a)
{
printf("fonction appelée\n");
return (a);
}
Comme c est vraie, le deuxième opérande n'est pas testé et don le message "fonstion appelée" n' apparaitra pas sur votre écran.
Les opérateurs relationnels sont utilisés dans des structures de test et de boucle. Le résultat envoyé par cet opérateur est de type entier et interprété comme une variable booléenne avec les relations suivantes:
· une relation est vrai et le résutat associé est différent de 0,
· une relation est fausse est le résultat associé est 0.
Plusieurs opérateurs relationnels peuvent être établies entre elles par les opérateurs logiques repris dans le paragraphe précédent.
L'opérateur renvoie la valeur vraie si son premier opérande est srictement inférieur au second et faux dans le cas contraire.
Exemple:
int a=10,b;
if ( a < 100 ) printf (" a est srictement plus petit que 100\n");
b = ! ( a >= 100);
if ( b) printf ("a est strictement plus petit que 100\n");
L'opérateur renvoie la valeur vraie si le premier opérande est plus petit ou égale au second opérande et faux dans le cas contraire.
Exemple:
int a=10,b;
if ( a < =100 ) printf (" a est plus petit ou égal à 100\n");
b = ! ( a > 100);
if ( b) printf ("a est plus petit ou égal à 100\n");
L'opérateur renvoie la valeur vraie si le premier opérande est strictement plus grand le second opérande et faux dans le cas contraire.
Exemple:
int a=10000,b;
if ( a > 100 ) printf (" a est srictement plus grand que 100\n");
b = ! ( a <= 100);
if ( b) printf ("a est strictement plus grand que 100\n");
L'opérateur renvoie la valeur vraie si le premier opérande est plus grans ou égale au second opérande et faux dans le cas contraire.
Exemple:
int a=1000,b;
if ( a >= 100 ) printf (" a est plus grand ou égal à 100\n");
b = ! ( a < 100);
if ( b) printf ("a est plus grand ou égal à 100\n");
L'opérateur renvoie la valeur vraie si le premier opérande est strictement égal au second et faux dans le cas contraire.
Exemple:
int a=10;
if ( a == 10 ) printf("a vaut 10\n");
else printf("a est différent de 10\n");
L'opérateur renvoie la valeur vraie si le premier opérande est différent du second et faux dans le cas contraire.
Exemple:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
int fonction(int a);
void main (void)
{
char a;
do
{
if (kbhit()) /* la fonction kbhit() renvoie une valeur non nulle si un caractère est disponible dans le tampon du clavier*/
{
a=getch(); /* la fonction getch() va chercher le caractère en attente dans le tampon du clavier*/
printf("le caractère %c a ete tape\n",a);
}
}while ((a !='q') && (a != 'Q')); /* boucle tant que a est différent de q et de Q*/
printf("sortie du programme\n");
}
Ces opérateurs permettent de travailler comme l'assembleur au niveau des bits. Les seuls opérandes possibles sont de type entier et le résultat renvoyé par ces opérateurs sont également des entiers. ces opérateurs acceptent les conversions arithmétiques usuelles, le résultat ayant le type de l'opérande après conversion.
Cet opérateur permet de compléménter à 1 son opérande. C'est un complément bit à bit qui est effectué; il ne faut pas le confondre avec l'opérateur ! qui donne le complément logique.
Exemple:
#include <stdio.h>
#include <stdlib.h>
int fonction(int a);
void main (void)
{
int a=0xAAAA;
printf("%x\n",~a);
}
si nous convertissons la valeur hexadécimale 0xAAAA en binaire, nous obtenons 101010101010. Si nous complémentons ensuite ce nombre binaire bit à bit, nous obtenons 0101010101010101 ce qui donne en hexadécimal 5555.
Cet opérateur effectue un ET bit à bit entre le premier opérande et le second. Il compare chaque bit du prmier opérande à celui du second opérande. Si les deux bits sont à 1, le bit résultant de l'opération est mis à 1. Dans le cas contraire, le bit résultant sera mis à zéro.
On appelle encore cet opérateur un opérateur de masquage car il permet de n'obtenir que d'une opérande l'ensemble des bits correspondant à ceux du second opérande qui sont à 1.
Exemple:
#include <stdio.h>
#include <stdlib.h>
int fonction(int a);
void main (void)
{
int a=0xAAAA;
int b=0x00FF;
int c=0x1254;
int d=0x0008;
printf("%x\n",a & c);
printf("%x\n",a & b);
printf("%x\n",a & d);
if (a & d) printf("le bit du rang 3 est à 1\n");
else printf("le bit du rang 3 est à 0\n");
}
Commentaires:
· l'opération a & c donne comme résultat 200,
|
Valeur de a en hexadécimal |
0xAAAA |
|
Valeur de a en binaire |
1010101010101010 |
|
Valeur de c en binaire |
0001001001010100 |
|
Le résultat de a & c en binaire |
0000001000000000 |
|
Le résultat de a & c hexadécimal |
0x0200 |
· l'opération a & b donne comme résultat 0x00AA. On a masqué l'octet de poids fort du premier opérande pour ne conserver que la partie basse.
|
Valeur de a en hexadécimal |
0xAAAA |
|
Valeur de a en binaire |
1010101010101010 |
|
Valeur de b en binaire |
0000000011111111 |
|
Le résultat de a & b en binaire |
0000000010101010 |
|
Le résultat de a & b en hexadécimal |
0x00AA |
· l'opération de a & d donne comme résultat 0x0008
|
Valeur de a en hexadécimal |
0xAAAA |
|
Valeur de a en binaire |
1010101010101010 |
|
Valeur de d en binaire |
0000000000001000 |
|
Valeur de d & a en binaire |
0000000000001000 |
|
Valeur de d & a en hexadécimal |
0x0008 |
Cette dernière opération permet de tester si le bit de rang 3 est à 1 ou à 0 dans le premier opérande.
L'opérateur ou bit à bit non exclusif compare chaque bit du premier opérande au bit correcpondant du second opérande. Si l'un des deux bits est à 1, le bit résultant est mis à 1. Dans le cas contraire, le bit résultant est mis à 0.
Exemple:
#include <stdio.h>
#include <stdlib.h>
int fonction(int a);
void main (void)
{
int a=0x1256;
int b=0x0008;
printf("%x\n",a|b);
}
Cet exemple permet de montrer comment on peut placer un bit à un dans une configuration binaire, sans changer la valeur des autres bits. Nous aurons ici le bit du rang 3 qui sera placé à 1 et nous obtiendrons donc comme résultat 0x125E.
L'opérateur ou exclusif compare chaque bit du premier opérande au bit correspondant du second. si un des bits est à 1 est que l'autre est à zéro, le bit résultant est à 1. Dans les autres cas, le bit résultant est à zéro.
Exemple:
#include <stdio.h>
#include <stdlib.h>
int fonction(int a);
void main (void)
{
int a=0x1256;
int b=0x0008;
int c=a|b;
printf("%x\n",c);
printf("%x\n",c^b);
}
Cet exemple montre la façon dont on peut complémenter un bit sans modifier la valeur des autres bits.
L'opérateur de décalage va décaler le premier opérande vers la gauche du nombre de positions spécifiées par le second opérande. Les deux opérandes doivent être de type entier. Avec le décalage vers la gauche, les bits vacant situés à droite sont mis à 0. Le résultat du décalage est indéfini si le second opérande est négatif ou plus grand que le nombre maximum de positions du premier opérande.
Exemple:
#include <stdio.h>
#include <stdlib.h>
int fonction(int a);
void main (void)
{
int a=0x1256;
int b=0x0001;
printf("%x\n",a | (b<<3));
}
Cet exemple permet de positionner un bit à 1 en renseignant uniquement le rang occupé par le bit sans devoir jongler avec les notations hexadécimales.
Remarque: l'opération de décalage à gauche de une position revient à multiplier le premier opérande par 2.
L'opérateur de décalage va décaler le premier opérande vers la droite du nombre de positions spécifiées par le second opérande. Les deux opérandes doivent être de type entier. Avec le décalage vers la droite, les bits vacant situés à gauche sont mis à 0 si l'entier est non signé. Si l'entier est signé, le remplissage s'effectue avec la valeur du bit de signe. Le résultat du décalage est indéfini si le second opérande est négatif ou plus grand que le nombre maximum de positions du premier opérande.
Exemple:
#include <stdio.h>
#include <stdlib.h>
int fonction(int a);
void main (void)
{
int a=-127,b=127;
printf("a vaut %x, b vaut %x\n",a,b);
printf("%x %x\n",(a>>4),(b>>4));
}
|
a avant le décalage (en hexadécimal) |
0xFF81 |
|
a après le décalage |
0xFFF8 |
|
b avant le décalage |
0x007F |
|
b après le décalage |
0x0007 |
Syntaxe: opérande1 ? opérande2 : opérande3
L'opérande 1 est considérée comme une variable booléenne. Elle peut don être de type entier, flottant ou pointeur.
En fonction de la valeur de cette variable booléenne, l'opérande2 ou l'opérande3 sera évaluée. Ces deux opérandes peuvent être de type entier, flottant ou pointeur.
Exemple:
#include <stdio.h>
#include <stdlib.h>
int fonction(int a);
void main (void)
{
int a=10,c,b=50;
c=(a>20)?a:b;
printf("%d\n",c);
}
La fonction printf donnera comme résultat c=50.
Un opérateur d'affectation assigne la valeur de l'opérande situé à main droite à l'emplacement mémoire désigné par l'opérande située à main gauche. Cependant, l'opérateur situé à main gauche de l'opérateur d'assignement doit être une expression lvalue modifiable. Les opérateurs d'affectation convertisent automatiquement leur opérande de droite en fonction du type de leur opérande de gauche.
L'opérateur affecte la valeur du second opérande à son premier.
Exemple:
int a,b=125;
a=10;
a=b+10;
Ces opérateurs reprennent l'ensemble des opérateurs arithmétiques *, +, -, /, % et l'opérateur d'affectation simple = pour former les opérateurs suivants: *=, +=, -=, /=, %=.
Exemple:
int a=10,b=20;
a=a+10;
/* Comme l'opérande de gauche se retrouve comme opérande de l'opérateur arithmétique, nous pouvons utiliser l'opérateur d'affectation arithmétique suivant: */
a+=10;
/* Cette règle est valable pour tous les autres opérateurs*/
a=a*b;
a*=b;
Ces opérateurs reprennent l'ensemble des opérateurs binaires &, |, >>, <<, ^ et l'opérateur d'affectatin simple = pour former les opérateurs suivants: &=, |=, >>=, <<=, ^=.
Exemple:
int a=0x1256,b=0x56AB;
a=a & b;
/* Comme l'opérande de gauche se retrouve comme opérande dans l'opérateur binaire, nous pouvons utiliser l'opérateur d'affectation binaire suivant: */
a &=b;
/* Cette règle est valable pour les autres opérateurs.*/
a=a>>b;
a>>=b;
L'opérateur d'incrémentation unaire est ++ et permet d'incrémenter son opérande de 1.
L'opérateur de décrémentation unaire est -- et permet de décrémenter son opérande de 1.
Cet opérateur peut affecter des variables de type entier, flottant ou des pointeurs. Pour ce dernier point, je vous renvoie au paragraphe traitant des opérations arithmétiques sur les pointeurs.
Ces opérateurs peuvent se placer avant ou après l'opérande. Si cet opérateur est seul , l'effet sera identique que l'opérande se place avant ou après. La différence de comportement apparaît lorsque l' opérateur d'affectation est utilisé dans la même expression.
Exemple:
#include <stdio.h>
#include <stdlib.h>
void main (void)
{
int a=10,b=20,c;
++a; / équivalent à a=a+1;
b++; / équivalent à b=b+1;
printf("a vaut: %d, b vaut: %d\n",a,b);
c=++a; /* a est incrémenté et ensuite affecté à c*/
printf("c vaut: %d, a vaut: %d\n",c,a);
c=a++; /* a est affecté à c et ensuite incrémenté*/
printf("c vaut: %d, a vaut: %d\n",c,a);
}
Pour la syntaxe "c=++a" on parlera de pré-incrémentation tandis que pour "c=a++" on parlera de post-incrémentation.
Il est util de signaler que l'emploi de cet opérateur est conseillé car il fait appel aux instructions équivalente INC ou SUB de l'assembleur qui sont pluis rapides que les instructions ADD et SUB.
Syntaxe: sizeof (expression)
Cet opérateur renvoie la place mémoire en octets associée à une variable ou un type. Lorsque cet opérateur est appliqué uaux types structurés ou aux variables, l'opérateur renvoie la taille actuelle qui peut donc inclure des caractères de remplissage insérés pour l'alignement.
Quand l'opérateur s'applique à un tableau dimensionné de façon statique, celui-ci renvoie la taille complète du tableau.
L'opérateur ne peut pas retourner la taille d'un tableau alloué dynamiquement ou un tableau externe.
Exemple:
#include <stdio.h>
#include <stdlib.h>
struct test
{
char a;
int b;
};
int tab[10];
void main (void)
{
printf("taille:%d\n",sizeof(tab));
printf("taille:%d\n",sizeof(struct test));
}
L'exécution du programme nous donne l'affichage suivant:
taille: 20
taille: 4
Le premier opérateur sizeof renvoie la valeur 20 car nous avons un tableau de 10 entiers codés chacun sur 2 octets, ce qui nous donne 20 octets au total.
Le deuxième opérateur nous renvoie la valeur 4 ce qui peut nous étonné car la structure contient un caratère codé sur un octet et un entier codé sur 2, ce qui donne trois octets au total et non quatre. C'est sans compter les caractères de remplissages pour les alignements.
L'opération de casting consiste à forcer la conversion d'un type de donnée dans un autre type. Les conversions peuvent être effectuées automatiquement par le compilateur ou explicitement en utilisant ce que l'on applelle un "cast".
Il faut apporter une information importante:
· Si vous utilisé le forçage automatique, le compilateur vous enverra un "WARNING" si vous tentez d'assigner une opérande occupant un emplacement mémoire plus grand que le second opérande.
· Si vous utilisez un cast, ce message d'erreur n'apparaîtra pas.
Exemples:
#include <stdio.h>
#include <stdlib.h>
void main (void)
{
int a=10,c;
long int b=20,d;
d=a;
}
Ce premier exemple ne donne pas de warning lors de la compilation car vous affectez un entier de type court (2 octets) à un entier de type long (4 octets). Il n'y a donc pas de risque de pertes d'informations.
#include <stdio.h>
#include <stdlib.h>
void main (void)
{
int a=10,c;
long int b=20,d;
c=b;
}
Ce deuxième exemple donne un warning lors de la compilation car il y a des risques de pertes d'informations: on veut affecter un entier long à un entier court.
#include <stdio.h>
#include <stdlib.h>
void main (void)
{
int a=10,c;
long int b=20,d;
c=(int)b;
}
Ce troisième exemple, bien qu'il puisse conduire à des pertes d'informations, ne donne pas de warning car vous avez effectué le "cast" vous-même.
· Pour le passage d'un entier court signé à un entier court non signé, tous les bits sont conservés.
· Pour le passage d'un entier court signé à un entier court non signé, le bit de signe est décalé vers la position la plus à gauche et les positions vides sont remplies avec des 0.
· Pour le passage d'un caractère signé à un entier signé ou non signé, le bit de signe est décalé vers la position la plus à gauche et les positions vides sont remplies par le bit de signe.
· Pour le passage d'un caractère non signé à un entier signé ou non signé, les positions vides sont remplies par des zéros.
· Si on passe d'un entier long à un entier court ou d'un entier à un caractère, seules les bits de poids faibles sont recopiés.
#include <stdio.h>
#include <stdlib.h>
void main (void)
{
int a=0x8010;
int b;
signed char c=-126;
signed long int d;
b=a;
printf("valeur de b en hexa: %x\n",b);
printf("a: %d b: %u\n",a,b);
b=c;
printf("valeur de b en hexa: %x\n",b);
d=a;
printf("valeur de d: %ld\n",d);
printf("taille de d: %d\n",sizeof(d));
printf("valeur de c: %d\n",c);
d=c;
printf("valeur de a en hexa: %x\n",d);
}
L'exécussion du programme donne l'affichage suivant:
Valeur de b en hexa: 8010
a=-32752 b=32784
Valeur de b en hexa:ff82
valeur de d: -32752
taille de d: 4
valeur de c: -126
valeur de a en hexa:ff82
· Si l'on passe d'un entier en un flottant, le type entier est converti en entier long puis cet entier long est converti en un type flottant. La partie décimale du flottant est alors remplie de zéros.
· Lorsque l'on convertit un flottant en en entier, la partie décimale du flottant disparaît.
Les conversion de flottants sont possibles mais pour le forçage d'un double vers un flottant, il faut faire attention aux valeurs maximum et minimum admises pour ces différents types.
On peut classer les instructions du langage C en trois grandes familles:
1. Les instructions de base
· Expression
· Instruction nulle
· blocs d'instructions
2. Les instructions de contrôle
· if else
· switch ...case
3. Les instructions de boucle
· while
· do...while
· for
4. Les instructions d'échappement
· break
· continue
· return
· goto
Syntaxe:
if (condition logique )
instruction 1
[else
instruction2]
Le mot clef if permet l'exécution de l'instruction1 si la condition logique est vraie (valeur non nulle); si else est présent et que la condition logique est fausse (zéro), l'instruction 2 est exécutée. Après l'exécution de l'instruction1 ou l'instruction2, le contrôle passe à l'instruction suivante.
Exemple:
#include <stdio.h>
#include <stdlib.h>
void main (void)
{
int a=10;
printf( "la valeur de a vaut:%d\n",a);
if (a >= 0)
printf("a est positif\n");
else
printf("a est négatif\n");
printf("fin du contrôle if..else\n");
}
Les mots clef sont switch, case et default.
Syntaxe: switch(expression)
{
[ case expression constante:]
...
[instruction]
...
[default:
instruction]
}
L'expresion doit obligatoirement être de type entier.Il faut faire très attention car l'emploi d'un type différent n'amène pas d'erreur à la compilation mais donne un fonctionnement de l'instruction tout à fait imprévisible.
Les mots clef switch et case permettent l'évaluation de l'expression et l'exécution des instructions associées avec l'expression constante qui correspond à l'expression initiale.
Si il n'y a pas de correspondance avec une des expressions constantes, l'instruction associée avec le mot clef default est exécutée. Si le mot clef default n'est pas utilisé, le contrôle passe à l'instruction qui suit le bloc switch.
Si rien n'est prévu dans votre bloc switch, la première correspondance entre l'expression initiale et une expression constante permettra l'exécution des instructions qui suivent le case correspondant et même ceux qui suivent. Pour pouvoir sortir du bloc avant le mot clef case suivant, il faut le faire précéder d'une instruction break, return ou goto.
Exemple:
#include <stdio.h>
#include <stdlib.h>
void main (void)
{
int a=1;
switch (a)
{
case 1: printf("a vaut 1\n");
case 2: printf("a vaut 2\n");
default: printf("a est différent de 1 et de 2\n");
}
}
L'exécution du programme donnera l'affichage des informations suivantes:
a vaut 1
a vaut 2
a est différent de 1 et de 2
Pour sortir correctement, nous allons utiliser l'instruction break qui permet de sortir du bloc de contrôle dans lequel on se trouve.
Exemple:
#include <stdio.h>
#include <stdlib.h>
void main (void)
{
int a=2;
switch (a)
{
case 1:
printf("a vaut 1\n");
break;
case 2:
printf("a vaut 2\n");
break;
default:
printf("a est différent de 1 et de 2\n");
}
}
L'exécussion du programme donne l'affichage suivant:
a vaut 2
Cette instruction de contrôle est particulièrement utile lorsque l'on désire réaliser des menus.
Exemple:
#include <stdio.h>
#include <stdlib.h>
void fonction1(void);
void fonction2(void);
void main (void)
{
char a;
printf("1 option 1 du menu\n");
printf("2 option 2 du menu\n");
printf("\n veuillez introduire votre choix(1 ou 2): ");
a = getchar(); /* permet la saisie d'un caractère au clavier*/
switch (a)
{
case '1':
fonction1();
break;
case '2':
fonction2();
break;
default:
printf("\nvous n'avez pas introduit une valeur correcte\n");
}
}
void fonction1(void)
{
printf("l'option 1 du menu a été choisie\n");
}
void fonction2(void)
{
printf("l'option 2 du menu a été choisie\n");
}
Syntaxe: while (condition logique)
instruction
Le mot clef while permet l'exécution répétée de l'instruction tant que la condition logique reste vraie. Si au départ la condition logique est fausse, l'instruction n'est pas exécutée une seule fois.
Si aucune modification dans la condition logique n'est apportée par l'instruction exécutée, le programme rentre dans un eboucle dont on ne peut sortir qu'au moyen des instructions goto, break ou return.
Nous pouvons modifier notre programme de menu de la façon suivante:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
void fonction1(void);
void fonction2(void);
void main (void)
{
int a;
while (a!='3')
{
printf("1 option 1 du menu\n");
printf("2 option 2 du menu\n");
printf("3 quitter le menu\n");
printf("\n veuillez introduire votre choix(1 ou 2): ");
a = getche();/* permet la saisie d'un caractère au clavier*/
printf("\n");
switch (a)
{
case '1':
fonction1();
break;
case '2':
fonction2();
break;
case '3':
break;
default:
printf("\nvous n'avez pas introduit une valeur correcte\n");
}
}
}
void fonction1(void)
{
printf("l'option 1 du menu a été choisie\n");
}
void fonction2(void)
{
printf("l'option 2 du menu a été choisie\n");
}
But de la fonction : permettre un affichage formaté de variables sur le flot de sortie standard (stdout).
Syntaxe : int printf(const char *format [,argument] ) ;
Entête requise : <stdio.h>
Valeur retournée par la fonction : elle retourne le nombre de caractères affichés ou une valeur négative si une erreur s’est produite.
Si des arguments suivent le champ du format, celui ci devra comprendre des spécifications pour déterminer le format de sortie de ces arguments. Ces spécifications sont reprisent dans des séquences d’échappement qui pour les formats d’arguments sont précédées par le caractère %.
D’autres séquences d’échappement peuvent être utilisées et elles sont alors précédées du caractère \.. Elles sont reprises dans le tableau suivant :
|
\a |
Sonnerie (Bell) |
|
\b |
Retour en arrière (Backspace) |
|
\f |
Saut de page (Form Feed) |
|
\n |
Saut de ligne (Form Feed) Line Feed et Carriage Return |
|
\r |
Retour chariot (Carriage Return) |
|
\t |
Tabulation horizontale |
|
\v |
Tabulation verticale |
|
\’ |
simple guillemet (quote) |
|
\’’ |
doubles guillemets (double quote) |
|
\\ |
backslash |
|
\ddd |
caractère ASCII codé en octal |
|
\xddd |
caractère ASCII codé en hexadécimal |
Spécification du format pour les arguments : %[flags][width][.précision][{h|l|L}] type.
|
·
Le type |
caractère obligatoire qui permet de renseigner que l’argument associé doit être interprété comme un caractère, comme une chaîne de caractères ou comme un nombre. |
|
||
|
caractère |
type |
format de sortie |
||
|
c |
char |
spécifie un caractère codé sur un seul octet |
||
|
d |
int |
entier décimal signé |
||
|
o |
int |
octal entier non signé |
||
|
u |
int |
entier décimal non signé |
||
|
x |
int |
hexadécimal entier non signé (avec les lettres en minuscule) |
||
|
X |
int |
hexadécimal entier non signé (avce les lettres en majuscule) |
||
|
f |
float ou double |
valeur signée sous la forme [-] dddd.dddd. Le nombre de chiffres avant le point décimal dépend de la grandeur du nombre et le nombre de chiffres après dépend de la précision demandée |
||
|
e |
float ou double |
valeur signée sous la forme [-]d.dddd e [signe]ddd. Il y a un seule chiffre avant le point décimal et le nombre de chiffres pour l’exposant est de trois. |
||
|
E |
float ou double |
identique execxpté que le e est mis en majuscule E. |
||
|
g |
float ou double |
valeur signée donné dans le format f ou e selon la forme la plus compacte pour la valeur et la précision donnés. |
||
|
n |
pointeur d’entier |
donne le nombre de caractères écrits ; cette valeur est stockée dans un entier dont l’adresse est donnée en argument. |
||
|
p |
pointeur de void |
affiche l’adresse sous la forme xxxx :yyyy où xxxx est le segment et yyyy l’offset. Les chiffres x et y sont donnés en hexadécimal sous forme majuscule. |
||
|
s |
string |
spécifie une chaîne de caractère. |
||
·
Spécification de format « width »
Ce champ est un nombre entier décimal non négatif contrôlant le nombre minimum de caractères affichés. Si le nombre de caractères de la valeur de sortie est plus petit que ce paramètre, des blancs sont ajoutés à gauche ou à droite de la valeur, dépendant des paramètres du flag (alignement à gauche ou à droite, jusqu'à ce que la largeur minimale soit atteinte.
Cette spécification de largeur ne va jamais causer de troncature : si le nombre de caractères sur la sortie est plus grand que la largeur précisée, alors tous les caractères seront affichés.
Si la spécification de la largeur est un astérisque (*), un entier dans la liste des arguments en fournit la valeur.
·
Spécification de format « précision »
Ce champ est un entier décimal non négatif, précédé par un point et qui spécifie le nombre de caractères devant être affichés, le nombre de positions décimales ou le nombre de chiffres significatifs. Contrairement à la spécification de la largeur, la précision peut amener une troncature de la valeur de sortie ou un arrondi de la valeur en virgule flottante.
Si la spécification de la précision est un astérisque, un entier dans la liste des arguments en fournit la valeur.
|
Type |
effets |
par défaut |
|
c |
la précision n’a pas d’effet |
le caractère est affiché |
|
d,i,u,o,x,X |
la précision spécifie le nombre minimum de chiffres qui doivent être affichés.Si le nombre de chiffres dans l’argument est inférieur que la précision, la valeur de sortie est complétée par des zéros sur la gauche. La valeur n’est pas tronquée lorsque le nombre de chiffres excède la précision. |
la précision par défaut est 1 |
|
e,E |
la précision spécifie le nombre de chiffres devant être affichés après le point décimal. Le dernier chiffre affiché est arrondi. |
La précision par défaut est de 6.Si la précision est 0 ou qu’il n’y a pas de chiffre renseigné après le point, aucune décimale n’est affichée. |
|
f |
la précision spécifie le nombre de chiffres après le point décimal. Si un point décimal apparaît, au moins un chiffre avant lui sera affiché. La valeur est arrondie au nombre approprié de chiffres. |
La précision par défaut est de 6.Si la précision est 0 ou qu’il n’y a pas de chiffre renseigné après le point, aucune décimale n’est affichée. |
|
g,G |
la précision spécifie le nombre de chiffres significatifs affichés. |
Six chiffres significatifs sont affichés. |
|
s |
la précision spécifie le nombre maximum de caractères devant être affichés. |
Les caractères sont affichés jusqu'à ce que un caractère nul soit rencontré. |
·
Spécification de format « flags »
La directive de fanion est un caractère qui justifie la sortie, l’affichage du signe, des blancs, des points décimaux et du préfixe octal ou hexadécimal. Plus d’une directive de fanion peut apparaître dans une spécification de format.
|
Flags |
effets |
par défaut |
|
- |
alignement à gauche du résultat avec le champ de largeur donné |
alignement à droite |
|
+ |
fait précédé la valeur en sortie d’un signe plus ou moins à condition qu’elle soit de type signé |
seul le signe négatif apparaît |
|
0 |
|
|
|
blanc ‘ ‘ |
fait précéder la valeur en sortie par un blanc si celle ci est positive et de type signé. Cette directive est ignorée si le + est utilisé. |
aucun blanc n’apparaît. |
|
# |
pour les types o, x et X, la directive force la valeur de sortie non nulle à être précédée respectivement de 0,0x et 0X. |
aucun blanc n’apparaît. |
|
|
pour les types e, E et f, la directive force la valeur de sortie à contenir un point décimal dans tous les cas. |
Le point décimal n’apparaît que s’il est suivi d’au moins un chiffre |
|
|
Pour les types g et G, la directive force la valeur de sortie à contenir un point décimal et les zéros après le point décimal sont affichés. |
le point décimal n’apparaît que s’il est suivi d’au moins un chiffre. Les zéros sont tronqués. |
|
|
Ignoré pour les types c,d,i,u et s |
|
·
Spécification de format « size ».
Les préfixes h, l et L spécifient
la « taille » de l’argument ( long ou court, caractère simple ou
large)
|
Préfixe utilisé |
effets |
type |
|
l |
long int |
d, i, o, x ou X |
|
l |
long unsigned int |
u |
|
h |
short int |
d, i, o, x ou X |
|
h |
short unsigned int |
u |
Exemple :
#include <stdio.h>
void main (void)
{
int a=-10;
unsigned int b=20;
char *string="ceci est un essai de texte";
float tab[]={ -12.321F,
4.7F,
256.56F,
1.0F,
-58.12F};
int i;
for (i=0;i<=4;i++)
printf("%10.4f\t%-10.4f\n",tab[i],tab[i]);
// cette boucle permet d’afficher deux tableaux contenant les mêmes valeurs. Le tableau de //gauche
// présente une justification à gauche et celui de droite une justification à droite.
for (i=0;i<=4;i++)
printf("%*.4f\t%-*.4f\n",10,tab[i],10,tab[i]);
// même boucle en faisant passer le champ de largeur en argument
printf("\nvaleur de l'entier a:%d\n",a);
printf("valeur de l'entier a avec l'option %%u: %u\n",a);
printf("affichage d'une chaîne de caractères: %s\n",string);
printf("affichage des 8 premiers caractères :%.8s\n",string);
// il faut remarquer que pour le type s, l'argument doit être un pointeur.
printf("affichage d'une adresse: %p\n",tab);
printf("affichage d'un nombre hexadécimal %#lX\n",0xaabc);
printf("affichage d'un nombre en hexadécimal %#hX\n",0x1d);
}
But de la fonction : permettre une lecture de données formatées sur le flot d’entrées standard (stdin).
Syntaxe : int scanf(const char *format[,argument]...) ;
Entête requise : <stdio.h>
Valeur retournée par la fonction : la fonction retourne le nombre de champs convertis avec succès et assignés ; le retour n’inclut pas les champs qui ont été lus mais pas assignés. Un retour d’une valeur nulle indique qu’aucun champ n’a été assigné. La valeur de retour est EOF pour une erreur ou si un caractère de fin de fichier ou un caractère de fin de chaîne est rencontré lors d’une lecture d’un caractère.
Remarque : la fonction « scanf » lit des données sur le flot d’entrées standard et ecrit ces données dans des emplacements mémoire donnés par les arguments. Chaque argument doit être le pointeur d’une variable du type qui correspond au type spécifié dans le format.
Spécification du format des arguments : %[*][width][{h|l|L}]type
Le format peut contenir les caractères suivants :
1. Un caractère d’espacement (un blanc ‘ ‘, une tabulation ‘\t’, une nouvelle ligne ‘\n’ ). Un caractère d’espacement a pour conséquence la lecture mais non le stockage de tous les caractères blancs consécutifs dans le flot d’entrée jusque le caractère suivant qui ne soit pas un caractère d’espacement. Un caractère d’espacement dans le format équivaut à n’importe quel nombre et combinaison de caractères blancs dans le flot d’entrée.
2. Les caractères non blancs, excepté le signe pour-cent ont pour conséquence la lecture mais non le stockage d’un caractère identique non blanc. Si le caractère suivant dans stdin n’est pas identique, la fonction « scanf » se termine.
3. La spécification du format, introduit par le caractère pour-cent permet à scanf de lire et de convertir les caractères du flot d’entrée dans une valeur du type spécifié. La valeur est assignée à un argument dans la liste des arguments.
Le format est lu de la gauche vers la droite. Les caractères qui ne correspondent pas à une spécification de format attendent une correspondance dans le flot d’entrée stdin. Les caractères de correspondance sont scannés mais pas stockés. Si un caractère dans stdin est en conflit avec le format spécifié, la fonction scanf se termine sans que le caractère fautif ne soit stocké.
Lorsque la première spécification de format est rencontré, la valeur du premier champ d’entrée est convertie en accord avec cette spécification et est stockée à l’emplacement mémoire renseigné par le premier argument. Le second format permet la conversion du second champ d’entrée et la valeur est stockée dans le second argument et ainsi de suite.
Un champ d’entrée est défini par l’ensemble des caractères précédents le premier caractère blanc (espacement, tabulation ou nouvelle ligne), ou jusqu’au caractère qui ne peut être converti en accord avec le format spécifié, ou jusqu'à ce que la largeur du champ soit atteinte si elle est spécifiée. Si il y a trop d’arguments pour les spécifications de formats donnés, les arguments en trop sont évalués mais ignorés. Le résultat de la fonction n’est pas prévisible si il n’y a pas assez d’arguments pour le nombre de formats.
Chaque champ dans la spécification du format est un simple caractère ou un nombre signifiant une option de format particulier. Le caractère « type » qui apparît après le dernier paramètre optionnel détermine si le champ d’entrée doit être interprété comme un caractère, une chaîne de caractères ou un nombre.
Une spécification de format simple contient seulement le signe pour-cent et le caractère de type (par exemple %d). Si le signe pour-cent est suvi par un caractère qui n’a pas de signification particulière comme caractère de contrôle de format, ce caractère et ceux qui suivent (jusqu’au prochain caractère pour-cent) sont traités comme une séquence ordinaire de caractères devant trouver une équivalence dans le flot d’entrée stdin.
Un signe pour-cent suivi d’un astérisque supprime l’affectation du champ suivant sur l’entrée, qui est interprété comme un champ du type spécifique mais qui n’est pas stocké.
·
Spécification de format
« type »
|
caractère(s) |
type d’entrée
attendu |
type d’argument |
|
c |
un caractère |
pointeur de char |
|
d |
entier décimal |
pointeur sur un int |
|
i |
entier décimal, octal ou hexadécimal |
pointeur sur un int |
|
o |
entier sous forme octale |
pointeur sur un int |
|
u |
entier décimal non signé |
pointeur sur un unsigned int |
|
x |
entier sous forme hexadécimale |
pointeur sur un int |
|
e, E, f, g, G |
valeur en virgule flottante composée d’un signe optionnel, d’une série de un ou plusieurs chiffres décimaux contenant un point décimal, et d’en exposant optionnel (e ou E) suivi par une valeur entière signée optionnelle. |
Pointeur sur un float. |
|
n |
pas d’entrée lue |
pointeur sur un int, dans lequel est stocké le nombre de caractères lus successivement à partir du flot d’entrée. |
|
s |
chaîne de caractère jusqu’au premier caractère blanc (espace, tabulation ou nouvelle ligne). Pour lire des chaîne de caractères non délimitées par des caractères blancs, il faut utiliser le jeu des crochets ([ ]) comme décrit au tableau suivant. |
Pointeur sur un tableau de char suffisamment grand pour contenir le champ d’entrée ainsi que le caractère nul qui est automatiquement ajouté. |
·
Spécification de format « width ».
|
Effets |
Préfixe utilisé |
type spécifié |
|
double |
l |
e, E, f, g ou G |
|
long int |
l |
d, i, o, x ou X |
|
long unsigned int |
l |
u |
|
short int |
h |
d, i, o, x ou X |
|
short unsigned int |
h |
u |
|
caractère simple |
l |
c |
|
chaîne de caractères |
h |
s |
Pour lire des chaîne de caractères qui ne sont pas délimitées par des espaces blancs, un jeu de caractères entre crochets peut remplacé le caractère de type « s ». Le champ d’entrée correspondant est lu jusqu’au premier caractère qui n’apparaît pas dans le jeu de caractères entre crochets. Si le premier caractère dans le jeu est l’accent circonflexe, l’effet est inversé.
Notons que la notation %[a-z] et %[z-a] sont interprétés comme équivalent à %[abcde...z]. Attention que ce n’est pas une convention de la norme ANSI standard.
Pour stocker une chaîne de caractères sans le caractère nul de fin de chaîne (‘\O’), il faut utiliser la spécification %nc, ou c est un entier décimal. Dans ce cas, le caractère c indique que l’argument est un pointeur de char. Les n caractères suivants sont lus et stockés sans que le caractère nul ne soit ajouté.
Si n n‘est pas spécifié, la valeur par défaut est de 1.
Exemple :
#include <stdio.h>
void main(void)
{
int entier;
char buffer[20];
int retour;
//lecture d'un entier
printf("introduisez un entier:");
scanf("%d",&entier);
printf("valeur introduite:%d\n",entier);
//lecture d'un entier de deux chiffres
printf("introduisez un entier:");
scanf ("%2d",&entier);
printf("valeur introduite:%d\n",entier);
//instruction permettant de vider le buffer d'entrée
//qui contient encore un carriage return.
fflush(stdin);
//lecture d'une chaîne de caractères
//cette lecture s'arrête au premier blanc rencontré
printf("1 introduisez une chaîne de caractères:");
scanf("%s",buffer);
printf("contenu du buffer:%s\n",buffer);
fflush(stdin);
//lecture d'une seconde chaîne de caractères
//la lecture ne s'arrête plus sur un blanc mais sur le cr
printf("2 introduisez une chaîne de caractères:");
scanf("%[^\n]",buffer);
printf("contenu du buffer:%s\n",buffer);
fflush(stdin);
//lecture d'une chaîne de 5 caractères y compris les blancs
//5 caractères sont attendus
printf("3 introduisez une chaîne de 5 caractères:");
scanf("%5c",buffer);
buffer[5]='\0';
printf("contenu du buffer:%s\n",buffer);
fflush(stdin);
buffer[0]='\0';
printf("4 introduisez une chaîne de caractères précédée du mot essai:");
retour=scanf("essai%[^\n]",buffer);
printf("contenu du buffer:%s\n",buffer);
printf("nombre de champs lus:%d\n",retour);
}
But de la fonction : permet la lecture d’un caractère sur le flot d’entrée stdin.
Syntaxe : int getchar(void)
Entête requise : <stdio.h>
Valeur retournée par la fonction : la fonction retourne le caractère lu. Pour indiquer une erreur de lecture ou une condition de fin de fichier, la fonction renvoie un EOF.
Exemple :
#include <stdio.h>
#include <conio.h>
void main(void)
{
char buffer[30];
int i=0;
printf("veuillez introduire une chaîne de caractères\n");
while ((buffer[i++]=getchar())!='\n');
buffer[i]='\0';
printf("chaîne introduite: %s",buffer);
}
But de la fonction : permet l’envoi d’un caractère sur le flot de sortie stdout.
Syntaxe : int putchar (int c)
Entête requise : <stdio.h>
paramètres : c est le caractère à écrire. Seule la
partie basse de 8 bits de l’entier est écrite.
Valeur retournée par la fonction : la fonction retourne le caractère écrit. Pour indiquer une erreur d’écriture ou une condition de fin de fichier, la fonction renvoie un EOF.
Exemple :
#include <stdio.h>
#include <conio.h>
void main(void)
{
char buffer[20];
int i=0;
printf("veuillez introduire une chaîne de caractères\n");
//saisie d'une chaîne de caractète
scanf("%[^\n]",buffer);
//affichage de la chaîne, caractère par caractère
while (putchar(buffer[i++])!='\0');
}
But de la fonction :permet de lire une chaîne de caractères à partie de la console.
Syntaxe : char* gets(char* buffer)
Entête requise : <stdio.h >
paramètres : buffer
est le pointeur d’un tableau de caractères devant recevoir la chaîne.
Valeur retournée par la fonction : la fonction renvoie le pointeur buffer si aucune erreur ne s’est produite et un pointeur NULL dans le cas contraire. Vous pouvez utiliser les macros ferror ou feof pour déterminer la cause de l’erreur.
Remarques : la fonction gets lit une ligne à partir du flot d’entrée standard stdin et la stocke dans un buffer. La ligne est constituée de tous les caractères jusqu’au premier caractère newline inclus. La fonction remplace alors la caractère newline par un caractère null .
Exemple :
#include <stdio.h>
#include <conio.h>
void main(void)
{
char buffer[81];
printf("veuillez introduire une chaîne de caractères\n");
gets(buffer);
printf("chaîne introduite: %s\n",buffer);
}
But de la fonction :permet d’écrire une chaîne de caractères vers stdout.
Syntaxe : int puts(const char* buffer)
Entête requise : <stdio.h >
paramètres : bufferr
est le pointeur d’un tableau de caractères contenant la chaîne à écrire.
Valeur retournée par la fonction : la fonction renvoie une valeur non négative si il n’y a pas d’erreur. Si la fonction puts échoue, elle retourne un EOF.
Remarques : la fonction puts écrit une chaîne de caractères vers le flot de sortie standard stdout, en remplaçant le caractère null par un caractère newline.
Exemple :
#include <stdio.h>
#include <conio.h>
void main(void)
{
char* buffer="écriture d'une chaîne";
puts(buffer);
//vous pouvez aussi utiliser la syntaxe suivante
puts("écriture d'une seconde chaîne");
}
But de la fonction :permet l’ouverture d’un fichier en mode bufferisé.
Syntaxe : FILE *fopen(const char *filename, const char *mode)
Entête requise : <stdio.h >
paramètres : filename est le nom du fichier à ouvrir,
mode est le type d’accès permis.
Valeur retournée par la fonction : la fonction renvoie un pointeur sur un descripteur de fichier. Une valeur de retour valant -1 indique une erreur et dans ce cas, errno est mis à une des valeurs suivantes :
· EACCES essai d’ouverture d’un fichier marqué en lecture seule pour y écrire ou si le mode de partage ne permet les opérations spécifiées ou si le nom donné est un répertoire.
· EEXIST demande de création d’un fichier dont le nom existe déjà.
· EINVAL
· ENOENT fichier ou chemin non trouvé.
Remarques :
1. La chaîne de caractères « mode » spécifie le type d’accès demandé pour le fichier comme suit :
|
r |
Ouverture d’un fichier en lecture. Si le fichier n’existe pas ou ne peut être trouvé, la fonction avorte. |
|
w |
Ouverture d’un fichier vide pour l’écriture. Si le fichier existe, son contenu est effacé. |
|
a |
Ouverture d’un fichier pour l’écriture en fin de fichier sans enlever le marqueur de fin de fichier EOF avant d’écrire les nouvelles données dans le fichier ; La fonction crée le fichier en premier si il n ‘existe pas. |
|
r+ |
Ouverture d’un fichier en lecture et en écriture. Le fichier doit exister. |
|
w+ |
Ouverture d’un fichier vide pour à la fois effectuer des lectures et des écritures. Si le fichier donné existe, ses données sont détruites. |
|
a+ |
Ouverture d’un fichier en lecture et en ajout. L’opération d’ajout inclut la suppression du marqueur de fin de fichier EOF avant l’écriture de nouvelles données et ce EOF est restauré avant que l’écriture soit terminée. Le fichier sera dans un premier temps créé si il n’existe pas. |
Quand un fichier est ouvert avec le mode d’accès a ou a+, toutes les opérations d’écritures se passent en fin de fichier. Le pointeur de fichier peut être repositionné en utilisant fseek ou rewind mais sera toujours repositionné en fin de fichier lorsqu’une opération d’écriture est effectuée. De cette façon, aucune donnée ne peut être endommagée.
Si le mode a est utilisé, le marqueur de fin de fichier n’est pas enlevé. Cela signifie que toute écriture nouvelle se fait au delà de ce caractère et que la commande « TYPE » du dos ne fera pas apparaître vos nouveaux enregistrements.
Quand les modes d’accès r+, w+ ou a+ sont spécifiés, les lectures et écritures sont spécifiées. Cependant, quand vous passez d’une lecture à une écriture, vous devez utiliser une des fonctions fflush, fsetpos, fseek ou rewind pour initialiser le tampon du fichier. La position courante peut être spécifiée en utilisant la fonction fsetpos ou fseek.
En plus d’un des attribut de mode précédent, les caractères suivants peuvent être inclus pour spécifier le mode de translation pour le caractère « new line ».
|
t |
Ouverture en mode texte. Dans ce mode, le caractère CTRL+Z est interprété comme un caractère de fin de fichier. Dans ce cas, il faut utiliser l’ouverture de fichier en mode a+ pour l’enlever. Cela doit être fait car une utilisation de fseek ou ftell pour se rendre en fin d’un fichier se terminant par un CTRL+Z pourrait poser des problèmes. |
|
b |
Ouverture d’un fichier en mode binaire. Danss ce mode, les translations faisant intervenir des « carriage return » ou « line feed » sont supprimées. |
Exemple :
But de la fonction :permet la fermeture d’un fichier ouvert en mode bufferisé ou la fermeture de tous les fichiers si la fonction fcloseall( ) est utilisée.
Syntaxe : int fclose (FILE * ptr_des)
Entête requise : <stdio.h >
paramètres : ptr_des est le pointeur du descripteur du fichier à fermer.
Valeur retournée par la fonction : la fonction retourne 0 si le fichier a é té fermé avec succès. La fonction fcloseall retourne le nombre total de fichiers fermés. Les deux fonctions renvoient un EOF pour indiquer qu’une erreur s’est produite.
Remarques: dans les deux fonctions, tous les tampons associés aux fichiers sont libérés avant toute fermeture.
Exemple:
But de la fonction :positionne le pointeur de fichier au début de celui ci.
Syntaxe : void rewind (FILE* ptr_des)
Entête requise : <stdio.h >
paramètres : ptr_des est le pointeur de descripteur du fichier à positionner.
Valeur retournée par la fonction : pas de retour de valeur.
Remarques : La fonction rewind est similaire à (void) fseek ( ptr_des, 0L, SEEK_SET) ;
Cependant, contrairement à fseek, la fonction rewind efface les indicateurs d’erreurs ainsi que l’indicateur de fin de fichier. Cette fonction permet aussi de vider le contenu du tampon. Vous pouvez donc l’utiliser pour vider le tampon du clavier en utilisant le descripteur de flot STDIN.
Attention car la fonction ne retourne pas d’erreur en cas d’échec de positionnement.
Exemple:
But de la fonction : permet de positionner le pointeur à un endroit spécifique du fichier.
Syntaxe : int fseek ( FILE *ptr_des, long offset, int origine)
Entête requise : <stdio.h >
paramètres : ptr_des est le pointeur du descripteur de fichier
offset précise le nombre d’octets dont on doit bouger par rapport à la référence renseignée par le paramètre origine.
Origine
précise la position initiale.
Valeur retournée par la fonction : La fonction retourne 0 si elle se déroule sans échec et une valeur non nulle en cas de succès. Pour les périphérique pour lesquels le déplacement n’a aucun sens, la valeur retournée est indéfinie.
Remarques : Pour les fichiers ouverts pour la mise à jour, l’opération suivante peut être une lecture ou une écriture.
L’argument d’origine doit être une des constantes suivantes :
· SEEK_CUR pour la position courante dans le fichier,
· SEEK_END pour la fin du fichier,
· SEEK_SET pour le début du fichier.
Pour un fichier ouvert en mode texte, la fonction fseek a ses limites parce que les translations CR/LF produisent des résultats inattendus. Les seules opérations autorisées en mode texte sont les suivantes :
1. se déplacer avec un offset de 0 relativement à une valeur d’origine,
2. se déplacer à partir du début du fichier en utilisant un offset retourné par la fonction ftell.
Dans les fichiers ouverts en mode texte en lecture/écriture, le caractère CTRL+Z est supprimé si possible par la fonction fopen pour permettre un fonctionnement correct de la fonction fseek.
En mode ajout, l’écriture se fait toujours en fin de fichier, et cette position ne peut être modifiée. La position courante est déterminée par la dernière opération d’entrée/sortie et non l’endroit de la prochaine opération d’écriture. Si aucune opération d’entrée/sortie ne s’est encore produite, alors le pointeur est au d ébut du fichier.
Exemple :
But de la fonction :permet de déterminer la position courante du pointeur de fichier.
Syntaxe : long ftell (FILE * ptr_des )
Entête requise : <stdio.h >
paramètres : ptr_des est un pointeur de descripteur de fichier,
Valeur retournée par la fonction : ftell retourne la position courante du fichier. La valeur retournée par ftell pour un fichier ouvert en mode texte ne reflète pas l’offset physique en octet à cause de la translation des paires CR/LF. L’utilisation conjointe des fonctions ftell et fseek permet un positionnement correct dans les fichiers. En cas d’erreur, la fonction retourne -1 et la variable errno contient alors une des constantes suivantes :
· EBADF pour préciser que le pointeur du descripteur de fichier n’est pas valide,
· EINVAL pour préciser que le descripteur n’est pas valide.
Pour les périphériques dont le positionnement n’aurait pas de sens, tels que terminaux ou imprimantes, ou lorsque le descripteur ne pointe pas sur un fichier ouvert, la valeur retournée est indéfinie.
Remarques : La position retournée par la fonction est l’offset relatif au début du fichier.
Notons que lorsque un fichier est ouvert en mode ajout, la posituion courante du fichier est déterminée par la dernière opération d’entrée/sortie et non par l’endroit ou la prochaine opération d’écriture doit se produire. Si par exemple un fichier est ouvert en mode ajout est que la dernière opération était une lecture, la position du fichier est l’endroit ou la prochaine opération de lecture doit se produire. Si aucune opération d’entrée/sortie ne s’est encore produite avec un fichier ouvert en mode ajout, le pointeur est initialisé au début du fichier.
But de la fonction :fixe l’indicateur de position du descripteur de fichier.
Syntaxe : int fsetpos ( FILE* ptr_des, const fpos_t *pos )
Entête requise : <stdio.h >
paramètres : ptr_des est le pointeur de descripteur de fichier,
pos
est le pointeur de la variable contenat la nouvelle position.
Valeur retournée par la fonction : Si elle se produit avec succès, la fonction renvoie 0. En cas d’échec, la fonction renvoie une valeur non nulle et la variable errno contient une des constantes suivantes :
· EBADF pour préciser que le fichier n’est pas accessible ou que l’objet pointé par ptr_des n’est pas valide,
· EINVAL pour préciser que le descripteur n’est pas valide.
Remarques : La position précisée comme paramètre doit nécessairement être obtenue en utilisant la fonction fgetpos. Après un appel à fgetpos, l’opération suivante peut être une entrée ou une sortie.
Exemple :
But de la fonction :permet de récupérer la position du pointeur de lecture/écriture.
Syntaxe : int fgetpos ( FILE * ptr_des, fpos_t* pos )
Entête requise : <stdio.h >
paramètres : ptr_des est le pointeur de descripteur de fichier,
pos
est le pointeur de la variable contenat la nouvelle position.
Valeur retournée par la fonction : La fonction fgetpos récupère la valeur courante de l’indicateur de position de fichier du descripteur et la stocke dans l’objet pointé par pos. Cette information peut ensuite être utilisée par la fonction fsetpos. La valeur de pos est dans un format tel que son utilisation n’est possible que par les fonctions fsetpos et fgetpos.
But de la fonction : écriture de données vers un flot (exemple : un fichier). L’écriture n’est pas formatée.
Syntaxe : size_t fwrite (const void *buffer, size_t size, size_t count, FILE * ptr_des)
Entête requise : <stdio.h >
paramètres : buffer est un pointeur vers les données à être écrites
size représente la taille de l’item à écrire
count représente le nombre de fois que l’item doit être écrit
ptr_des représente un pointeur de
descripteur de fichier
Valeur retournée par la fonction : la fonction retourne le nombre d’items complets écrits qui peut être plus petit que le paramètre count si une erreur s’est produite. Si une erreur se produit, l’indicateur du pointeur de position de fichier ne peut être déterminé.
Remarques : la fonction fwrite écrit un nombre count d’items, chacun d’une taille size, du buffer vers la sortie ptr_des. Le pointeur de position est incrémenté par le nombre d’octets écrits. Si le fichier est ouvert en mode texte, chaque carriage return est remplacer avec la paire carriage return line feed. Le remplacement n’a pas d’effet sur la valeur retournée.
Exemple :
But de la fonction : lecture d’une donnée non formatée sur un flot. (Exemple : un fichier).
Syntaxe : size_t fread ( void *buffer, size_t, size_t count, FILE * ptr_des)
Entête requise : <stdio.h >
paramètres : buffer est un pointeur vers le lieu de stockage des données
size représente la taille de l’item à lire
count représente le nombre de fois que l’item doit être lu
ptr_des représente un pointeur de
descripteur de fichier
Valeur retournée par la fonction : la fonction fread retourne le nombre d’items complètement lus, qui peut être plus petit que le paramètre count si une erreur s’est produite ou si la fin de fichier est rencontrée avant que le total ne soit atteint. Il faut utiliser la fonction feof ou ferror pour distinguer une erreur d’une fin de fichier. Si la taille et le nombre valent 0, la fonction retourne 0 et le contenu du buffer reste inchangé.
Remarques : La fonction fread lit le nombre count d’items, chacun ayant une taille de size octets, à partir de ptr_des et les stocke dans le buffer. Le pointeur de position du fichier est incrémenté du nombre d’octets lus. Si le fichier est ouvert en mode texte, une paire CR/LF est remplacé avec un simple caractère LF. Le remplacement n’a pas d’effet sur le pointeur de position ou sur la valeur retournée. Le pointeur de position est indéterminé si une erreur se produit. La valeur d’une lecture partielle de l’item ne peut être déterminé.
Exemple :
But de la fonction : écriture formatée de données sur un flot (Exemple : un fichier)
Syntaxe : int fprintf( FILE *ptr_des, const char *format, [arguments] )
Entête requise : <stdio.h >
paramètres : ptr_des est un pointeur de descripteur de fichier
format est une chaîne de caractère de contrôle de format
arguments sont les arguments optionnels
Valeur retournée par la fonction : la fonction retourne le nombre d’octets écrits. Elle retourne une valeur négative en cas d’erreur.
Remarques : pour les formats et arguments, nous vous renvoyons à la fonction printf qui est analogue.
Exemple :
But de la fonction : lecture formatée d’une donnée sur le flot (fichier par exemple)
Syntaxe : int fscanf( FILE *ptr_des, const char *format [,arguments] )
Entête requise : <stdio.h >
paramètres : ptr_des est un pointeur de descripteur de fichier
format est une chaîne de caractère de contrôle de format
arguments sont les arguments optionnels
Valeur retournée par la fonction : la fonction retourne le nombre de champs convertis et assignés avec succés ; la valeur retournée n’inclut pas les champs lus mais non assignés. La valeur de retour 0 indique qu’aucun champ n’a été assigné. Si une erreur se produit ou si la fin de fichier est atteinte avant la première conversion, la valeur retournée sera EOF.
Remarques : la fonction fscanf lit les données à partir de la position courante du fichier. Chaque argument doit être un pointeur d’une variable du type correspondant au format renseigné. Pour les formats, nous vous renvoyons à la fonction scanf qui est analogue.
Exemple :
Remarque : c’est une fonction correspondant à la macro getchar.
Syntaxe : int fgetchar (void )
En-tête requise : <stdio.h>
Remarque : c’est une fonction correspondant à la macro putchar.
Syntaxe : int fputchar (int c )
En-tête requise : <stdio.h>
But de la fonction : saisie d’une chaîne de caractère sur le flot (un fichier par exemple)
Syntaxe : char *fgets(char *buffer, int n, FILE *ptr_des)
Entête requise : <stdio.h >
paramètres : buffer est l’endroit de stockage de la chaîne
n est le nombre maximum de caractères devant être lus
ptr_des est un pointeur de descripteur de fichier
Valeur retournée par la fonction : la fonction retourne une chaîne de caractères. NULL est retourné pour indiquer une erreur ou si la fin de fichier est atteinte. Il faut utiliser la fonction feof ou ferror pour déterminer quelle erreur s’est produite.
Remarques : la fonction lit une chaîne de caractères depuis le flot et la stocke dans le buffer. La fonction lit cette chaîne à partir de la position courante du fichier jusqu’au premier caractère new line rencontré, jusqu'à la fin du fichier ou si le nombre maximum de caractères lus est atteint. Le résultat stocké dans le buffer se voit ajouté un caractère nul. Le caractère new line, si il est lu, est inclus dans la chaîne.
Exemple :
But de la fonction : écriture d’une chaîne de caractères sur le flot
Syntaxe : int fputs (const char *buffer, FILE *ptr_des )
Entête requise : <stdio.h >
paramètres : buffer contient la chaîne de caractères à écrire
ptr_des est le pointeur de descripteur de fichier
Valeur retournée par la fonction : la fonction retourne une valeur non négative en cas de réussite. En cas d’erreur, la fonction renvoie un EOF.
Remarques : la fonction écrit la chaîne de caractères à la position courante du fichier. Elle ne copie pas le caractère nul.
Nous retrouvons dans la conversion des données des conversions implicites qui ne réclament pas l’utilisation de fonctions spécifiques. Ces conversions ont été analysées dans le paragraphe traitant des types de variables et des moulages (casting).
Je reprendrais à titre informatif la conversion implicite d’un flottant vers un entier avec la troncature de la partie fractionnaire.
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
int a;
float b=10.55F;
a=(int)b; /*ou a=b mais cette dernière syntaxe
génère un warning lors de la compilation*/
printf("valeur de l'entier:%d\n",a);
}
Nous retrouvons également des conversions de données explicites qui réclament l’utilisation de fonctions. Ces fonctions permettent la conversion d’une donnée numérique vers une chaîne de caractères et d’une chaîne de caractères vers une donnée numérique.
Ces fonctions sont reprises dans le tableau ci après.
|
|
Entier |
Entier long |
Flottant |
|
|
Conversion en chaîne |
itoa |
|||
|
Conversion en donnée numérique |
atoi |
atol strtol |
strtoul |
atof, strtod |
But : cette fonction permet la conversion d’un entier vers une chaîne de caractères. Cette conversion peut se faire en différentes bases.
Syntaxe : char *itoa( int valeur, char *buffer, int base) ;
Paramètres : valeur est la donnée numérique à convertir
*buffer est le pointeur sur le tableau de caractères devant contenir la conversion
base est la base dans laquelle doit s’effectuer la conversion. La valeur doit être comprise entre 2 et 36.
En tête requise : <stdlib.h>
Valeur retournée : la fonction retourne un pointeur sur le tableau contenant la conversion.
Remarques : il est important que la taille du tableau soit suffisante pour contenir le résultat de la conversion. Le compilateur ne génère pas d’erreur en cas du non respect de cette règle mais l ‘exécution du programme risque de provoquer des plantages. La chaîne convertie peut contenir jusqu’à 17 caractères.
Si la base est 10, la présence d’un nombre négatif dans la variable numérique aura comme conséquence l’ajout du caractère –dans la conversion.
Exemple :
#include <stdlib.h>
#include <stdio.h>
void main( void )
{
char buffer[20];
int i = 3445;
itoa( i, buffer, 10 );
printf( "conversion de l’entier %d (base 10): %s\n", i, buffer );
itoa( i, buffer, 16 );
printf( "conversion de l’entier %d (base 16): 0x%s\n", i, buffer );
itoa( i, buffer, 2 );
printf( "conversion de l’entier %d (base 2): %s\n", i, buffer );
}
But : cette fonction permet la conversion d’un entier long vers une chaîne de caractères. Cette conversion peut se faire en différentes bases.
Syntaxe : char *ltoa( long valeur, char *buffer, int base) ;
Paramètres : valeur est la donnée numérique à convertir
*buffer est le pointeur sur le tableau de caractères devant contenir la conversion
base est la base dans laquelle doit s’effectuer la conversion. La valeur doit être comprise entre 2 et 36.
En tête requise : <stdlib.h>
Valeur retournée : la fonction retourne un pointeur sur le tableau contenant la conversion.
Remarques : il est important que la taille du tableau soit suffisante pour contenir le résultat de la
conversion. Le compilateur ne génère pas d’erreur en cas du non respect de cette règle mais l ‘exécution du programme risque de provoquer des plantages. La chaîne convertie peut contenir jusqu’à 33 caractères.
Si la base est 10, la présence d’un nombre négatif dans la variable numérique aura comme conséquence l’ajout du caractère –dans la conversion.
Exemple :
#include <stdlib.h>
#include <stdio.h>
void main( void )
{
char buffer[20];
long l = -344115L;
ltoa( l, buffer, 16 );
printf( "conversion de l’entier long %ld (base 16): 0x%s\n", l, buffer );
ltoa( l, buffer, 10 );
printf( "conversion de l’entier long %ld (base 10):%s\n", l, buffer );
}
But : cette fonction permet la conversion d’un entier long non signé vers une chaîne de caractères. Cette conversion peut se faire en différentes bases.
Syntaxe : char *ultoa( unsigned long valeur, char *buffer, int base) ;
Paramètres : valeur est la donnée numérique à convertir
*buffer est le pointeur sur le tableau de caractères devant contenir la conversion
base est la base dans laquelle doit s’effectuer la conversion. La valeur doit être comprise entre 2 et 36.
En tête requise : <stdlib.h>
Valeur retournée : la fonction retourne un pointeur sur le tableau contenant la conversion.
Remarques : il est important que la taille du tableau soit suffisante pour contenir le résultat de la
conversion. Le compilateur ne génère pas d’erreur en cas du non respect de cette règle mais l ‘exécution du programme risque de provoquer des plantages. La chaîne convertie peut contenir jusqu’à 33 caractères.
Exemple :
void main( void )
{
char buffer[20];
unsigned long ul = 1234567890UL;
ultoa( ul, buffer, 16 );
printf( "conversion de l’entier non signé %lu (base 16): 0x%s\n", ul, buffer );
}
But : cette fonction permet la conversion d’un double vers une chaîne de caractères en précisant le nombre de chiffres significatifs.
Syntaxe : char *ecvt( double valeur, int compte, int *pos_dec, int *signe) ;
Paramètres : valeur est la donnée numérique à convertir
compte est le nombre de chiffres désirés
pos_dec est un pointeur sur un entier qui contiendra la position du point décimal
signe est un pointeur d’entier contenant le signe du nombre converti.
En tête requise : <stdlib.h>
Valeur retournée : la fonction retourne un pointeur sur une chaîne interne. Lors de l’appel suivant à ecvt ou fcvt, la valeur stockée est détruite.
Remarques : La fonction convertit un nombre en virgule flottante vers une chaîne de caractères. Le paramètre valeur contient le nombre devant être converti. Le paramètre compte indique le nombre maximal de chiffres significatifs à convertir. Si la valeur dépasse cette précision, elle est arrondie ; au contraire, si cette valeur est inférieure, la chaîne de caractères est complétée avec des zéros. La chaîne de caractère est terminée après conversion par un caractère nul (‘\0’).
L’entier contenant la position du point décimal a une valeur nulle ou négative si le point décimal se situe en début de chaîne ou avant le premier chiffre.
L’entier contenant le signe a une valeur nulle si le nombre est positif et dans les autres cas, le nombre est négatif.
Exemple :
#include <stdlib.h>
#include <stdio.h>
void main( void )
{
int decimal, sign;
char *buffer;
int precision = 10;
double source = 3.1415926535;
buffer =ecvt( source, precision, &decimal, &sign );
printf( "source: %2.10f buffer: %s decimal: %d sign: %d\n",
source, buffer, decimal, sign );
}
But : cette fonction permet la conversion d’un double vers une chaîne de caractères en précisant le nombre de chiffres après le point décimal.
Syntaxe : char *fcvt( double valeur, int compte, int *pos_dec, int *signe) ;
Paramètres : valeur est la donnée numérique à convertir
compte est le nombre de chiffres décimaux désirés
pos_dec est un pointeur sur un entier qui contiendra la position du point décimal
signe est un pointeur d’entier contenant le signe du nombre converti.
En tête requise : <stdlib.h>
Valeur retournée : la fonction retourne un pointeur sur une chaîne interne. Lors de l’appel suivant à ecvt ou fcvt, la valeur stockée est détruite.
Remarques : La fonction convertit un nombre en virgule flottante vers une chaîne de caractères. Le paramètre valeur contient le nombre devant être converti. Le paramètre compte indique le nombre maximal de chiffres décimaux à convertir. Si la valeur dépasse cette précision, elle est arrondie ; au contraire, si cette valeur est inférieure, la chaîne de caractères est complétée avec des zéros. La chaîne de caractère est terminée après conversion par un caractère nul (‘\0’).
L’entier contenant la position du point décimal a une valeur nulle ou négative si le point décimal se situe en début de chaîne ou avant le premier chiffre.
L’entier contenant le signe a une valeur nulle si le nombre est positif et dans les autres cas, le nombre est négatif.
Exemple :
#include <stdlib.h>
#include <stdio.h>
void main( void )
{
int decimal, sign;
char *buffer;
double source = 3.1415926535;
buffer = fcvt( source, 7, &decimal, &sign );
printf( "source: %2.10f buffer: %s decimal: %d sign: %d\n",
source, buffer, decimal, sign );
But : cette fonction permet la conversion d’un double vers une chaîne de caractères. La conversion se fait dans un tableau fourni comme paramètre à la fonction
Syntaxe : char *gcvt( double valeur, int chiffre, char *buffer) ;
Paramètres : valeur est la donnée numérique à convertir
chiffre est le nombre de chiffres significatifs désirés
buffer est un pointeur sur un tableau contenant la conversion
En tête requise : <stdlib.h>
Valeur retournée : la fonction retourne un pointeur sur le tableau contenant la conversion .
Remarques : La fonction convertit un nombre en virgule flottante vers une chaîne de caractères (le point décimal et le signe sont inclus). Le paramètre valeur contient le nombre devant être converti. Le paramètre chiffre indique le nombre maximal de chiffres significatifs à convertir. La fonction essaye d’abord la conversion dans le format décimal ; si la taille disponible n’est pas suffisante, le format exponentiel sera utilisé. Un caractère nul est ajouté à la chaîne après conversion.
Exemple :
#include <stdlib.h>
#include <stdio.h>
void main( void )
{
char buffer[50];
double source = -3.1415e5;
gcvt( source, 7, buffer );
printf( "source: %f buffer: '%s'\n", source, buffer );
gcvt( source, 7, buffer );
printf( "source: %e buffer: '%s'\n", source, buffer );
}
But : cette fonction permet la conversion d’une chaîne de caractères vers un entier.
Syntaxe : int atoi (const char *chaîne) ;
Paramètres : * chaîne est un pointeur sur une chaîne de caractères à convertir.
En tête requise : <stdlib.h>
Valeur retournée : la fonction retourne l’entier résultant de la conversion . Si la fonction ne peut convertir la chaîne, elle renvoie 0.
Remarques : la fonction atoi ne reconnaît pas le point décimal ni la notation exponentielle.
La chaîne de caractère doit avoir la forme suivante :
[espace][signe] chiffres
La conversion s’arrête au premier caractère que la fonction ne peut traiter.
Il faut veiller à ce que la donnée à convertir ne puisse dépasser la valeur maximale représentable par un entier court.
Exemple :
#include <stdlib.h>
#include <stdio.h>
void main( void )
{
char *s;
int i ;
s = " 9885 francs";
i = atoi( s );
printf( "atoi test: chaîne ASCII : %s\t\tentier: %d\n", s, i );
}
But : cette fonction permet la conversion d’une chaîne de caractères vers un entier long.
Syntaxe : long atol (const char *chaîne) ;
Paramètres : * chaîne est un pointeur sur une chaîne de caractères à convertir.
En tête requise : <stdlib.h>
Valeur retournée : la fonction retourne l’entier résultant de la conversion . Si la fonction ne peut convertir la chaîne, elle renvoie 0.
Remarques : la fonction atol ne reconnaît pas le point décimal ni la notation exponentielle.
La chaîne de caractère doit avoir la forme suivante :
[espace][signe] chiffres
La conversion s’arrête au premier caractère que la fonction ne peut traiter.
Il faut veiller à ce que la donnée à convertir ne puisse dépasser la valeur maximale représentable par un entier court.
Exemple :
#include <stdlib.h>
#include <stdio.h>
void main( void )
{
char *s;
long int i ;
s = "98854 dollars";
l = atol( s );
printf( "atol test: chaîne ASCII: %s\t\tentier long: %ld\n", s, l );
}
But : cette fonction permet la conversion d’une chaîne de caractères vers un double.
Syntaxe : double atof (const char *chaîne) ;
Paramètres : * chaîne est un pointeur sur une chaîne de caractères à convertir.
En tête requise : <stdlib.h>
Valeur retournée : la fonction retourne le double résultant de la conversion . Si la fonction ne peut convertir la chaîne, elle renvoie 0.
Remarques : la fonction atol ne reconnaît pas le point décimal ni la notation exponentielle.
La chaîne de caractère doit avoir la forme suivante :
[espace] [signe] [chiffres] [.chiffres] [ {d | D | e | E }[signe]chiffres]
La conversion s’arrête au premier caractère que la fonction ne peut traiter.
Il faut veiller à ce que la donnée à convertir ne puisse dépasser la valeur maximale représentable par un entier court.
Exemple :
#include <stdlib.h>
#include <stdio.h>
void main( void )
{
char *s;
double x ;
s = " -2309.12E-2";
x = atof( s );
printf( "atof test: chaîne ASCII : %s\tdouble: %lf\n", s, x );
s = "7.8912654773d2";
x = atof( s );
printf( "atof test: chaîne ASCII : %s\tdouble: %lf\n", s, x );
}
But : cette fonction permet la conversion d’une chaîne de caractères vers un double.
Syntaxe : long strtol (const char *chaîne, char **fin_conv, int base) ;
Paramètres : * chaîne est un pointeur sur une chaîne de caractères à convertir.
**fin_conv est l’adresse du pointeur vers la variable contenant le caractère ayant arrêté la convertion.
base base dans laquelle la valeur est exprimée. Entre 2 et 36.
En tête requise : <stdlib.h>
Valeur retournée : la fonction retourne le long résultant de la conversion . La conversion s’arrête lorsque un caractère n’est pas reconnu comme faisant partie du nombre. Cela peut être le caractère nul ou le premier caractère numérique plus grand ou égal à la base.
Remarques :
La chaîne de caractères doit avoir la forme suivante :
[espaces] [{+ | –}] [0 [{ x | X }]] [chiffres]
Un espace blanc peut être considéré comme un espace ou une tabulation et est ignoré .Si la base est nulle, le premier chiffre de la chaîne permet à la fonction de déterminer la base. Si le premier caractère est nul et que le second n’est pas ‘x’ ou ‘X’, la chaîne est interprétée comme un octal ; autrement, elle est considérée comme un entier décimal. Si le premier caractère est nul est le suivant ‘x’ ou ‘X’, la chaîne est interprétée en hexadécimal.
Exemple :
#include <stdlib.h>
#include <stdio.h>
void main( void )
{
char *string, *stopstring;
long l;
string = "3756Arrêt de la conversion";
l=strtol(string,&stopstring,10);
printf("dernier caractère converti:%c\n",*stopstring);
printf("chaîne '%s' convertie en entier:%ld\n",string,l);
/* conversion automatique hexadécimale*/
string="0x1f";
l=strtol(string,&stopstring,0);
printf("chaîne '%s'convertie en entier:%ld\n",string,l);
/*conversion automatique octale*/
string="012";
l=strtol(string,&stopstring,0);
printf("chaîne '%s'convertie en entier:%ld\n",string,l);
}
But : cette fonction permet la conversion d’une chaîne de caractères vers un double.
Syntaxe : double strtod (const char *chaîne, char **fin_conv) ;
Paramètres : * chaîne est un pointeur sur une chaîne de caractères à convertir.
**fin_conv est l’adresse du pointeur vers la variable contenant le caractère ayant arrêté la convertion.
En tête requise : <stdlib.h>
Valeur retournée : la fonction retourne la valeur du double,
excepté lorsque la représentation du nombre provoque un débordement et dans ce
cas, la fonction retourne +/- HUGE_VAL. Le signe de HUGE_VAL correspond au
signe de la valeur qui ne peut être représenté. La fonction retourne 0 si la
conversion ne peut être effectuée ou que le nombre est trop petit pour être
représenté. La variable errno contient la valeur ERANGE.
Remarques : La fonction arrête la conversion lors de la lecture du premier caractère qu’elle ne peut reconnaître comme faisant partie du nombre. Cela peut être le caractère nul.
La chaîne de caractères doit avoir la forme suivante :
[espaces] [signe] [chiffres] [.chiffres] [ {d | D | e | E}[signe]chiffres]
Un espace blanc peut comprendre un espace ou une tabulation, et est ignoré . Si aucun caractère ne précède le point décimal, au moins un caractère doit le suivre.
Exemple :
#include <stdlib.h>
#include <stdio.h>
void main( void )
{
char *string, *stopstring;
double d;
string="1.12345678e5test";
d=strtod (string,&stopstring);
printf("caractère ayant stoppé la conversion:%c\n",*stopstring);
printf("chaîne '%s'convertie en double:%e\n",string,d);
}
But : cette fonction permet la copie d’une chaîne source vers une chaîne destination.
Syntaxe : char* strcpy (char *chaîne1, char *chaîne2) ;
Paramètres : * chaîne1 est un pointeur sur la chaîne source.
* chaîne2 est un pointeur sur la chaîne destination.
En tête requise : <string.h>
Valeur retournée :
Remarques :
Exemple :