summaryrefslogtreecommitdiff
path: root/article/bizarrerie-du-langage-c.md
blob: 166889690ab17d9d8279d618b9f6530a09e173f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
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'opérateur virgule

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 réalité, 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 ceci :
```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 appelle 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 signifie `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.
Ce 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/trigraphe_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.