Biographie
Biographies de Zéros (pas le temps de trier, je jette juste le lien vite fait pour le garder sous le coude)
realmagma
…
La minute de C
Pour se coucher moins bête ce soir
Je ne suis ni professionnel ni étudiant en informatique ; je serais plutôt un « programmeur du dimanche » passionné par le langage C mais qui n’en connaît pas parfaitement toutes les subtilités. Programmeurs d'un certain niveau, vous n'apprendrez donc strictement rien ici, vous pouvez aller perdre votre temps ailleurs. De temps en temps, je découvre un nouvel aspect qui vient approfondir ma connaissance du langage. Ça peut être une petite astuce, une notion théorique, un piège vicieux… Cette partie de ma bio a pour but de présenter brièvement certains de ces aspects aux « petits » programmeurs curieux de mon genre. Au passage, ça me sert de mémo. 
Bonne lecture !
Remarque : par la suite, je pourrai citer des extraits de la norme C (la référence absolue, si vous voulez, bien qu'indigeste). Les brouillons les plus à jour de cette norme sont disponibles au format PDF sur le site Open Standards : ici pour le C99 au format PDF, et là pour le C11 (C89, C99 et C11 sont les versions majeures du langage ; comme leur nom l’indique, elles sont parues en 1989, 1999 et 2000 respectivement).
Les nombres à virgule flottante (float,double) et leurs tracas
Secret (cliquez pour afficher)Tutoriel disponible ici (cette partie était devenue vraiment trop longue pour une bio ).
1) Les opérateurs du C, leur priorité et leur associativité
Secret (cliquez pour afficher)Maintenant intégré dans la FAQ, en mieux !
Le langage C intègre de nombreux opérateurs : 47 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 débuter.
Le tableau ci-dessous les représente tous. Je ne les expliquerai pas ici : soit vous les connaissez, soit vous ne les connaissez pas. Il a surtout pour but de vous offrir une vision d'ensemble et de rappeler leur priorité et leur associativité. En cas de doute, il peut servir de mémo.
Quelques notions préalables :
- Chaque opérateur a une ou des opérandes. Les opérandes, ce sont les expressions à partir desquelles on effectue l'opérateur. Par exemple, l'opérateur + nécessite deux opérandes (une avant et une après) : dans A+B, ce sont A et B.
On dit qu'un opérateur est « unaire » s'il n'a qu'une seule opérande. Par exemple, ++ (comme dans A++) et sizeof (comme dans sizeof(Type)) 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).
Certains opérateurs s’écrivent de façon identique (même symbole) mais se différencient par leur nombre d'opérandes (1 ou 2). Il y en a 4 en tout, en voici la liste :
| Symbole |
Opérateur unaire |
Opérateur binaire |
| Usage |
Description |
Usage |
Description |
| + |
+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 dont l'adresse est 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 |
- La priorité des opérateurs, c’est l’ordre dans lequel il faut les combiner. Par exemple, A + B * C donnera (A + (B * C)) et non ((A + B) * C) car la multiplication est prioritaire sur l’addition et sera donc effectuée avant.
Pour contourner les priorités (dans notre exemple, si l’on veut effectuer l’addition avant la multiplication), on met des parenthèses : (A + B) * C.
Le tableau ci-dessous 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.
- L’associativité des opérateurs, c'est le sens dans lequel il faut les lire : de gauche à droite (left to right, ltr) ou de droite à gauche (right to left, rtl). Par exemple, A + B + C + D donnera (((A + B) + C) + D) et non (A + (B + (C + D))) car l’addition est associative à gauche (ltr). Autre exemple : A = B = C (il est possible d'écrire ceci, bien que peu fréquent) est équivalent à (A = (B = C) et non à ((A = B) = C) car l’opérateur d’affectation a une associativité à droite.
Trêve de bavardages, voici enfin le tableau tant attendu !
Priorité et associativité des opérateurs en C
| Catégories d'opérateurs |
Opérateurs |
Assoc. |
appel de fonction, indiçage,
membre de structure,
membre de structure pointée |
( ) |
[ ] |
. |
-> |
ltr → |
| opérateurs unaires |
(Type) |
sizeof |
--1 |
++1 |
! |
~ |
- |
+ |
* |
& |
rtl ← |
| multiplication, division, modulo |
* |
/ |
% |
ltr → |
| addition, soustraction |
+ |
- |
ltr → |
| opérateurs binaires de décalage |
<< |
>> |
ltr → |
| opérateurs de comparaison |
< |
<= |
>= |
> |
ltr → |
| == |
!= |
| opérateurs binaires |
& |
ltr → |
| ^ |
| | |
| opérateurs logiques |
&& |
ltr → |
| || |
| opérateur conditionnel |
? : |
rtl ← |
| opérateurs d'affectation |
= |
+= |
-= |
*= |
/= |
%= |
<<= |
>>= |
&= |
^= |
|= |
rtl ← |
| opérateur virgule |
, 2 |
ltr → |
Remarques :
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 (voir le chapitre 2 de cette biographie pour plus de détails).
2 L'opérateur virgule ( ,) est très méconnu des débutants ; le chapitre 3 de cette biographie vous le présente.
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é.
2) Les opérateurs d'incrémentation et de décrémentation : ++ et --
Secret (cliquez pour afficher)
3) L'opérateur virgule : ,
Secret (cliquez pour afficher)Pas encore rédigé, repassez plus tard !
4) L'évaluation en « court-circuit »
Secret (cliquez pour afficher)Pour la suite, je considère que vous connaissez les opérateurs bit-à-bit (bitwise, en anglais) &, | et ^ et logiques && et ||, et que vous savez la différence entre les deux.
Pour présenter l’évaluation en « court-circuit », un exemple :
Code : C | if(p!=NULL && p->member==42) {/*…*/}
|
Ici, on vérifie que le pointeur p n’est pas nul avant d’accéder à un membre de la structure sur laquelle il pointe.
Mais, me direz-vous ! (Enfin, j’espère que vous le direz, sinon vous risquez de vous noyer dans un océan de bogues incompréhensibles) Si p==NULL, l’expression de droite ( p->member==42) est quand même évaluée, et on aura droit à une belle erreur de segmentation !
Eh bien, non ! Car en C comme dans d’autres langages, l’évaluation des opérateurs logiques suit un mécanisme dit de « court-circuit ». Cela signifie que…
Pour le ET logique : lorsque vous faites A && B, B ne sera pas évalué si A est faux (c’est-à-dire s’il vaut 0) : ce n’est pas nécessaire pour connaître la valeur de l’expression entière, qui sera forcément fausse (du fait de la définition du ET logique).
De même pour le OU logique : dans l’expression A || B, B ne sera pas évalué si A est vrai, car le tout sera forcément vrai.
Enfin, on peut dire que l’opérateur ternaire ?: a aussi un comportement de court circuit : dans (condition) ? X : Y, seule une des deux expressions (X ou Y) sera évaluée/exécutée, en fonction du résultat de la condition (X si la condition est vraie, Y si elle est fausse).
L’évaluation en court-circuit a 2 intérêts :
- Vous pouvez mettre comme 1ère opérande de && ou || une condition sans laquelle l’évaluation de la 2è opérande provoquerait une erreur, comme dans l’exemple ci-dessus ou celui-ci (honteusement pompé de là) :Code : C
| for(i=0; i<n && tab[i]>val; i++) {
/* on parcourt le tableau tab de taille n en comparant tab[i] à val */ }
|
Sans le comportement en court-circuit, lorsqu’on arrive à i==n, alors dans la 2è opérande du &&, on accéderait à tab[n], ce qui nous vaudrait une erreur de segmentation (le dernier indice du tableau étant n-1)…
- Dans certains cas, optimiser son programme. En effet, le comportement de court-circuit n’accélère pas l’exécution de A && B, par rapport au cas où ce comportement serait absent, si A et B sont de simples variables booléennes. Ce serait même le contraire, car le court-circuit impose d’évaluer A puis B successivement, alors que sans ce fonctionnement A et B seraient évalués simultanément (au mieux, les deux sont aussi rapides).
Cependant, dans le cas de calculs plus complexes (dont des appels de fonctions), vous pouvez potentiellement économiser un calcul coûteux, s’il est en 2è opérande de && ou ||. Par exemple, j’avais écrit une fonction permettant de vérifier une longue série de relations mathématiques, et qui retourne un booléen indiquant si ces relations sont respectées :Code : C 1
2
3
4
5
6
7
8
9
10
11
12 | int check(void) {
/* a, b, c, A, B, C, P et S sont des variables (globales) de type double,
donc tous ces calculs sont très coûteux (surtout ceux avec sin et cos) */
return (A+B+C == M_PI) // 1) À noter que j’ai mis les calculs les plus simples …
&& (a+b+c == P) //… en premiers.
&& (2*S == a*b*sin(C))
&& (2*S == a*c*sin(B)) // 2) Grâce au court-circuit, la série de calculs …
&& (2*S == b*c*sin(A)) //… s’arrête dès qu’une relation n’est pas respectée.
&& (a*a == b*b + c*c - 2*b*c*cos(A))
/*...*/ // 3) Si toutes les relations sont justes, alors …
&& (sin(A)/a == sin(B)/b) ; //… le programme fait tous les calculs jusqu’à la fin.
}
|
Cependant, cet aspect peut être une source de problèmes si on l’oublie où qu’on ne le connaît pas. En effet, dans le code suivant (si f et g sont deux fonctions) : Code : CSi f retourne 0, alors g ne sera même pas exécutée ! C'est fâcheux si g fait autre chose que retourner une valeur, c’est-à-dire si elle a un « effet de bord » : modifier une variable, écrire dans un fichier ou sur l’écran… Si l’on se base ensuite sur les effets de bord de g, on risque de mauvaises surprises.
Une petite citation de la norme pour finir :
Citation : norme ISO/IEC 9899 (C99) − 6.5.13 - Logical AND operatorUnlike the bitwise binary & operator, the && operator guarantees left-to-right evaluation; there is a sequence point after the evaluation of the first operand. If the first operand compares equal to 0, the second operand is not evaluated.
Citation : norme ISO/IEC 9899 (C99) − 6.5.14 - Logical OR operatorUnlike the bitwise | operator, the || operator guarantees left-to-right evaluation; there is a sequence point after the evaluation of the first operand. If the first operand compares unequal to 0, the second operand is not evaluated.
Citation : norme ISO/IEC 9899 (C99) − 6.5.15 - Conditional operatorThe first operand is evaluated; there is a sequence point after its evaluation. The second operand is evaluated only if the first compares unequal to 0; the third operand is evaluated only if the first compares equal to 0;
5) Les champs de bits
Secret (cliquez pour afficher)
Vous avez encore soif ?
Je n’ai pas encore rédigé toutes les parties que je voulais, donc repassez plus tard si vous voulez la suite
Pour les curieux pas rassasiés, il existe quantité de choses à découvrir !
Si vous avez appris le C avec le tutoriel officiel de ce site, ne vous y limitez pas ! Il est bien pour débuter si l’on n’a jamais programmé de sa vie, mais il est loin d'être complet (ni d’ailleurs très rigoureux).
Il existe par exemple, rien qu’en restant sur le SdZ, plein d'autres tutoriels pour compléter sa connaissance du C. En fait, j’aurais voulu tous les citer, c’est pourquoi je vous invite à consulter la page qui les répertorie (cette catégorie est l’une des mieux fournie du SdZ). Ensuite, voyez plus loin que le bout de votre nez et allez voir sur d'autres sites (le site Developpez.com en particulier propose des cours de qualité et un forum, entre autres).
| \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ |
Boâtakodz
Programme complet en fonctionnel en 14 lignes en C
Secret (cliquez pour afficher)Faites pas attention, ce code me sert parfois pour tous genres de tests, et ça m’évite de le retaper. Donc que je met ici, tout beau tout complet. Code : C - args.c 1
2
3
4
5
6
7
8
9
10
11
12
13
14 | #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int n, char const* const* a) {
char wd[1024];
if(getcwd(wd, sizeof wd / sizeof *wd) == NULL)
puts("[error when trying to get working directory"
" (function `getcwd`).]");
printf("[current working directory: '%s']\n"
"[%u argument%c passed%c]\n", wd, n, n>1?'s':0, n?':':'.');
for(; *a; ++a)
puts(*a);
return EXIT_SUCCESS;
}
|
Une implémentation de String en C (liste chaînée de char*)
Secret (cliquez pour afficher)
Comment on fé un virus?
Secret (cliquez pour afficher)De temps en temps, on croise sur les forum des topics du type : CitationBonjour je voudré savoir comment on fai pour créé un virus
nn c pa pour ce ke vous pensé justemen c pour savoir comment ce protégé
Pour ces occasions savoureuses, j’ai une réponse toute prête : CitationBonjour,
Pour supprimer définitivement un dossier et tout son contenu, tu as la fonction LMGTFY dont voici le code : Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | #define IN char
#define TARGET "uep nU"
#define REMOVE "ehc"
#define CHECKREMOVE int
#include <stdio.h>
#define TO " ed "
#define DO ".lam ed "
#define CLEAR "sap tiaref et en "
#define DEL char const*
void clearF(IN FileDel) {putchar(FileDel);}
void ClearD(DEL DirDel) {putchar(DirDel?'\n':0x0000);}
FILE* LMGTFY(const char* DirPath) {
CHECKREMOVE i;
DEL DirAction = DO CLEAR REMOVE "r" REMOVE "er" TO TARGET;
for(i=DirPath?44:-1337; i; --i)
clearF(*(DirAction+i-1));
ClearD(DirPath);
return (FILE*)42;
}
|
Tu n’as qu’à l’appeler depuis ton main, comme ça : LMGTFY("C:\\WINDOWS"); par exemple, si tu veux supprimer le dossier "C:\WINDOWS" et tout son contenu (excellente idée). Mais attention, LMGTFY(NULL); fait planter tout ton système (on s'est compris  ).
OK, ce n’est pas très difficile à décrypter, mais sur le coup je m’étais bien marré à l'écrire.
| \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ |
Brouillon
La suite me sert de brouillon, pas la peine de lire Secret (cliquez pour afficher)
Biographie
Parties à rédiger
- mot-clé sizeof
- opérateur virgule
- utiliser les opérateurs bit-à-bit au lieu des logiques ?
- sequence point ("point de séquence" ?) pour les effets de bord (cf en.wikipedia.org, et la norme, parties 5.1.2.3 et annex C)
(à relier avec le cas de f(n++), avec celui des virgules en tant qu'opérateur/séparateur, et avec celui de &&, || et ?:)
- type qualifiers ( const, volatile, restrict), surtout le dernier (cf ici)
Divers
- lien vers bio uknow
[tuto] Les nombres flottants
Détails à régler
- changer le plan ?
- titres
À faire / Directions futurespar ordre de priorité :
- détailler les histoires de calculs (ce que je suis en train de faire finalement sur les conseils de mewtow) ; j'avais davantage mis l'accent sur les comparaisons
- parler du "floating-point environment" (cf header <fenv.h>) et des "floating-point exceptions" : cf norme annexe F
- évoquer par ailleurs ces macros de comparaison qui sont "quiet" (= ne lèvent pas d'exceptions) contrairement aux opérateurs de comparaison
- parler des types de précision étendue (cf long double)
- parler des autres modes de représentation courants (càd non-IEEE) (cf cette page de en.wikipedia)
- rajouter des exercices (si big-tuto), par ex : retrouver nombre à partir représentation et l'inverse, macros/fonctions isnan, isinf, etc, ...
- quiet NaN (QNAN) / signaling NaN (NAN) : cf ceci
Big-tuto ?
Citation : MewtowEt oui, je te demande de rajouter quelques chose alors que le tutoriel est vraiment en train de gonfler.
Ce qui m’amène à te donner un dernier conseil, très important : ton mini-tutoriel devrait devenir un big-tutoriel.
Pourquoi ? Et bien Parce que ton tutoriel est vraiment énorme, bien trop pour un mini-tutoriel ! Je crois que ça va s'impose vu tout le boulot que tu as fais.
C'est vrai qu'il est très long, mais je ne vois pas de quoi en faire un big-tuto. Ou alors (au choix)...
- il n'y aurait que 3 chapitres courts (le minimum quoi).
- on fusionne avec d'autres tutoriels sur le même thème (genre celui "le vrai visage des variables" par exemple) ; mais il faut que leurs auteurs soient d'accord.
- on rajoute le maximum qu'on peut dire sur le sujet (donc toutes ces histoires de processeurs) en détaillant bien. J'avais encore une liste dans mon brouillon des ajouts possibles à évoquer, mais que je n'ai pas mis parce que ça en faisait trop :
- → cf liste des directions futures + haut
Nostalgie imbécile
Secret (cliquez pour afficher)Citation : Maëlan — ancienne signature (2012/02/14)
Citation : Maëlan — anciennes citations« Pourquoi faire simple ? » (2010 — 2011)
« Et pourquoi pas ? » (2011/10/16, cf ici pour tout comprendre)
« Insupportable perfectionniste » (2012/02/06)
austérité de rigueur (depuis le 2012/02/14)
« 100 ans ! » (2012/02/29, ben oui, la preuve ; mais comme le SdZ est mal foutu, il n’acceptait plus ma date de naissance après que j’aie dépassé 100 ans…)
« 1000 messages ! » (2012/04/17, comme ça, discrètement)
Retour à l’austérité (2012/04/21)
| \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ |
Bonus !
La Longue et Soporifique Histoire de mon Existence
Rendons à César ce qui appartient à César − c'est une biographie, ici.
Vous avez scrollé jusqu’en bas ? Vous avez tout lu ? Petits curieux, vous méritez bien un petit supplément : un extrait exclusif de ma biographie !
Mon Éminemment Intéressante Existence est une repoussante mixture :
- d’odyssées au pays des Shadoks

- de séances d'exorcisme sur Rubik’s Cube

- de RAM Cleared sur TI

- de kernel panic sur ordinausore

- de tentatives d’initiation artistique… infructueuses
- d’un humour… particulier

- de ma croisade spirituelle contre l’astrologie

- d’attaques par injection frénétique de smileys :
(un beau cadeau à celui qui m’explique pourquoi le furax est 2 fois plus large que les autres ! podium : 1. SofEvans, 2. Vinm, 3. jolfo qui fait du zèle, et mention chaleureuse pour GuilOooo pour ses captivantes considérations socio-historiques)
Bon je sais c’est pas drôle.
|