summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorache <ache@ache.one>2018-02-08 03:18:40 +0100
committerache <ache@ache.one>2018-02-08 03:18:40 +0100
commitb3ee18b156653d0bea34bd760eb0f8ceb316e828 (patch)
tree50c64000f9b6db331c0ae9f827ff960fd2be7b43
parentcss for the logo of the article (diff)
New article langage C
-rw-r--r--article/bizarrerie-du-langage-c.md624
-rw-r--r--article/duckduckgo-google-en-mieux.md4
-rw-r--r--article/res/digraphe_c.pngbin0 -> 10068 bytes
-rw-r--r--article/res/trigraphe_c.pngbin0 -> 5052 bytes
4 files changed, 625 insertions, 3 deletions
diff --git a/article/bizarrerie-du-langage-c.md b/article/bizarrerie-du-langage-c.md
new file mode 100644
index 0000000..288eda4
--- /dev/null
+++ b/article/bizarrerie-du-langage-c.md
@@ -0,0 +1,624 @@
+Les bizarreries du langage C
+============================
+
+![The C Programming Language logo](res/c_language.svg)
+Le C est un langage à la syntaxe simple. Les seules complexités de ce langage viennent du fait qu'il agit de manière proche de la machine.
+Pourtant, une partie des syntaxes autorisées par le C n'est pratiquement jamais enseignée.
+Attaquons nous à ces cas mystérieux !
+
+
+>!attention
+> Pour comprendre ce billet, il est nécessaire d'avoir des bases dans un langage ayant une syntaxe et un fonctionnement proche du C.
+
+
+### Les opérateurs inusités
+
+Il existe deux opérateurs dans le langage C qui ne sont pratiquement jamais utilisés.
+Le premier est l'opérateur virgule.
+En C la virgule sert à séparer factoriser les éléments d'une définition ou séparer les éléments d'une fonction. En bref, c'est un élément de ponctuation.
+Mais pas seulement ! C'est également un opérateur.
+
+L'instruction suivante, bien qu'inutile est tout à fait valide :
+
+```c
+printf("%d", (5,3) );
+```
+
+Elle affiche 3. L'opérateur ',' sert à juxtaposer des expressions.
+La valeur de l'expression complète est égale à la valeur de la dernière expression.
+
+Cet opérateur se révèle très utile dans une boucle `for` pour multiplier les itérations.
+Par exemple pour incrémenter `i` et décrémenter `j` dans la même itération du `for` on peut faire :
+```c
+for( ; i < j ; i++, j-- ) {
+ // [...]
+}
+```
+
+Ou encore, dans de petits `if` pour les simplifier :
+
+```c
+if( argc > 2 && argv[2][0] == '0' )
+ action = 4, color = false;
+```
+
+Ici, on assigne `action` et `color`. Normalement pour faire 2 assignations, on aurait du mettre des accolades au `if`.
+
+On peut également s'en servir pour retirer des parenthèses.
+
+```c
+while( c = getchar(), c != EOF && c != '\n' ) {
+ // [...]
+}
+// Est strictement équivalent à :
+while( (c = getchar()) != EOF && c != '\n' ) {
+ // [...]
+}
+```
+
+Mais surtout, ne pas abuser de cet opérateur ! On peut, de manière assez rapide, obtenir des choses illisibles.
+Cette remarque est d'ailleurs également valide pour le prochain opérateur !
+
+### L'opérateur ternaire
+
+Le ternaire pour les intimes. Le seul opérateur du langage C qui prenne 3 opérandes.
+Il sert à simplifier des expressions conditionnelles.
+
+Par exemple pour afficher le minimum de deux nombres, sans les ternaires, on ferait :
+
+```c
+int min = a;
+if( b < a)
+ min = b;
+printf("%d", min);
+```
+
+Alors qu'avec les ternaires on fait :
+```c
+printf("%d", a<b ? a : b);
+```
+
+Grâce à la ternaire, on a économisé un nom de variable et quelques lignes.
+Mais surtout on a gagné en lisibilité. Quand on sait en lire une ...
+
+Pour lire une expression ternaire, il faut lire la découper en 3 parties :
+```c
+expression_1 ? expression_2 : expression_3
+```
+La valeur d'une expression ternaire est `expression_2` si la valeur de `expression_1` est évaluée à vrai et `expression_3` sinon.
+
+En somme, cela simplifie un peu la lecture pour de courte expression.
+L'expression `a<b ? a : b` se lit “ Si `a` est inférieur à `b` alors `a` sinon `b` ”.
+
+J'insiste encore sur le fait que cet opérateur, s'il est mal utilisé peu nuire à la lisibilité du code.
+D'ailleurs, on peut très bien mettre une expression ternaire dans une expression ternaire :
+
+```c
+printf("%d", a<b ? a<c ? a : b<c ? b : c : b < c ? b : c);
+```
+
+Désormais, on prend le minimum de trois nombres. C'est aéré, mais impossible à suivre.
+Le plus lisible est d'utiliser une macro :
+```c
+#define MIN(a,b) a<b ?a : b
+
+printf("%d", MIN(a,MIN(b,c)));
+```
+
+
+Et voilà ! Deux opérateurs qui vont désormais gagner un peu d'intérêt.
+Et surtout être mieux compris !
+Puis d'ailleurs, même les opérateurs qu'on connait déjà, on n'en maîtrise pas forcément la syntaxe.
+
+### L'accès à un tableau
+
+On nous a toujours appris que pour afficher le 3em élément d'un tableau, on faisait :
+
+```c
+int tab[5] = {0, 1, 2, 3, 4, 5};
+printf("%d", tab[2]);
+```
+
+2 et non 3, car un tableau en C commence à 0.
+Un tableau commence à 0, c'est une histoire d'adresse.
+L'adresse du premier élément est en fait l'adresse du tableau.[^tableau]
+Et par arithmétique des pointeurs, l'adresse du 3em élément est `tab+2`.
+
+Donc on aurait très bien pu écrire :
+
+```c
+printf("%d", *(tab+2));
+```
+
+Puis comme l'addition est commutative, on `tab+2` ou `2+tab` sont équivalents.
+En fait, on aurait même pu faire :
+
+```c
+printf("%d", 2[tab]);
+```
+
+C'est tout à fait valide. Et pour cause ; la syntaxe `E[F]` est strictement équivalente à `*((E)+(F))`.
+Du coup, le titre de cette section est un peu trompeur. Cet opérateur n'a pas grand chose à voir avec les tableaux en fait.
+C'est du sucre syntaxique pour cacher l'arithmétique des pointeurs.
+
+Par exemple pour afficher un caractère '=' pour vrai, '!' pour faux et '~' pour aucun des deux.
+On pourrait faire :
+```c
+if( is_good == 1 )
+ printf("%c", '=');
+else if( is_good == 0 )
+ printf("%c", '!');
+else
+ printf("%c", '~');
+```
+
+Mais il existe plus simple :
+```c
+printf("%c", "!=~"[is_good]);
+
+// Ou comme on l'a vu :
+printf("%c", is_good["!=~"] ); // Affiche '!' si is_good vaut 0
+ // '=' si is_good vaut 1
+ // '~' si is_good vaut 2
+```
+
+En somme, tout le monde écrit `tab[3]` et pas `3[tab]`. Du coup, il n'y a aucun intérêt à écrire `3[tab]`.
+Mais c'est toujours bien de savoir que ça existe. ^^
+
+
+### L'initialisation
+
+L'initialisation, c'est quelque chose qu'on maîtrise en C. C'est le fait de donner une valeur à une variable lors de sa déclaration.
+En gros, on définit sa valeur.
+
+Pour un tableau:
+```c
+int tab[10] = {0};
+tab[2] = 5;
+```
+
+>!secret
+>
+> `{0}` peut également être utilisé pour un nombre. Toute valeur initialisant une variable simple (pointeur nombre, ...) peut optionnellement prendre des accolades.
+>
+>```c
+>int a = {11};
+>float pi = {3.1415926};
+>char* s = {"unicorn"};
+>```
+>
+>Ce qui caractérise le tableau du coup est surtout le fait d'utiliser des virgules entre les accolades.
+
+On initialise le tableau avec des 0 car, lors qu'on initialise un tableau toute valeur non spécifiée est par défaut 0.
+La ligne suivante est une affectation et non une initialisation.
+
+Si on veut seulement une initialisation ; Puisque l'initialisation d'un tableau se faire suivant l'ordre de ses valeurs, on devrait écrire :
+```c
+int tab[10] = {0, 0, 5};
+```
+
+Mais en fait, il existe une autre syntaxe qui permet de faire plus simple :
+```c
+int tab[10] = {[2] = 5};
+```
+
+On dit simplement que la troisième case vaut 5. Le reste est par défaut 0.
+
+Une syntaxe équivalente existe d'ailleurs pour les structures ou les unions.
+
+>!comment
+> Pour l'exemple, on va prendre une structure `point` que je vais utiliser plusieurs fois dans ce billet, idem pour la structure `message`.
+
+On peut ainsi initialiser un point en utilisant ses composantes.
+
+```c
+typedef struct point {
+ int x,y;
+} point;
+
+
+point A = {.x = 1, .y = 2};
+```
+
+Ici, il n'y avait pas d'ambigüité.
+Mais pour une structure plus complexe, cette syntaxe est vraiment avantageuse.
+
+Tenez :
+
+```c
+typedef struct message {
+ char src[20], dst[20], msg[200];
+} message;
+
+// [...]
+
+message to_send = {.src="", .dst="23:12:23", .msg="Code 10"};
+
+// Est bien plus claire que :
+
+message to_send = {"", "23:12:23", "Code 10"};
+
+// D'ailleurs, je ne l'ai pas fait mais avec cette syntaxe pas besoin de se souvenir de l'ordre
+// des champs de la structure
+
+message to_send = { .msg="Code 10", .dst="23:12:23", .src=""};
+
+// Et aussi puisque tout champ d'une structure est inialisé à sa valeur nulle s'il n'est pas initialisé explicitement.
+// On peut également omètre src.
+
+message to_send = { .dst="23:12:23", .msg="Code 10"};
+```
+
+
+Avec ces syntaxes. On peut également alourdir le code, mais généralement, on gagne en lisibilité.
+Parfois, ces syntaxes sont très judicieusement utilisées !
+Comme ici, dans ce décodeur de base64 :
+>```c
+>static int b64_d[] = {
+> ['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4,
+> ['F'] = 5, ['G'] = 6, ['H'] = 7, ['I'] = 8, ['J'] = 9,
+> ['K'] = 10, ['L'] = 11, ['M'] = 12, ['N'] = 13, ['O'] = 14,
+> ['P'] = 15, ['Q'] = 16, ['R'] = 17, ['S'] = 18, ['T'] = 19,
+> ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23, ['Y'] = 24,
+> ['Z'] = 25, ['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29,
+> ['e'] = 30, ['f'] = 31, ['g'] = 32, ['h'] = 33, ['i'] = 34,
+> ['j'] = 35, ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39,
+> ['o'] = 40, ['p'] = 41, ['q'] = 42, ['r'] = 43, ['s'] = 44,
+> ['t'] = 45, ['u'] = 46, ['v'] = 47, ['w'] = 48, ['x'] = 49,
+> ['y'] = 50, ['z'] = 51, ['0'] = 52, ['1'] = 53, ['2'] = 54,
+> ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59,
+> ['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63, ['='] = 64
+>};
+>```
+Source: [Taurre](https://openclassrooms.com/forum/sujet/defis-8-tout-en-base64-19054?page=1#message-6921633)
+
+
+### Les expressions littéralement composées
+
+Puisque nous parlons des tableaux. Il existe une syntaxe simple pour utiliser des tableaux constants.
+
+Je voudrais utiliser ce tableau :
+```c
+int tab[5] = {5, 4, 5, 2, 1};
+printf("%d", tab[i]); // Avec i égale à quelque chose >=0 et <5
+```
+
+Cependant, je ne l'utilise qu'une seule fois ce tableau ...
+C'est pas cool d'avoir à utiliser un identificateur juste pour ça.
+
+Et bien je peux faire ça :
+```c
+printf("%d", ((int[]){5,4,5,2,1}) [i] ); // Avec i égale à quelque chose >=0 et <5
+```
+
+Ce n'est pas super lisible pour le coup. Mais il existe pleins de cas où cette syntaxe est très utile :
+
+```c
+
+// Pour envoyer notre message :
+send_msg( (message){ .dst="192.168.11.1", .msg="Code 11"} );
+
+// Pour afficher la distance entre deux points
+printf("%d", distance( (point){1, 2}, (point){2, 3} ) );
+
+// Ou encore sous Linux, en programmation système
+execvp( "bash" , ((char*[]){"bash", "-c", "ls", NULL}) );
+
+```
+
+Ce type d'expression est une expression littérale, c'est-à-dire qu'elle est immuable.
+Comme pour les chaînes de caractères littérales.
+
+Ainsi ceci n'est pas valide :
+
+```c
+"lache"[0] = 'v' // Est une expression invalide puisque "lache" est une expression littérale
+```
+
+De même ceci n'est pas valide :
+
+```c
+((int[]){1,2,3})[0] = 0;
+```
+
+On appele donc ces expressions des compound literals.
+Ou une expression littérale composée si l'on s'essaye à traduire.
+
+
+### Les VLAs
+
+Les tableaux à taille variable (ou *Variable Length Arrays* en anglais, soit VLAs) sont des tableaux dont la taille n'est connue qu'à la compilation. Si vous n'avez jamais entendu parler des VLAs, ceci devrait vous choquer :
+
+```c
+int n = 11;
+
+int tab[n];
+
+for(int i = 0 ; i < n ; i)
+ tab[i] = 0;
+```
+
+Ce code, bien que valide, à dût être réprimendé par de nombreux professeurs. En effet, on nous apprend qu'un tableau doit avoir une taille connue à la compilation.
+Et bien, les VLA sont l'exeption. Apparu avec la norme C99, les VLAs jouissent d'une mauvaise réputation. À cela, il existe plusieurs raisons que je ne détaillerais pas ici.
+
+Je vais simplement parler des comportements non-intuitifs introduits avec les VLAs. Comme par exemple :
+
+```c
+printf("%zu", sizeof(int[puts("Je retourne :")]) );
+```
+
+En temps normal, l'expression entre crochet serait constante. Il n'y aurait pas eu besoin de l'exécuter. Cependant, avec les VLAs, on est obligé d'exécuter ce code afin de connaitre la taille du tableau.
+
+Du coup, ce code affiche bien :
+
+```shell
+Je retourne :
+56
+```
+
+56 étant la taille du tableau (`14*sizeof(int)`). 14 étant le nombre de caractères affichés par `puts`.
+
+Ceci est d'autant plus étrange que `sizeof` a pour habitude de ne pas interpréter l'expression que l'on lui en paramètre. Afin de faciliter l'allocation dynamique d'un tableau, il est courant d'utiliser cette syntaxe :
+
+```c
+char* str = malloc( n * sizeof *str);
+```
+
+Ici, l'expression `*str` n'a pour l'instant pas de comportement définit (str n'étant pas encore initialiser) mais peu importe car elle n'est pas exécutée. Son type est déterminé à la compilation et `sizeof` là remplace par la taille de son type.
+
+Le type de `*str` étant `char`, sa taille est donc de 1 byte. Et le code précédant est traduit pas le compilateur par :
+
+```c
+char* str = malloc( n );
+```
+
+On peut même faire plus bizarre encore :
+
+```c
+void foo(int a[][puts("Avant foo")]) {
+ puts("Début de foo");
+
+}
+int main(void) {
+ int b[3][3];
+ f(b);
+}
+```
+
+
+Humm, curieux effet de bord. Il y a deux choses assez intéressantes dans ce code. La première, assez évidente, c'est le fait d'avoir un appel de fonction dans la définition du type.
+La deuxième chose intéressante est le fait que foo prenne en argument un tableau à taille variable, mais qu'on lui passe en paramètre un tableau de taille fixe ! En fait, on aurait pu écrire n'importe quelle expression qui retourne un entier.
+Ces deux points sont causés par les VLAs. Voyons ça plus doucement.
+
+### La définition d'un type VLA
+
+Pour définir une fonction qui prend un tableau d'entiers de taille `N` en paramètre, on peut simplement faire :
+
+```c
+void foo(int tab[N]);
+```
+
+Mais en fait, peut importe la taille, la fonction ne fait pas vraiment la différence. Pour elle, c'est simplement un pointeur sur un entier. Du coup, on peut très bien écrire :
+
+
+```c
+// Si on veut gardé l'idée que c'est un tableau (au moins à la base)
+void foo(int tab[]);
+
+// Ou si on préfère traduire l'idée qu'on manipule un pointeur.
+void foo(int* tab);
+```
+
+Tout ceci est possible car par arithmétique des pointeurs, `tab[9]` ne dépends pas de la taille du tableau, mais plutôt de la taille d'un `int`.
+Mais du coup, ça se complexifie si on a des types un peu plus compliqués, par exemple ... Des types dont on ne connait pas la taille à la compilation.
+Oui je parle de VLAs. Pour un double tableau, on doit spécifier une taille à la compilation la déclaration ressemble à :
+
+```c
+void foo(int tab[][5]);
+```
+
+On a donc au final un pointeur sur un tableau d'entier de taille 5, soit de taille complète `5*sizeof int`.
+Et donc pour un tableau de taille `n` (VLAs). On doit faire :
+
+```c
+void foo(int tab[][*]);
+```
+
+Ouais ... Car ici, c'est une déclaration et pas une définition. `n` n'est pas censé avoir de valeur pour le moment du coup, ça n'aurait pas vraiment de sens. Sauf si `n` était global, mais du coup, elle aurait une valeur fixe pour le moment. Bref, on a donc introduit cette syntaxe.
+Puis pour la définition on utilise bien évidemment `n` :
+
+```c
+void foo(int tab[][n]) { // Où n est une variable globale
+
+}
+```
+
+Personne n'aime les variables globales. Du coup on préfèrerait que `n` soit passé en argument :
+
+```c
+void foo(int n, int tab[][n]);
+
+void foo(int n, int tab[][n]) {
+
+}
+```
+
+Puis en fait, pour être tout à fait précis, dans les crochets, un VLA prend en paramètre une expression qui retourne une variable entière. Donc ici, si le tableau faisait $2n$ on aurait pût écrire :
+
+```c
+void foo(int n, int tab[][*]);
+
+void foo(int n, int tab[][2*n]);
+```
+
+Cool. Non en fait, c'est même vraiment méga cool. Car sans ça, on aurait du bidouiller avec des adresses pour arriver à la même chose. On aurait pût cacher la misère avec des fonctions. Une autre solution, très courante, était d'utiliser le tableau à deux dimensions comme un tableau à une dimension.
+Comme ceci :
+
+```c
+int tab[3][3];
+
+tab[0][8] == tab[2][2]; // 8 = (2*3+2)
+```
+
+Mais ça reste bien moins pratique que l'utilisation de VLAs.
+
+Mais alors une dernière question reste en suspens. Peut-on utiliser cette astuce pour des tableaux 2D qui ne sont pas des VLAs. À cela il existe une réponse simple. Un tableau reste un tableau, son agencement en mémoire est le même. Du coup, pas de problème à utiliser un tableau à deux dimensions fixes dans une fonction qui prend en paramètre un VLA.
+
+
+### Une histoire d'étiquettes
+
+En C, s'il y a un truc dont on ne doit pas parler, ce sont bien les *étiquettes*.
+On les utilses avec la structure de contrôle `goto` ! L'interdite !
+
+Pour les cacher, on remplace les `goto` par des structures de contrôles adapées et plus claires comme `break` ou `continue`.
+De manière à ne jamais avoir à utiliser `goto`.
+
+Du coup, on n'apprend jamais ce qu'est une étiquette ...
+
+Voici comment on utilise `goto` et une étiquette :
+
+```c
+goto end;
+
+
+end: return 0;
+}
+```
+En gros, une étiquette est un nom que l'on donne à une instruction.
+
+
+Et bien on en utilise des étiquettes ! Dans les `switch` !
+
+```c
+
+switch( action ) {
+ case 0 :
+ do_action0();
+ case 1:
+ do_action1();
+ break;
+ case 2:
+ do_action2();
+ break;
+ default:
+ do_action3();
+}
+```
+
+Ici, les `case` et le `default` sont en fait des étiquettes ! Comme pour les goto.
+Sauf qu'elles sont pour les `switch`, et inutilisables par les `goto` ...
+
+
+>!question
+> Pourquoi tu nous parles de ça ?
+
+Déjà, c'est bien de savoir que ça s'appele une étiquette. Ensuite, parce que je vais vous parlez d'un classique.
+Le dispositif de Duff.
+
+C'est une sorte de boucle déroulée optimisée. Le but est de réduire le nombre de tests de vérification de fin de boucle (ainsi que le nombre de décrémentation).
+
+Voici la version historique écrite par Tom Duff :
+
+```c
+{
+ register n = (count + 7) / 8;
+ switch (count % 8) {
+ case 0: do { *to = *from++;
+ case 7: *to = *from++;
+ case 6: *to = *from++;
+ case 5: *to = *from++;
+ case 4: *to = *from++;
+ case 3: *to = *from++;
+ case 2: *to = *from++;
+ case 1: *to = *from++;
+ } while (--n > 0);
+ }
+}
+```
+
+Peu importe ce que signifit `register`. Aussi, `to` est un pointeur particulié mais ce n'est pas vraiment important.
+
+Ici, ce dont je veux vous parlez, c'est cette boucle `do-while` en plein milieu d'un switch.
+
+Le test que l'on cherche à effectuer le moins possible est `--n > 0`.
+
+En temps normal, `n` serrait en fait `count`. Et on devrait faire le test `count` fois.
+De même pour sa décrémentation.
+
+C'est-à-dire :
+
+```c
+while( count-- > 0 )
+ *to = *from++;
+```
+
+En divisant par 8 (nombre arbitraire) on divise également par 8 le nombre de tests et de décrémentations.
+Cependant, si `count` n'est pas divisible par 8, on a un problème, on ne fait pas toutes les instructions.
+Se serrait bien de pouvoir sauter directement à la 2em instruction, si on a seulement 6 instructions restantes.
+
+Et c'est là que les étiquettes peuvent nous aider !
+Grâce au `switch` on peut sauter directement à la bonne instruction.
+
+Il suffit d'étiqueter chaque instruction avec le nombre d'instructions qu'il reste à faire dans la boucle.
+Puis de sauter dans la boucle avec la structure de contrôle `switch` sur le reste d'instructions à réaliser.
+
+Ensuite, on execute nos paquets de 8 instructions normalement.
+
+Il est très rare d'avoir à utiliser ce type d'astuces.
+D'ailleurs, c'est une optimisation d'un autre temps.
+Mais comme je voulais vous parler de syntaxe alors, il était nécessaire de parlez des étiquettes.
+
+
+### Les caractères trop spéciaux
+
+On va remonter encore dans le temps.
+Je vais vous citer une dernière chose.
+Un temps où tous les caractères n'étaient pas aussi accessibles que de nos jours sur autant de type de claviers.
+
+Les claviers n'avaient pas forcément de touches de composition. Ainsi, il était impossible de taper le caractère '#'.
+
+On pouvait alors remplacer la caractère '#' par la séquence "??=".
+Et pour chaque caractère composé, il existait une séquence à base de "??".
+Voici une image des séquences de trigraphe et de leur représentation en caractère.
+
+![Trigraphes](res/digraphe_c.png)
+
+Il existe également des digraphes. C'est à peu près la même chose mais en deux caractères :
+
+![Digraphes](res/digraphe_c.png)
+
+La différence **principale** est dans une chaîne de caractère :
+
+```c
+puts("??= est un croisillon");
+puts("%:");
+```
+
+Ces mécanismes du moyen age sont encore valides de nos jours en C.
+Du coup, cette ligne de code est tout à fait valide :
+
+```c
+??=define FIRST tab<:0]
+```
+
+La seule utilité de cette syntaxe de nos jours est d'obfusquer un code source très facilement.
+Une combinaison d'un ternaire avec un trigraphe et un digraphe et vous avez un code absolument illisible. ;)
+
+```c
+printf("%d", a ?5??((tab):>:0);
+```
+
+**À ne jamais utiliser dans un code sérieux** donc.
+
+
+### Conclusion
+
+Voilà, c'est fini, j'espère que vous avez appris quelque chose de ce billet.
+Surtout n'oubliez pas d'utiliser ces syntaxes avec parcimonie;
+
+Vous pouvez (re)découvrir de nombreuses codes abusants de la syntaxe du langage C aux [IOCCC](http://ioccc.org/winners.html).
+
+
+[^tableau]: Pour être tout à fait précis. On aurait tout à fait pû prendre $1$ comme premier élément du tableau. On aurait alors dû translater chaque accès de $1$ de manière transparente. Cepdendant, ce n'est pas ce qui fût retenu car c'était plus simple et *plus rapide à compiler* puis c'est resté. Un article de Mike Hoye nommé [Citation Needed](http://exple.tive.org/blarg/2013/10/22/citation-needed/#content) en parle bien mieux que moi.
+
+
diff --git a/article/duckduckgo-google-en-mieux.md b/article/duckduckgo-google-en-mieux.md
index e925f06..3195467 100644
--- a/article/duckduckgo-google-en-mieux.md
+++ b/article/duckduckgo-google-en-mieux.md
@@ -1,15 +1,13 @@
DuckDuckGo, Google en mieux ?
===============
-![Logo DuckDuckGo](res/DuckDuckGo_Logo.svg)
-
+![Logo DuckDuckGo](res/DuckDuckGo_Logo.svg)
Depuis un certain temps déjà, Google ne se consacre plus entièrement à
son moteur de recherche. En effet, la firme est déjà bien implentée dans
la plupart des pays occidentaux où elle est le site le plus visité. Mais
la concurrence, elle, s'est développée. Les moteurs de recherches tels
que DuckDuckGo évoluent avec le temps, jusqu'à devenir plus pratique et
ergonomique que Google.
-
DuckDuckGo est un moteur de recherche assez jeune mais qui a beaucoup
d'avenir. En effet, il allie rapidité et fonctionnalités innovantes,
tout en conservant la vie privée de l'utilisateur
diff --git a/article/res/digraphe_c.png b/article/res/digraphe_c.png
new file mode 100644
index 0000000..ffb7fd2
--- /dev/null
+++ b/article/res/digraphe_c.png
Binary files differ
diff --git a/article/res/trigraphe_c.png b/article/res/trigraphe_c.png
new file mode 100644
index 0000000..7d3dd82
--- /dev/null
+++ b/article/res/trigraphe_c.png
Binary files differ