Vous êtes sur la page 1sur 97

FAQ C

Date de publication :

Dernière mise à jour :

Cette FAQ a été réalisée à partir des questions fréquemment posées sur les forums de
www.developpez.com et de l'expérience personnelle des auteurs.

Je tiens à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle
propose sont correctes ; les auteurs font le maximum, mais l'erreur est humaine. Cette
FAQ ne prétend pas non plus être complète. Si vous trouvez une erreur, ou que vous
souhaitez devenir rédacteur, lisez Comment participer à cette FAQ ?.

Sur ce, je vous souhaite une bonne lecture.


Ont contribué à cette FAQ :

Melem - Swoög - fearyourself - PRomu@ld - Jean-


Marc.Bourguet - David.Schris - gege2061 - Clément Cunin
- LFE - Bob - joellel - Haypo - GoldenEye - nyal - gl -
Emmanuel Delahaye - Elijha - Anomaly - Laurent Gomila -
Aurelien.Regat-Barrel - Davidbrcz - publicStaticVoidMain
- Alp - Médinoc - dourouc05 - Pouet_forever -
FAQ C

1. Information générale (7) ............................................................................................................................................................4


1.1. A propos de cette FAQ (3) .............................................................................................................................................. 5
1.2. Documentation et outils (4) ..............................................................................................................................................7
2. Les types et les variables (35) .................................................................................................................................................. 8
2.1. Généralités (10) .................................................................................................................................................................9
2.2. Structures et unions (6) .................................................................................................................................................. 13
2.3. Les opérateurs (12) ......................................................................................................................................................... 17
2.4. Les variables (4) ............................................................................................................................................................. 21
2.5. Les nombres flottants (3) ............................................................................................................................................... 22
3. Pointeurs, tableaux et chaînes de caractères (40) ................................................................................................................... 25
3.1. Les pointeurs (10) ...........................................................................................................................................................26
3.2. Les tableaux (10) ............................................................................................................................................................ 30
3.3. Les chaînes de caractères (15) ....................................................................................................................................... 36
3.4. L'allocation dynamique de mémoire (5) ........................................................................................................................ 41
4. Les fonctions et les variables globales (10) ............................................................................................................................43
4.1. Les fonctions (8) .............................................................................................................................................................44
4.2. Les variables globales (2) ...............................................................................................................................................50
5. Les entrées/sorties (32) ............................................................................................................................................................51
5.1. Entrées/sorties en général (5) ......................................................................................................................................... 52
5.2. Gestion du clavier et de l'écran en mode console (19) ..................................................................................................54
5.3. Les fichiers et les dossiers (6) ........................................................................................................................................63
5.4. Réseau (2) ....................................................................................................................................................................... 68
6. Compilation et édition des liens (18) ......................................................................................................................................70
6.1. Le préprocesseur (12) ..................................................................................................................................................... 71
6.2. Techniques de compilation et d'édition de liens (6) ...................................................................................................... 76
7. Divers (31) ............................................................................................................................................................................... 78
7.1. Les nombres aléatoires (4) ............................................................................................................................................. 79
7.2. Gestion des dates et heures (10) .................................................................................................................................... 81
7.3. Gestion des processus (2) ...............................................................................................................................................87
7.4. Communication avec l'environnement (4) ......................................................................................................................89
7.5. Gestion des erreurs (3) ................................................................................................................................................... 91
7.6. Bonnes pratiques (3) .......................................................................................................................................................93
7.7. Divers (5) ........................................................................................................................................................................ 96

-3-
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Information générale

-4-
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Information générale > A propos de cette FAQ


Comment bien utiliser cette FAQ ?
Auteurs : Clément Cunin ,
Le but :
Cette FAQ a été conçue pour être la plus simple possible d'utilisation. Elle tente d'apporter des réponses simples et
complètes aux questions auxquelles sont confrontées tous les débutants (et les autres).

L'organisation :
Les questions sont organisées par thème, les thèmes pouvant eux-mêmes contenir des sous-thèmes. Lorsqu'une question
porte sur plusieurs thèmes, celle-ci est insérée dans chacun des thèmes, rendant la recherche plus facile.

Les réponses :
Les réponses contiennent des explications et des codes sources. Certaines sont complétées de fichier à télécharger
contenant un programme de démonstration. Ces programmes sont volontairement très simples afin qu'il soit aisé de
localiser le code intéressant. Les réponses peuvent également être complétées de liens vers d'autres réponses ou vers
un autre site en rapport.

Nouveautés et mises à jour :


Lors de l'ajout ou de la modification d'une question/réponse, un indicateur est placé à coté du titre de la question.
Cet indicateur reste visible pour une durée de 15 jours afin de vous permettre de voir rapidement les modifications
apportées.

J'espère que cette FAQ pourra répondre à vos questions. N'hésitez pas à nous faire part de tous commentaires/
remarques/critiques.

Comment participer à cette FAQ ?


Auteurs : Clément Cunin ,
Pour participer, postez simplement votre proposition ou remarque (nouvelle question/réponse, erreur dans la FAQ, liens
morts, etc.) dans le sous-forum Contribuez du forum C ou, dans certains cas particuliers seulement (par exemple, si vous
avez déjà réalisé une FAQ et que vous souhaitez la fusionner avec la présente), contactez directement le responsable.

Remerciements
Auteurs :
Un grand merci à tous ceux qui ont pris de leur temps pour la réalisation de cette FAQ.

Aux rédacteurs :
Remerciements tout d'abord à tous ceux qui ont rédigé les questions et les réponses.
Alp, Anomaly, Aurelien.Regat-Barrel, Bob, Clément Cunin, David.Schris, Davidbrcz, dourouc05, Elijha, Emmanuel
Delahaye, fearyourself, gege2061, gl, GoldenEye, Haypo, Jean-Marc.Bourguet, joellel, Laurent Gomila, LFE, Médinoc,
Melem, nyal, Pouet_forever, PRomu@ld, publicStaticVoidMain, Swoög.

Aux correcteurs :
Remerciements également aux personnes qui ont relu les textes pour supprimer un maximum de fautes de français.
Anomaly, gege2061, LFE, pharaonix.

-5-
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Aux visiteurs :
Remerciements enfin à tous ceux qui ont consulté cette FAQ et qui, par leurs remarques, nous ont aidé à la perfectionner.

Un merci tout particulier :


Un merci tout particulier à Clément Cunin et à Nono40 qui nous ont créé de superbes outils très utiles pour générer
ces FAQ.

-6-
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Information générale > Documentation et outils


Où trouver des cours et/ou tutoriels de langage C ?
Auteurs : LFE ,
Cette question a fait l'objet d'un énorme travail de recherche et les résultats se trouvent ici :
Les meilleurs cours et tutoriels de langage C
Les meilleurs livres de langage C

Comment se procurer la norme du langage C ?


Auteurs : gl , Emmanuel Delahaye ,
Le C est normalisé par l'ISO. La norme du C est donc disponible à l'achat sur le site de l'ISO (C90, C99) ainsi que sur
le site de l'ANSI (C89). Son prix est toutefois relativement élevé.
Le dernier draft de la version 99 de cette norme incluant TC1, TC2 et TC3 (Technical Corrigendum) est librement
téléchargeable sur www.open-std.org. ( Télécharger le PDF)

Quel compilateur utiliser ?


Auteurs : LFE ,
De nos jours, on utilise de plus en plus un environnement de développement (un logiciel qui intègre un éditeur (la zone
d'édition de code), un compilateur, un débogueur ainsi que d'autres outils qui s'avèrent généralement très utiles au
programmeur) car ils faicilitent énormément l'écriture, la génération et le test de logiciels.
Code::Blocks, multi-plateforme, et Visual C++ Express, disponible sous Windows uniquement, sont de très bons
EDIs (EDI : Environnement de Développement Integré) et conviennent aussi bien au débutant qu'au programmeur
déjà averti (en plus ils sont gratuits !).
Les meilleurs compilateurs C reprend également une liste de compilateurs et EDIs téléchargeables gratuitement.

Où trouver des exercices de langage C ?


Auteurs : gege2061 ,

Le livre Le langage C - Norme ANSI contient de nombreux exercices pour tous les niveaux et les solutions se trouvent
ici.

-7-
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Les types et les variables

-8-
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Les types et les variables > Généralités


Quelle est la taille d'un char ?
Auteurs : gl ,
Par définition, sizeof(char) vaut 1, c'est-à-dire qu'un char représente un byte. Toutefois, contrairement à une idée
répandue, en langage C un byte (multiplet et non octet) ne fait pas forcément 8 bits. La norme requiert néanmoins
qu'un byte doit faire au moins 8 bits et la taille exacte d'un char, en bits, est donnée par la macro CHAR_BIT définie
dans le fichier limits.h.

Quelle est la taille des différents types ?


Auteurs : gl ,
La norme n'impose pas la taille d'un type, cette taille dépend donc de l'implémentation. Toutefois la norme impose des
plages minimales pour chaque type :

+--------------------+-----------------------+----------------------+----------------------+
| Type | min minimum imposé | max minimum imposé | => Taille min : |
+--------------------+-----------------------+----------------------+----------------------+
| signed char | -127 | 127 | |
+--------------------+-----------------------+----------------------+ 8 bits |
| unsigned char | 0 | 255 | |
+--------------------+-----------------------+----------------------+----------------------+
| short | -32767 | 32767 | |
+--------------------+-----------------------+----------------------+ 16 bits |
| unsigned short | 0 | 65535 | |
+--------------------+-----------------------+----------------------+----------------------+
| int | -32767 | 32767 | |
+--------------------+-----------------------+----------------------+ 16 bits |
| unsigned int | 0 | 65535 | |
+--------------------+-----------------------+----------------------+----------------------+
| long | -2147483647 | 2147483647 | |
+--------------------+-----------------------+----------------------+ 32 bits |
| unsigned long | 0 | 4294967295 | |
+--------------------+-----------------------+----------------------+----------------------+
| long long | -9223372036854775807 | 9223372036854775807 | |
+--------------------+-----------------------+----------------------+ 64 bits |
| unsigned long long | 0 | 18446744073709551615 | |
+--------------------+-----------------------+----------------------+----------------------+

De plus la relation 'sizeof(short) <= sizeof(int) <= sizeof(long)' doit toujours être vérifiée.

-9-
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Les valeurs effectives des limites pour chaque type sont définies par l'implémentation et se trouvent dans les fichiers
limits.h (pour les entiers) et float.h (pour les flottants).

Que signifient signed et unsigned ?


Auteurs : Melem ,
Ces mot-clés ne peuvent s'utiliser qu'avec les types entiers c'est-à-dire char, short (short int), int, long (long int) et long
long (long long int). Il permettent d'indiquer si le type supporte des valeurs négatives (signed) ou non (unsigned). Sur
un type autre que char, signed est toujours facultatif.

Quelle est la différence entre char, signed char et unsigned char ?


Auteurs : Melem ,
Le type char désigne, selon l'implémentation, signed char ou unsigned char. Le type réel du type char doit être indiqué
dans la documentation du compilateur.

Pourquoi mon compilateur ne connaît pas le type long long ?


Auteurs : Melem ,
Parce que ce type n'a été introduit qu'avec la norme C99. Vous utilisez probablement un compilateur non conforme à
cette norme ou peut-être que les réglages de votre compilateur sont tels que seules les fonctionnalités C90 sont activées.

Quel est le rôle de l'opérateur sizeof ?


Auteurs : Melem ,
Il permet de connaître la taille, en bytes, d'une variable ou d'un type.

lien : Quelle est la taille d'un char ?

Existe-t-il un type booléen en C ?


Auteurs : LFE , David.Schris , gege2061 ,
Avant la norme C99, il n'existait pas en C de type booléen à proprement parler, mais il est très simple à 'créer' si le
compilateur utilisé ne le propose pas déjà en utilisant :

#define BOOL int


#define TRUE 1
#define FALSE 0

Ou :

typedef enum {
FALSE, TRUE
} BOOL;

- 10 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

En effet, en C, toute valeur nulle (0, 0.0, etc.) désigne un 'faux' tandis que toute valeur non nulle (1, -1, 2.0, etc.) désigne
un 'vrai'.
La norme C99 introduit le type _Bool et l'en-tête stdbool.h définit les macros bool (_Bool), true (1) et false (0) ainsi que
__bool_true_false_are_defined qui permet de savoir si les booléens sont nativement supportés.
Une solution permettant d'écrire du code compatible C99 consiste à utiliser les types natifs sur un compilateur conforme
C99 et définir un type booléen maison dans le contraire. Par exemple :

#ifndef __bool_true_false_are_defined
# define bool int
# define true 1
# define false 0
# define __bool_true_false_are_defined
#endif

Qu'est-ce que le type size_t ?


Auteurs : Melem ,
Ce type défini dans stddef.h (qui est inclus par de nombreux fichiers d'en-tête ...) correspond au type de la valeur
retournée par l'opérateur sizeof. La norme requiert que size_t soit un entier non signé mais le type exact auquel il
correspond dépend de l'implémentation.

Qu'est-ce que le type wchar_t ?


Auteurs : Melem ,
wchar_t (wide character) est le type qui permet de représenter n'importe quel élément du jeu de caractères courant de
l'environnement. Il peut être défini à l'aide d'un typedef ou être nativement supporté par le compilateur. A noter que
char et wchar_t étant des types différents, un caractère "large" (un caractère de type wchar_t) ne s'écrit pas de la même
façon qu'un simple caractère. Les caractères larges doivent toujours être précédés d'un L. Par exemple L'A', L'*' et
L'1' sont des caractères larges. C'est valable également pour les chaînes de caractères larges, par exemple : L"Bonjour"
au lieu de "Bonjour". De même, les fonctions de manipulation des caractères et/ou chaînes de caractères larges ne sont
pas les mêmes que celles qui sont dédiées aux caractères et/ou chaînes de caractères simples bien que l'on puisse noter
une certaine similitude au niveau des noms et des paramètres requis. Par exemple, pour les caractères et chaînes de
caractères larges, on utilise wprintf à la place de printf, wcscpy à la place de strcpy, iswalpha à la place de isalpha, etc.
Selon les fonctions que vous utilisez, vous devez inclure wchar.h et/ou wctype.h.
Le programme suivant montre un exemple d'utilisation des caractères larges.

#include <stdio.h>
#include <wchar.h>

int main()
{
wchar_t ws[256];

wcscpy(ws, L"Hello");
wcscat(ws, L", world !");
wprintf(L"%s\n", ws);

return 0;

- 11 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Comment créer un synonyme d'un type existant ?


Auteurs : LFE , gl , Melem ,
Dans certains cas très simples, un #define peut déjà faire l'affaire (par exemple : #define ENTIER int). Dans des cas un
peu plus complexes, il faudra définir le synonyme à l'aide du mot-clé typedef. Pour ce faire, il suffit, dans une déclaration
valide d'une variable du type voulu, remplacer l'identifiant de la variable par le nom qu'on veut donner à son type puis
de commencer la ligne par "typedef".
Supposons par exemple que nous souhaitons définir un synonyme pour le type int en utilisant cette méthode. Nous allons
donc donner un dexuième nom, ENTIER, à int.
1. Prennons donc une déclaration valide d'une variable de type int : int x;
2. Remplaçons l'identifiant de la variable (x) par le nom qu'on veut donner au type (ENTIER) : int ENTIER;
3. Ajoutons enfin le mot-clé typedef : typedef int ENTIER;
Voici quelques exemples supplémentaires :

typedef int VECTEUR[3];


typedef void * POINTEUR;
typedef struct { int value; } INTVALUE;

VECTEUR u, v; /* int u[3], int v[3]; */


POINTEUR p1, p2; /* void * p1, * p2; */
INTVALUE n; /* struct { int value; } n; */

- 12 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Les types et les variables > Structures et unions


Comment créer un alias pour le nom d'une structure ?
Auteurs : LFE , gl , Melem ,
typedef struct {
int x;
int y;
} POINT2D;

Ou :

struct point2d_s {
int x;
int y;
};

typedef struct point2d_s POINT2D;

Ou encore:

typedef struct point2d_s {


int x;
int y;
} POINT2D;

lien : Comment créer un synonyme d'un type existant ?

Quelle est la différence entre une structure et une union ?


Auteurs : LFE , gl , Melem ,
Une structure est composée de différents champs qui ne se chevauchent pas.

#include <stdio.h>

struct point_s {
int x;
int y;
};

int main(void)
{
struct point_s A;

A.x = 1;
A.y = 2;

printf("A = (%d, %d)\n", A.x, A.y);

return 0;
}

Une union est composée de différents champs qui se superposent ou, en d'autres termes, qui utilisent le même
emplacement mémoire. Il n'est donc tout simplement pas possible d'utiliser tous ces champs en même temps.

#include <stdio.h>

union duo_u {
int n;

- 13 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

double x;
};

int main(void)
{
union duo_u d;

d.n = 1;

/* Nous avons selectionne le champ n, nous devons utiliser ce champ */


/* et uniquement ce champ jusqu'a ce que nous n'en aurions plus besoin. */

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

d.n = 2;

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

d.x = 3.0;

/* Nous avons selectionne le champ x, nous devons utiliser ce champ */


/* et uniquement ce champ jusqu'a ce que nous n'en aurions plus besoin. */

printf("d.x = %f\n", d.x);

d.x = 4.0;

printf("d.x = %f\n", d.x);

return 0;
}

Comme nous pouvons le constater, les unions sont donc très pratiques pour économiser de la mémoire.

Pourquoi la taille d'une structure n'est pas forcément égale à la somme des tailles de ses champs ?
Auteurs : Bob , gl ,
A cause des contraintes d'alignement imposées par certains processeurs. Certains processeurs imposent en effet qu'une
donnée de type X doit se trouver en mémoire à une adresse respectant certains critères. Les champs d'une structure
ne sont donc pas forcément adjacents !

Comment copier une structure ?


Auteurs : Melem ,
En utilisant l'opérateur = ou la fonction memcpy (string.h).

#include <stdio.h>
#include <string.h>

struct personne_s {
char nom[21];
int age;
};

int main(void)
{
struct personne_s A, B, C;

strcpy(A.nom, "A");

- 14 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

A.age = 26;

B = A;
memcpy(&C, &A, sizeof(C));

printf("A.nom = %s\n", A.nom);


printf("A.age = %d\n", A.age);

printf("B.nom = %s\n", B.nom);


printf("B.age = %d\n", B.age);

printf("C.nom = %s\n", C.nom);


printf("C.age = %d\n", C.age);

return 0;
}

Comment comparer deux structures ?


Auteurs : Melem ,
L'opérateur == n'étant pas défini pour les structures, il faut faire une comparaison champ par champ. On ne peut
pas non plus utiliser la fonction memcmp car les champs d'une structure ne sont pas forcément adjacents comme les
éléments d'un tableau par exemple. Les espaces créés entre les différents champs lorsqu'ils existent peuvent contenir
des valeurs différentes même pour deux structures identiques or memcmp ne fait qu'une comparison bit par bit des
contenus des mémoires comparées. Elle ne peut donc être utilisée pour comparer des structures.

lien : Pourquoi la taille d'une structure n'est pas forcément égale à la somme des tailles de ses champs ?

Que signifie 'unsigned int i : <n>;' ?


Auteurs : nyal ,
'unsigned int i : <n>;' est une déclaration qui ne peut figurer que dans la définition d'une structure et qui permet de
créer un champ, ici i, dont la taille est spécifiée en bits. i est ce qu'on appelle en langage C un champ de bits.

struct test_s
{
unsigned int i : 1;
unsigned int j : 2;
};

int main(void)
{
struct test_s s;

s.i = 0; /* A la fin de l'instruction, on aura s.i = 0 en binaire soit 0 en decimal. */


s.i++; /* A la fin de l'instruction, on aura s.i = 1 en binaire soit 1 en decimal. */
s.i++; /* A la fin de l'instruction, on aura s.i = 0 en binaire soit 0 en decimal. */

s.j = 0; /* A la fin de l'instruction, on aura s.j = 00 en binaire soit 0 en decimal. */


s.j++; /* A la fin de l'instruction, on aura s.j = 01 en binaire soit 1 en decimal. */
s.j++; /* A la fin de l'instruction, on aura s.j = 10 en binaire soit 2 en decimal. */
s.j++; /* A la fin de l'instruction, on aura s.j = 11 en binaire soit 3 en decimal. */
s.j++; /* A la fin de l'instruction, on aura s.j = 00 en binaire soit 0 en decimal. */

return 0;
}

- 15 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Chaque implémentation doit au moins supporter les types int et unsigned int dans la déclaration d'un champ de bits.
Une implémentation particulière peut toutefois supporter également d'autres types.

- 16 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Les types et les variables > Les opérateurs


Quelle est la différence entre = et == ?
Auteurs : LFE ,
L'opérateur = est l'opérateur d'assignation, c'est-à-dire qu'il attribue la valeur qui se trouve à sa droite à l'élement qui
se trouve à sa gauche.
L'opérateur == est l'opérateur de comparaison, c'est-à-dire qu'il renvoie vrai si les éléments à sa gauche et à sa droite
sont identiques.

Pourquoi if (a = b) ne génère pas d'erreur de compilation ?


Auteurs : LFE , Melem ,
Parce qu'en C, l'opérateur = est valide dans un test bien que la plupart des compilateurs émettent un avertissement à
ce sujet. Le programme suivant :

#include <stdio.h>

int main(void)
{
int a = 5, b = 10;

if (a = b)
{
/* Vrai car l'expression a = b vaut la valeur de a apres affectation c'est-a-dire : 10. */
/* Or en C, toute valeur differente de 0 (par exemple 10) vaut 'VRAI', d'ou le resultat. */

printf("Coucou !\n");
}

return 0;
}

Affichera Coucou !
Le programme suivant par contre n'affichera rien du tout.

#include <stdio.h>

int main(void)
{
int a = 5, b = 10;

if (a == b)
{
/* Faux car a (5) est different de b (10). */

printf("Coucou !\n");
}

return 0;
}

lien : Quelle est la différence entre = et == ?

- 17 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

lien : Existe-t-il un type booléen en C ?

Que signifie 'x = (a == b) ? 5 : 10' ?


Auteurs : LFE ,
L'opérateur ?: est un opérateur ternaire c'est-à-dire qu'il prend 3 opérandes. L'expression a ? b : c vaut b si a vaut
'vrai' et c sinon. Ainsi, avec l'instruction 'x = (a == b) ? 5 : 10;', x vaut 5 si (a == b) et 10 dans le cas contraire.

Comment calculer le reste d'une division entière ?


Auteurs : Bob ,
Sur un nombre positif, l'opérateur modulo (%) correspond strictement à sa définition mathématique, c'est-à-dire calcule
le reste de la division entière. Par exemple 5 % 3 donne 2 (reste de la division de 5 par 3). Retenez donc qu'un nombre
quelconque modulo N sera toujours compris entre 0 et N, N exclu. La norme n'exige pas que l'opérateur modulo puisse
également s'utiliser avec un nombre négatif.

Que fait exactement l'opérateur / ?


Auteurs : Laurent Gomila ,
Voici un petit rappel des règles s'appliquant aux divisions en C :
• Si les deux opérandes sont de type entier (char, int, short, long, long long) alors la division effectuée sera entière,
et donnera de ce fait toujours un résultat entier.
• Si au moins l'une des deux opérandes est de type flottant (float, double, long double) alors la division effectuée
sera réelle, donnant un résultat décimal.
• Le type du résultat de la division est celui de l'opérande réelle (si au moins l'une des deux l'est) possédant la plus
grande capacité.

double r1 = 7 / 10; /* r1 = 0 */
double r2 = 7.0 / 10; /* r2 = 0.7 */
double r3 = 7 / 10.0f; /* r3 = 0.7f */
double r4 = 7.0 / 10.0f; /* r4 = 0.7 */

Quelle est la différence entre i++ et ++i ?


Auteurs : LFE ,
L'opérateur ++ est un opérateur d'incrémentation : il ajoute 1 à la variable à laquelle il est appliqué. La seule différence
est que i++ permet d'utiliser et seulement ensuite de l'incrémenter, alors que ++i incrémente la variable avant de
l'utiliser.
L'opérateur -- fonctionne exactement de la même façon, sauf qu'il s'agit d'une décrémentation.

Que font les opérateurs << et >> ?


Auteurs : LFE , gl ,
Ces deux opérateurs effectuent un déplacement (shift) des bits dans une variable.
<< effectue un décalage à gauche du nombre de positions précisées
>> effectue un décalage à droite du nombre de positions précisées

- 18 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

<<= effectue un décalage à gauche du nombre de positions précisées et assigne le résultat à la première opérande
>>= effectue un décalage à droite du nombre de positions précisées et assigne le résultat à la première opérande

unsigned char c1, c2, c3, c4;

c1 = 5; /* 00000101 */
c2 = 4; /* 00000100 */

c3 = c1 << 2; /* 00000101(c1) -> 00010100(c3) */


c4 = c2 >> 2; /* 00000100(c2) -> 00000001(c4) */

c1 <<= 2; /* 00000101(c1) -> 00010100(c1) */


c2 >>= 2; /* 00000100(c2) -> 00000001(c2) */

Que signifie E1 & E2 ?


Auteurs : LFE , gl ,
Cela signifie qu'on effectue un ET bit à bit entre E1 et E2. Le bit résultant est 1 lorsque les 2 bits sont à 1, 0 sinon.
0 & 0 -> 0
0 & 1 -> 0
1 & 0 -> 0
1 & 1 -> 1

++ unsigned char c1, c2;


++
++ c1 = 5; /* 00000101 */
++ c2 = 4; /* 00000100 */
-> c1 & c2 = 4 /* 00000100 */

On dit qu'on a masqué c1 avec c2. On appelle ça (c2) un masque car il permet de cacher (masquer) certains bits de
c1 (cacher signifie mettre à zéro).

lien : Comment accéder à un bit d'une variable ?

Que signifie E1 | E2 ?
Auteurs : LFE , gl ,
Cela signifie qu'on effectue un OU bit à bit entre E1 et E2. Le bit résultant est 1 lorsque un au moins des 2 bits est
à 1, 0 sinon.
0 | 0 -> 0
0 | 1 -> 1
1 | 0 -> 1
1 | 1 -> 1

++ unsigned char c1, c2;


++
++ c1 = 5; /* 00000101 */
++ c2 = 4; /* 00000100 */

- 19 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

-> c1 | c2 = 5 /* 00000101 */

lien : Comment accéder à un bit d'une variable ?

Quelle est la différence entre (E1 && E2) et (E1 & E2) ?
Auteurs : LFE , gl ,
L'opérateur && désigne l'opérateur ET logique. E1 && E2 correspond à l'expression 'E1 est vrai ET E2 est vrai'.
L'opérateur & désigne l'opérateur ET binaire. E1 & E2 retourne donc le ET bit à bit entre E1 et E2.
De même, || désigne l'opérateur OU logique. E1 || E2 correspond à l'expression 'E1 est vrai OU E2 est vrai'. L'opérateur
| désigne l'opérateur OU binaire. E1 | E2 retourne donc le OU bit à bit entre E1 et E2.
L'opérateur ^ quant à lui permet de faire un OU EXCLUSIF (XOR) binaire. Il n'y a pas d'opérateur qui fasse le OU
EXCLUSIF logique (E1 '^^' E2 = (E1 && !E2) || (E2 && !E1)).

lien : Que signifie E1 & E2 ?


lien : Que signifie E1 | E2 ?

Comment est évaluée l'expression E1 && E2 ?


Auteurs : gl ,
Lors de l'utilisation des opérateurs && et ||, les différents membres sont évalués de gauche à droite. L'évaluation est
stoppée dès que le résultat final est connu de manière certaine, c'est-à-dire :
• Si lors de l'utilisation de && un membre est évalué à faux.
• Si lors de l'utilisation de || un membre est évalué à vrai.

Comment accéder à un bit d'une variable ?


Auteurs : gl ,
Il est souvent intéressant d'accéder aux bits d'une variable, ce qui permet, par exemple, de stocker 8 flags (nombre
de bits minimal d'un char) dans un seul char plutôt que d'utiliser un char par flag. Pour manipuler les bits, il suffit
d'utiliser les opérateurs du langage : & (opérateur ET) pour tester un bit, | (opérateur OU) pour positionner un bit et
& (opérateur ET) et ~ (opérateur NON) pour enlever un bit
Voici deux petites macros, permettant respectivement de positionner un flag et de lire la valeur d'un flag :

#define SET(flag, bit) ((flag) |= (1 << (bit)))


#define CLEAR(flag, bit) ((flag) &= ~(1 << (bit)))
#define GET(flag, bit) ((flag) & (1 << (bit)))

Remarque : dans ces macros, nous considérons que le bit de poids faible est le bit 0.

- 20 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Les types et les variables > Les variables


Que signifie le mot-clé const ?
Auteurs : gl ,
Le mot-clé const indique que la variable est en lecture seule. Une variable constante doit donc être initialisée car en
dehors de son initialisation, il n'est pas possible de lui assigner une valeur. Etant donné que les arguments d'une fonction
se comportent, hormis quelques subtilités, comme de simples variables locales à cette fonction, initialisés avec les valeurs
passés en arguments lors d'un appel, le mot-clé const peut également s'utiliser avec les arguments d'une fonction.

Que signifie le mot-clé static ?


Auteurs : gl , David.Schris ,
Le modificateur static a deux significations différentes selon le contexte dans lequel il agit :
• Sur une variable locale, il permet de définir une variable maintenue pendant toute la durée d'exécution du
programme (comme les variables globales) seulement, puisqu'elle a été déclarée à l'intérieur d'une fonction, sa
visibilité sera limitée au corps de cette fonction.
• Sur une variable globale, le modificateur static permet de réduire la visibilité de la variable au fichier où elle est
déclarée.

Associé à une fonction, ce mot-clé réduit la visibilité de cette fonction au fichier source où elle est déclarée. Une telle
fonction ne pourra donc pas être utilisée depuis un autre fichier.

Que signifie le mot-clé volatile ?


Auteurs : GoldenEye ,
Le modificateur volatile signale au compilateur que la variable est susceptible d'être modifiée par le programme mais
aussi par des facteurs extérieurs.
La déclaration d'un objet/d'une variable en tant que volatile avertit le compilateur de ne pas faire d'hypothèses sur la
valeur de l'objet/variable pendant l'évaluation des expressions dans lesquelles il/elle apparaît car la valeur peut changer
à tout moment.

Qu'est-ce que sont que la pile et le tas ?


Auteurs : LFE ,
La pile et le tas sont deux zones de la mémoire qui ont chacunes des rôles spécifiques. La pile, de taille plus réduite,
sert à contenir entre autres les variables locales des fonctions. Le tas, d'une taille plus importante, sert à contenir les
variables permanentes et les espaces alloués dynamiquement.

- 21 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Les types et les variables > Les nombres flottants
Comment tronquer un réel ?
Auteurs : Bob ,
En l'affectant à une variable de type entier.

double f = 10.9;
int n = f; /* n contient la troncature de f (c'est-a-dire 10) */

L'affectation d'une valeur réelle à un entier pousse cependant la plupart des compilateurs à émettre un avertissement
(warning). Pour éviter ces messages, il vaut mieux caster le réel en int puis affecter le résultat à la variable entière.

Comment arrondir un réel ?


Auteurs : Jean-Marc.Bourguet ,
Les fonctions floor() et ceil(), déclarées dans math.h, permettant d'obtenir respectivement l'entier immédiatement
inférieur (la "partie entière") et l'entier immédiatement supérieur. Notez toutefois que ces fonctions retournent des
"double" et non des "int". Pour arrondir un nombre à son entier le plus proche, il suffit de lui ajouter 0.5 et de prendre
la partie entière du résultat.
Maintenant, pour arrondir un nombre à n décimales après la virgule, on peut utiliser la méthode suivante :
- multiplier le nombre par 10 à la puissance n
- arrondir le résultat puis diviser par 10 à la puissance n
La fonction suivante arrondit le nombre passé en argument à n décimales après la virgule.

#include <math.h>

unsigned dix_puissance_n(unsigned n)
{
unsigned i, res = 1;

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


res *= 10;

return res;
}

double arrondi(const double x, unsigned n)


{
unsigned N = dix_puissance_n(n);
return floor(x * N + 0.5) / N;
}

Comment comparer deux réels ?


Auteurs : Melem ,
L'opérateur == est défini pour les nombres réels cependant, étant donné que cet opérateur ne fait qu'une comparaison
bit à bit des valeurs comparées, il ne doit être utilisé que pour ce type de comparaison or ce n'est généralement pas ce
que l'on souhaite faire. Pour tester l'égalité de deux réels, il faut calculer leur différence et voir si elle est assez petite (et
assez petite est à définir par le programmeur !). En effet, les calculs avec les nombres réels ne se font pas toujours avec
une précision infinie. Cela signifie qu'un calcul qui devrait donner pour résultat 0.0 par exemple peut ne pas retourner
exactement 0.0 à cause des éventuelles pertes en précision qui ont pu avoir lieu lors des calculs intermédiaires et c'est
pourquoi, le programmeur doit toujours lui-même définir une certaine tolérance à l'erreur à chaque calcul utilisant
les nombres réels.

- 22 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

La source de ce problème, c'est qu'il existe une infinité de nombres réels et l'ordinateur n'est bien entendu pas capable
de pouvoir les représenter tous. En d'autres termes, l'ensemble des nombres représentables par l'ordinateur est discret
et fini et non continu. La distance entre deux réels consécutifs de cet ensemble n'est cependant pas forcément constante.
Il existe une macro définie dans float.h appelée DBL_EPSILON qui représente la plus petite valeur x de type double
tel que 1.0 + x != 1.0. En d'autres termes, DBL_EPSILON est le réel tel que le successeur de 1.0 dans l'ensemble des
nombres représentables par le système est 1.0 + DBL_EPSILON.
Donc comment choisir epsilon ? Tout dépend de l'ordre de grandeur des valeurs comparées et de la tolérance à l'erreur
que l'on veut avoir. Il n'y a donc rien d'absolu (ou "d'universel") que l'on peut affirmer sur ce sujet. Si les nombres
comparés sont de l'ordre de l'unité (mais pas trop proches de zéro), on peut choisir un epsilon absolu. Si la distance
entre les deux nombres (c'est-à-dire |x - y|) est inférieure à epsilon, alors on doit les considérer comme "égaux". Dans les
autres cas, il faut généralement être beaucoup moins tolérant (comparaison de nombres proches de zéro par exemple)
ou beaucoup plus tolérant (comparaison de grands nombres). Il faut par exemple considerer un epsilon e tel que x est
égal à y (on suppose que |x| < |y|) s'il existe un réel k tel que |k| < e et y = x + k.x. e est appelée erreur relative maximale
autorisée. k est l'erreur relative qu'il y a entre x et y. L'avantage de cette méthode, c'est qu'elle choisit automatiquement
le epsilon absolu qui va le mieux en fonction des nombres comparés. C'est donc cette méthode qui est utilisée dans la
plupart des applications.
Voici une fonction qui permet de comparer deux réels avec deux epsilons spécifiés : le premier absolu et le deuxième
relatif. Choisir 0 comme epsilon absolu pour l'ignorer (c'est-à-dire, comparer directement en utilisant le epsilon relatif).

/* Fonction dbl_cmp : Compare deux reels x et y */


/* Retourne : */
/* -1 si x < y */
/* 0 si x = y */
/* 1 si x > y */

int dbl_cmp(double x, double y, double eps_a, double eps_r)


{
double a, b;
int ret;

/* On tente tout d'abord une comparaison bit a bit. */

if (x < y)
{
a = x;
b = y;
ret = -1;
}
else if (x > y)
{
a = y;
b = x;
ret = 1;
}
else
ret = 0;

/* Si x != y, on tente alors une comparaison avec tolerance a l'erreur. */

if (ret != 0)
{
/* Si eps_a != 0, l'utiliser. */

if (b - a < eps_a)
{
ret = 0;
}

if (ret != 0)
{
/* Si on a encore ret != 0 (i.e. x != y), utiliser eps_r. */

if ((b - a) / a < eps_r)

- 23 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

ret = 0;
}
}

return ret;
}

- 24 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Pointeurs, tableaux et chaînes de caractères

- 25 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Pointeurs, tableaux et chaînes de caractères > Les pointeurs


Qu'est-ce qu'un pointeur ?
Auteurs : Bob ,
Chaque donnée en mémoire a une "adresse". Cette adresse permet au processeur de retrouver la donnée et de demander
sa lecture ou son écriture. Un pointeur est une variable qui contient une adresse (ça peut être l'adresse d'une variable,
celle d'une fonction ou encore celle d'un élément d'un tableau, etc.). Par abus de langage, on utilise aussi parfois le mot
"pointeur" pour désigner une "adresse".

Quand utiliser les pointeurs ?


Auteurs : Bob ,
Il est difficile de fournir une liste exhaustive des cas où on doit on devrait utiliser les pointeurs mais voici quelques
exemples :
• Chaque fois qu'on veut passer "une variable" à une fonction.
• Chaque fois qu'on veut passer un objet de taille trop importante à une fonction. En effet, il est moins coûteux
(en termes temps d'exécution et de consommation de mémoire) de passer l'adresse de l'objet seulement que de
passer une copie de l'objet lui-même (qui peut faire plusieurs octets contre 4 seulement pour une adresse sur un
processeur Intel 32 bits par exemple) à la fonction.
• Chaque fois qu'on veut allouer dynamiquement de la mémoire.
• Chaque fois que le langage l'impose (par exemple, le nom d'un tableau, sauf lorsqu'il apparaît en argument de
l'opérateur sizeof, est toujours converti par le compilateur en l'adresse de son premier élément !).

lien : Comment passer "une variable" à une fonction ?

Qu'est-ce que NULL ?


Auteurs : LFE , gl ,
NULL est une macro définie dans <stddef.h> (qui est inclus par stdio.h, stdlib.h, etc.) et sert à représenter une adresse
invalide. NULL est par exemple utilisé dans les fonctions retournant un pointeur pour indiquer que la fonction a échoué.
Affecter NULL à un pointeur sert donc à indiquer que le pointeur est actuellement inutilisé (ou pointe "nulle-part").
La norme stipule que n'importe quelle expression entière vallant 0 ou une telle expression castée en void * représente
une telle adresse.

Comment utiliser un pointeur sur une structure ?


Auteurs : gl , fearyourself , Melem ,
Un pointeur sur une structure se déclare comme tout autre pointeur :

struct base {
int a;
double b;
};

struct base Elem;


struct base * p = &Elem;

Pour accéder au membre de la structure via le pointeur il est nécessaire de déréférencer ce pointeur :

- 26 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

(*p).a = 5;

Cette écriture pouvant se simplifier en :

p->a = 5;

(*p).a n'est cependant pas strictement équivalent à p->a car la première déréférence p (lit sizeof(struct base) octets à
partir de cette adresse) avant d'entrer dans le champ a alors que p->a accède directement à la mémoire utile. (*p).a est
donc non seulement plus long a écrire que p->a mais également moins performant.

Quelle est la différence entre sizeof(struct data) et sizeof(struct data *) ?


Auteurs : Bob , gl ,
sizeof(struct data) fournit la taille de la structure alors que sizeof(struct data *) fournit celle d'un pointeur.

Comment déclarer un pointeur sur une fonction ?


Auteurs : Melem ,
Le plus simple est de définir dans un premier temps le type de la fonction sur laquelle on veut pointer :

typedef int F(void); /* F : type de la fonction f. */

/* Voici la fonction f en question : */

int f(void)
{
return 1;
}

Un pointeur sur f se déclare donc ainsi :

F * p = &f;

En fait, le nom d'une fonction (f), lorsqu'il n'est pas utilisé dans sizeof ou avec l'opérateur & ("adresse de"), est toujours
converti par le compilateur en &f. Cela signifie qu'on aurait pu également écrire tout simplement :

F * p = f;

Et enfin, pour appeler f, les deux écritures suivantes sont toutes valides :

y = p();
y = (*p)();

Sans typedef, p devrait avoir été déclaré ainsi :

int (*p)(void) = f;

- 27 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

En effet, on veut un pointeur p vers une fonction qui ne prend aucun argument et qui retourne un int. (*p) vaut
donc "une fonction" (même si une telle expression, sauf si elle est utilisée comme argument de sizeof, sera toujours
automatiquement convertie en l'adresse de la fonction ...). Cette fonction prend void comme argument retourne un int.
Autrement dit, (*p)(void) vaut un int d'où int (*p)(void) !

p et q pointent sur deux objets identiques mais (p == q) renvoie toujours faux ! Pourquoi ?
Auteurs : LFE , Melem ,
C'est tout à fait normal, le test (p == q) compare les deux pointeurs et non le contenu des emplacements qu'ils pointent.
Pour comparer le contenu, il faut comparer *p et *q ou utiliser la fonction memcmp.

int * p, * q, n = 10;

/* On suppose ici que malloc ne peut pas echouer */

p = malloc(sizeof(int));
q = malloc(sizeof(int));

memcpy(p, amp;&n, sizeof(int)); /* Ou simplement *p = n; */


memcpy(q, amp;&n, sizeof(int)); /* Ou simplement *q = n; */

/* (*p == *q) mais (p != q) */

free(p);

p = q;

/* Maintenant (p == q) et donc naturellement (*p == *q) */

free(p); /* Ou free(q) */

Qu'est-ce que *p++ incrémente ?


Auteurs : LFE ,
Les opérateurs unaires (*, ++ et --) ont une priorité élevée et sont évalués de droite à gauche.
*p++; retourne la valeur de la zone mémoire désignée par p et incrémente ensuite p.
(*p)++; incrémente quant à lui la valeur de la zone mémoire désignée par *p.

Quelle est la différence entre pointeurs constants et pointeurs sur constante ?


Auteurs : gl ,
Il convient de ne pas confondre les pointeurs constants (le pointeur ne pourra pas être modifié, mais la variable pointée
si) et les pointeurs sur constantes (le pointeur pourra être modifié mais pas la variable pointée).

Exemple de pointeur constant


char buf[] = "bonjour";
char * const p = buf; /* Le pointeur p est declare constant */

p++; /* Incorrect car on ne peut pas modifier une constante */


p[4]++; /* Correct */

Exemple de pointeur sur des constantes


char buf[] = "bonjour";

- 28 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Exemple de pointeur sur des constantes


char const * p = buf; /* Ou const char * p = buf; */

p++; /* Correct */
p[4]++; /* Incorrect car les donnees pointees par p ont ete declarees constantes (const char) */

Il est bien entendu possible de combiner les deux pour créer un pointeur constant sur des constantes :

Exemple de pointeur constant sur des constantes


char buf[] = "bonjour";
char const * const p = buf;

p++; /* Incorrect */
p[4]++; /* Incorrect */

Comment connaître le type d'une variable adressée par un pointeur void * ?


Auteurs : gl , LFE ,
La mémoire n'étant pas typée (le contenu de la mémoire dépend toujours de l'interprétation qu'on en fait), on ne peut
pas connaître le type d'un objet pointé par un void *. Si le pointeur est utilisé pour créer une fonction générique, il est
nécessaire de rajouter à la fonction un paramètre indiquant la taille ou le type de la variable utilisée :

typedef enum {
TYPES_char,
TYPES_short,
TYPES_int,
TYPES_long,
TYPES_float,
TYPES_double
} TypePtr;

void MyFunction(void * Ptr, TypePtr Type);

- 29 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Pointeurs, tableaux et chaînes de caractères > Les tableaux


Que signifie 'int t[] = {10, 20};' ?
Auteurs : Melem ,
'int t[] = {10, 20};' crée un tableau de 2 éléments initialisé avec les valeurs t[0] = 10 et t[1] = 20. Cette écriture est donc
strictement équivalente à : 'int t[2] = {10, 20};'.

Que signifie 'int t[10] = {10, 20};' ?


Auteurs : Melem ,
'int t[10] = {10, 20};' crée un tableau de 10 éléments initialisé avec les valeurs t[0] = 10, t[1] = 20 et t[2] à t[9] = 0. Notez
bien que la mise à 0 des éléments non initialisés d'une variable locale tableau n'a lieu que lorsqu'un élément au moins
a été initialisé.

Soit t un tableau. Quelle est la différence entre t, &t et &(t[0]) ?


Auteurs : Bob , Melem ,
t représente l'objet tableau, &t l'adresse de ce tableau et &(t[0]) l'adresse du premier élément du tableau. Ces trois
expressions sont donc très différentes et n'ont à priori aucun point commun. Toutefois, &t et &(t[0]) représentent la
même adresse, même si elles n'ont pas le même type (pour rappel, les adresses sont typées en C). t tout court quant à
lui, lorsqu'il n'est pas utilisé dans sizeof ou avec l'opérateur & ("adresse de"), est toujours converti par le compilateur
en &(t[0]). D'autres conversions de ce genre existent également (avec plus ou moins de conditions pour avoir lieu) et
elles sont nombreuses. Ce sont ce qu'on appelle les conversions implicites.
La chose la plus importante à retenir est donc qu'une expression de type tableau (comme notre t ci-dessus, qui est de
type int [10]), sauf lorsqu'elle est utilisée en unique argument de sizeof ou de l'opérateur & ("adresse de"), est toujours
convertie par le compilateur en un pointeur vers son premier élément. Sinon il est reste un tableau.

Soit t un tableau. Que signifie *(t + 3) ?


Auteurs : gege2061 ,
Pour comprendre le sens de cette expression, il faut au moins savoir deux choses :
- Les adresses sont typées en C. Ajouter 1 à un pointeur revient à se déplacer en mémoire de la taille de la donnée
pointée et non de 1 byte.
- Le nom d'un tableau (t), sauf lorsqu'il apparaît dans un sizeof ou à droite de l'opérateur & ("adresse de"), représente
toujours un pointeur vers son premier élément (c'est-à-dire &(t[0])).
Le petit programme suivant devrait rendre les choses plus claires.

#include <stdio.h>

int main(void)
{
int t[] = {10, 20, 30, 40};
int * p = t;

/* p pointe sur t[0] donc *p equivaut a t[0]. */

printf("t[0] = %d\n", *p);

/* p pointe sur t[0] donc p + 1 pointe sur t[1]. *(p + 1) equivaut donc a t[1]. */

- 30 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

printf("t[1] = %d\n", *(p + 1));

/* p pointe sur t[0] donc p + 2 pointe sur t[2]. *(p + 2) equivaut donc a t[2]. */

printf("t[2] = %d\n", *(p + 2));

/* p pointe sur t[0] donc p + 3 pointe sur t[3]. *(p + 3) equivaut donc a t[3]. */

printf("t[3] = %d\n", *(p + 3));

return 0;
}

Ou, sans l'aide de la variable p :

#include <stdio.h>

int main(void)
{
int t[] = {10, 20, 30, 40};

printf("t[0] = %d\n", *t);


printf("t[1] = %d\n", *(t + 1));
printf("t[2] = %d\n", *(t + 2));
printf("t[3] = %d\n", *(t + 3));

return 0;
}

Quel est le rôle de l'opérateur [] ?


Auteurs : gege2061 , Melem ,
Cet opérateur permet d'accéder à un élément d'un tableau. Il requiert deux opérandes : une de type pointeur et l'autre
de type entier. L'expression X[Y] est équivalente à *((X) + (Y)). Autrement dit, si p possède un type pointeur, alors les
expressions p[1], 1[p] et *(p + 1) sont strictement équivalentes, tout comme p[-1], (-1)[p] et *(p - 1), etc.

lien : Soit t un tableau. Que signifie *(t + 3) ?

Comment déclarer et utiliser un tableau "à plusieurs dimensions" ?


Auteurs : Melem ,
En langage C, on ne classe pas les tableaux selon leurs dimensions, un tableau "à plusieurs dimensions" n'est d'ailleurs
en C qu'un tableau dont les éléments sont eux aussi des tableaux. Il n'y a donc pas des règles spéciales pour ces tableaux
"à plusieurs dimensions". Voici un exemple de programme manipulant un tableau dont les éléments sont des tableaux.

#include <stdio.h>

int main(void)
{
int t[10][4]; /* t est un tableau de 10 elements ou */
/* chaque element est un tableau de 4 entiers */

size_t j, i;

/* Parcourons les elements du tableau (t[0] a t[9]) */


for(j = 0; j < sizeof(t) / sizeof(t[0]); j++)
{
/* Pour chaque element t[j] du tableau, initialisons */

- 31 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

/* les elements de t[j] (t[j][0] a t[j][3]) */

for(i = 0; i < sizeof(t[j]) / sizeof(t[j][0]); i++)


{
t[j][i] = j;

/* Puis affichons t[j][i] */

printf("%d\t", t[j][i]);
}

printf("\n");
}

return 0;
}

Que signifie 'int (*p)[4];' ?


Auteurs : Melem ,
'int (*p)[4];' crée une variable p tel que (*p) est un tableau de 4 entiers. p est donc un pointeur vers un tableau de 4
entiers. Il est de type int (*)[4]. On ne peut pas utiliser un tel nom de type pour déclarer une variable par exemple
mais on peut par contre l'utiliser dans un cast ou un sizeof. Le programme suivant donne un exemple d'utilisation d'un
pointeur vers tableau.

#include <stdio.h>

int main(void)
{
int t[10][4];
int (*p)[4] = t; /* p pointe sur t[0]. *p <=> t[0]. */;

( *p )[0] = 0; /* t[0][0] = 0 */
( *p )[1] = 0; /* t[0][1] = 1 */

( *(p + 1) )[0] = 1; /* t[1][0] = 1 */


( *(p + 1) )[1] = 1; /* t[1][1] = 1 */

printf("t[0][0] = %d\n", t[0][0]);


printf("t[0][1] = %d\n", t[0][1]);
printf("t[1][0] = %d\n", t[1][0]);
printf("t[1][1] = %d\n", t[1][1]);

return 0;
}

Comment déclarer et utiliser un tableau de pointeurs de fonctions ?


Auteurs : joellel , Melem ,
#include <stdio.h>

/* Definition d'un type fonction */

typedef int F(void);

/* Voici deux fonctions de type F */

int f1(void);
int f2(void);

- 32 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

/* Maintenant la fonction principale */

int main(void)
{
F * t[2];

t[0] = f1;
t[1] = f2;

printf("t[0]() = %d\n", t[0]());


printf("t[1]() = %d\n", t[1]());

return 0;
}

int f1(void)
{
return 1;
}

int f2(void)
{
return 2;
}

Etant donné que les fonctions, à l'instar des variables permanentes, existent en mémoire pendant toute l'exécution du
programme, l'adresse d'une fonction peut être utilisée pour initialiser une variable permanente (en termes techniques,
l'adresse d'une fonction, tout comme l'adresse d'une variable permanente, est donc, en C, une "expression constante").

#include <stdio.h>

typedef int F(void);

int f1(void);
int f2(void);

F * t[] = {f1, f2};

int main(void)
{
printf("t[0]() = %d\n", t[0]());
printf("t[1]() = %d\n", t[1]());

return 0;
}

int f1(void)
{
return 1;
}

int f2(void)
{
return 2;
}

Comment passer un tableau en paramètre à une fonction ?


Auteurs : Bob , gl , Melem ,
Lorsqu'un tableau figure en paramètre d'une fonction, il est automatiquement converti en son adresse (plus précisément
en l'adresse de son premier élément). Un tableau n'est donc jamais copié par une fonction. La méthode généralement

- 33 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

utilisée pour passer un tableau à une fonction c'est de passer un pointeur vers son premier élément et sa taille (si
nécessaire) comme paramètres.

#include <stdio.h>

void initialiser_tab(int * ptr, size_t n_elements)


{
size_t i;

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


ptr[i] = (int)i;
}

int main(void)
{
int t[10];
size_t i, n_elements = sizeof(t) / sizeof(t[0]);

/* t peut egalement s'ecrire &(t[0]) */


initialiser_tab(t, n_elements);

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


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

return 0;
}

Etant donné qu'un tableau est toujours converti en un pointeur vers son premier élément, les prototypes suivants sont
strictement les mêmes :

void initialiser_tab(int * ptr, size_t n_elements)


void initialiser_tab(int ptr[], size_t n_elements)
void initialiser_tab(int ptr[10], size_t n_elements) /* Le 10 ne sert absolument a rien */

Voici un exemple avec un tableau "à deux dimensions" :

#include <stdio.h>

/* initialiser_tab_2 : initialise un tableau de N elements ou */


/* chaque element est un tableau de M entiers . */

void initialiser_tab_2(int * ptr, size_t N, size_t M)


{
/* ptr = &(t[0][0]) d'ou : */
/* - ptr[i] <=> t[0][i] */
/* - ptr[M * j] <=> t[j][0] */
/* - ptr[M * j + i] <=> t[j][i] */

size_t j, i;

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


{
for(i = 0; i < M; i++)
{
ptr[M * j + i] = (int)j;
}
}
}

int main(void)
{
int t[10][4];
size_t j, i;
size_t N = sizeof(t) / sizeof(t[0]), M = sizeof(t[0]) / sizeof(t[0][0]);

- 34 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

/* &(t[0][0]) peut egalement s'ecrire t[0] ou encore (int *)t */


initialiser_tab_2(&(t[0][0]), N, M);

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


{
for(i = 0; i < M; i++)
printf("%d\t", t[j][i]);

printf("\n");
}

return 0;
}

Comment copier un tableau ?


Auteurs : Bob , Emmanuel Delahaye ,
Pour copier un tableau, on a deux solutions. La première consiste à copier chaque case du tableau numéro 1 dans la
case correspondante du tableau numéro 2. Mais cette manière est parfois complexe et risque d'être lente.
La deuxième possibilité est d'utiliser la fonction memcpy(). Elle admet trois paramètres, le premier étant le tableau de
destination et le deuxième le tableau source. Enfin, le troisième est le nombre d'octets à copier. Par exemple, pour copier
le tableau Tab1 dans le tableau Tab2, il suffit de faire :

#include <string.h>

int Tab1[10], Tab2[10];

memcpy(Tab2, Tab1, sizeof Tab2);

- 35 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Pointeurs, tableaux et chaînes de caractères > Les chaînes de caractères
Qu'est-ce qu'une chaîne de caractères ?
Auteurs : Bob , Emmanuel Delahaye , gl ,
Une chaîne de caractères est un groupe de caractères réunis dans un ordre précis. En langage C, un caractère spécial,
noté '\0', doit apparaître à la fin de toute chaîne sans quoi il serait impossible de détecter sa fin. Ainsi, en C, la chaîne
"abc" par exemple est constituée de 4 caractères à savoir le caractère 'a', le caractère 'b', le caractère 'c' et enfin le
caractère de fin de chaîne '\0'. Plus précisément, "abc" désigne un tableau de 4 caractères initialisé avec 'a', 'b', 'c' et '\0'.

Qu'est-ce que le caractère NUL ?


Auteurs : Bob , Melem ,
Le caractère NUL, '\0', est le caractère dont tous les bits sont à zéro, autrement dit le caractère vallant 0.

Pourquoi ce NUL est-il obligatoire en fin d'une chaîne ?


Auteurs : Bob , Melem ,
Car il permet d'indiquer la fin de la chaîne. Quand on pointe sur une chaîne, tous les caractères qui suivent le caractère
de fin de chaîne seront ignorés par les fonctions recevant en argument une chaîne de caractères.

Quelle est la différence entre 'a' et "a" ?


Auteurs : Melem ,
'a' est l'expression d'un caractère tandis que "a" désigne un tableau de 2 caractères composé de deux caractères : 'a'
et '\0'.

Que signifie 'char s[21] = "Bonjour";' ?


Auteurs : Bob , gl , Emmanuel Delahaye ,
'char s[21] = "Bonjour";' crée un tableau de 21 éléments (de type char) initialisé avec la chaîne "Bonjour" ('B', 'o', 'n',
'j', 'o', 'u', 'r' et '\0'). Cette écriture est strictement équivalente à :

char s[21] = {'B', 'o', 'n', 'j', 'o', 'u', 'r', '\0'};

La fonction strcpy permet de copier une chaîne vers un autre tableau.

#include <stdio.h>
#include <string.h>

int main(void)
{
char s[21];

strcpy(s, "Bonjour");
printf("%s\n", s);

- 36 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

return 0;
}

Que signifie 'char * p = "Bonjour";' ?


Auteurs : Bob , gl , Emmanuel Delahaye ,
'char * p = "Bonjour";' crée un pointeur p qui pointe sur une chaîne vallant "Bonjour". L'emplacement des chaînes
littérales dépend de l'environnement, le langage C ne garantit pas que cette zone soit modifiable.

char * p = "Bon*our";

p[3] = 'j'; /
* Incorrect (mais accepte par le compilateur !). Il n'est pas garanti que p[3] soit modifiable. */
strcpy(p, "Salut"); /* Incorrect (mais accepte par le compilateur !). Pour la meme raison. */
p = "Salut"; /* Correct. p pointe maintenant sur une chaine de caracteres valant "Salut". */

N.B. : Pour écrire du code portable, il faut considérer que la zone est en lecture seule et il est donc conseillé de définir
le pointeur en tant que pointeur sur des constantes.

char const * p = "Bon*our";

p[3] = 'j'; /
* Incorrect. Immediatement rejete par le compilateur. On ne peut pas modifier une 'constante' ! */
strcpy(p, "Salut"); /* Incorrect. Immediatement rejete par le compilateur. Pour la meme raison. */
p = "Salut"; /* Correct. p pointe maintenant sur une chaine de caracteres valant "Salut". */

Comment convertir une chaîne de caractères en minuscules ?


Auteurs : Bob ,
Il faut utiliser la fonction tolower(). Mais elle ne convertit qu'un seul caractère, il faut donc utiliser une boucle. Sous
Windows on peut utiliser directement les fonctions CharLower ou CharLowerBuff qui convertissent une chaîne de
caractères en minuscules.

Comment convertir une chaîne de caractères en majuscules?


Auteurs : Bob ,
Il faut utiliser la fonction toupper(). Mais elle ne convertit qu'un seul caractère, il faut donc utiliser une boucle. Sous
Windows on peut utiliser directement les fonctions CharUpper ou CharUpperBuff qui convertissent une chaîne de
caractères en majuscules.

Comment convertir un nombre en chaîne de caractères ?


Auteurs : Bob , gl ,
Pour convertir un nombre en chaîne de caractères, on utilise la fonction sprintf() (déclarée dans stdio.h). Cette fonction
fait partie de la norme ANSI-C, elle peut donc être utilisée sous n'importe quelle plateforme.
Pour placer un nombre entier dans une chaîne de caractères, on procédera donc ainsi :

#include <stdio.h>

- 37 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

char buf[32];
int n = 10;

sprintf(buf, "%d", n);

sprintf() admet différents paramètres, comme


• %s pour une chaîne de caractères,
• %f pour un nombre réel,
• %u pour un nombre non signé,
• %x pour un nombre hexadécimal.

N.B. :la norme C99 a introduit la fonction snprintf() similaire à sprintf() dans son foncionnement mais prennant en
paramètre la taille maximale de la chaîne résultante.

Comment convertir une chaîne de caractères en nombre ?


Auteurs : Bob , gl ,
Si une chaîne de caractères contient un nombre, il est possible de placer ce nombre dans une variable grâce à la fonction
strtol() :

char buf[32] = "15";


long n;

n = strtol(buf, NULL, 10);

Le troisième paramètre de strtol() correspond à la base utilisée (décimale dans l'exemple). Le deuxième paramètre
contiendra après l'éxecution de la fonction l'adresse où la conversion de la chaîne de caractères s'est arrêtée. Si la chaîne
a entièrement été traitée, ce paramètre pointe donc sur le caractère '\0' de fin de chaîne :

char buf[32] = "15";


long n;
char * end

n = strtol(buf, &end, 10);


if (*end == '\0')
{
/* La chaine a enteirement pu etre convertie */
}
else
{
/* La chaine contenait au moins un caractere ne pouvant pas etre converti en nombre */
}

Comment concaténer deux chaînes de caractères ?


Auteurs : Bob ,
Pour cela, il existe plusieurs méthodes. Si on désire concaténer deux chaînes et placer le résultat dans une troisième,
on peut utiliser sprintf(). Par exemple :

#include <stdio.h>

char Chaine1[32], Chaine2[32], ChaineFinale[64];

sprintf(ChaineFinale, "%s%s", Chaine1, Chaine2);

- 38 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Mais on peut également utiliser la fonction strcat(). Cette fonction ne prend que deux paramètres (et non pas trois), elle
ajoute la deuxième chaîne à la première. Par exemple :

#include <string.h>

char Chaine1[64], Chaine2[32];

strcat(Chaine1, Chaine2); /* Chaine1 contient maintenant Chaine1 + Chaine2 */

N.B. : pour utiliser strcat, il faut impérativement que la chaîne de destination soit initialisée avec une chaîne de caractères
valide (terminée par le caractère de fin de chaîne '\0') et ait une taille suffisante pour pouvoir ajouter la seconde chaîne.

Comment comparer 2 chaînes de caractères ?


Auteurs : LFE , gl ,
Pour comparer 2 chaînes, il ne faut pas utiliser l'opérateur == .

La fonction strcmp le permet et si l'on désire effectuer une comparaison sur une longueur donnée de la chaîne, il faut
alors utiliser la fonction strncmp. A noter que la comparaison se termine lors de l'apparition de la première différence
entre les 2 chaînes, ou que la fin de chaîne est atteinte.
• une valeur 0 indique que les chaînes sont identiques
• une valeur négative indique que la chaîne 1 contient des caracètres dont la valeur ASCII est inférieure à ceux
de la chaîne 2
• une valeur positive indique que la chaîne 1 contient des caracètres dont la valeur ASCII est supérieure à ceux
de la chaîne 2

const char * chaine1 = "blabla";


const char * chaine2 = "blabla";
const char * chaine3 = "blablu";

if (strcmp(chaine1, chaine2) == 0)
{
/* les chaines sont identiques */
}

if (strcmp(chaine2, chaine3) != 0)
{
/* les chaines sont différentes */
}

if (strncmp(chaine2, chaine3, 3) == 0)
{
/* les chaines sont identiques en se limitant a comparer les 3 premiers caracteres */
}

Pourquoi la comparaison avec une chaîne lue par fgets échoue toujours ?
Auteurs : gl ,
Lors de la lecture d'une chaîne de caractères dans un fichier via la fonction fgets(), il reste dans la chaîne le caractère de
saut de ligne '\n'. Ainsi lorsque l'on compare cette chaîne lue avec une autre chaîne, qui devrait être égale, la comparaison
échoue toujours. Il faut donc supprimer le caractère '\n' avant de travailler sur notre chaîne.

char s[256];
FILE * fp;

- 39 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

...
if (fgets(s, sizeof s, fp) != NULL)
{
char * p = strchr(s, '\n');

if (p != NULL)
*p = 0;
}

Comment créer un tableau de chaînes de caractères ?


Auteurs : Bob ,
Pour cela, il faut créer un tableau à deux dimensions. Par exemple pour créer un tableau de 10 chaînes de caractères
(de 256 caractères chacune), on pourra procéder ainsi :

#include <string.h>
...
char Tableau[10][256];
...
strcpy(Tableau[0], "azerty");

Dans cet exemple, on place le mot "azerty" dans la chaîne de caractères d'indice 0.

Pourquoi '\' doit s'écrire '\\' ?


Auteurs : LFE , gl ,
Car \ est le caractère utilisé pour marquer le début d'une séquence spéciale dans l'écriture d'un caractère en langage
C. Par exemple, le caractère '\n' représente le caractère "fin de ligne". \n est donc une séquence spéciale qui représente
la fin de ligne et non le caractère \ suivi du caractère n. Pour obtenir le caractère \, il faut utiliser une séquence spéciale
qui n'est autre que \\.
L'oubli du deuxième \ pour obtenir ce caractère dans une chaîne est source de bien d'ennuis pour les programmeurs
débutants sous Windows comme le montre l'exemple suivant :

remove("c:\developpez\faqc\test.txt"); /* Ne fonctionne pas */


remove("c:\\developpez\\faqc\\test.txt"); /* Fonctionne */

- 40 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Pointeurs, tableaux et chaînes de caractères > L'allocation dynamique de mémoire
Faut-il toujours libérer la mémoire allouée dynamiquement ?
Auteurs : LFE , gl ,
Il faut toujours libérer les ressources que l'on a allouées. A chaque malloc ou calloc doit correspondre un et un seul free.
Certains systèmes libèrent automatiquement de telles ressources à la fin de l'exécution du programme. Toutefois ce
comportement n'est pas garanti et la libération, si elle a lieu, est faite uniquement lors de l'arrêt du programme, il
convient donc de ne pas compter sur cet effet de bord et libérer soi-même les différentes ressources allouées.

Que contient ma mémoire après son allocation ?


Auteurs : LFE , gl , PRomu@ld ,
Si la mémoire a été allouée avec malloc, elle contient ... n'importe quoi. En effet, malloc ne fait qu'allouer de la mémoire,
mais ne l'initialise pas. Pour initialiser la zone mémoire, on peut utiliser calloc qui remplit la mémoire allouée de 0. On
peut aussi l'initialiser soi-même mais le résultat est le même.

#include <stdlib.h>
#include <string.h>

...

char * p = malloc(16); /* allocation d'une memoire de 16 octets */


if (p != NULL)
{
memset(p, 0x00, 16); /* initialisation de la memoire allouee */
...
free(p);
}

Ou :

#include <stdlib.h>

...

char * p = calloc(16, sizeof(char)); /* allocation d'une memoire initialisee (16 octets) */


if (p != NULL)
{
...
free(p);
}

Que vaut un pointeur après free() ?


Auteurs : LFE , gl ,
Le pointeur reste inchangé (en effet il est passé par valeur à free) mais la mémoire qu'il pointe ne doit plus être utilisée
par le programme (on dit que la mémoire a été "libérée").
Une bonne habitude à prendre est de mettre tout pointeur libre à NULL :

char * p = malloc(10);

...

free(p);

- 41 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

/* p ne sert plus a rien (pour le moment ...), mettons-le a NULL */

p = NULL;

Ce qui provoque, en cas d'accès à p, une erreur systématique qui est plus simple à corriger qu'un programme au
comportement erratique.

Comment connaître la taille d'un tableau dynamique ?


Auteurs : Haypo , gl ,
Il n'existe pas en C de fonction permettant de connaître la taille d'un tableau alloué dynamiquement, il faut donc
impérativement conserver la taille de chaque tableau alloué. Une implémentation particulière peut cependant fournir
une fonction permettant d'obtenir cette information (la fonction _msize sous Windows par exemple).

Comment allouer dynamiquement un tableau à 2 dimensions ?


Auteurs : LFE ,
Si on désire allouer un tableau de N * M élements, il y a 2 méthodes possibles :
- On alloue un tableau de N pointeurs vers des tableaux de M éléments chacun.

/* On veut avoir int * tab[N], sauf que tab sera cree dynamiquement */

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


/* On cree maintenant les N tableaux de M elements chacun */
for(i = 0; i < N; i++)
tab[i] = malloc(M * sizeof(int));

En fait, cette méthode ne crée pas vraiment un tableau de N * M entiers mais un tableau de N pointeurs, chaque pointeur
pointant vers un tableau de M entiers. L'avantage, c'est que l'accès à un "élément" de ce tableau se fait comme si le
tableau était réellement un tableau "à deux dimensions" : 'tab[j][i] = ...;'. L'inconvénient, c'est qu'elle est plus difficile
à mettre en oeuvre par rapport à l'autre méthode.
- On alloue réellement un tableau de N * M éléments.

int * tab = malloc(N * M * sizeof(int));

L'avantage, c'est que c'est simple à mettre en oeuvre. L'inconvénient, c'est que l'accès à un élément du tableau ne se
fait pas aussi aisément qu'avec l'autre méthode : 'tab[M * j + i] = ...;'.

- 42 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Les fonctions et les variables globales

- 43 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Les fonctions et les variables globales > Les fonctions
Qu'est-ce qu'un prototype ?
Auteurs : gl , Emmanuel Delahaye ,
Un prototype est la partie qui, dans la définition d'une fonction, définit les relations entre la fonction le monde extérieur.
Par exemple dans :

double f(double x, int n)


{
...
}

Toute la première ligne constitue le prototype de la fonction. Elle indique :


• Le nom de la fonction : f
• Le type de la valeur retournée : double
• Le nombre de paramètres requis : 2 dont le premier de type double (x) et le deuxième de type int (n)

Une fonction avant d'être utilisée doit être déclarée. La déclaration permet au compilateur de s'assurer que la fonction
sera toujours correctement utilisée dans le programme. Pour déclarer une fonction, il suffit de prendre son prototype
puis d'ajouter un point-virgule à la fin.

lien : Est-il possible de définir une fonction sans fournir le prototype ?

Est-il possible de définir une fonction sans fournir le prototype ?


Auteurs : gl ,
Oui, et c'était d'ailleurs le seul moyen de définir une fonction avant la normalisation du langage C, c'est-à-dire dans le
C originel (K & R C). Voici un exemple de définition d'une fonction selon cette méthode :

double f(x, n)
double x; int n;
{
...
}

Et voici comment on devait déclarer la fonction :

double f(); /
* Les parentheses, toujours vides, qui suivent f servent a indiquer que f est une fonction. */
/
* Sans elles, la ligne definirait plutot une variable globale de type double nommee f. */

Ces méthodes ne permettent pas au compilateur de contrôler la validité des paramètres passées à une fonction à chaque
appel à cause du manque de renseignement qu'elles lui fournissent d'où l'invention du prototype par l'ANSI.

lien : Qu'est-ce qu'un prototype ?

Comment passer "une variable" à une fonction ?


Auteurs : Bob , Melem ,
Supposons que l'on veut écrire une fonction, reset, qui permette de mettre à 0 une variable de type int. Faisons donc :

- 44 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

void reset(int n)
{
n = 0;
}

L'extrait de code suivant :

int a = 10;
reset(a);

Ne met pas a à 0 (après l'appel reset(a), on aura donc toujours "a = 10" !) car reset(a) ne fait que passer la valeur de
a (ici 10) à la fonction reset qui copie ensuite cette valeur dans sa variable locale n puis met cette variable à 0 ! C'est
donc n que la fonction modifie et non a !
Pour que la fonction reset puisse modifier la valeur de "la variable" qu'on lui a transmise, ce n'est pas la valeur de la
variable qu'il faut lui fournir mais plutôt son adresse. Connaissant l'adresse de la variable, la fonction peut aussi bien
lire que modifier sa valeur. Le bon code est donc :

#include <stdio.h>

void reset(int * adr);

int main(void)
{
int a = 10;

reset(&a);
printf("a = %d\n", a); /* affiche "a = 0" */

return 0;
}

void reset(int * adr)


{
*adr = 0;
}

Comment créer une fonction qui retourne plus d'une valeur ?


Auteurs : Bob , Melem ,
Une fonction ne peut retourner au plus qu'une valeur, mais il est possible de créer une fonction qui retourne d'autres
valeurs via un ou plusieurs paramètres. Par exemple :

#include <stdio.h>
#include <math.h>

void decomposer_nombre(double x, double * ptr1, double * ptr2);

int main(void)
{
double x, e, d;

printf("x = ");
scanf("%lf", &x); /* Supposons que l'utilisateur tape 2.5 */

decomposer_nombre(x, &e, &d); /* On donne une entree (x), on obtient deux sorties (e et d) */

printf("e = %f\n", e); /* Pour x = 2.5, affiche e = 2.000000 */


printf("d = %f\n", d); /* Pour x = 2.5, affiche d = 0.500000 */

return 0;

- 45 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

void decomposer_nombre(double x, double * ptr1, double * ptr2)


{
/* Nous allons mettre dans *ptr1 la partie entiere de x */
/* et dans *ptr2 sa partie decimale */

*ptr1 = floor(x);
*ptr2 = x - *ptr1;
}

Cette fonction retourne deux valeurs, un indice de réussite (le retour de la fonction) et une valeur entière.

Comment créer une fonction qui retourne une chaîne de caractères ?


Auteurs : Bob , LFE , gl , Melem ,
Une première méthode consiste à passer à la fonction un pointeur vers la zone mémoire destinée à contenir la chaîne
à retourner. Par exemple :

#include <stdio.h>
#include <string.h>

void get_string(char * Buffer, size_t BufferLen)


{
/* On ne peut pas utiliser strcpy car cette fonction
ne permet pas d'indiquer le nombre max de caracteres qu'on veut copier */
strncpy(Buffer, "MaChaine", BufferLen);

/* Il est possible que Buffer n'ait pas pu contenir toute la chaine ... */
Buffer[BufferLen - 1] = '\0';
}

int main(void)
{
char Buffer[100];

get_string(Buffer, sizeof(Buffer));
printf("%s\n", Buffer);

return 0;
}

Une autre façon de faire est de retourner un pointeur vers des caractères situées en mémoire globale. Par exemple :
- A l'aide d'une chaîne statique :

#include <stdio.h>

const char * get_string(void)


{
return "MaChaine";
}

int main(void)
{
printf("%s\n", get_string());

return 0;
}

- A l'aide d'une chaîne allouée dynamiquement :

- 46 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char * alloc_string(void)
{
char * Buffer, s[] = "MaChaine";

Buffer = malloc(strlen(s) + 1);

if (Buffer != NULL)
strcpy(Buffer, s);

return Buffer;
}

int main(void)
{
char * Buffer = alloc_string();

if (Buffer != NULL)
{
printf("%s\n", Buffer);
free(Buffer);
}

return 0;
}

Comment retourner un pointeur de fonction ?


Auteurs : gege2061 , Alp ,
Voici un exemple de fonction, func, ne prenant aucun argument et qui retourne un pointeur sur une fonction de type
int f(const char * s) (par exemple puts) :

int (*func(void))(const char *)


{
return puts;
}

Le prototype de func qui paraît pour le moins ésotérique a en fait été obtenu par la méthode suivante : func retournant
un pointeur, func(void) vaut ce pointeur et *func(void) la chose pointée. Cette chose pointée est une fonction qui prend
en argument un const char * et qui retourne un int donc (*func(void))(const char *) est de type int d'où le prototype
de f : int (*func(void))(const char *).
Bien entendu, ici encore un typedef allège énormément l'écriture.

#include <stdio.h>

typedef int F(const char *);

F * func(void);

int main(void)
{
func()("Bonjour."); /* <=> puts("Bonjour."); */
return 0;
}

F * func(void)
{
return puts;

- 47 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Comment définir une fonction acceptant un nombre d'arguments variable, comme printf() ?
Auteurs : LFE ,
Les '...' dans la liste des paramètres d'une fonction indiquent que la fonction peut accepter 0, un ou plusieurs paramètres
à la place. Parmi les paramètres de la fonction, il doit cependant y a voir au moins un qui explicite (fixé). Les macros
va_start(), va_arg() et va_end() définies dans stdarg.h permettent de parcourir les paramètres. Dans l'exemple ci-
dessous, on parcourt les paramètres jusqu'à en rencontrer un qui vaut 0.

#include <stdio.h>
#include <stdarg.h>

void printf_total(const char * format, ...);

int main(void)
{
print_total("Le total est : %d\n", 1, 2, 3, 4, 0);
return 0;
}

/* Fonction print_total : affiche la somme des arguments */

void printf_total(const char * format, ...)


{
int total = 0;
va_list ap;
int arg;

va_start(ap, format);

while ((arg = va_arg(ap, int)) != 0)


total += arg;

printf(format, total);

va_end(ap);
}

Combiner plusieurs options en un seul paramètre ?


Auteurs : gege2061 ,
Le système présenté ici permet de fournir à une fonction une valeur qui regroupe un ensemble de flags (drapeaux)
souvent utilisés pour permettre à l'utilisateur de fournir un nombre variable d'options sans utiliser le système de fonction
à nombre variable d'arguments (va_list). Pour cela, il faut commencer par définir les différentes options possibles :

enum {
OPTION1 = 1 << 0,
OPTION2 = 1 << 1
};

Notez que les valeurs des options doivent être des puissances de deux pour avoir un bit égal à 1 et les autres égals à 0.
D'après l'énumération précédente, OPTION1 vaut 1 soit 000001 en binaire et OPTION2 vaut 000010, alors pour passer
les deux paramètres à une fonction dont le prototype serait le suivant :

- 48 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

void foo(int options)

on écrit :

foo(OPTION1 | OPTION2)

D'après les propriétés du OU binaire, la fonction va recevoir la valeur 000011. Ensuite, il va falloir décomposer cette
valeur pour retrouver les différentes options. Il faut faire l'opération inverse du OU binaire grâce au ET binaire :

options & OPTION1

La valeur de cette expression est différente de 0 (soit vrai) si l'un des bits de OPTIONS1 se retrouve dans options. Il
faut faire le test pour chacune des options proposées. Voici un code complet :

#include <stdio.h>

enum {
OPTION1 = 1 << 0,
OPTION2 = 1 << 1
};

void foo(int);

int main(void)
{
foo(OPTION1 | OPTION2);
return 0;
}

void foo(int options)


{
if (options & OPTION1)
printf("OPTION1 trouve\n");
if (options & OPTION2)
printf("OPTION2 trouve\n");
}

lien : Comment accéder à un bit d'une variable ?

- 49 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Les fonctions et les variables globales > Les variables globales
Comment déclarer et utiliser une variable globale définie dans un autre fichier source ?
Auteurs : gl ,
Le mot-clé extern permet de déclarer une variable globale déjà définie. La variable peut avoir été définie dans le même
fichier source ou ailleurs. Dans l'exemple suivant, le projet est constitué de deux fichiers sources, globales.c et main.c,
le premier contenant la définition de deux variables globales et le deuxième utilisant ces variables.

Fichier : globales.c
int globale_1 = 1;
int globale_2 = 2;

Fichier : main.c
#include <stdio.h>

extern int globale_1;

int get_globale_2(void);

int main(void)
{
printf("globale_1 = %d\n", globale_1);
printf("globale_2 = %d\n", get_globale_2());

return 0;
}

int get_globale_2(void)
{
extern int globale_2;

return globale_2;
}

Comment restreindre une variable globale à un unique fichier ?


Auteurs : gl ,
Une variable globale peut voir sa portée réduite à un unique fichier (c'est-à-dire qu'elle n'est plus visible par les autres
fichiers sources), en employant le mot clé static. Il est ainsi possible de définir deux variables globales statiques de même
nom dans deux fichiers différents sans qu'elles interfèrent entre elles.

lien : Que signifie le mot-clé static ?

- 50 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Les entrées/sorties

- 51 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Les entrées/sorties > Entrées/sorties en général


Qu'est-ce que stdin, stdout et stderr ?
Auteurs : gl ,
Ces sont des flux ouverts automatiquement à chaque démarrage d'un programme.
- stdin est le flux associé à l'entrée standard du programme (qui est, sur de nombreux systèmes, par défaut le clavier).
- stdout est le flux associé à la sortie standard du programme (qui est, sur de nombreux systèmes, par défaut l'écran).
- stderr est le flux associé à l'erreur standard du programme (qui est, sur de nombreux systèmes, par défaut l'écran).
stdout et stderr sont tout deux des flux de sortie et peuvent s'utiliser avec toutes les fonctions d'écriture de fichiers
(fprintf, fputc, etc.). Les fonctions de sortie ne prenant pas de flux en paramètre (printf, putchar, etc.) travaillent sur
stdout.
stdin est un flux d'entrée et peut donc s'utiliser avec toutes les fonctions de lecture de fichiers (fscanf, fgetc, etc.). Les
fonctions d'entrée ne prenant pas de fichier en paramètre (scanf, getchar, etc.) travaillent sur stdin.

A quoi correspond le spécificateur 'b' du mode d'ouverture d'un fichier ?


Auteurs : gl ,
Le C connait deux familles de fichiers :
• Les fichiers binaires.
• Les fichiers textes.

La différence entre ces deux types de fichiers réside dans l'interprétation que les fonctions de gestions de fichiers vont
faire des données présentes dans le fichiers :
• En mode binaire, aucune interprétation des données n'est effectuée.
• En mode texte, certaines séquences de caractères sont interprétés comme étant une fn de ligne ou une fin de fichier

L'utilisation du spécificateur 'b' dans le mode d'ouverture permet d'indiquer qu'il s'agit d'un fichier binaire et de le
traiter comme tel. Si rien n'est précisé, le fichier est ouvert en mode texte.
N.B. : certains compilateurs ont introduit le spécificateur 't' dans le mode d'ouverture afin d'indiquer qu'il s'agit d'un
fichier texte. Ce spécificateur n'étant pas décrit par la norme, il n'est pas portable et ne doit donc pas être utilisé.

A quoi sert la "fonction" feof ?


Auteurs : gl ,
Contrairement à une pratique trop souvent rencontrée, la fonction feof() ne sert pas à détecter la fin d'un fichier mais,
lorsqu'une lecture a échoué, à savoir si cet échec est du à la rencontre de la fin de fichier.
La bonne pratique pour lire intégralement un fichier consiste donc à parcourir le fichier tant qu'aucune erreur de
lecture n'est remontée et d'utiliser ferror et feof pour déterminer, suite à une erreur, la cause de cette erreur.
Le programme suivant compte les octets contenus dans un fichier pour déterminer sa taille.

#include <stdio.h>

int main(void)
{
FILE * fp = fopen("machin.chose", "rb");

if (fp == NULL)
fprintf(stderr, "La fonction fopen a echoue.\n");
else
{
int c;

- 52 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

long taille = 0;

while ((c = fgetc(fp)) != EOF)


{
/* Utilisation du caractere lu. */
/* Dans notre cas, on n'en a rien a faire. */
/* On incremente tout simplement notre compteur. */

taille++;
}

if (feof(fp))
{
printf("Fin de fichier atteinte !\n");
printf("La taille du fichier est : %ld octets.\n", taille);
}
else
{
if (ferror(fp))
printf("Arret du programme en raison d'une erreur de lecture !\n");
}
}

return 0;
}

Pourquoi getc retourne un int et non un char ?


Auteurs : Melem ,
getc lit le caractère courant sur le flux d'entrée spécifié en paramètre et retourne sa valeur en tant qu'entier (int) car
en cas d'échec (c'est-à-dire lorsqu'aucun caractère n'a pu être lu), elle doit retourner une valeur qui ne peut être celle
d'un "caractère" valide et cette valeur est représentée par la macro EOF.

Qu'est-ce que le type wint_t ?


Auteurs : Melem ,
wint_t (en quelque sorte "wide integer") est le type qui permet de représenter n'importe quel caractère large ou la
valeur WEOF, l'équivalent de EOF pour les fonctions travaillant avec les caractères larges.

lien : Qu'est-ce que le type wchar_t ?

- 53 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Les entrées/sorties > Gestion du clavier et de l'écran en mode console
Pourquoi faut-il valider les caractères tapées par Entrée ?
Auteurs : Melem ,
Les caractères tapés ne sont pas directement transmis au programme mais placés (par le système) dans un tampon. Le
système ne transmettra le contenu de ce tampon que lorsque l'utilisateur aura émis le caractère de validation à savoir
'\n'. En langage C, avec un clavier plus ou moins conventionnel, ce caractère est provoqué par la touche Entrée.

Qu'est-ce que le caractère '\n' ?


Auteurs : Melem ,
En langage C, ce caractère, appelé également Line Feed (en abrégé LF), indique la fin d'une ligne. Aussi bien en lecture
(scanf, ...) qu'en écriture (printf, ...), le système requiert une ligne complète avant de réellement considérer les données,
sinon elles seront tout simplement placées dans un tampon. Si on tente de l'afficher, il provoque le passage à la ligne
suivante.

Qu'est-ce que le caractère '\r' ?


Auteurs : Melem ,
Ce caractère, appelé également Carriage Return (en abrégé CR), si l'on tente de l'afficher, provoque le retour en début
de la ligne courante.

La touche Entrée envoie-t-elle le caractère '\r', '\n' ou '\r' suivi de '\n' ?


Auteurs : Melem ,
La touche Entrée (Retour Chariot) envoie le caractère '\r'. Cependant, cette touche sert en réalité à terminer la ligne
plutôt qu'à retourner au début de celle-ci. Or, selon le système, il est possible qu'une ligne doit être terminée non pas
par un '\r' mais par un '\n' (à l'exemple d'UNIX) ou un '\r' suivi d'un '\n' (DOS/Windows) par exemple. Ainsi, avant
que ce caractère n'atteigne le programme, il aura déjà été converti en "caractère" de fin de ligne (qui peut être '\n', '\r'
suivi de '\n', etc.). Cette conversion se faisant de manière totalement opaque pour le programme, on a bien l'impression,
dans le programme, que la touche Entrée provoque l'émission du caractère de fin de ligne. Par contre, si le programme
effectue une lecture directe au clavier (à l'aide de la fonction _getch() de DOS/Windows par exemple) plutôt qu'une
lecture de haut niveau (getchar()), on obtient bien le caractère '\r' et non le caractère de fin de ligne quand l'utilisateur
appuie sur Entrée.
D'autre part, en langage C, les entrées/sorties se font par défaut en mode texte. Dans ce mode, le caractère de fin de
ligne est toujours représenté par le caractère '\n'. Plus précisément, quand une fonction de lecture recontre une fin de
ligne, elle la convertit en '\n' avant de la retourner au programme. Quand le programme demande l'écriture de '\n', il
sera tout d'abord converti en caractère de fin de ligne avant d'être effectivement écrit. En mode binaire, ces conversions
n'ont pas lieu.

- 54 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

A noter que pour l'écran, bien qu'il faut normalement écrire '\r' (Retour Chariot) suivi de '\n' (Nouvelle ligne) pour
commencer une nouvelle ligne, il est possible, toujours en fonction du système utilisé, que '\n' seulement suffise pour y
arriver. Mais dans tout le cas, l'affichage de '\r' seulement provoque le retour au début de la ligne courante.

A quoi sert la fonction fflush ?


Auteurs : Melem ,
Cette fonction sert à forcer l'écriture physique des données se trouvant dans le tampon associé à un flux sortant. Par
exemple :

#include <stdio.h>

int main(void)
{
int n;

printf("Entrez un nombre entier : ");

/* En langage C, une ligne doit etre terminee par le caractere '\n'. Tant que */
/* la ligne n'est pas terminee et que le tampon associe au fichier n'est pas plein, */
/* les caracteres transmis ne seront pas effectivement ecrits mais tout simplement */
/* places dans le tampon. On peut cependant forcer le vidage de ce tampon a l'aide */
/* de la fonction fflush. */

fflush(stdout);

scanf("%d", &n);

printf("Merci pour : %d\n", n);

return 0;
}

Cependant, la norme ajoute que le tampon associé à un flux sortant doit être également vidé lorsqu'une opération de
lecture nécessite l'émission de ces caractères. Dans de nombreuses implémentations, une demande de lecture au clavier
provoque le vidage du tampon associé à l'écran. Le 'fflush(stdout);' juste après notre printf serait donc automatiquement
appelé au moment du scanf et dans ce cas, on peut tout simplement l'omettre.

Comment lire une ligne de manière securisée ?


Auteurs : gl , Anomaly , Emmanuel Delahaye ,
En utilisant fgets.

fgets(char * s, int n, FILE * flux);

fgets stoppe la lecture si la ligne a été lue dans son intégralité (c'est-à-dire si le caractère '\n' a été lu) ou si elle a lu n -
1 caractères (le n-ième étant reservé pour le caractère de fin de chaîne, '\0'). Lorsque fgets() lit une ligne complète, le
caractère '\n' est présent dans le tampon, il faut donc prévoir sa suppression.
Voici une fonction effectuant la lecture d'une ligne en limitant la taille et en supprimant le caractère '\n' s'il est présent :

#include <stdio.h>
#include <string.h>

char * read_stdin(char * buffer, size_t taille)

- 55 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

{
char * result = fgets(buffer, taille, stdin);

if (result != NULL)
{
char * lf = strchr(buffer, '\n'); /* On cherche le caractere '\n'. */

if (lf != NULL) /* S'il est present, ... */


*lf = '\0'; /* ... on le supprime */
else
{
/*
* Le '\n' n'est pas present. Ca signifie qu'il reste au moins un
* caractere dans stdin. On peut choisir de les ignorer et de vider
* stdin ou d'agrandir le buffer si c'est possible (realloc()) et de
* rappeler fgets() autant de fois que necessaire...
*
* Si on ne fait rien, la prochaine lecture sur stdin se fera sans attente
* et recuperera ce qui n'a pas ete lu ...
*/
}
}

return result;
}

N.B. : le caractère '\n' n'est pas présent dans deux cas :


• La saisie est plus longue que taille demandée, dans ce cas les caractères supplémentaires restent présents.
• La fin de fichier est rencontrée.

lien : Comment vider le buffer clavier ?


lien : Pourquoi gets est-elle dépréciée en faveur de fgets ?

Comment vider le buffer clavier ?


Auteurs : Haypo , LFE , Emmanuel Delahaye , gl ,
La méthode la plus sûre pour vider le buffer clavier consiste à consommer tout les caractères présents dans ce buffer
jusqu'à ce qu'il soit vide :

#include <stdio.h>

void clean_stdin(void)
{
int c;

do {
c = getchar();
} while (c != '\n' && c != EOF);

- 56 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Comment générer EOF avec le clavier ?


Auteurs : Melem ,
Cela dépend du système. Par exemple, il suffit de taper, en début de ligne, Ctrl + Z sous DOS/Windows et Ctrl + D sous
UNIX, puis de valider par Entrée.

Pourquoi ne faut-il pas utiliser fflush(stdin) pour vider le buffer clavier ?


Auteurs : gl , Melem ,
Dans certaines implémentations, lorsque stdin est attaché au clavier, fflush(stdin) supprime tous les caractères encore
présents dans le buffer du clavier. Appliquer fflush sur un flux entrant, comme stdin, n'est cependant pas portable car
la norme ne précise l'effet de fflush que sur un flux sortant.

lien : A quoi sert la fonction fflush ?


lien : Comment vider le buffer clavier ?

Qu'est-ce qu'un terminal en mode brut (UNIX) ?


Auteurs : Anomaly ,
Un terminal a deux modes de fonctionnement. Dans le mode normal (cooked), on saisit la ligne tranquillement,
avec possibilité de l'éditer avec Backspace, avant de l'envoyer en enfonçant Entrée. Le programme n'a pas du tout
connaissance de la ligne avant que l'utilisateur enfonce Entrée.
Dans le mode brut (raw), aucune interprétation n'est faite. Les effets sont notamment les suivants :
• getchar() lit réellement un caractère sans attente la frappe d'Entrée.
• fgets() lit exactement le nombre de caractères demandés.
• Il n'y a pas d'écho local des caractères tapés.
• Si on tape Backspace, le programme reçoit '\b' (code 8) ou DEL (code 127), selon le système.
• Ctrl-C (interruption), Ctrl-D (EOF), Ctrl-Z (suspension) et les autres combinaisons de contrôle ne sont pas
interprétées, mais le code ASCII correspondant est retourné.
• Si on tape Entrée, le programme reçoit '\r' (au lieu de '\n' en mode cooked).
• Si on tente d'afficher '\n', le programme saute une ligne sans retourner au début de la ligne (il faut donc utiliser
'\r' suivi de '\n' pour revenir en début de ligne puis aller à la ligne suivante).

Comment faire passer un terminal en mode brut (UNIX) ?


Auteurs : Anomaly ,
Voici une fonction qui permet de passer d'un mode à l'autre. On appelle mode_raw(1) pour activer le mode raw et
mode_raw(0) pour revenir en mode cooked. Pensez à toujours revenir en mode normal à la fin du programme.

#include <termios.h>
#include <unistd.h>

void mode_raw(int activer)


{
static struct termios cooked;
static int raw_actif = 0;

- 57 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

if (raw_actif == activer)
return;

if (activer)
{
struct termios raw;

tcgetattr(STDIN_FILENO, &cooked);

raw = cooked;
cfmakeraw(&raw);
tcsetattr(STDIN_FILENO, TCSANOW, &raw);
}
else
tcsetattr(STDIN_FILENO, TCSANOW, &cooked);

raw_actif = activer;
}

Comment faire pour lire un caractère sans attendre la frappe d'Entrée ?


Auteurs : Anomaly , Melem ,
Sous DOS/Windows, avec la fonction _getch(), déclarée dans conio.h. Cette fonction n'émet pas d'écho. Si vous voulez
que le caractère tapé soit également affiché, utilisez la fonction getche(). getch et getche retournent toutes le code du
caractère tapé. Avec un compilateur C/C++ moderne, préférez les noms _getch et _getche aux anciens noms getch et
getche.

#include <stdio.h>
#include <conio.h>

int main(void)
{
printf("Hello, world.\n");

printf("Appuyez sur une touche pour continuer ...");


fflush(stdout);
_getch();

return 0;
}

Sous UNIX, il faut passer le terminal en mode brut (raw).

lien : Que signifie l'underscore (_) en début du nom d'une fonction ou d'une macro, etc. ?
lien : Comment faire passer un terminal en mode brut (UNIX) ?

Comment intercepter une touche sans bloquer le programme (DOS/Windows) ?


Auteurs : Elijha , gl ,
Il arrive des fois qu'on ait besoin de savoir si une touche est présente, mais sans bloquer le déroulement du programme.
La fonction kbhit() permet de savoir si une touche est disponible ou non dans le buffer du clavier. Si une touche est
présente, alors une lecture par la fonction getch() suffit. Avec un compilateur C/C++ moderne, préférez les noms _kbhit
et _getch aux anciens noms kbhit et getch.
Voici une source intégrant la lecture de touches simple et double code.

#include <stdio.h>
#include <conio.h>

- 58 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

int main(void)
{
int touche;

while (!_kbhit()) /* On boucle jusqu'a ce que l'utilisateur appuie sur une touche */
{
printf("Appuyez sur n'importe quelle touche !\n");
}

/* Une touche a enfin ete frappee. Lire son code. */

touche = _getch();

if (touche >= 32) /* code d'un caractere imprimable */


printf("Vous avez appuye sur '%c'.\n", touche);
else
{
/* C'est un caractere special. Ca peut etre un caractere de controle */
/* (ECHAP, ENTREE, etc.) ou encore une touche etendue (F1 .. F12, Fleches). */

printf("Vous avez appuye sur une touche.\n");


}

return 0;
}

lien : Comment faire pour lire un caractère sans attendre la frappe d'Entrée ?
lien : Comment gérer les touches étendues (F1..F12, flèches) ?
lien : Que signifie l'underscore (_) en début du nom d'une fonction ou d'une macro, etc. ?

Comment simuler la fonction _kbhit sous UNIX ?


Auteurs : Anomaly ,
La fonction _kbhit() permet, sous DOS et Windows, de savoir si une touche est disponible ou non dans le buffer du
clavier. La fonction ci dessous fournit le même service sous un environnement de type UNIX.

#include <unistd.h>
#include <sys/time.h>

int unix_text_kbhit(void)
{
struct timeval tv = { 0, 0 };
fd_set readfds;

FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds);

return select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv) == 1;


}

Les caractères détectés peuvent être lus ensuite par fgets() ou getchar().

- 59 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Attention, cette fonction ne détectera des caractères tapés que s'ils ont été suivis par Entrée. Pour détecter l'arrivée de
caractères dès leur frappe, comme avec _kbhit, il faut que le terminal fonctionne en mode brut (raw).

lien : Comment faire passer un terminal en mode brut (UNIX) ?

Comment gérer les touches étendues (F1..F12, flèches) ?


Auteurs : LFE ,
Les touches F1 à F12 et les touches fléchées sont des touches qui, lorsque l'on appuie dessus, renvoient 2 codes l'un à
la suite de l'autre. Ce sont ce que l'on appelle des touches étendues.
Pour gérer ce type de touches, il faut lire un premier caractère du buffer clavier, détecter qu'il s'agit d'un code de touche
étendue, et relire un second caractère pour identifier la touche. Ce premier 'caractère' lu dépend du système et peut
également varier selon les touches étendues (généralement il a la valeur 0 ou 224). A noter que les codes étendus, c'est-
à-dire celui qui vient en seconde position, est différent suivant les plateformes.
Voici un extrait de code qui fonctionne sous DOS/Windows, la fonction getch n'étant définie que par ces systèmes.

int c = getch();

if (c == 0 || c == 224) /* Si c'est une touche etendue */


{
c = getch();
/* c contient maintenant le code de la touche etendue */
}

lien : Comment faire pour lire un caractère sans attendre la frappe d'Entrée ?

Comment se positionner dans une console ?


Auteurs : Haypo , gl ,
Si la console accepte les séquences d'échappement ANSI (il s'agit d'une spécification de combinaisons de caractères
qu'un terminal doit interpréter comme une commande et non comme de simples caractères à imprimer ...), il est possible
de les utiliser pour positionner le curseur au point de coordonnées (x, y) dans cette console.

#include <stdio.h>

void goto_x_y(unisgned y, unsigned x)


{
printf("\033[%u;%uH", y, x);
}

Cela fonctionne sous DOS et de nombreux environnements de type UNIX mais malheureusement pas sous Windows.
Sous Windows, la solution la plus naturelle consiste à appeler la fonction SetConsoleCursorPosition() en lui fournissant
un handle sur la console et les coordonnées de la position souhaitée.

#include <windows.h>

void GotoXY(SHORT x, SHORT y)


{
/
* STD_OUTPUT_HANDLE fait reference a la sortie standard du programme qui est par defaut la console */
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
COORD Pos;

Pos.X = x;
Pos.Y = y;

- 60 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

SetConsoleCursorPosition(hConsole, Pos);
}

Comment effacer l'écran ?


Auteurs : Haypo ,
Normalement, cela peut simplement se faire en appelant une commande système comme cls sous DOS/Windows et clear
sous UNIX. Cette technique n'est cependant pas la plus efficace à cause des limitations connues de la fonction system. Il
n'existe pas non plus hélas de fonction standard qui permette de le faire. Voici donc quelques fonctions que vous pouvez
utiliser pour arriver à vos fins.
MS-DOS :

#include <dos.h>

void dos_clear_screen(void)
{
/* Il suffit de reinitialiser le mode video (appeler la fonction 00h de l'interruption 10h) */
union REGS inregs, outregs;

inregs.h.ah = 0x00; /* Fonction 00h : Change de mode video */


inregs.h.al = 0x03; /* Mode = 03h : 80x25 caracteres, 16 couleurs */

int86(0x10, &inregs, &outregs); /* INT 10h */


}

Windows :

#include <windows.h>

void windows_clear_screen(void)
{
HANDLE hConsole;
CONSOLE_CONS_BUFFER_INFO Info;
DWORD NbOctetsEcrits; /* Requis par FillConsoleOutputCharacter */
COORD Debut = {0, 0};

/
* STD_OUTPUT_HANDLE fait reference a la sortie standard du programme qui est par defaut la console */
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

/* Lit les infos sur le buffer de l'ecran */


GetConsoleScreenBufferInfo(hConsole, &Info);

/* Remplit l'ecran avec le caractere espace */


FillConsoleOutputCharacter(hConsole, ' ', Info.dwSize.X*Info.dwSize.Y, Debut, &NbOctetsEcrits);

/* Remet le curseur au debut de l'ecran */


SetConsoleCursorPosition(hConsole, Debut);
}

Linux :

#include <ncurses.h>

void unix_clear_screen(void)
{
clear();
move(0, 0);

- 61 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

lien : Que fait la fonction system ?

Où trouver des fonctions portables de gestion du clavier et de l'écran ?


Auteurs : LFE , Melem ,
Le matériel étant une notion complètement inconnue du langage C, il faut normalement toujours faire appel aux
fonctions du système pour y avoir accès. CONIO (conio.h) sous DOS et Windows et ncurses sous UNIX mettent à
disposition du programmeur des routines permettant de gérer le clavier et l'écran en mode console à un niveau inférieur
à celui que permet le C standard mais comme nous venons de le dire, aucune des deux bibliothèques n'est portable (il
peut cependant exister des portages ...).
PDCurses, un sous-ensemble de ncurses, est une bibliothèque portable de gestion du clavier et de l'écran en mode
console.

Où trouver une bibliothèque de gestion graphique ?


Auteurs : LFE , Melem ,
La création d'interfaces graphiques est un domaine non couvert par le C. Pour développer des applications graphiques
(c'est-à-dire des fenêtres, des boutons, etc.), vous pouvez soit utiliser directement les fonctions de votre système (c'est-
à-dire l'API Windows sous Windows, le protocole X sous UNIX, etc.), soit faire appel à une bibliothèque
éventuellement portable comme GTK+.
Si vous voulez par contre faire du graphisme (jeux 2D ou 3D ...), vous devriez plutôt vous tournez vers des bibliothèques
spécialisées comme allegro, SDL ou encore OpenGL à moins que vous ayez envie de réinventer la roue en
utilisant les fonctions de très très bas niveau du système.

- 62 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Les entrées/sorties > Les fichiers et les dossiers


Comment tester l'existence d'un fichier ?
Auteurs : LFE , Laurent Gomila ,
La solution la plus simple pour vérifier si un fichier existe c'est d'essayer de l'ouvrir.

FILE * fp = fopen("fichier.txt", "rb");

if (fp == NULL)
{
/* L'ouverture du fichier "fichier.txt" a echoue => le fichier "fichier.txt" n'existe pas. */
}
else
{
/* Le fichier a pu etre ouvert => le fichier existe. */
fclose(fp);
}

Cependant, cette technique n'est pas à 100% sûre car si le fichier a pu être ouvert, c'est en effet qu'il existe. Mais s'il n'a
pas pu être ouvert, ça ne signifie pas forcément que le fichier n'existe pas (plusieurs raisons peuvent amener la demande
d'ouverture d'un fichier à l'échec : le fichier n'existe pas, le système ne dispose pas d'assez de mémoire pour effectuer
l'opération, vous n'avez pas le droit d'ouvrir le fichier, etc.). Il n'existe malheureusement pas de méthode standard qui
permette de connaître la cause de l'échec de l'ouverture du fichier. Sous DOS/Windows et les systèmes de type UNIX,
on peut tester si errno est égal à ENOENT (No entry (no such file or directory)).

Comment connaître la taille d'un fichier ?


Auteurs : LFE , gl , Aurelien.Regat-Barrel ,
Cette petite fonction permet de calculer très simplement la taille d'un fichier.

#include <stdio.h>

int fsize(const char * fname, long * ptr)


{
/* Cette fonction retourne 0 en cas de succes, une valeur differente dans le cas contraire. */
/* La taille du fichier, si elle a pu etre calculee, est retournee dans *ptr */

FILE * f;
int ret = 0;

f = fopen(fname, "rb");
if (f != NULL)
{
fseek(f, 0, SEEK_END); /* aller a la fin du fichier */
*ptr = ftell(f); /* lire l'offset de la position courante par rapport au debut du fichier */
fclose(f);
}
else
ret = 1;

return ret;
}

Le problème c'est que sous Windows par exemple, la taille d'un fichier peut dépasser la plus grande valeur positive
représentable par le type long. La fonction GetFileAttributesEx permet de connaître entre autres la taille d'un fichier
sans limitation comme dans le cas précédent. Rappelons que sur Win32, la taille du type unsigned int est de 4 octets (32
bits) ce qui signifie que la valeur maximale représentable par ce type est 2 ^ 32 - 1. GetFileAttributesEx retourne la taille

- 63 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

du fichier sur une valeur de 64 bits ce qui assez grand pour représenter la taille de n'importe quel fichier manipulable
sous Windows. En fait, il s'agit de deux valeurs de 32 bits : la partie haute, qui vaut 0 si la taille du fichier est inférieure
à 2 ^ 32 octets soit 4 Go et la partie basse, les premiers 32 bits.

#include <windows.h>

int FileSize(const char * FileName, unsigned int * ptr)


{
/* Cette fonction retourne 0 en cas de succes, une valeur differente dans le cas contraire. */
/* La taille du fichier, si elle a pu etre calculee, est retournee dans *ptr */

WIN32_FILE_ATTRIBUTE_DATA attr;
int ret = 0;

if (GetFileAttributesEx(FileName, GetFileExInfoStandard, &attr))


{
/* La taille du fichier (entre autres) a pu etre lue */

if (attr.nFileSizeHigh == 0)
{
/* Le fichier fait moins de 4 Go */

*ptr = attr.nFileSizeLow;
}
else
ret = 2;
}
else
ret = 1;

return ret;
}

Comment copier un fichier ?


Auteurs : gl ,
Il n'existe pas en C de fonction réalisant la copie d'un fichier. Il est donc nécessaire de créer une fonction lisant le fichier
pour le copier dans le fichier de destination.

#include <stdio.h>

int copier_fichier(char const * const source, char const * const destination)


{
FILE* fSrc;
FILE* fDest;
char buffer[512];
int NbLus;

if ((fSrc = fopen(source, "rb")) == NULL)


{
return 1;
}

if ((fDest = fopen(destination, "wb")) == NULL)


{
fclose(fSrc);
return 2;
}

while ((NbLus = fread(buffer, 1, 512, fSrc)) != 0)


fwrite(buffer, 1, NbLus, fDest);

- 64 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

fclose(fDest);
fclose(fSrc);

return 0;
}

Remarque : il est aisé de modifier cette fonction afin de concaténer le fichier source avec le fichier destination en ouvrant
ce fichier en mode ajout ("ab").
Sous Windows, il y a la fonction CopyFile qui permet de ne pas avoir à coder sa propre fonction.

BOOL CopyFile( LPCTSTR lpExistingFileName, /* Nom du fichier source */


LPCTSTR lpNewFileName, /* Nom du fichier destination */
BOOL bFailIfExists /* Si !
= 0, la copie sera annulée si le fichier existe déjà */
);

Comment supprimer une ligne dans un fichier texte ?


Auteurs : gl ,
Le langage C ne fournit pas de fonction pour supprimer une ligne dans un fichier, il faut obligatoirement lire le fichier
et de recopier chaque ligne hormis celles que nous désirons supprimer. Voici un exemple de programme supprimant les
lignes commencant par # dans un fichier (afin de ne pas surcharger inutilement le code de l'exemple, nous supposons
qu'aucune ligne ne fait plus de 256 caractères).

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
char ligne[256];
FILE * fIn;
FILE * fOut;

if ((fIn = fopen("texte.txt", "r")) == NULL)


return EXIT_FAILURE;

if ((fOut = fopen("texte.tmp", "w")) == NULL)


{
fclose(fIn);
return EXIT_FAILURE;
}

while (fgets(ligne, sizeof ligne, fIn))


{
if (ligne[0] != '#')
fputs(ligne, fOut);
}

fclose(fIn);
fclose(fOut);

rename("texte.tmp", "texte.txt");

return 0;
}

- 65 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Le principe est le même pour supprimer un enregistrement d'un fichier binaire.

Comment supprimer un fichier ?


Auteurs : LFE ,
Sous Windows, à l'aide de DeleteFile (inclure windows.h) et sous UNIX (et plus généralement n'importe quel système
se conformant à la norme POSIX), à l'aide de la fonction unlink (déclarée dans unistd.h). La bibliothèque standard du
C fournit également une fonction qui sait remplir cette tâche qui n'est autre que remove (déclarée dans stdio.h).

#include <stdio.h>

int remove(const char * pathname);

Comment lister le contenu d'un dossier ?


Auteurs : Bob ,
Sous Windows, on utilise les fonction FindFirstFile() et FindNextFile() en recherchant les fichiers nommés "*.*" (c'est-
à-dire tous les fichiers, tous les dossiers). Le HANDLE retourné par FindFirstFile() doit être fermé dès qu'il n'est plus
nécessaire grâce à la fonction FindClose(). Ces fonctions ne parcourent pas les sous dossiers. Le programme suivant
liste le contenu du répertoire courant.

#include <stdio.h>
#include <windows.h>

int main(void)
{
WIN32_FIND_DATA File;
HANDLE hSearch;

hSearch = FindFirstFile("*.*", &File);


if (hSearch != INVALID_HANDLE_VALUE)
{
do {
printf("%s\n", File.cFileName);
} while (FindNextFile(hSearch, &File));

FindClose(hSearch);
}

return 0;
}

Dans un environnement conforme à la norme POSIX, on utilisera plutôt opendir, readdir puis closedir.

#include <stdio.h>
#include <dirent.h>

int main(void)
{
DIR * rep = opendir(".");

if (rep != NULL)
{
struct dirent * ent;

while ((ent = readdir(rep)) != NULL)


{

- 66 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

printf("%s\n", ent->d_name);
}

closedir(rep);
}

return 0;
}

- 67 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Les entrées/sorties > Réseau


Comment obtenir le nom de la machine locale ?
Auteurs : gl , Melem ,
Sous Windows, à l'aide de GetComputerNameEx et sous UNIX à l'aide de gethostname. Les programmes suivant
utilisent ces fonctions pour afficher le nom de la machine locale.
Windows :

#define _WIN32_WINNT 0x0500 /


* GetComputerNameEx n'existe que depuis Windows 2000 (Windows NT 5.0 *) */

#include <stdio.h>
#include <windows.h>

int main(void)
{
char lpBuffer[256];
DWORD dwBufLength = sizeof(lpBuffer);

/
* ComputerNameDnsFullyQualified indique qu'on veut le nom complet de la machine (HostName.DomainName). */

if (GetComputerNameEx(ComputerNameDnsFullyQualified, lpBuffer, &dwBufLength))


printf("%s\n", lpBuffer);
else
fprintf(stderr, "La fonction GetComputerNameEx a echoue.\n");

return 0;
}

UNIX :

#include <stdio.h>
#include <unistd.h>

int main(void)
{
char hostname[256];

if (gethostname(hostname, sizeof(hostname)) == 0)
printf("%s\n", hostname);
else
fprintf(stderr, "La fonction gethostname a echoue.\n");

return 0;
}

A noter que la fonction gethostname existe aussi sous Windows mais fait partie de l'API WinSock donc son utilisation
nécessite toutes les initialisations préalables requises par cette API.

Comment obtenir l'adresse IP d'une machine dont je connais le nom ?


Auteurs : gl , Melem ,
La conversion nom de machine vers adresse IP peut se faire grâce à la fonction gethostbyname(). Cette fonction prend
en paramètre le nom de la machine et fournit en retour une structure contenant, entre autres, les différentes adresses
de la machine. La fonction suivante affiche les adresses IP d'une machine dont le nom est fourni en paramètre.

void PrintIp(const char * HostName)

- 68 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

{
struct hostent * host;
struct in_addr addr;

if ((host = gethostbyname(HostName)) != NULL)


{
int i;

for(i = 0; host->h_addr_list[i] != NULL; i++)


{
memcpy(&addr.s_addr, host->h_addr_list[i], sizeof(addr.s_addr));
printf("IP : %s\n", inet_ntoa(addr));
}
}
else
printf("La fonction gethostbyname a echoue.\n");
}

Il convient bien entendu d'inclure les fichiers d'en-tête correspondants (netdb.h sous UNIX, winsock2.h sous Windows)
et de lier avec les bibliothèques nécessaires.

- 69 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Compilation et édition des liens

- 70 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Compilation et édition des liens > Le préprocesseur


Qu'est-ce que le préprocesseur ?
Auteurs : gl , Melem ,
Avant d'être effectivement compilés, les fichiers sources sont tout d'abord traités par un programme appelé
préprocesseur qui se chargera d'exécuter toutes les commandes commençant par # (#include, #define, etc.) jusqu'à ce
qu'il n'y en ait plus une seule. C'est à ce moment et à ce moment seulement, c'est-à-dire après passage du préprocesseur
si besoin était, que la compilation proprement dite (c'est-à-dire traduction du fichier source en fichier objet) peut enfin
être lancée.

Que signifie #define N 10 ?


Auteurs : gl ,
#define N 10 définit une macro N que le préprocesseur devra remplacée par 10 après son passage. Cela signifie que :

#define N 10

int f(void)
{
return N + 1;
}

Donnera après passage du préprocesseur :

int f(void)
{
return 10 + 1;
}

Comment définir et utiliser une macro paramétrée ?


Auteurs : gl ,
Les macros peuvent accepter des paramètres, un peu comme les fonctions. Le nom de la macro doit être immédiatement
suivi (sans même un espace) de la parenthèse ouvrante, des paramètres puis de la parenthèse fermante. Vient ensuite
le corps de la macro qui peut s'étaler sur plusieurs lignes, chacune sauf la dernière se terminant par un anti-slash :
\. Par exemple :

#define PRINT(x) printf("MESSAGE : %s\n", x)

int main(void)
{
PRINT("Hello, world !");

return 0;
}

Donnera après passage du préprocesseur :

int main(void)
{
printf("MESSAGE : %s\n", "Hello, world !");

return 0;

- 71 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Que signifie #define MYMACRO ?


Auteurs : gl , Melem ,
#define MYMACRO définit une macro MYMACRO qui sera remplacée, après passage du préprocesseur, par ... rien !
Par exemple :

#define IN
#define OUT

int f(IN int n, OUT int * p1, OUT int * p2)


{
*p1 = n - 1;
*p2 = n + 1;
return 2 * n;
}

Donnera après passage du préprocesseur :

int f( int n, int * p1, int * p2)


{
*p1 = n - 1;
*p2 = n + 1;
return 2 * n;
}

Comment savoir si une macro est définie ?


Auteurs : Melem ,
Le test if defined(NOM_MACRO) ou ifdef NOM_MACRO permet de savoir si une macro appelée NOM_MACRO est
définie. Voici quelques exemples d'utilisation.

#include <stdio.h>

#if defined(UPPERCASE)
# define MESSAGE "HELLO, WORLD !"
#else
# define MESSAGE "Hello, world !"
#endif

int main(void)
{
printf("%s\n", MESSAGE);
return 0;
}

Dans cet exemple, puisque la macro UPPERCASE n'a pas été définie, tout ce qui est situé entre le test et le else sera
ignoré (supprimé !) par le préprocesseur. Plus précisément, du bloc if ... else ... endif, il ne restera plus que ce qui

- 72 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

se trouvait entre le else et le endif. Ici, MESSAGE sera donc remplacé par le préprocesseur par "Hello, world !". Si
UPPERCASE avait cependant été définie, MESSAGE aurait alors été remplacé par "HELLO, WORLD !".

Quels problèmes peuvent poser l'utilisation des macros ?


Auteurs : gl ,
Il y en a plusieurs ! Voici quelques points auxquels il faut faire très bien attention lorsqu'on fait usage des macros.
Parenthèses :
Soit la macro CARRE dont le rôle est de fournir le carré de la valeur passée en paramètre. Si elle est définie de la sorte :
'#define CARRE(x) x * x', Son utilisation pour un paramètre tel que 9 + 1 sera erronée. En effet 'CARRE(9 + 1)' sera
remplacé par '9 + 1 * 9 + 1' qui en C est équivalent à '9 + (1 * 9) + 1', la multiplication étant prioritaire par rapport
à l'addition, ce qui n'est bien entendu pas le carré de 9 + 1. Il convient donc de toujours parenthéser à l'extrême les
macros, la nôtre devant s'écrire alors : '#define CARRE(x) ((x) * (x))'.
Effets de bord :
Soit la macro MAX fournissant le maximum de deux nombres : '#define MAX(x, y) ((x) > (y) ? (x) : (y))'. Appelée de la
manière suivante : 'k = MAX(3, 2)', k aura évidemment la valeur 3. Soient maintenant i et j, deux variables de type int
vallant toutes 2. On pense alors qu'avec 'k = MAX(++i, j)', k aura encore la valeur 3 comme dans l'exemple précédent
alors que cette fois-ci elle aura la valeur 4 ! En effet, 'k = MAX(++i, j)' est remplacé par le préprocesseur par 'k = ((+
+i) > (j) ? (++i) : (j))'. Comme i a été incrémenté deux fois, on aura k = 4 et non 3 comme il était attendu.
Nom des paramètres :
Une erreur de ce genre peut également arriver : avec '#define ERR_PRINT_INT(n) fprintf(stderr, "%d\n", n)',
ERR_PRINT_INT(10) sera remplacé par fprintf(stderr, "%d\10", 10) !

Quel est le rôle de # dans la définition d'une macro ?


Auteurs : gl , Melem ,
Dans la définition d'une macro, l'opérateur # permet de transformer un argument en chaîne de caractères, quel que
soit l'argument. Par exemple :

#include <stdio.h>

#define TOSTR(x) #x
#define NNNNN 99999

int main(void)
{
printf("%s\n", TOSTR(10000)); /* --> "10000" */
printf("%s\n", TOSTR(1 + 1)); /* --> "1 + 1" */
printf("%s\n", TOSTR(n + 1)); /* --> "n + 1" */
printf("%s\n", TOSTR(float)); /* --> "float" */
printf("%s\n", TOSTR(NNNNN)); /* --> "NNNNN" */

return 0;
}

Comme on peut le constater, TOSTR(NNNNN) est traité par le préprocesseur comme #NNNNN et, sachant qu'il est en
train de développer une macro, va remplacer cette séquence en "NNNNN" et non en "99999" ! Pour avoir "99999" au
lieu de "NNNNN", on pourra utiliser l'astuce suivante :

#define TOSTR(x) __STR(x)


#define __STR(x) #x

- 73 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Dans ce cas, au passage du préprocesseur, TOSTR(NNNNN) sera dans un premier temps remplacé par __STR(99999)
qui sera à son tour (dans une deuxième passe), remplacé par "99999".

Quel est le rôle de ## dans la définition d'une macro ?


Auteurs : Laurent Gomila , Melem ,
Dans la définition d'une macro, l'opérateur ## permet de concaténer deux arguments. Ainsi avec : '#define CAT(x, y)
x##y', CAT(C, 90) sera remplacé par le préprocesseur par C90. Voici un autre exemple avec programme complet :

#include <stdio.h>
#include <wchar.h>

#define WIDESTR(x) L##x


#define AU_REVOIR "Au revoir"

int main(void)
{
wprintf(WIDESTR("%s\n"), WIDESTR("Bonjour")); /* --> wprintf(L"%s\n", L"Bonjour"); */

return 0;
}

Par contre, WIDESTR(AU_REVOIRE) sera traité par le préprocesseur comme L##AU_REVOIR et, sachant qu'il est
en train de développer une macro, va remplacer cette séquence en LAU_REVOIR et non en L"Au revoir" ! Pour avoir
L"Au revoir" au lieu de LAU_REVOIR, on pourra utiliser l'astuce suivante :

#define WIDESTR(x) ___WSTR(x)


#define ___WSTR(x) L##x

Dans ce cas, au passage du préprocesseur, WIDESTR(AU_REVOIR) sera dans un premier temps remplacé par
___WSTR("Au revoir") qui sera à son tour (dans une deuxième passe), remplacé par L"Au revoir".

Que signifie #pragma ... ?


Auteurs : Melem ,
La directive pragma permet d'envoyer une commande (par exemple une option de compilation, d'édition des liens, etc.)
au compilateur. Ces commandes sont toutefois spécifiques au compilateur. La norme requiert que lorsque le compilateur
ne reconnaît pas une commande, il doit tout simplement l'ignorer (les compilateurs émettent généralement un warning
pour informer l'utilisateur de ce fait).

Que signifie #error ... ?


Auteurs : Melem ,
La directive error permet d'interrompre la compilation, comme si une erreur s'était produite. Par exemple, pour faire
une source qui ne puisse compiler que si la taille d'un char sur la cible est de 8 bits, on peut faire :

#include <limits.h>
...
#if (CHAR_BIT != 8)
# error Ce programme requiert que la taille d'un char soit de 8 bits.
#endif

- 74 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

...

lien : Quelle est la taille d'un char ?

Peut-on utiliser sizeof dans un #if ?


Auteurs : gege2061 , Melem ,
Non car sizeof (...) est évaluée pendant la compilation, pas avant. L'astuce suivante permet de générer une véritable
erreur de compilation lorsqu'un test échoue (ici, on va générer une erreur lorsque sizeof(int) est différent de sizeof(long)).

/* Si sizeof(int) != sizeof(long), le tableau assert_int_long aura une taille negative, */


/* ce qui ne sera pas apprecie par le compilateur ... */
static int assert_int_long[sizeof(int) == sizeof(long) ? 1 : -1];

Pourquoi je n'arrive pas afficher la date courante avec __DATE__ ?


Auteurs : gl ,
__DATE__ est une macro, elle est remplacée par le préprocesseur par la date de compilation du fichier. Elle est donc
traitée par le préprocesseur, pas évaluée à l'exécution.

lien : Comment obtenir la date et l'heure courante ?

- 75 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Compilation et édition des liens > Techniques de compilation et d'édition de liens
Qu'est-ce que le compilateur ?
Auteurs : Melem ,
Le compilateur (en anglais : compiler) est le programme qui traduit les fichiers sources (unités de compilation) en
fichiers objet (unités compilées). Un fichier objet est un fichier qui contient, entre autres, l'équivalent en langage machine
de ce qui était contenu dans le fichier source original. Le compilateur ne produit pas de fichier exécutable, cette tâche
étant confiée à l'éditeur de liens.

lien : La compilation séparée

Qu'est-ce que l'éditeur de liens ?


Auteurs : Melem ,
L'éditeur de liens (en anglais : linker) est le programme qui lie les fichies objets entre-eux pour produire un fichier
exécutable. L'ensemble préprocesseur + compilateur + éditeur de liens constitue ce qu'on appelle le plus souvent
un "compilateur" (ici, minimal certes) car c'est en effet généralement ce dernier que le programmeur invoque
pour traduire ses fichiers sources en fichiers exécutables. Les 3 grandes phases de cette "compilation" (traduction),
rappelons-le, sont :
- la précompilation, effectuée par le préprocesseur
- la compilation proprement dite, effectuée par le compilateur
- l'édition des liens, effectuée par l'éditeur de liens.

lien : La compilation séparée

Qu'est-ce que la cross-compilation ?


Auteurs : Melem ,
Il peut arriver, souvent ou rarement selon votre domaine, que vous ayez besoin de compiler un programme pour une
plateforme différente de celle qui est utilisée pour le développement. Par exemple, pour créer une application pour un
téléphone portable, vous n'allez certainement pas programmer avec votre téléphone. Vous développerez plutôt votre
application toujours sur ordinateur mais la compilerez pour le téléphone et non pour votre système. C'est la cross-
compilation.

Qu'est-ce qu'un exécutable au format "binaire plat" ?


Auteurs : Melem ,
Les fichiers exécutables des systèmes tels que Windows, Linux, etc. ne contiennent pas que du code exécutable par
le processeur. Ils contiennent aussi, entre autres, un en-tête qui décrit le fichier (type du fichier, localisation du code
exécutable dans le fichier, localisation des ressources dans le fichier, etc.). Lorsqu'on demande donc à les exécuter, le
système va tout d'abord analyser le fichier, le valider, localiser la partie exécutable, la placer en mémoire avant d'enfin

- 76 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

demander au processeur d'exécuter le programme ainsi chargé. Un exécutable au format binaire plat (en anglais : flat
binary) est un fichier qui ne contient que du code directement exploitable par le processeur.

Qu'est-ce que l'édition statique des liens ?


Auteurs : dourouc05 ,
L'édition statique des liens est une technique qui permet qu'une application soit distribuée sans fichiers de bibliothèque
(.dll, .so, .dylib). En fait, l'édition statique les inclut dans l'exécutable. On parle aussi de "compilation statique".
Cette méthode a des avantages comme des inconvénients. Elle rend les exécutables plus lourds mais elle élimine les
indirections (les renvois vers telle ou telle bibliothèque, tout est inclus dans l'exécutable !) et la nécessité de charger
des bibliothèques au démarrage. Les applications liées statiquement aux différentes bibliothèques démarrent donc plus
vite. Le problème, c'est que si plusieurs applications utilisent les mêmes bibliothèques, une copie de chaque bibliothèque
sera créée en mémoire pour chaque application, ce qui résulte en une utilisation énorme de mémoire.
Parmi les inconvénients, comptons aussi la difficulté de mise à jour : si même seule une bibliothèque a été mise à jour,
c'est tout l'exécutable qu'il faudra recompiler pour utiliser la nouvelle version !

lien : La compilation séparée

Qu'est-ce que l'édition dynamique des liens ?


Auteurs : dourouc05 ,
L'édition dynamique des liens lie votre application à une bibliothèque partagée (DLL sous Windows : Dynamic Link
Library, SO sous Linux : Shared Object, DYLIB sous Mac OS : Dynamic Library) chargée à l'exécution. En fait, si
la bibliothèque (en anglais : library d'où la formulation "une lib" pour désigner "une bibliothèque") est déjà chargée
en mémoire, elle ne le sera pas à nouveau. Ceci permet d'économiser de la mémoire. Toutefois, même dans ce cas, les
systèmes d'exploitation modernes fonctionnant exclusivement en mode protégé, les applications "mappent" (associent,
attachent) toujours les bibliothèques dans leur espace d'adressage avant de les utiliser. Cela signifie que même s'il est
vrai qu'il n'existe qu'une seule instance de la bibliothèque en mémoire physique, chaque application se comporte comme
si elle était la seule à l'utiliser.
Le chargement des applications qui se lient dynamiquement aux différentes bibliothèques est cependant plus lent que le
chargement des applications liées statiquement : il faut que l'OS charge aussi les bibliothèques puis les initialise avant
que l'application puisse s'exécuter.
La mise à jour des applications devient cependant aisée (si seule l'implémentation, c'est-à-dire le corps des fonctions, a
changé) : il suffit de remplacer les fichiers qui ont subi des modifications par leurs nouvelles versions. Si c'est l'interface
même (c'est-à-dire le prototype) d'une qui fonction a changé, ou si une fonction a été supprimée, ce qui est rare car les
développeurs se soucient souvent du problème de compatibilité (en pratique, s'il doit y avoir des changements profonds,
on ajoute des fonctions plutôt que de modifier le prototype des fonctions déjà existantes), alors il faudra évidemment
recoder l'application pour l'adapter à la nouvelle version.
Au niveau de la compilation, les applications apprennent les différentes bibliothèques avec lesquelles elles devront se
lier à l'exécution grâce à une bibliothèque d'importation (un fichier .lib ou .a) qui indique le nom de la bibliothèque
qui lui est associée et qui liste les fonctions contenues dans cette bibliothèque sans leurs implémentations (elles, stockées
dans la bibliothèque). Cela rend donc les applications plus légers.

lien : La compilation séparée

- 77 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Divers

- 78 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Divers > Les nombres aléatoires


Que renvoie la fonction rand() ?
Auteurs : LFE ,
La fonction rand() renvoie des nombres pseudo-aléatoires qui sont les termes d'une suite pseudo-aléatoire. Les termes
de cette suite sont des nombres compris entre 0 et RAND_MAX. Si cette suite est toujours la même, alors ces nombres
seront aussi toujours les mêmes. Il faut donc utiliser une suite différente à chaque exécution pour avoir des résultats assez
satisfaisants. C'est là qu'intervient la fonction srand(). Cette fonction, qui permet d'initialiser le générateur de nombres
aléatoires avec le nombre passé en argument appelé graine (attention, il n'est spécifié nul part que ce nombre sera le
premier terme de la suite ...), doit toujours être appelée en début de tout programme utilisant les nombres aléatoires.
Comme notre but est d'avoir une suite différente à chaque exécution, le problème revient donc en fait à changer de
graine à chaque exécution. Une méthode largement admise comme solution est d'utiliser le temps pour graine. Ainsi,
tout programme utilisant les nombres aléatoires doit toujours commencer par : 'srand((unsigned)time(NULL));'.
rand() et srand() sont déclarées dans stdlib.h. time est déclarée dans time.h.

lien : Comment obtenir la date et l'heure courante ?

Pourquoi rand() me renvoie toujours la même valeur ?


Auteurs : gl ,
Vous appelez certainement srand() plus d'une fois dans le programme. En effet, srand() permet d'initialiser le
générateur de nombres pseudo-aléatoires. Toutefois, si l'unité de temps dans la fonction time est la seconde (ce qui est
généralement le cas) et que srand(time(NULL)) est appelé plus d'une fois lors de la même seconde, le générateur est
réinitialisé à chaque fois avec la même valeur et génèrera la même séquence de nombres. Ainsi :

int i;

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


{
srand((unsigned)time(NULL));
printf("tirage : %d\n", rand());
}

Affiche plusieurs fois le même nombre lorsqu'elle est exécutée dans la même seconde. Pour avoir un fonctionnement
correct, il faut utiliser :

int i;

srand((unsigned)time(NULL));

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


printf("tirage : %d\n", rand());

Comment obtenir un nombre aléatoire (réel) entre 0 et x ?


Auteurs : Melem ,
Il suffit d'écrire une fonction linéaire qui permet de passer de l'intervalle [0, RAND_MAX] à [0, x]. Par exemple :

double rand_x(double x)
{
return rand() * (x / RAND_MAX);
}

- 79 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Bien entendu, plus x est grand, plus il y aura des nombres qui ne seront jamais retournés. Dans des applications simples,
x n'est jamais aussi grand (généralement 1 tout au plus !) alors que RAND_MAX vaut au minimum (cela est imposé
par la norme) 32767. Si on devait vraiment générer aléatoirement un nombre x nettement grand devant 1, on pourra
toujours générer dans un premier temps la partie entière puis après seulement générer la partie fractionnaire ...

Comment obtenir un nombre aléatoire (entier) entre 0 et N ?


Auteurs : gl , Jean-Marc.Bourguet ,
La méthode la plus simple pour obtenir un nombre "aléatoire" entre 0 (inclus) et N (inclus) consiste à utiliser l'opérateur
modulo : 'randvalue = rand() % (N + 1);'. Cette méthode permet, certes, d'obtenir un nombre toujours compris entre
0 et N, mais la suite de nombres obtenus n'est pas forcément très "aléatoire" car :
• Elle utilise les bits de poids faible des nombres retournés par la fonction rand() or de nombreux générateurs de
nombres aléatoires simples génèrent des nombres avec des bits de poids faible non aléatoires.
• Elle introduit un biais lorsque RAND_MAX n'est pas un multiple de N + 1.

La méthode suivante permet d'éliminer les différents biais sans favoriser les bits de poids faible :

#include <stdlib.h>

int rand_n(int n)
{
int partSize = 1 + (n == RAND_MAX ? 0 : (RAND_MAX - n) / (n + 1));
int maxUsefull = partSize * n + (partSize - 1);
int draw;

do {
draw = rand();
} while (draw > maxUsefull);

return draw / partSize;


}

lien : Les nombres aléatoires en C et en C++

- 80 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Divers > Gestion des dates et heures


Qu'est-ce que l'heure système ?
Auteurs : Bob , Melem ,
L'heure système est la date (date système) et l'heure (heure système) utilisées en interne par le système. La majorité des
systèmes utilisent le temps universel comme heure système.

Qu'est-ce que l'heure locale ?


Auteurs : Bob , Melem ,
L'heure locale est la date (date locale) et l'heure (heure locale) qu'il fait dans un point particulier du globe. Elle peut se
calculer à l'aide d'une formule mathématique qui fait entrer en jeu l'heure système et les coordonnées de ce point.

Comment obtenir la date et l'heure courante ?


Auteurs : LFE ,
La fonction time permet d'obtenir la date et l'heure courante (heure système).

#include <time.h>

time_t t = time(NULL); /* t contient maintenant la date et l'heure courante */

La valeur retournée peut également être récupérée via un pointeur vers un buffer assez grand pour la contenir. Voici
donc une deuxième méthode :

#include <time.h>

time_t t;

time(&t); /* t contient maintenant la date et l'heure courante */

Comment décomposer une date ?


Auteurs : Haypo , gl ,
La fonction time fournit la date système sous forme d'un nombre de secondes écoulées depuis une date précise. Une telle
date est appelée un 'timestamp'. La majorité des systèmes utilisent le 01/01/1970 à 00:00:00 UTC comme référence. Les
fonctions gmtime et localtime permettent de décomposer cette date en année, mois, jour, heure, minute et seconde en
remplissant une structure struct tm définie par :

struct tm
{
int tm_sec; /* secondes (0,59) */
int tm_min; /* minutes (0,59) */
int tm_hour; /* heures depuis minuit (0,23) */
int tm_mday; /* jour du mois (0,31) */
int tm_mon; /* mois depuis janvier (0,11) */
int tm_year; /* années écoulées depuis 1900 */
int tm_wday; /* jour depuis dimanche (0,6) */
int tm_tm_yday; /* jour depuis le 1er janvier (0,365) */
int tm_isdst;
};

- 81 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Voici donc un petit programme affichant la date et l'heure courante (locale) selon le format français :

#include <stdio.h>
#include <time.h>

const char *
NomJourSemaine[] = {"Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"};

const char * NomMois[] = {"janvier", "fevrier", "mars" , "avril" , "mai" , "juin" ,


"juillet", "aout" , "septembre", "octobre", "novembre", "decembre"};

int main(void)
{
time_t timestamp;
struct tm * t;

timestamp = time(NULL);
t = localtime(&timestamp);

/* Affiche la date et l'heure courante (format francais) */


printf("Nous sommes %s, ", NomJourSemaine[t->tm_wday]);
printf("le %02u %s %04u, ", t->tm_mday, NomMois[t->tm_mon], 1900 + t->tm_year);
printf("et il est %02uh %02umin %02usec.\n", t->tm_hour, t->tm_min, t->tm_sec);

return 0;
}

Pour obtenir l'heure système, il suffit de remplacer localtime par gmtime.

Comment formater une date sous un format spécifique ?


Auteurs : LFE , gege2061 ,
La fonction strftime permet de formater une date en appliquant le même principe de chaîne de formatage que sprintf.

#include <time.h>

size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);

Le premier argument de la fonction est le pointeur vers le tampon destiné à recevoir la chaîne formattée et le second un
entier indiquant la taille de ce tampon. Le paramètre format est une chaîne de caractère décrivant le format souhaité
dont voici les spécificateurs de format :

+------+-------------------------------------------------------------+
| Code | Format correspondant |
+------+-------------------------------------------------------------+
| %a | Nom du jour en abrégé |
| %A | Nom du jour complet |
| %b | Nom du mois en abrégé |
| %B | Nom du mois complet |
| %c | MM/JJ/AA HH:MM:SS |
| %d | Numéro du jour dans le mois (01 à 31) |
| %H | Heure sur 24 heures (00 à 23) |
| %I | Heure sur 12 heures (01 à 12) |
| %m | Numéro du mois dans l'année (01 à 12) |
| %M | La minute (0 à 59) |
| %p | AM ou PM suivant la partie de la journée |
| %S | La seconde (0 à 59) |
| %u | Numéro du jour de la semaine (1 (lun.) à 7 (dim.)) |
| %w | Numéro du jour de la semaine (0 (dim.) à 6 (sam.)) |
| %x | MM/JJ/AA |

- 82 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

| %X | HH:MM:SS |
| %y | Année sur deux chiffres |
| %Y | Année sur quatres chiffres |
| %Z | Nom du fuseau horaire |
| %% | Le caractère % |
+------+-------------------------------------------------------------+

Voici un exemple pour illustrer cela :

#include <stdio.h>
#include <time.h>

int main(void)
{
char buffer[256];
time_t timestamp = time(NULL);

strftime(buffer, sizeof(buffer), "%A %d %B %Y - %X.", localtime(&timestamp));


printf("%s\n", buffer);

return 0;
}

Comment convertir une date en chaîne de caractères ?


Auteurs : LFE , Melem ,
La fonction ctime permet de convertir un timestamp en une chaîne de caractères, en ajustant la date à la date et l'heure
locales.

#include <stdio.h>
#include <time.h>

int main(void)
{
time_t t = time(NULL);

printf("%s\n", ctime(&t));

return 0;
}

La fonction asctime permet de convertir une structure struct tm en une chaîne de caractères représentant la même date.
Le programme précédent est donc équivalent à :

#include <stdio.h>
#include <time.h>

int main(void)
{
time_t t = time(NULL);

printf("%s\n", asctime(localtime(&t)));

return 0;

- 83 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Comment soustraire deux dates ?


Auteurs : gege2061 ,
La fonction difftime permet de soustraire deux dates. Ces dernières doivent être sous la forme d'une struture de type
time_t, que l'on peut obtenir, soit par l'intermédiaire de la fonction time pour la date du jour, soit avec la fonction
mktime. Voici un exemple qui calcule le nombre de secondes écoulées depuis le 1er Janvier 2000 minuit :

#include <stdio.h>
#include <time.h>

int main(void)
{
time_t today;
struct tm an2000;

an2000.tm_mday = 1; /* 1er */
an2000.tm_mon = 0; /* janvier */
an2000.tm_year = 100; /* 2000 (2000 - 1900) */
an2000.tm_hour = 0; /* 0 heure */
an2000.tm_min = 0; /* 0 minute */
an2000.tm_sec = 0; /* 0 seconde */

today = time(NULL);

printf("Il s'est coule %g secondes depuis le 1er janvier 2000 minuit.\n",


difftime(today, mktime(&an2000)));

return 0;
}

Comment connaître l'heure système (Windows) ?


Auteurs : Bob ,
L'unité de temps utilisée par la fonction time est la seconde. Windows permet de connaître l'heure système jusqu'à la
milliseconde grace à la fonction GetSystemTime.

void GetSystemTime(SYSTEMTIME *lpSystemTime);

lien : Comment connaître l'heure locale (Windows) ?

Comment connaître l'heure locale (Windows) ?


Auteurs : Bob ,
En utilisant GetLocalTime au lieu de GetSystemTime. Le programme suivant affiche la date et l'heure locales en
utilisant cette fonction.

#include <stdio.h>
#include <windows.h>

int main(void)
{
SYSTEMTIME Time;

- 84 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

GetLocalTime(&Time);

printf("Nous sommes le : %02d/%02d/%04d.\n",


Time.wDay, Time.wMonth, Time.wYear);

printf("Et il est : %02dh %02dmn %02ds %03dms.\n",


Time.wHour, Time.wMinute, Time.wSecond, Time.wMilliseconds);

return 0;
}

Comment chronométrer des temps d'exécution ?


Auteurs : gl , Emmanuel Delahaye ,
La fonction clock() permet de mesurer le temps CPU en ticks d'horloge. Pour obtenir ce temps en seconde, il suffit de
convertir à l'aide de CLOCKS_PER_SEC.

#include <stdio.h>
#include <time.h>

int main(void)
{
clock_t start, end;
double elapsed;

start = clock(); /* Lancement de la mesure */

/* ... */ /* Faire quelque chose */

end = clock(); /* Arret de la mesure */

elapsed = ((double)end - start) / CLOCKS_PER_SEC; /* Conversion en seconde */

printf("%.2f secondes entre start et end.\n", elapsed);

return 0;
}

Sous Windows, il y a aussi la fonction GetTickCount() qui retourne le nombre de millisecondes écoulées depuis le
lancement du système. Malheureusement GetTickCount() n'a généralement une précision que de l'ordre de quelques
ms et si on veut mesurer des temps plus faibles (temps d'execution d'une fonction, mesure du framerate dans les
jeux, ...), il vaudra mieux utiliser la fonction QueryPerformanceCounter. La précision du compteur utilisée par cette
fonction peut être connue à l'aide de QueryPerformanceFrequency. Ces deux fonctions travaillent sur des entiers larges
(LARGE_INTERGER), une union représentant un entier de 64 bits. Le membre QuadPart de cette union permet
d'accéder intégralement à l'entier qu'il représente.

#include <stdio.h>
#include <windows.h>

int main(void)
{
LARGE_INTEGER start, end, freq;
double elapsed;

QueryPerformanceFrequency(&freq);

QueryPerformanceCounter(&start); /
* Lancement de la mesure */

- 85 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

/* ... */ /
* Faire quelque chose */

QueryPerformanceCounter(&end); /
* Arret de la mesure */

elapsed = (1000.0 * (end.QuadPart - start.QuadPart)) / freq.QuadPart; /


* Conversion en millsecondes */

printf("%.0f millisecondes entre start et end.\n", elapsed);

return 0;
}

- 86 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Divers > Gestion des processus


Comment donner la main à un autre processus ?
Auteurs : Melem ,
Les fonctions de la famille exec (qui sont standardisées par POSIX) permettent de charger puis de lancer un nouveau
processus. Le nouveau processus est placé dans la mémoire précédemment occupée par l'ancien processus.

#include <stdio.h>
#include <unistd.h> /* <process.h> sous DOS/Windows */
#include <string.h>

int main(void)
{
char path[256], *p, *argv[2];

printf("Tapez le chemin vers le programme a executer : ");


fgets(path, sizeof(path), stdin);
if ((p = strchr(path, '\n')) != NULL)
*p = '\0';

argv[0] = path;
argv[1] = NULL;
execv(path, argv);

printf("La fonction execv a echoue.\n");

return 0;
}

Si vous avez un compilateur C/C++ moderne et que vous êtes sous Windows, préférez les noms _exec* (par exemple
_execv) aux noms POSIX exec* sauf si vous voulez avoir un code unique pour les deux systèmes.

lien : Que signifie l'underscore (_) en début du nom d'une fonction ou d'une macro, etc. ?

Comment créer un nouveau processus ?


Auteurs : Davidbrcz , publicStaticVoidMain , Melem ,
Sur les systèmes de type UNIX, à l'aide de fork + exec et sous DOS/Windows, à l'aide de spawn (ou directement avec
l'API CreateProcess). Avec un compilateur C/C++ moderne, préférez les noms spawn* aux anciens noms spawn*.
fork permet à un processus de se dupliquer en 2 processus exactement identiques sur le prochain code à exécuter, sur
les valeurs des variables (ceci inclut aussi le gestionaire de signaux) mais pas sur les locks mémoires, les compteurs de
temps ou sur les signaux en attente.

#include <stdio.h>
#include <unistd.h>

int main(void)
{
pid_t pid = fork();

/* Le deuxieme processus reprend à partir d'ici. */

if (pid == -1) {
/* Erreur */
} else if (pid == 0) {
/* On est dans le fils. */
/* Il ne reste plus qu'a faire un exec pour charger le processus qu'on veut executer. */
} else {
/* On est dans le pere. */

- 87 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

/
* On peut attendre la fin du processus fils avant de continuer a l'aide de la fonction waitpid. */
}

return 0;
}

Sous Windows (_P_WAIT n'étant pas définie sous DOS ...) :

#include <stdio.h>
#include <process.h>

int main(void)
{
const char * argv[] = {"hello", NULL};

printf("Bonjour.\n");

_spawnvp(_P_WAIT, "hello.exe", argv); /* Attendre la fin de hello.exe avant de continuer. */

printf("Au revoir.\n");

return 0;
}

lien : Que signifie l'underscore (_) en début du nom d'une fonction ou d'une macro, etc. ?
lien : L'API CreateProcess (Windows)

- 88 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Divers > Communication avec l'environnement


Comment accéder aux paramètres de la ligne de commandes ?
Auteurs : LFE , gl ,
Pour accéder aux paramètres de la ligne de commandes, il suffit de déclarer la fonction main() de la façon suivante :

int main(int argc, char * argv[])


{
return 0;
}

- argc est le nombre de paramètres constituant la ligne de commande.


- argv est un tableau de pointeurs sur des chaînes de caractères qui représentent les paramètres de la ligne de commande.
- argv[0] représente le "nom du programme" et argv[1] à argv[argc - 1] les paramètres passés au programme. argv[argc]
vaut toujours NULL.
Attention, le "nom du programme" ne signifie pas le nom de l'exécutable. argv[0] peut pointer vers :
- le nom de l'exécutable
- le chemin complet de l'exécutable
- une chaîne vide
- autre chose !
Mais il est garanti que le contenu de argv[0] ne sera jamais NULL (ce qui implique que argc vaut toujours au moins 1 ...).

lien : Comment récupérer les variables d'environnement ?

Comment récupérer les variables d'environnement ?


Auteurs : LFE , joellel , gl ,
La fonction getenv() permet de lire la valeur d'une variable d'environnement.
La fonction putenv(), introduite par POSIX, permet quant à elle de créer ou de modifier une variable d'environnement.
Ces fonctions sont toutes déclarées dans stdlib.h.
Il existe aussi sur certains compilateurs une forme de déclaration de la fonction main() qui donne accès aux variables
d'environnement via un paramètre de main().

int main(int argc, char * argv[], char * envp[])


{
return 0;
}

envp pointe sur un tableau de pointeurs vers des chaînes de caractères, chacune représentant une variable
d'environnement sous la forme : "nom=valeur", où nom correspond au nom de la variable et valeur à sa valeur. Le
dernier élément du tableau pointé par envp contient NULL.

lien : Comment accéder aux paramètres de la ligne de commandes ?

Que fait la fonction system ?


Auteurs : Melem ,
Cette fonction, déclarée dans stdlib.h, permet de demander au système d'exécuter la commande passée en argument,
comme si on l'avait vraiment tapée depuis l'invite des commandes. system passe en fait la commande à l'interpréteur de
commandes du système, le programme qui va réellement exécuter la commande. Si ce dernier est absent, la commande
sera donc tout simplement ignorée.

- 89 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

La manière dont un programme récupère le chemin vers l'interpréteur de commandes (généralement en vue de lui
passer une commande ...) est bien entendu dépendante du système. Sous DOS et Windows par exemple, ce chemin
est renseigné par la variable d'environnement COMSPEC. Un programme lit donc la valeur de cette variable pour
connaître le chemin vers l'interpréteur de commandes (qui est normalement command.com sous DOS et cmd.exe sous
Windows NT). Ainsi, lorsqu'un programme est lancé par émulation (par exemple un programme MS-DOS lancé sous
Windows NT), system pourra ne plus fonctionner car l'interpérteur de commandes renseigné par l'émulateur peut par
exemple tout simplement être introuvable.
En fait, la norme ne précise même pas la manière dont comment la fonction system doit traiter la chaîne qui lui est
passée. Ainsi, dans de nombreux environnements (incluant DOS, Windows et UNIX), si la chaîne commence par le nom
ou le chemin d'un fichier exécutable, system va lancer le programme sans invoquer l'interpréteur de commandes.

Comment récupérer le résultat d'une commande ?


Auteurs : gl ,
Il est possible de lancer une commande du système et de récupérer, via un pointeur de fichier (FILE *), le résultat de
cette commande grâce à la fonction popen introduite par POSIX. Cette fonction s'utilise de manière similaire à fopen
mais attend une commande à exécuter à la place d'un nom de fichier

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
FILE * pp;
char buf[256];

if ((pp = popen("ls -l", "r") == NULL)


{
perror("popen");
exit(EXIT_FAILURE);
}

while (fgets(buf, sizeof buf, pp))


fputs(buf, stdout);

pclose(pp);

return 0;
}

- 90 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Divers > Gestion des erreurs


Comment obtenir le message correspondant à un n° d'erreur positionné dans errno ?
Auteurs : gl ,
De nombreuses fonctions en C, positionnent la variable errno pour indiquer le type d'erreur lorsqu'une erreur se
produit. Il est possible d'afficher la description de l'erreur grâce à la fonction perror(), déclarée dans stdio.h. Le code
suivant permet par exemple d'indiquer la raison de l'échec de l'ouverture d'un fichier.

#include <stdio.h>

...

FILE * f;

if ((f = fopen("fic.txt", "r")) == NULL)


{
/* Affichons la description de l'erreur avec pour titre "fic.txt" */

perror("fic.txt");
}

Il est également possible de récupérer cette chaîne d'erreur plutôt que de l'afficher en utilisant la fonction strerror().

#include <stdio.h>
#include <errno.h> /* pour errno */
#include <string.h> /* pour strerror */

...

FILE * f;

if ((f = fopen("fic.txt", "r")) == NULL)


{
/* Affichons la description de l'erreur avec pour titre "fic.txt" */

fprintf(stderr, "fic.txt: %s.\n", strerror(errno));


}

Comment obtenir le message correspondant à un n° d'erreur retourné par GetLastError() (Windows) ?


Auteurs : LFE , Médinoc ,
Le petit bout de code ci-dessous permet d'obtenir le message correspondant à un n° d'erreur obtenu par GetLastError().

#include <windows.h>

void GetErrorDescription(LPTSTR lpBuffer, DWORD dwBufferSize)


{
FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
lpBuffer,
dwBufferSize,
NULL
);

- 91 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Comment être informé d'une erreur d'exécution (violation d'accès, division par 0, etc.) ?
Auteurs : Melem ,
En interceptant le signal associé à l'erreur. Les seuls signaux normalisés à ce jour sont :

• SIGABRT : Le programme est sur le point d'être arrêté prématurément (du à un appel à abort() par exemple).
• SIGFPE : Une erreur de nature arithmétique s'est produite (par exemple une division par 0).
• SIGILL : Une instruction illégale a été rencontrée (par exemple une instruction nécessitant un niveau de
privilège élevé).
• SIGINT : Une interruption du programme a été demandée par l'utilisateur (Ctrl + C sous DOS/Windows).
• SIGSEGV : Une violation d'accès s'est produite (tentative d'écriture dans une mémoire en lecture seulement,
etc.).
• SIGTERM : La terminaison du programme a été demandée.

Il n'est cependant pas requis que le système soit capable de générer lui-même ces signaux (en effet un signal peut être
explicitement généré à l'aide de la fonction raise ...). De plus, la norme stipule que dans le cas où le signal généré est
SIGFPE, SIGILL ou SIGSEGV, la seule manière portable de le traiter est de mettre fin au programme dans la procédure
de traitement du signal (ce qui signifie que celle-ci ne doit pas retourner). Par exemple :

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

void sig_handler(int s);

int main(void)
{
signal(SIGSEGV, sig_handler);

"Bon*our"[3] = 'j'; /* Si "Bon*our" se trouve dans une zone en lecture seule, */


/* cette instruction va causer le systeme a emetrre le signal SIGSEGV */
/* s'il en est capable. */

printf("Aucun signal recu.\n");

return 0;
}

void sig_handler(int s)
{
if (s == SIGSEGV)
{
fprintf(stderr, "Signal SIGSEGV recu.\n");
exit(EXIT_FAILURE);
}
}

- 92 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Divers > Bonnes pratiques


Faut-il caster malloc ?
Auteurs : gl , Pouet_forever ,
Contrairement aux idées reçues, le cast du malloc / calloc / realloc n'est pas obligatoire en C.
Dans les premières versions du C, le pointeur générique (void *) n'existait pas. La fonction malloc renvoyait un pointeur
de type char *, ce qui rendait le cast obligatoire lorsque le pointeur utilisé pour stocker cette valeur retournée est de
type différent.
Le C ANSI a cependant introduit le type "pointeur générique" (void *) qui, au vu des conversions implicites, peut être
assigné, sans cast, à un pointeur de n'importe quel type (de même, un pointeur de n'importe quel type peut être assigné,
sans cast, à un pointeur générique). Depuis, il n'était donc plus nécessaire de caster malloc.
Si votre compilateur refuse de compiler votre code tant que vous ne castez pas malloc, alors :

• soit vous utilisez un compilateur non conforme (ce qui est très peu probable de nos jours à moins que vous
utilisiez un compilateur datant de l'avant-normalisation, c'est-à-dire un compilateur des années 70 ou 80 ...).
• soit vous compilez en mode "C++", c'est-à-dire que votre compilateur (qui est donc apparemment capable de
compiler aussi bien du code C que du code C++) pense que votre code est écrit en C++ (dans lequel le cast de
void * vers un autre type pointeur est obligatoire) et non en C (dans lequel caster malloc est non seulement
inutile mais aussi de mauvais style). Généralement les fichiers sources C++ portent l'extension .cpp et les
fichiers sources C l'extension .c, mais cela peut changer d'un compilateur à un autre. Il faut donc faire bien
attention sur ce point là.

Pourquoi est-il déconseillé d'utiliser scanf() ?


Auteurs : gl , Emmanuel Delahaye ,
La fonction scanf() est une fonction destinée, comme le f dans son nom l'indique, aux saisies formatées et s'avère ainsi
très peu adaptée aux saisies humaines qui peuvent présenter des erreurs de format, ces erreurs de format pouvant
entraîner des débordements de tampon, des boucles infinies, des perturbations sur les saisies suivantes, etc. Il est donc
conseillé de remplacer les saisies formatées effectuées avec scanf() par une lecture complète via fgets() suivie d'une
analyse de la chaîne lue.
Voici par exemple un programme qui entre dans une boucle infinie si jamais l'utilisateur tape quelque chose qui
commence par un caractère qui ne apparaître en début d'un nombre (une lettre par exemple) :

#include <stdio.h>

int main(void)
{
int n = 0;

while (n != 1)
{
printf("Tapez 1 pour quitter.\n");
scanf("%d", &n);
}

return 0;
}

En effet, le spécificateur de format %d indique que scanf doit effectuer la lecture des caractères à partir de la position
courante dans le tampon de lecture de l'entrée standard avec les règles suivantes :

Si des caractères sont présents


1. Passer tous les caractères blancs (espaces, tabulations, fins de ligne).
2. Lire les caractères présents jusqu-à la rencontre du premier caractère blanc ou

- 93 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

invalide dans l-expression d-un nombre entier. Le caractère qui a arrêté la lecture
n-est pas lu par scanf. Il sera donc le premier caractère rencontré dans la
prochaine lecture sur l-entrée standard.
3. Convertir la chaîne lue en entier et stocker le résultat à l-adresse spécifiée.
Sinon
- Attendre que l-utilisateur tape une ligne.
- Appliquer 1, 2 puis 3.
Fin Si

Ainsi, en tapant "ab12" par exemple, 'a' étant un caractère invalide dans l'expression d'un entier, le curseur de scanf
n'avancera jamais (il restera éternellement bloqué face à ce caractère !).
L'utilisation de fgets permet d'éliminer ce problème.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void clean_stdin(void); /* Voir '[FAQ] Comment vider le buffer du clavier ?'. */

int main(void)
{
int ok = 0; /* 0 = false */

while (!ok)
{
char buffer[256], *p;
int n;

printf("Tapez 1 pour quitter.\n");

/* Lecture de la chaine tapee. */

fgets(buffer, sizeof(buffer), stdin);

/* Supprimer le '\n' de la chaine lue s'il est present. */

if ((p = strchr(buffer, '\n')) != NULL)


*p = '\0';
else
{
/* Si le '\n' est absent, vider le buffer du clavier. */

clean_stdin();
}

/* Analyse de la chaine lue. */

n = strtol(buffer, &p, 10); /* strtol permet de passer de "123" (buffer) a 123 (n). */

if (*p == '\0')
{
/* On peut utiliser n */

if (n == 1)
ok = 1;
}
}

return 0;

- 94 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

lien : Comment vider le buffer clavier ?

Pourquoi gets est-elle dépréciée en faveur de fgets ?


Auteurs : gl , Emmanuel Delahaye ,
La fonction gets() ne permet pas de spécifier la taille du tampon destiné à contenir la chaîne lue et expose donc
le programme aux attaques par débordement de tampon. Cette fonction ne doit donc jamais être utilisée dans un
programme digne d'être respecté.

lien : Comment lire une ligne de manière securisée ?

- 95 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Sommaire > Divers > Divers


Quelles options de compilation utiliser pour compiler avec gcc ?
Auteurs : gege2061 , gl , Anomaly ,
Le langage C exige une grande rigueur lors de l'écriture du code. Il peut être fastidieux de vérifier que toutes les
variables sont correctement initialisées, ou encore détecter les erreurs tel que la confusion entre le opérateur = et ==.
Heureusement, le compilateur gcc est capable de détecter ce genre d'erreur, il suffit pour cela de lui passer un certain
nombre d'arguments. Voici la ligne de commande type qui permet de faire du code conforme à la norme ANSI (C89)
et de corriger un grand nombre de comportements indéfinis :

-O2 -Wall -W -Werror -ansi -pedantic

Cependant ces options peuvent être trop contraignantes : par exemple, avec GTK+ les prototypes des fonctions callback
sont imposés, mais il se peut que tous les arguments ne vous servent pas dans la fonction, dans ce cas gcc refusera de
compiler votre code. Pour pallier ce problème, utilisez plutot les options :

-O2 -Wall -Werror -ansi -pedantic

Notons que ces options forcent le compilateur à passer en mode "C89 strict", pour pouvoir utiliser le mode "C99", il
faut remplacer -ansi par -std=c99, la ligne d'option devient donc :

-O2 -Wall -Werror -std=c99 -pedantic

Attention : contrairement à ce que son nom peut laisser penser, -Wall n'active pas tous les warning mais tous ceux jugés
importants par l'équipe de gcc.

lien : GCC Home Page

Que signifie l'underscore (_) en début du nom d'une fonction ou d'une macro, etc. ?
Auteurs : Melem ,
Il signifie la plupart du temps que la fonction ou la macro en question n'est pas standard (donc pas très portable ...). Plus
précisément, qu'elle fait partie de la version de la bibliothèque standard proposée par l'environnement que vous utilisez
mais qu'elle n'est pourtant pas reconnue par la norme. Par exemple, les fonctions et macros DOS/Windows _getch,
_kbhit, _spawn*, _P_WAIT, etc. font partie de la bibliothèque du C (appelée CRT sous Windows pour C Run-Time
Library) alors qu'elles ne sont pas standard, d'où l'underscore. Cette règle de l'underscore ne s'applique toutefois bien
évidemment pas aux fonctions des bibliothèques tierces qui accompagnent le compilateur (APIs du système et autres
bibliothèques spécifiques). Par exemple, sous les systèmes supportant POSIX (c'est-à-dire UNIX et tous ses clones), les
fonctions read, write, exec* font partie de l'API POSIX (on les appelle souvent "appels systèmes" car se sont la plupart
du temps des fonctions du système) et non de la bibliothèque du C donc pas besoin d'underscore. L'API POSIX est
cependant proposée en tant qu'extension de CRT sous Windows (et elle n'est pas supportée à 100% ...) d'où l'apparition
des noms avec underscore (_read, _write, _exec*, etc.).
Cette convention de nommage fut adoptée pour la première fois par le langage C++ et est utilisée aujourd'hui par tous
les compilateurs respectant la norme de ce langage (la première version de cette norme date de 1998). Elle n'est pas
obligatoire en C, mais beaucoup de compilateurs C (ou C++ ...) sont en fait des compilateurs C/C++ (c'est-à-dire qu'ils
savent compiler aussi bien du code C que du code C++, même si ce sont des langages différents) donc ils appliquent ce
système aux deux langages, ce qui n'est pas incompatible avec la norme du C.

- 96 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans
FAQ C

Pour les mot-clés non standard, on met deux underscores en avant au lieu d'un seul. Par exemple : __declspec, __stdcall,
__asm, __int32, __int64, etc.

Que signifie l'erreur : unresolved external symbol _WinMain@16 ?


Auteurs : gege2061 ,
Cette erreur est due à l'absence de la fonction WinMain. Cette fonction remplace le main d'un programme C standard
dans le cas d'un programme spécifiquement conçu pour Windows. Pour supprimer cette erreur, modifiez le type de
votre projet en projet d'application console.

Que signifie le warning : no new line at end of file ?


Auteurs : gl ,
La norme stipule qu'une ligne complète doit être terminée par un saut de ligne. Ce warning signale que la dernière ligne
du fichier n'est pas complète. Pour supprimer ce warning, il suffit de la compléter.

Pourquoi mon programme se lance et se termine immédiatement sans que je ne puisse rien voir ?
Auteurs : Aurelien.Regat-Barrel , gl ,
Sous Windows, lorsqu'on lance une application console depuis l'interface graphique (c'est-à-dire en double-cliquant),
une console se crée pour faire tourner le programme et se détruit aussitôt que ce dernier ait terminé. Pour maintenir
la console jusqu'à ce que l'utilisateur tape une touche pour la fermer, il suffit d'ajouter une instruction bloquante juste
avant de quitter la fonction main, par exemple à l'aide de 'system("pause");'.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
printf("Hello World !\n");
system("PAUSE");
return 0;
}

N.B : Dans certains EDI (Code::Blocks, Visual C++, etc.), la commande 'Lancer' ('Run') qui permet de tester un
programme sans quitter l'EDI ne lance pas directement le programme, surtout quand ce dernier est une application
console, mais lance un programme qui va à son tour lancer le vôtre puis après que ce dernier ait terminé, affiche un
message vous invitant à appuyer sur une touche pour fermer la console. Ce message ne fait donc pas du tout partie
de votre programme.

- 97 -
Les codes sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Pour le reste, ce document constitue une oeuvre
intellectuelle protégée par les droits d'auteurs. Ce document issu de http://www.developpez.com est soumis à deux licences, en fonction des contributeurs : -
Les contributions de LFE sont soumises aux termes de la la licence GNU FDL traduite en français ici. Permission vous est donnée de distribuer, modifier des
copies des contributions de LFE tant que cette note apparaît clairement : "Ce document issu de http://www.developpez.com est soumis à la licence GNU FDL
traduite en français ici. Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement". - Pour ce qui est
des autres contributions : Copyright © 2002-2010 Developpez LLC : Tous droits réservés Developpez LLC. Aucune reproduction, ne peut en être faite sans