Source: https://openclassrooms.com/forum/sujet/foire-aux-questions-langage-c-98714?page=3
Timestamp: 2019-12-07 09:41:24+00:00

Document:
Foire Aux Questions — langage C - Votre question y est peut-être... par Kevin Leonhart - page 3 - OpenClassrooms
Foire Aux Questions — langage C
Votre question y est peut-être...
23 mars 2010 à 9:36:44
Citation : ZéroZéroHuit-008
Il convient parfaitement à la valeur que renvoie sizeof()
Ici, il faudrait ôter les parenthèses. Elles sont certes nécessaires pour entourer un type (comme dans sizeof(int)), mais l'opérateur sizeof n'a pas que des types comme opérande (comme dans sizeof ma_chaine).
A part ça, le type size_t est nécessaire lorsqu'on manipule des tableaux d'une taille supérieure à la moitié de la capacité d'adressage. Ce n'est pas courant sur les systèmes actuels, mais du temps de Windows 16 bits et des segments de 64 ko, ça l'était.
7 avril 2010 à 2:22:23
[8][7] Comment créer un tableau 2D dynamique ?
Créer un 'vrai' tableau 2D avec l'allocation dynamique n'est pas possible, en revanche il est possible de créer un tableau de pointeurs qui se comportera comme un tableau 2D, à l'exception près que les éléments de ce tableau de seront pas contigüs (ou très peu de chances).
#define N_1ERE_DIM 5
#define N_2NDE_DIM 10
char ** tab = NULL;
/* Allocation de la 'première' dimension (le tableau de pointeurs). */
if ((tab = malloc(N_1ERE_DIM * sizeof *tab)) == NULL)
/* Vous pouvez aussi faire malloc(N_1ERE_DIM * sizeof(char*)) */
/* Allocation de la 'seconde' dimension. */
for (i = 0; i < N_1ERE_DIM; i++)
if ((tab[i] = malloc(N_2NDE_DIM * sizeof *tab[i])) == NULL)
/* Vous pouvez aussi faire sizeof(char) (notez l'étoile en moins). */
/* Pour libérer on fait le chemin inverse. */
for (i = 0; i < N_1ERE_DIM; i++) {
Remarque : Cette solution est relativement couteuse en allocations. Et bien qu'elle présente certains avantages (notamment la possibilité de "grappiller" de la mémoire), il est souvent plus intéressant d'utiliser pour les données un seul grand tableau, avec un tableau de pointeurs aux index correspondants.
typedef unsigned** Matrix;
Matrix init_matrix (unsigned y, unsigned x)
Matrix mat = malloc (y * sizeof *mat);   /* index (1ere dimension) */
if ( mat != NULL )
*mat = malloc(y * x * sizeof **mat); /* données (2eme dimension) */
if (*mat != NULL)
for (i=0,j=0; i<y*x; i+=x,j++)
mat[j] = (*mat)+i;
void delete_matrix (Matrix* mat)
if (**mat != NULL)
free(**mat), **mat = NULL;
free(*mat), *mat = NULL;
Matrix mat = NULL;
mat = init_matrix (N_1ERE_DIM,N_2NDE_DIM);
delete_matrix (&mat);
15 juin 2010 à 16:30:37
[8][9] Que fait la fonction qsort() ? Et comment l'utiliser ?
La fonction qsort() (définie dans <stdlib.h>) sert a trier toutes sortes de tableaux (int, char, double, struct...), en ordre croissant ou décroissant.
qsort (void *base, size_t nmemb, size_t size, int(*compar)(const void*, const void *));
*base représente le début du tableau a trier.
nmemb est le nombre de cases du tableau.
size représente la taille d'une case.
et enfin, *compar est un pointeur sur une fonction de comparaison que l'on fournit a qsort().
La fonction de comparaison sert a déterminer l'ordre du tri. Elle renvoie -1 si le premier élément doit précèder le second, 1 si le second élément doit précéder le premier et 0 si les éléments sont équivalents.
/* fonction d'affichage */
void affiche (int tab[], unsigned sz)
/* fonction de comparaison pour tri en ordre croissant */
return *pa > *pb ? 1 : *pa < *pb ? -1 : 0;
/* fonction de comparaison pour tri en ordre decroissant */
int comp_dec(const void *a, const void *b)
return *pa < *pb ? 1 : *pa > *pb ? -1 : 0;
int t[10] = { 143, 17, 271, 503, 648, 219, 38, 190, 145, 100 };
qsort(t, 10, sizeof(int), comp);
affiche (t, 10);
qsort(t, 10, sizeof(int), comp_dec);
On évitera généralement une fonction de comparaison du genre return *pa - *pb; à cause du risque d'overflow.
Dans le cas d'un tri de chaines de caractères, la fonction strcmp() (définie dans <string.h>) est tout indiquée pour comparer.
22 août 2010 à 23:21:16
je remarque que la question "Je suis sous Visual, mon programme s'exécute normalement mais il disparait après trois secondes. Que faire ?" est fréquemment posée sur le forum C. La réponse étant "Il faut simplement compiler avec CTRL+F5" ; Je pense que l'on pourrait intégrer cette question à cette FAQ, qu'en dites vous ?
2 septembre 2010 à 22:02:00
Un petit rappel ne serait pas de trop :
Citation : Kevin Leonhart
Pour contribuer à cette FAQ, merci de ne pas poster directement ici mais d'aller sur le sujet qui lui est dédié et de faire tes propositions là-bas. Ceci est valide pour les corrections d'erreurs dans les questions déjà existantes comme pour la proposition de nouvelles questions.
30 juillet 2011 à 15:50:51
[6][4] Mon compilateur me dit "Undefined reference"
Tout d'abord, il faut savoir que l'erreur ne survient pas à la compilation mais lors de l'édition de liens. Cette erreur signifie que votre éditeur de liens n'a pas trouvé le code de la fonction que vous voulez appeler.
La fonction est de vous
En premier lieu, vérifiez que vous ne vous êtes pas trompé dans le nom de la fonction et que le fichier la contenant a bien été ajouté à votre projet. Prenez également garde au nombre et au type des paramètres lorsque vous appelez votre fonction.
Autrement, cette erreur est également assez courante lorsque vous créez un projet SDL avec Code::Blocks. En effet, cet IDE va créer automatiquement un fichier main.cpp, tandis que vos autres fichiers porteront l'extention .c.
Dans ce cas, il vous faudra remplacer votre main.cpp par un main.c. Pour cela, rien de plus simple, il vous suffit de rajouter un nouveau fichier main.c à votre projet, d'y coller le contenu de votre fichier main.cpp et de supprimer ce dernier en faisant Clic droit -> "Remove file from project".
La fonction n'est pas de vous
Cette erreur arrive si vous avez mal configuré une installation (SDL ou autre bibliothèque). Reprenez l'installation et la configuration du projet pas à pas.
Undefined reference to SDL_main
Cette erreur arrive lorsque vous utilisez un mauvais prototype de la fonction main. Pour un projet SDL, utilisez l'un des deux prototypes suivants (ils sont équivalents) :
Undefined reference to IMG_Load
En téléchargeant SDL_Image, les fichiers à linker (.lib et .dll) se trouvent dans deux dossiers : x64 et x86. Ne copiez que les fichier se trouvant dans x86.
Ce post est bien ici et non dans la FAQ bibliothèques tierces. Ce n'est pas une erreur.
Merci à _Fender_ pour ce post.
L'ajout SDL_Image a été suggéré par damjuve.
5 août 2011 à 12:40:54
[8][10] J'ai un problème lors d'une lecture/écriture avec mon fichier, que faire ?
Tout d'abord, êtes vous certains que votre fichier a bien été ouvert? Vous pouvez le vérifier aisément en examinant le retour de la fonction fopen (elle retourne un pointeur nul en cas d'erreur). Si votre fichier n'est pas ouvert, vérifier l'orthographe de son nom et que ce dernier se situe bien dans le même dossier que vos fichiers sources.
Ensuite, êtes vous certains de vous situer au bon endroit dans votre fichier? Si vous êtes à la fin de ce dernier, il n'y aura forcément rien à lire. Dans un tel cas, appelez la fonction rewind pour revenir au début ou une des deux autres fonctions de positionnement fseek et fsetpos.
Enfin, votre fichier est-il ouvert en lecture/écriture ('+' dans le mode)? Si oui, sachez qu'il est préférable d'éviter ce mode d'ouverture car il impose plusieurs contraintes. Toutefois, si ce mode d'ouverture s'impose pour votre programme, voici les règles à suivre imposées par la Norme C(99):
Citation : Norme C99 7.19.5.3 § 6 p 272
intervening call to a file positioning function, unless the input operation encounters end-
of-file. Opening (or creating) a text file with update mode may instead open (or create) a
binary stream in some implementations.
- une lecture ne doit pas être suivie d'une écriture sans un appel à une fonction de positionnement (rewind, fseek, fsetpos) sauf si l'opération de lecture à rencontrer la fin de fichier;
- une écriture ne doit pas être suivie d'une lecture sans un appel à fflush ou à une fonction de positionnement.
La première contrainte est probablement la plus gênante puisqu'il faut appeler une fonction de positionnement alors que vous ne souhaitez pas forcément vous déplacer. Une solution simple est d'effectuer cet appel:
fseek(fichier, 0L, SEEK_CUR);
Ainsi, vous ne vous êtes pas déplacer mais avez respecter les contraintes imposées par la Norme
Merci à uknow pour ses corrections.
Staff 5 août 2011 à 12:58:48
Je me permets de rajouter quelques détails à ta réponse que tu peux ajouter si tu les juges utiles .
Pour l'histoire du fichier dans "le même dossier que vos fichiers sources." il me semble que c'est pour CodeBlocks (seulement?).
Ensuite, êtes vous certains de vous situer au bon endroit dans votre fichier? Si vous être à la fin de ce dernier, il n'y aura forcément rien à lire. Dans un tel cas, appelé la fonction rewind pour revenir au début ou une des deux autres fonctions de positionnement fseek ou fsetpos.
Ceci concerne surtout les modes "a+" et "w+"; ou "r+" si on vient d'écrire quelque chose à la fin.
- une écriture ne doit pas suivre une lecture sans un appel à fflush ou à une fonction de positionnement.
Sauf si cette lecture a rencontré un EOF.
Je rajoute: fflush ne doit pas être utilisée si le mode d'ouverture est "r+" ou "rb+". Donc les autres façons de faire sont plus applicables.
Je rajoute: fflush ne doit pas être utilisée si la dernière opération effectuée est une lecture. Donc les autres façons de faire sont plus applicables.
Pour finir fseek(fichier, 0 , SEEK_CUR); économiserait un appel à ftell .
Sinon c'est très utile ce que tu viens de rédiger.
5 août 2011 à 14:02:01
Je viens seulement de me rendre compte qu'il y avait un sujet pour proposé des entrées dans la FAQ. J'avais envoye ma proposition à GuilOooo par mp, mais je me rends compte que cela aurait été mieux de la poster là-bas, histoire d'avoir l'avis et les corrections de tout le monde
J'avoue que je ne sais pas du tout, je pensais que c'était pour tous les IDE, mais je me trompe peut-être...
Oui, c'est vrai cela vise indirectement les cas d'ouvertures en lecture/écriture, mais pas seulement. Le cas typique est l'exercice du pendu avec dictionnaire. Il est fréquent de voir des membres oubliés l'appel à rewind entre la lecture pour obtenir le nombre de mots dans le fichier et la lecture d'un mot "au hasard".
Sinon, merci pour la faute d'orthographe, c'est corrigé
Mince, j'ai sauté ce passage, je l'ajoute, merci beaucoup
Ah bon? Tu en es certains? Je ne vois pas cette restriction dans la Norme...
Pour finir fseek(fichier, 0 , SEEK_CUR); économiserait un appel à ftell
En fait, le problème est que le comportement de SEEK_CUR n'est garantit que pour les flux ouvert en mode binaire. Pour les fichiers ouvert en mode texte, on est obligé de se contenter de SEEK_SET:
Citation : Norme C99 7.19.9.2 § 4 p 302
and whence shall be SEEK_SET.
EDIT: je viens de me rendre compte que j'ai opérer une inversion dans mes explications sur les contraintes imposées par la Norme, je corrige aussi
Staff 5 août 2011 à 14:16:40
La norme ne traite pas directement ce cas particulier à ce que je sache, mais comme tu le sais fflush ne s'applique pas à un flux entrant, et "r+" ne doit pas faire exception (non?).
Nous avons assisté à un problème de ce genre il y a un moment sur le SDZ justement (GurneyH aussi était dans la discussion je crois) . Il y a tellement longtemps que je ne pourrai pas retrouver le sujet dans mon historique, mais je vais quand même chercher.
Edit : Je viens de trouver quelque chose sur msdn assurant que fflush est applicable même en mode "r+"; mais il se peut que ce soit dépendant des versions :
Source : http://msdn.microsoft.com/en-us/librar [...] v=vs.71).aspx
Edit2 : C'est bon j'ai trouvé la discussion, et tu y as participé en plus . Ca remonte à un an presque .
Donc je réctifie ce que j'avais écrit. La norme dit que fflush ne doit pas être appliquée à un flux dont la dernière opération était une lecture. Donc je pense que c'est valable aussi bien à "r+" qu'aux autres modes.
(Pour revoir la conversation : http://www.siteduzero.com/forum-83-584 [...] fichiers.html ).
17 août 2011 à 18:37:14
[6][6] Mon programme en console se ferme tout seul sans que je puisse rien faire.
C'est probablement dû au fait que la console de Windows est programmée pour se fermer automatiquement à la fin de l'exécution du programme qui l'avait sollicitée.
Effectuer une pause à la fin de votre programme : consultez l'entrée [2][2] de cette FAQ si vous ne savez pas comment faire.
Cependant, comme le souligne Kevin Leonhart :
La plupart des systèmes disposant d'une vraie console n'ont pas besoin et n'apprécient pas les pauses en fin de programme ; la console étant un environnement persistant et qui ne se ferme pas comme sous Windows.
Lancer votre programme (fichier *.exe si vous êtes sous Windows) depuis une console que vous avez ouverte préalablement ; si vous ne savez pas comment faire, suivez cette procédure :
Démarrer > Exécuter (ou raccourci clavier Windows+R) ;
dans la petite fenêtre qui s'ouvre, tapez « cmd » et validez ;
vous avez ouvert une console ! Maintenant, rendez-vous dans le dossier où se trouve le fichier exécutable (*.exe) de votre programme en tapant la commande cd /d chemin\du\dossier\de\votre\projet\ (pressez Entrée pour valider) ;
il ne vous reste plus qu'à lancer votre programme avec la commande monProgamme.exe (si vous utilisez Code::Blocks, la commande est bin\Debug\monProgamme.exe ou bin\Release\monProgamme.exe, selon votre mode de compilation).
Vous pouvez l'exécuter ainsi autant de fois que vous voulez sans que la fenêtre ne se ferme. Il vous suffit, pour le relancer, de taper à nouveau la commande (ou de la rappeler avec la touche ↑ (flèche haut), si vous êtes paresseux).
Configurer votre IDE pour qu'il attende votre accord pour fermer la console après la fin de votre programme (cette manipulation ne modifie en rien votre programme lui-même, si vous le lancez en dehors de votre IDE, il n'y aura pas de pause à la fin). Voici la marche à suivre pour Code::Blocks :
Project > Properties… > onglet Build targets ;
dans la zone de gauche, sélectionnez le mode de compilation qui vous intéresse (« Debug » ou « Release ») ;
dans la zone de droite, cochez la case « Pause when execution ends ». Il ne vous reste plus qu'à valider (bouton OK).
Désormais (vous n'avez pas besoin de recompiler), une pause se produira après la fin de l'exécution de votre programme, avec le message suivant :
Process returned 0 (0x0)   execution time : 0.219 s
Ce message vous indique la valeur retournée par main et le temps d'exécution de votre programme. Il vous suffit d'appuyer sur une touche pour fermer la console.
Staff 25 août 2011 à 10:55:28
Je reviens sur le sujet de fseek( stream , 0 , SEEK_CUR );
Comportement indéfini (Annexe J : les comportements indéfinis) :
- The fseek function is called for a text stream with a nonzero offset and either the offset was not returned by a previous successful call to the ftell function on a stream associated with the same file or whence is not SEEK_SET (7.19.9.2).
Si je n'ai pas compris de travers, le comportement ne concerne que des valeurs d'offset non-nulles. Comme dans notre appel l'offset est nul donc je suppose que cela ne rentre pas dans cette catégorie décrite par la norme comme étant indéfinie.
De même pour l'autre paragraphe :
- For a text stream, either offset shall be zero, or offset shall be a value returned by an earlier successful call to the ftell function on a stream associated with the same file and whence shall be SEEK_SET.
Là encore une fois il est dit que l'offset doit être nul, ou alors s'il est non-nul il faut qu'il soit le résultat d'un appel réussi à ftell et que le whence soit SEEK_SET.
25 août 2011 à 12:21:05
Ah oui, tu sembles avoir raison. Si on reprend le texte de la Norme au sujet de fseek:
La phrase semble bien dire que soit l'offset doit être nul, soit l'offset doit être une valeur retournée par ftell avec le dernier argument égal à SEEK_SET.
Histoire d'avoir d'autres avis, je suis allé faire un tour sur fr.comp.lang.c et je suis tombé sur ce sujet qui confirme ton point de vue. Je vais donc corriger mon entrée pour prendre cela en compte, merci beaucoup
25 septembre 2011 à 20:19:12
Ne poste pas sur la FAQ C, elle n'est pas faite pour cela. Crée un nouveau sujet et expose ton problème dans celui-ci
26 décembre 2011 à 21:23:40
[8][11] Faut-il caster le retour des fonctions d'allocation dynamique ?
On voit beaucoup de programmeurs qui castent la valeur de retour de malloc / calloc / realloc, pourtant en C ce n'est pas nécessaire. En effet, les fonctions d'allocation dynamique retournent une valeur de type void*, ce qui fait qu'elle peut être convertie implicitement dans n'importe quel type : il n'est donc pas nécessaire de caster.
Si cependant votre code refuse de compiler en vous sortant une erreur du type "invalid conversion from 'nom du type' to 'void*'" c'est que vous compilez en C++, car contrairement au C les conversions implicites du type void* vers un autre type sont interdites.
Merci à Taurre pour ses corrections.
26 décembre 2011 à 22:51:36
une valeur de type void*, ce qui fait qu'elle peut être convertie implicitement dans n'importe quel type :
Pas en type pointeur vers fonction me semble-t-il.
26 décembre 2011 à 22:56:29
Oui en fait je pensais à tous les types dans le sens long* ou double* par exemple.
1 janvier 2012 à 1:51:43
Non, il y a pas mal de cast interdit même avec void* ...
Genre structure et union par exemple ...
Tous les types non-scalaires si je me souviens bien ... Faut voir la norme pour plus d'informations ... On oublie vite ce genre de détails ^^"
29 janvier 2012 à 16:00:08
non, on peut caster un void* en struct (..)*
pour les unions, je ne sais pas (je pense pas, là; même si au final on a qu'à allouer l'élément le plus gros)
En fait, je pense que du moment que l'on connaisse la taille de l'objet il devrait pas y avoir de soucis
29 janvier 2012 à 16:46:05
Un pointeur sur void peut-être converti vers n'importe quel type de pointeur sur objet et inversément. Cela exclut donc seulement les pointeurs de fonctions
Citation : Norme C11 6.3.3.3 § 1 p 55
A pointer to void may be converted to or from a pointer to any object type. A pointer to
29 janvier 2012 à 18:11:36
Je parle bien de structure et non de pointeur sur structure
Sinon, ça marche aussi pour les pointeurs d'union ...
J'ai légèrement la flemme de chercher dans la norme le passage décrivant cette interdiction ...
29 janvier 2012 à 18:23:30
Ah ok, désolé, au temps pour moi
C'est spécifié au niveau de l'opérateur de conversion
Citation : Norme C11 6.5.4 § 2 p 91
Unless the type name specifies a void type, the type name shall specify atomic, qualified,
or unqualified scalar type, and the operand shall have scalar type.
29 janvier 2012 à 18:33:02
#define PRINT(p) printf("@%s {%p}\n", __FUNCTION__, (p))
PRINT((void*)f);
void *g = f;
/* Conversion implicite {void -> void(*)(void)} */
void (*p)(void) = g;
PRINT((void*)p);
@main {0x80483f0}
@f {0x80483f0}
Est-ce indéfini par la norme ? (désolé pour le flood sur le topic, mais je ne savais pas trop où réagir)
29 janvier 2012 à 19:16:20
Oui, la norme spécifie seulement le comportement pour une conversion d'un pointeur de fonction vers un autre, pas pour la conversion d'un pointeur de fonction vers un pointeur sur objet:
Citation : Norme C11 6.3.2.3 § 8 p 56
Cependant, cette conversion est supportée par pas mal de compilateur (cf Annexe J.5.7)
15 avril 2012 à 13:18:53
[8][12] Que signifie « ++i » ? Quelle différence avec « i++ » ?
Saviez-vous qu’il existe en fait deux opérateurs d’incrémentation (++), et autant pour la décrémentation (--) ? On parle d’incrémentation ou de décrémentation postfixée (n++, n--) ou préfixée (++n, --n). La version préfixée ressemble beaucoup à la postfixée, mis à part que l’opérateur se place avant son opérande.
Quelle est la différence ? Les opérateurs préfixés modifient la variable, puis renvoient sa nouvelle valeur, alors que les postfixés renvoient la valeur de la variable avant de la modifier.
int  a=5,  b=5;
printf("%d\n", ++a);    // affiche 6
// a vaut maintenant 6
printf("%d\n", b++);    // affiche 5
// b vaut maintenant 6
L’intérêt de tout ça ? L’opérateur préfixé est en principe plus rapide que le postfixé. En effet, le postfixé impose de créer une copie temporaire de la variable pour renvoyer sa valeur avant modification. Vous pouvez donc optimiser vos programmes en utilisant les versions préfixées partout ou vous n’avez pas besoin de l’ancienne valeur, par exemple dans vos boucles : for(i=0; i<n; ++i).
Cependant, en pratique, votre compilateur favori est sans doute capable de faire cette optimisation tout seul (c’est le cas de GCC et de Clang entre autres). Voyant que vous ne récupérez pas la valeur de retour de l'expression (i++), il sait qu’il peut la remplacer par (++i).
En C++, c’est différent. En effet, dans le cas d’un objet, les opérateurs ++ et -- sont en fait des appels de fonctions maquillés par la surcharge des opérateurs, et le compilateur ne peut donc pas optimiser (car il ne sait pas ce qui se cache derrière, contrairement au C).
Utiliser les opérateurs adaptés est donc une bonne habitude à prendre. D’autant plus qu’en C++, le gain de performance peut être significatif car l’objet est peut-être très lourd à copier.
Pour plus d’informations à ce sujet, allez voir ici.
16 avril 2012 à 21:10:30
[3][9] Comment passer d’un caractère de ‘0’ à ‘9’ au nombre correspondant (et vice-versa) ?
Comme vous devez le savoir, les caractères (stockés dans le type char) sont en fait représentés par des nombres. Les valeurs varient selon votre système (compilateur et machine) mais c’est souvent celles de l’ASCII.
Parmi les caractères disponibles, il y a notamment les chiffres de zéro à neuf : '0', '1', '2', …, '9'. Cependant, leur valeur n’est pas 0, 1, 2, …, 9 (en ASCII, '0' vaut 48).
Heureusement, il existe une astuce simple pour passer de l’un à l’autre :
/* pour passer du caractère au nombre */
c = '6';          // c vaut maintenant '6' (soit 54 en ASCII)
i = c - '0';      // i vaut maintenant 6, et non 54 (si ASCII)
/* pour passer du nombre au caractère */
i = 7;            // i vaut maintenant 7
c = i + '0';      // c vaut maintenant '7' (soit 55 en ASCII), et non le caractère spécial de valeur 7 (si ASCII)
Écrire simplement « c = i » (ou « i = c » dans l’autre sens) n’aurait pas marché, car dans le 1er cas i aurait valu 54 au lieu de 6, et dans le 2nd cas c aurait valu 7, ce qui correspond en ASCII à un caractère spécial.
Cette astuce se base sur le fait que les caractères chiffres ('0', '1', '2', '3'…) ont des valeurs consécutives ; la 1ère valeur est celle du chiffre zéro, soit '0' dans le cas général.
Il suffit donc d’une simple opéraiton de décalage (rajouter ou soustraire la valeur '0') pour passer d’un nombre de 0 à 9 au caractère chiffre correspondant, et vice-versa.
NB : Si jamais un évêque à plume du forum C (ou n’importe qui de façon générale, n’attendez pas les plumes) vient vous sermonner que cette solution n’est pas portable, vous lui mettrez sous le bec cet extrait de la norme :
Citation : norme C99 (draft n1256) − 5.2.1, §3
Both the basic source and basic execution character sets shall have the following members:
the 10 decimal digits 0 1 2 3 4 5 6 7 8 9
[…] In both the source and execution basic character sets, the value of each character after 0
in the above list of decimal digits shall be one greater than the value of the previous. […]</span>
Cette astuce ne marche pas si vous voulez dépasser 9 (eh oui, les chiffres arabes s’arrêtent à 9). Elle ne marche pas non plus si vous espérez convertir les lettres ('A', 'B', 'C', …) en nombres selon le système hexadécimal (où la lettre A signifie 10, B signifie 11, C 12…). En effet, dans le jeu de caractères du système, il n’est pas garanti que les lettres soient consécutives aux chiffres, ni même qu’elles soient « rangées » dans l’ordre et de façon croissante. Enfin, il faut distinguer les minuscules des majuscules (ce sont deux caractères différents).
Dans la plupart des jeux (dont l’ASCII), les lettres latines sont bien consécutives et triées ; par conséquent, sur ces systèmes, il est possible de recourir à une astuce similaire pour passer de la lettre au nombre :
if(c>='0' && c<='9')    // Si c est un chiffre de 0 à 9   [PORTABLE]
if(c>='A' && c<='Z')    // Si c est une lettre majuscule  [NON PORTABLE]
if(c>='a' && c<='z')    // Si c est une lettre minuscule  [NON PORTABLE]
Le +10 servant à donner la valeur 10 et non 0 à la lettre A. Toutefois, cette solution n’est pas portable.
17 avril 2012 à 21:48:50
[8][13] Que signifient les deux-points « : » à coté d’un membre de structure ?
co-rédigé par Taurre et Maëlan
int  a  :2,
b  :3,
c  :1,
d  :1,
e  :1;
Peut-être avez-vous déjà rencontré des codes qui ressemblent à ça.
Les deux-points à côté d’un membre d'une structure (ou d’une union) signifient que ce dernier est un « champ de bits » (“bit-field” en anglais). Cela permet de spécifier, pour un champ de type int , unsigned int ou _Bool (C99), le nombre de bits que l’on souhaite utiliser (ce nombre ne doit bien évidemment pas excéder le nombre de bits composant le type).
Cette technique peut-être très utile afin de gagner de la place quand vous savez que vous n’utiliserez jamais toute la capacité du type int, unsigned int ou _Bool : dans un seul int « normal », vous faites tenir plusieurs membres de ce type.
Pour faire cela, il vous faut être sûr des valeurs que pourront prendre vos membres. Cette technique restreint en effet les valeurs disponibles. Dans un champ de m bits, vous pouvez écrire 2m valeurs différentes (de 0 à 2m-1 dans le cas d’un nombre non signé).
Ainsi, vous pouvez par exemple faire des booléens ou des flags qui ne prennent que les valeurs 0 ou 1 et n’ont donc besoin que d’un bit, tout ça sans utiliser d’opérations bit-à-bit (c’est le compilateur qui s’en charge pour vous). Par exemple :
/** Structure contenant 8 booléens **/
struct Booleans {
_Bool  b1  :1,    /* (le type _Bool est apparu en C99) */
b2  :1,
b3  :1,    /* Ces membres n’utilisent qu’un bit et ne peuvent */
b4  :1,    /*  donc valoir que 0 ou 1. */
b5  :1,
b6  :1,
b7  :1,
b8  :1;
/** Exemple d’utilisation **/
struct Booleans  myBools =  {1, 1, 0, 1, 0, 1, 1, 0};
myBools.b3 =  1;
myBools.b7 =  myBools.b2;
if(myBools.b1)    { … }
/* ⇒ On s’en sert comme des membres normaux. */
/*/ Taille totale de la structure **/
printf("sizeof Booleans: %u\n", sizeof(struct Booleans) );
// ↑ probablement 1
printf("sizeof _Bool[8]: %u\n", sizeof(_Bool[8]) );
// ↑ probalement 8
/* ⇒ Si on n’avait pas eu recours aux champs de bits pour stocker
8 booléens (un tableau par exemple), on aurait utilisé plus de
Il est possible de spécifier un champ de bit sans nom, par exemple :
struct StructureAvecBourrage {
int  a  :3,
:2,    /* champ anonyme de 2 bits */
b  :3;
Les champs anonymes ne peuvent évidemment pas être utilisés. Ils permettent de régler l’alignement des autres champs en laissant de l’espace précis entre eux.
Dernière possibilité, on peut spécifier un champ anonyme de longueur 0. Cela indique au compilateur de remplir le reste de l’unité de stockage et de faire commencer le champ suivant à la prochaine unité. Par exemple :
struct StructureAvecAlignement {
b  :2,
:0,    /* champ anonyme s’étendant jusqu’à la fin du « int » conteneur */
c  :3;    /* champ commençant au « int » suivant */
Point norme :
Vous ne pouvez faire ceci que pour des membres de structure ou union.
Cela se limite aux types entiers int, signed int, unsigned int ou _Bool (depuis C99). Les champs de bits sont du même type que leur « conteneur ». Dans le cas de int, le fait que les champs de bits soient signés ou non est au choix de l’implémentation.
La norme n’impose pas d’ordre pour les champs de bits (des bits de poids forts vers les bits de poids faibles ou le contraire) : c’est à l’implémentation de trancher.
Attention donc si vous tentez de gérer un ensemble de flags avec une union de cette façon :
uint8_t flags;        /* ensemble de 8 flags */
struct {              /* accès individuel aux divers flags */
_Bool  hasBuffer    :1,
usesTruck    :1,
hasDrunk     :1,
knowsTruth   :1,
hasReadDraft :1,
hasSeenWolf  :1,
canFly       :1,
isLastMohic  :1;
Pour les mêmes valeurs de flags, l’entier les englobant tous (le membre flags) pourra avoir une valeur différente d’un système à l’autre, selon l’ordre dans lequel sont rangés les champs de bits.
S’il ne reste plus assez de place dans l’unité de stockage pour le prochain champ de bits, celui-ci peut être décalé pour commencer avec l’unité suivante (donc ajout de bits inutilisés), ou à cheval sur les deux unités (ce qui diminue les performances) ; à l’implémentation de décider.
Conclusion : les champs de bits sont sympathiques et parfois bien pratiques, mais si vous visez la portabilité, mieux vaut les oublier. Si vous avez vraiment besoin de condenser vos données, utilisez manuellement les opérations binaires (&, |, ^).
18 avril 2012 à 22:22:00
[8][14] Quels sont les opérateurs du langage C ? Comment se combinent-ils ?
co-rédigé par Taurre et Maëlan, co-corrigé par candide et rz0 (merci à eux)
Un opérateur spécifie une opération à appliquer à des éléments qu’on appelle ses opérandes. Cette opération peut produire une valeur calculée, avoir un « effet de bord » (une affectation par exemple) ou les deux.
3 * 5;    /* renvoie la valeur 15 */
i++;      /* renvoie la valeur de i, et augmente de 1 la valeur
de cette variable (→ effet de bord) */
3 et 5 sont les opérandes de la première opération, i est l’unique opérande de la seconde. Ces exemples montrent au passage que tous les opérateurs n’ont pas le même nombre d’opérandes.
Contrairement aux fonctions de la bibliothèque standard, les opérateurs sont des éléments du langage. Ils constituent, avec leurs opérandes, les composants élémentaires d’une expression.
Mais, le C compte combien d’opérateurs alors ?
Hé bien, le langage C intègre de nombreux opérateurs : 48 en tout ! Certains sont plus fréquents que d'autres, mais tous ont leur utilité. Le tutoriel officiel de M@teo21, comme de nombreux cours pour débutants, ne les présente pas tous car ils ne sont pas utiles pour commencer.
Ci-dessous, un grand tableau vous les représente tous. Le but n’est pas de tous les expliquer ici, mais d’en offrir une vue d’ensemble et de rappeler leur caractéristiques : arité, priorité et associativité.
Rhôô, les gros mots ! Ça veut dire quoi tout ça ?
On appelle arité le nombre d’opérandes d’un opérateur. En C, elle varie de 1 à 3.
On dit qu’un opérateur est « unaire » s'il n’a qu’un seul opérande. Par exemple, ++ (comme dans A++) et sizeof (comme dans sizeof A) sont des opérateurs unaires.
De même, les opérateurs ayant deux opérandes sont dits « binaires » (rien à voir avec la base 2),
et ceux en ayant trois « ternaires » (il n’en existe qu’un en C, l’opérateur ? : qui s’utilise ainsi : A?B:C et qui est simplement appelé opérateur ternaire).
La priorité des opérateurs permet de déterminer l’ordre dans lequel les opérations composant une expression seront combinées.
Dans une expression comportant plusieurs opérations, celles-ci sont emboîtées de telle sorte qu’une opération serve d’opérande à une autre. Ça ne se fait pas n’importe comment, c’est la priorité qui impose la façon de lire : une opération prioritaire servira d’opérande à une opération qui l’est moins (la prioritaire est donc calculée avant).
Prenons un exemple, l’expression 5 && 0 + 3. On pourrait calculer sa valeur de deux manières :
— effectuer le ET logique puis l’addition : ( (5 && 0) + 3 ) ;
— effectuer l’addition puis le ET logique : ( 5 && (0 + 3) ).
L’ordre des opérations est extrêmement important puisqu’il va déterminer la valeur de l’expression. Dans le premier cas on obtiendra 3, et dans l’autre on obtiendra 1. Grâce à la priorité, nous savons que l’addition doit s’effectuer avant le ET (l’addition est prioritaire sur le ET) et que la valeur de l’expression sera donc de 1.
Malheureusement, la priorité des opérateurs ne suffit pas à lever toute ambigüité. En effet, que faire dans les cas où les opérateurs ont la même priorité ? Par exemple, dans l’expression 6 / 2 * 3, qui de la division ou de la multiplication doit être réalisée la première (sachant qu’elles ont la même priorité) ?
— Si l’on fait la division d’abord : ( (6 / 2) * 3 ) nous donne 9.
— Si l’on fait la multiplication d’abord : ( 6 / (2 * 3) ) nous donne 1.
C'est ici que l’associativité intervient, en déterminant un sens de lecture. Pour les opérateurs * et /, l’associativité est de gauche à droite, donc on lit l’expression dans ce sens ; on rentre dans le premier cas et on obtient 9. L’expression pourrait être reformulée comme suit : (6 / 2) * 3.
Les deux associativités possibles sont évidemment de la gauche vers la droite (left to right, ltr) et de la droite vers la gauche (right to left, rtl).
Pour contourner les règles de priorité et d’associativité (si l’on veut combiner les opérations différemment), on met des parenthèses.
Trêve de bavardages, voici enfin le tableau ! Il est organisé de telle sorte qu’un opérateur prioritaire sur un autre sera placé plus haut que ce dernier ; deux opérateurs de même priorité seront sur la même ligne.
Les opérateurs du langage C : priorité, associativité et arité
Catégories d'opérateurs
appel de fonction, indiçage,
membre de structure,
membre de structure pointée ( ) [ ] . -> --1 ++1 ltr → 2 (1
et --)
opérateurs unaires (Type) sizeof _Alignof2 * & ! ~ - + --1 ++1 rtl ← 1
multiplication, division, modulo * / % ltr → 2
addition, soustraction + -
décalage binaire << >>
comparaison < <= >= >
opérations binaires &
opérations logiques &&
opérateur conditionnel ? : rtl ← 3
affectation = += -= *= /= %= <<= >>= &= ^= |= 2
virgule , 3 ltr →
1 Il existe en fait deux opérateurs d’incrémentation et deux de décrémentation : les opérateurs postfixés (i--, i++) mais aussi les opérateurs préfixés (--i, ++i) qui sont moins connus des débutants (voir l’entrée [8][12] de cette FAQ). Les postfixés sont prioritaires sur les préfixés et ont une associativité contraire.
2 L’opérateur _Alignof a été introduit en C11.
3 L’opérateur virgule (,) est très méconnu des débutants.
Cette page très complète du Wikipedia anglais apporte plus de détail. Elle présente tous les opérateurs du C et du C++, en indiquant s’ils sont surchargeables et comment, ainsi que leur priorité et leur associativité.
Pour finir, voici deux exemples d’expressions afin de mettre en pratique ce que vous venez d’apprendre. Pour chacune d’entres elles, nous allons déterminer l’ordre dans lequel le calcul des valeurs sera effectué.
*(char*)p + 5 * 3
Les opérateurs prioritaires sont * et (char*). Ces derniers ont la même priorité, il est donc nécessaire de recourir à leur associativité. Cette dernière étant de droite à gauche, c’est la conversion en char* qui sera effectuée la première. Enfin, il reste une addition et une multiplication et comme vous le savez, c’est la multiplication qui est prioritaire. Au final, l’expression peut-être réécrite comme suit :
( *( (char *)p ) )  +  (5 * 3)
t[i].x++
Les opérateurs [], . et ++ ont tous la même priorité, il va donc falloir recourir à leur associativité. Cette dernière étant de gauche à droite, l’expression peut s’écrire :
( (t[i]).x )++
Certains opérateurs s’écrivent de façon identique (même symbole) mais se différencient par leur arité (1 ou 2). Il y en a 4 en tout, en voici la liste :
Opérateur binaire
+ +A signe : détermine le signe de l’expression A A + B addition ou soustraction : retourne la somme ou la différence de A et B
— -A A - B
* *A indirection : retourne la valeur pointée par A A * B multiplication : retourne le produit de A et B
& &A référence : retourne l’adresse de la variable A A & B ET binaire (opérateur bit-à-bit) : retourne l’entier tel que chaque bit
est à 1 si et seulement si le bit correspondant de chaque opérande est à 1
21 mai 2012 à 12:38:42
[1][8] Comment peut-on lire un caractère sans appuyez sur Enter ?
La solution dépend de votre système d'exploitation :
- sous Windows, il est possible d'utiliser la fonction getch (déclarée dans l'en-tête conio.h);
- sous Unixoïde (GNU/Linux, BSD, Mac OS X, Solaris, ...), il est nécessaire de modifier les attributs du terminal (à l'aide des fonctions de l'en-tête termios.h), afin d'utiliser le mode non canonique.
Voici une petite fonction identique à getchar, mais qui lit un caractère sans attendre que l'utilisateur appuye sur Enter :
#	define _XOPEN_SOURCE 600
#	include <termios.h>
#	include <conio.h>
instant_getchar(void)
struct termios term, tmp;
tmp = term;
tmp.c_lflag &= ~ICANON;
tmp.c_cc[VMIN] = 1;
tmp.c_cc[VTIME] = 0;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tmp) < 0)
tcsetattr(STDIN_FILENO, TCSAFLUSH, &term);
instant_getchar();
17 juin 2012 à 17:41:54
[8][15] Quel est le meilleur langage entre le C et le C++ ?
Réponse courte : aucun des deux. Réponse plus longue : les deux langages sont différents sur de nombreux points. Le C est un langage impératif inventé au début des années 1970 et qui était destiné à la programmation système ; le C++ a été inventé une dizaine d'années plus tard, et son but était d'ajouter la POO au C, au départ avec seulement les classes, puis avec de nombreux éléments (héritage, polymorphisme, surcharge d'opérateurs, templates, RAII, etc). Le temps a passé, et les deux langages se sont beaucoup différenciés, au point que le C++ ne ressemble plus vraiment au "C with class" qu'il était au départ.
Comme le montre ce petit passage historique, il existe de nombreuses différences entre les deux langages au niveau du contenu et des caractéristiques, mais il en existe d'autres. Le C++ permet une certaine abstraction (comme string à la place des char*, ou cout et cin à la place de printf et scanf) et certaines techniques utiles (comme les templates pour la généricité, les vector, des sortes de tableaux dynamiques ou les algorithmes de la bibliothèque standard), mais il reste plus compliqué que le C et plus long à maitriser. Le C++ est d'autant plus dur à maitriser qu'il se base sur le C (et sur ses défauts) en plus de ceux qu'il ajoute. La façon de penser et de coder est aussi différente entre les deux langages : en C, on pense plus en terme de fonctions (un problème peut se découper en sous-problèmes, qui peuvent se découper en sous-problèmes, etc), en C++ on pense plus en terme de classes et de méthodes qui manipulent des attributs (on manipule des objets qui représentent des concepts).
En tant que programmeurs amateurs, vous êtes libres de choisir le langage que vous préférez. Le meilleur moyen de découvrir lequel des deux langages conviendra le plus pour un certain projet, c'est de les essayer et de choisir celui qui plait le plus. Chacun a ses avantages et ses inconvénients, et chacun sait être puissant quand on l'utilise bien. Lisez donc des tutoriels sur chacun des deux, faites votre choix et surtout, prenez du plaisir à programmer avec ! Quand vous aurez plus d'expérience en programmation, vous choisirez le langage le plus adapté en fonctions de vos besoins et de vos compétences. En attendant, amusez-vous !
Une discussion sur le SdZ.
Sur cplusplus.com.
Une page plus complète, ailleurs sur le Web
Exceptions ou retours de fonctions ? qui traite d'une question plus spécifique, mais dont la conclusion est intéressante.
Ajout : un post de lmghs qui compile des liens intéressants.
informaticienzero, j'ai d'autres liens pour toi sur le sujet C ou C++:
- Plein d'articles de Dan Saks qui bosse dans l'embarqué et qui milite pour utiliser le C++ pour ce qu'il apporte en plus du C: http://www.embedded.com/user/Dan_Saks
- Un autre article du même magasine en ligne: http://www.embedded.com/electronics-bl [...] the-question-
- Un sujet sur dvpz au sujet duquel des deux est mieux pour commencer: http://www.developpez.net/forums/d8853 [...] /#post5031469
- Un "témoignage" de int21h qui va dans le sens de la mouvance pédagogique comme quoi le C laisse des traces en C++ : http://www.siteduzero.com/forum-83-749 [...] html#r7217357
- Une redite sur la véritable différence du C++ (le RAII) : http://garrys-brain.blogspot.fr/2012/0 [...] ure-of-c.html
(Pour avoir mis les mains dans le code d'openjpeg (j'ai juste corrigé les utilisations bugguées de realloc) il y a peu, je vous confirme que le RAII m'a beaucoup manqué)
- GCC qui migre au C++: http://gcc.gnu.org/wiki/GCCGathering2011
- dans l'autre sens, il y a le développeur de ZeroMQ qui regrette d'avoir utilisé le C++: http://www.250bpm.com/blog:4 , http://www.250bpm.com/blog:8
- d'autres rectifications de ma part sur les véritables apports du C++, et le choix selon un angle pédagogique: http://www.siteduzero.com/forum-83-767 [...] html#r7369078
Merci à Taurre pour les corrections.

References: § 6
 § 4
 § 1
 § 2
 § 8
 §3