-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathC.language.txt
1969 lines (1788 loc) · 114 KB
/
C.language.txt
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
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
┏━━━━━━━━━━━━━━━━┓
┃ C_SYNTHESE ┃
┗━━━━━━━━━━━━━━━━┛
┌──────────────────┐
│ AVANT-PROPOS │
└──────────────────┘
VERSIONS DE C ==> #Créé en 1972. Trois standards :
# - C89, avec 15 standard libraries notamment
# - En 1995, iso646.h, wchar.h et wctype.h sont ajoutés
# pour le support Unicode et international
# - C99, avec notamment 6 standard libraries en plus :
# complex.h, fenv.h, inttypes.h, stdbool.h,
# stdint.h, and tgmath.h, notamment pour les
# mathématiques poussées, et la résolution des
# problèmes liés à la taille d'un int
CONVENTIONS D'ECRITURE #Voici celles que j'utiliserai.
==> #Les suffixes :
VAR #Nom de variable ("label") contenant une LIT, et non
#une adresse.
ADR #Nom de variable ("label") contenant une adresse mémoire
#("pointeur")
LIT #Valeur littérale, data, simple (ex : 3, 'h') ou composé
#(ex : 1 + 1)
VAL #VAR ou LIT (dont FONC_LIT)
#Les préfixes :
CHAR_ #Type char
SHORT_ #Type short
INT_ #Type int
LONG_ #Type long
LONG_LONG_ #Type long long
TOUINT_ #Type short, int, long ou long long
SIZE_T_ #Type size_t, souvent un unsigned int
SSIZE_T_ #Type ssize_t, souvent un signed int
WCHAR #Type wchar_t
FLOAT_ #Type float
DOUBLE_ #Type double
LONGDOUBLE_ #Type long double
TOUFLOAT_ #Type float, double ou long double
U_ #Unsigned
VOID_ #Type void
FONC_ #Fonction. FONC_VAR est le nom de la fonction, FONC_LIT
#la valeur qu'elle retourne.
#Les types complexes :
STKT #Type struct
STKTMBR #Type "membre de structure"
UNIO #Type union
UNIOMBR #Type "membre d'union"
ENUM #Type enum
#Les spéciaux :
ARR #Array, utilisé comme une ADR. ARR_ADR équivaut donc à
#une ADR_ADR.
STR #String, il s'agit d'une ARR, plus précisément d'une
#CHAR_ADR. STR_LIT signifie "STR" (dont les " ")
WSTR #Array de WCHAR
FILE #Pointeur vers un fichier, utilisé comme une ADR.
TYPE #Mot renvoyant un type :
# - simple ("unsigned char", "int", etc.)
# - complexe ("struct MOT", "enum MOT")
# - créé par typedef
#Il peut être :
# - précédé facultativement de const et/ou volatile
# - précédé facultativement de extern, auto, register
# ou static
# - précédé facultativement de inline, pour une
# fonction
# - suivi de * s'il s'agit d'un type de pointeur
# - suivi de *() s'il s'agit d'un type de pointeur de
# fonction
MOT #Mot arbitraire.
KEYWORDS ==> #Les voici :
auto break case char const continue default do
double else enum extern float for goto if
int long register return short signed sizeof static
struct switch typedef union unsigned void volatile while
IDENTIFIERS ==> #Désigne :
# - un nom de variable ("label"), pointeur ou non, ou
# de fonction.
# - la nom d'une structure, union ou enum
#Limitations :
# - maximum 31 caractères
# - [[:alpha:]_][[:alnum:]_]\*
# - une extern variable ou fonction a souvent besoin
# d'être unique dans ses 6 premiers caractères.
#Recommandations :
# - const variables, #define macros et variables
# globales doivent être en uppercase
# - Si l'identifier contient plusieurs mots, les
# séparer par _, ou mettre une majuscule au début de
# chaque mot concaténé.
DATA OBJECT ==> #Désigne une variable, pointeur ou non, par opposition
#aux fonctions.
TRIGRAPHES ==> #Obsolète.
#Pour les systèmes de disposant pas de tout ASCII lors
#de la compilation (option -trigraphs avec gcc),
#remplace les caractères manquant par des trigraphes :
??= # - #
??( # - [
??) # - ]
??< # - {
??> # - }
??/ # - \
??! # - |
??- # - ~
??' # - ^
┌─────────────────┐
│ GENERALITES │
└─────────────────┘
int main(void) { #La fonction main() est la fonction principale d'un
COMMANDES ;... #programme C. Obligatoirement nommée ainsi. Retourne une
return EXIT_SUCCESS;#INT_LIT à l'environnment (pour qui souvent 0 == succès
} #et 1 == échec).
#main() doit donc retourner la valeur de vérité de
#l'environnment (constante EXIT_SUCCESS, souvent = 0, et
#opposée à EXIT_FAILURE, souvent = 1, voir <stdlib.h>)
#Le programme commence et s'achève avec main()
#main() n'a pas d'arguments, d'où le "void", sauf dans
#le cas qui suit.
int main(int argc #Seul moyen pour donner des arguments à main(), dans cet
[, char *argv[]] #ordre, et avec cette syntaxe.
[, char *arge[]]) { # - argc contient le nombre d'arguments positionnels de
... # l'exécutable lors de son exécution (le script étant
# lui-même un argument, l'argument n°0).
# - argv est un array de strings contenant les
# arguments positionnels. Elle trie automatiquement
# ces derniers pour placer les options avant les
# opérandes
# - arge est un array de strings contenant les
# environment variables sous la forme VAR=VAL. Il
# est plus portable d'utiliser la variable globale
# environ. Utiliser donc plutôt environ. Dans tous
# les cas, c'est propre à Unix
#C'est le système qui invoquera main() avec ces trois
#arguments en fonction des arguments passés au programme
#en ligne de commande.
#Les noms des variables peuvent être différents, mais
#il s'agit de la convention.
COMMANDES; #Une commande se termine toujours par ;, sauf les
#instructions au pré-processeur, qui se terminent par
#newline.
#Les whitespaces, dont newline, servent à séparer la
#plupart des tokens (bien que non-nécessaire parfois,
#comme pour FONC_VAR() ou FONC_VAR (), ou VAR++ ou
#VAR ++). Leur nature et nombre n'importe pas (on peut
#utiliser les espaces autant que les newlines)
#Exception : entre deux " " ou ' ', on ne peut pas
#inclure de newline, sauf en faisant un trailing newline
#auquel cas la tabulation qui suit est prise en compte
#dans les guillemets. Pour éviter cela, il est possible
#de fermer des " ", faire une newline, puis rouvrir les
#" ", qui seront concaténées toutes les deux.
#Les trailing newlines sont possibles pour les
#instructions au pré-processeur aussi.
/*commen-
taires*/ #Commentaires, remplacés par un single espace par le
//commentaires #préprocesseur
CASSE ==> #C est sensible à la casse
VOCABULAIRE ==> #Voici :
bloc #Ensemble de commandes entourées de { }
définir #Annoncer qu'une variable ou fonction existe, et qu'elle
#doit être utiliser sous ce type ou prototype. Toute
#définition déclare en même temps.
déclarer #Synonyme de définir, sauf dans le cas d'un fichier
#voulant utiliser une variable ou fonction définie dans
#un autre fichier : il ne peut pas la redéfinir, mais
#doit la redéclarer.
initialiser
affecter #Associer une valeur à une variable
exécuter
déréférencer #Utiliser la valeur affectée à une variable
ORDRE RECOMMANDE ==> #Il est recommandé de suivre cet ordre :
#Un bloc doit commencer par :
# - les déclarations
# - puis les initialisations
# - puis les exécutions
#Une fonction doit :
# - être déclarée avant la fonction dans laquelle elle
# s'exécute (si cette dernière est dans le même
# fichier), et en dehors de tout bloc
# - être exécutée dans un bloc
# - être définie en dehors de tout bloc, après la
# fonction dans laquelle, etc.
#Une variable :
# - doit être déclarée avant d'être initialisée, ou les
# deux en même temps
# - doit être déclarée et initialisée avant d'être
# exécutée
MANIPULATION DE
VARIABLES ==> #Voici :
TYPE1 VAR1[ = VAL1] #Déclare VAR... avec comme type TYPE1
[, [TYPE2] VAR2 #Si [ = VAL], affecte également VAL à VAR
[ =VAL2]]... #Sit [TYPE2] est omis, TYPE1 est utilisé.
VAR #Exécute VAR
QUALIFIERS ==> #Ils précèdent TYPE lors d'une déclaration uniquement :
const #Indique que la variable sera read-only (constante). Sa
#valeur est écrite en dur dans l'exécutable.
#Si TYPE désigne un pointeur :
# - const TYPE *VAR signifie que la valeur pointée par
# VAR est read-only
# - TYPE const *VAR signifie que le pointeur lui-même
# est read-only (et non sa valeur pointée)
volatile #Empêche le compiler d'optimiser cette variable, par
#exemple en la supprimant (si inutile) ou en changeant
#son type tel que déclaré dans le source.
#Utilité possible :
# - permet à une variable sig_atomic_t de rester
# sig_atomic_t, et donc utilisable par un signal
# handler.
# - avec longjmp : cf doc
# - avec une variable static pouvant être accédée par
# autre chose que le programme courant (par exemple
# si on fait en sorte qu'elle soit dans un registre
# CPU), et testée alors que sa valeur semble connue
# à l'échelle du programme => évite l'optimisation
# out.
# - Autre exemple : variable partagée par deux threads
# assez petite pour être optimisée dans un registre
# CPU, ou classe partagée dont l'une de ses CLASSDT
# partagée peut l'être ; auquel cas elle deviendrait
# locale à chaque thread.
# Cela ne s'applique pas aux variable déclarées dans
# des critical sections, qui ne peuvent pas être
# partagées par plusieurs threads par définition :
# utiliser éventuellement const_cast.
┌─────────────────┐
│ COMPILATION │
└─────────────────┘
COMPILATION ==> #Voici les étapes de la création d'un exécutable, à partir
#d'un code source. Pour chaque fichier source
#(extension .c) :
# - Le préprocesseur fait des remplacements de texte en
# fonction des instructions commençant par #
# - La compilation (compile-time) parse le contenu pour le
# traduire en assembleur (donne des résultats différents
# en fonction de l'OS, car chaque OS a différents system
# calls)
# - L'assemblage traduit l'assembleur en langage machine
# (fichier objet)
# - Le linking (link-time) lie les différents fichiers
# sources (s'il y en plusieurs) sous forme d'un seul
# exécutable (parfois appelé improprement loading)
LOADING ==> #Le loading est le lancement d'un exécutable (exécuté par le
#system call execve() sous Unix), avec :
# - la validation du lancement
# - la copie en mémoire des éléments nécessaires au
# lancement du programme
# - la création de la stack frame de ce programme
# - l'initialisation des registres (dont le stack pointer)
# - le saut vers l'adresse du code copié en mémoire
RUNTIME ==> #Exécution à proprement parler de l'exécutable. Si une
#library dynamique est requise, elle est alors copiée en
#mémoire.
HEADERS ==> #Un header est un fichier texte écrit en C, contenant des
#déclarations de :
# - constantes
# - fonctions
# - structures, unions et enum
# - typedef
# - enum
#et des instructions au préprocesseur, mais pas
#d'initialisations (sauf pour les constantes). Ainsi, seule
#l'interface (l'aspect syntaxique) et non l'implémentation
#(comment la fonction agit) est défini.
#L'extension est souvent .h sous Unix.
#L'extension pour un fichier C préprocessed, mais pas
#compilé est souvent .i
#Ils se trouvent en général dans /usr/include
#Les headers sont en général spécifiques à l'OS (un même
#header varie en fonction de l'OS, et d'ailleurs les
#libraries auquel il fait référence aussi)
LIBRARIES ==> #Un fichier objet est un fichier en langage machine (par
#exemple un programme C après préprocesseur et assembleur,
#et avant linker).
#Il est possible de lier un fichier objet lors de la
#compilation avec un autre, ce qui permet d'utiliser les
#fonctions et les constantes qu'il a initialisées comme si
#elles l'étaient dans ce même programme.
#Il faut cependant pour ce faire les déclarer (et non les
#définir) dans le premier fichier objet (avec le mot-clef
#extern, ou en dehors de tout bloc), ce qui est le rôle des
#headers. Sans cela, deux fichiers, même liés entre eux, ne
#partagent aucune variable ni fonction, pas même constante
#ou globale. Cela implique également qu'il faut #include
#les headers pour chaque fichier objet le nécessitant.
#On peut outrepasser la question de la portée aveugle entre
#deux fichiers en liant une fonction d'un fichier vers un
#autre, et en faisant un call by address sur cette
#fonction.
#Un fichier objet peut donc ne pas avoir de main(), et être
#juste un "grimoire" de fonction. S'il est destiné à être
#réutilisé, il s'agit d'une library (biblitothèque), sinon
#d'un module du programme (ce qui permet de découper le
#programme en plusieurs tâches).
LINKING ==> #Il y a deux linkings possibles :
# - static linking : copie en dur, lors du compile-time,
# dans l'exécutable, le contenu d'une library (static
# library, .a sous Unix, .lib sous windows). Le terme
# s'applique aussi aux static variables, qui ont des
# mécanismes de linking, mais au sein d'un même fichier.
# - dynamic linking : ne copier qu'une sorte de lien
# symbolique (par exemple le nom de la biblitothèque,
# qui est recherchée en runtime ensuite dans plusieurs
# répertoires types, et dont le contenu est seulement
# alors copié en mémoire). L'extension est .so sous Unix
# et .dll sous Windows.
# Avantage :
# - efficience
# - si cette fonction est utilisée par un autre
# programme en cours, elle est partagée (shared
# library, ou dynamic library). Cela minimise la
# mémoire vive utilisée.
# - updater la library update tous les programmes
# l'utilisant.
# Cependant :
# - la modifier peut donc poser des problème de
# backward compatibility, etc.
#Lorsque les libraries sont accessibles non pas via des
#noms de fichiers, mais via une interface orientée objet,
#cela devient des "shared objects".
#Les libraries se trouvent en général sous Unix dans /lib, /
#usr/lib et /usr/locale/lib sous Unix.
ASPECT PRATIQUE ==> #Pour un projet :
# - faire un FICHIER.c contenant le main()
# - mettre les définitions de fonctions dans des MODULE.c,
# regroupés par thème
# - mettre toutes les déclarations précédant un main()
# dans un ou plusieurs headers, et inclure également cet
# header dans les modules appelant des fonctions
# déclarées dans cet header
# - relier le tout avec un makefile
┌───────────────────┐
│ PREPROCESSEUR │
└───────────────────┘
PREPROCESSEUR ==> #Il s'agit de la première étape du compilateur.
#Si un texte est remplacé par une instruction au
#préprocesseur, le texte de remplacement est rescanné,
#ce qui permet d'avoir des instructions nested.
#Cependant un #define VAR MOTS qui contient VAR dans
#MOTS ne sera pas réévalué en fonction de lui-même.
#include "FILE" #Est remplacé par le contenu de FILE (chemin relatif ou
#absolu). Parfois, la recherche est case insensitive, et
#seuls les six premiers caractères peuvent être
#recherchés. Chemin relatif si ne commence pas par /.
#FILE est souvent un header.
#include <FILE> #Même chose, mais recherche FILE (chemin relatif) dans
#le répertoire INCLUDE_PATH (souvent /usr/include/ sous
#Unix, ainsi que /usr/local/include/). Il est possible
#d'ajouter des répertoires de recherche avec l'option -
#I DIR de gcc.
#define VAR [MOTS] #Remplace toute occurence de VAR par MOTS, si MOTS est
#présent, sauf dans une STR_LIT.
#VAR ne peut plus être déclarée ou initialisée : définit
#donc une constante (appelée macro). Elle est typée
#automatiquement en fonction de MOTS.
#Déprécié en faveur de const VAR car #define :
# - ne permet pas de typer manuellement VAR
# - remplace VAR, mais ne l'évalue pas ensuite. Peut
# entraîner des side-effects.
# - est global, ce qui n'est pas toujours souhaité.
#Peut être utilisé cependant :
# - car est utilisé par #ifdef, #ifndef et #undef
# - sans MOTS, permet simplement de définir VAR, et de
# l'utiliser ensuite avec #ifdef, etc.
# - est en général utilisé dans les headers.
#Mettre MOTS entre parenthèses pour éviter de le coller
#à ce qui est à côté de lui.
#define MOT(VAR1 #Définit une macro également. MOTS doit contenir chaque
[, VAR2]...) MOTS #VAR. Lorsque MOT( ... ) apparaît, il sera remplacé par
#MOTS. De plus, les valeurs se trouvant alors entre
#parenthèses et correspondant aux VAR de la macro seront
#remplacées par leurs équivalences dans MOTS.
#Ces équivalences peuvent :
# - être précédées d'un #, ce qui fait que les
#caractères seront tous échappés (dont " "), et que des
#" " englobantes seront rajoutées lors de la
#substitution
# - deux équivalences peuvent être séparées par ,
# auquel cas, elles deviendront concaténées lors de
# la substitution
#Il est parfois souvent de mettre MOTS entre parenthèses
#pour qu'il soit évalué avant les éventuels caractères
#contigus à lui, ainsi que chacun des équivalences des
#VAR dans MOTS.
#undef VAR #Supprime l'effet d'un #define VAR, s'il y en a eu un,
#sinon, ne fait rien.
#if TEST
COMMANDES... #Boucle if/else if/else évaluée par le préprocesseur.
#elif TEST #TEST ne prend en compte qu'une macro en lvalue.
COMMANDES... #Ne peut pas contenir sizeof
#else TEST #TEST peut également contenir l'expression :
COMMANDES... # - define VAR
#endif #qui est vraie si VAR a été définie par #define.
#Mettre un commentaire :
# - #endif // TEST
#est une bonne pratique, pour rappeler le TEST de départ
#(notamment pour un header guard)
#ifdef VAR #Equivaut à if define VAR
#ifndef VAR #Equivaut à if ! ( define VAR ). Ces deux derniers sont
#souvent présents dans les headers (dont le début et la
#fin) pour :
# - vérifier l'architecture d'un système
# - vérifier que ce header n'a pas déjà été chargé. Si
# ce n'est pas le cas, il est chargé, et un #define
# est effectué sur VAR pour locker tout double
# chargement (header guard)
#pragma MOT #Envoie une pragma-option MOT au compilateur. Les
#pragma-options sont spécifiques à chaque compilateur.
#erreur STR_LIT #Interrompt le programme et imprime STR_LIT (qui n'est
#pas entouré de " " ici) comme message d'erreur.
#file INT_LIT [STR_LIT] #Fait que __LINE__ devient artificiellement INT_LIT, et,
#optionnellement, que __FILE__ devienne STR_LIT
┌─────────────────────────────────┐
│ PORTEE, LIFETIME ET LINKAGE │
└─────────────────────────────────┘
SYNTHESE ==> #Voici :
-----------------+---------+-----------+----------+-----------+--------+--------
NOM | PORTEE | LIFETIME | LINKING |MEM. ALLOC.|IN-BLOC |OUT-BLOC
-----------------+---------+-----------+----------+-----------+--------+--------
auto | locale | automatic | aucun | automatic | défaut | non
register | locale | automatic | aucun | automatic | oui | non
static (in-bloc) | locale | static | internal | static | oui | non
static (out-bloc)| globale | static | internal | static | non | oui
extern | globale | static | external | static | non | défaut
-----------------+---------+-----------+----------+-----------+--------+--------
PORTEE ==> #Une variable peut avoir une portée :
# - locale : son contenu ne peut être lu qu'au sein du
# même bloc où elle a été déclarée (dont le bloc
# d'une loop ou d'un statement). Le call by address
# est un moyen de "tricher" sur la portée locale des
# paramètres d'une variable.
# - globale : son contenu peut être lu au sein de
# n'importe quel bloc.
LIFETIME ==> #La lifetime (duration) d'une valeur de variable peut
#être :
# - automatic : la mémoire est allouée à la variable
# sur le stack : dont dès qu'elle entre dans le
# bloc où elle est déclarée, et est libérée une fois
# que l'on en est sorti. Si pas initialisé, rempli
# du garbage du stack.
# - static : la mémoire est allouée durant le loadtime.
# Ainsi, elle peut toujours être accédée si la
# variable a une portée globale et le linking
# adéquat, ou si la variable a une portée locale et
# que le bloc de déclaration est à nouveau appelé.
# Est initialisé à 0 compile-time si pas initialisé.
LINKING ==> #Le linking d'une variable peut :
# - ne pas exister (la variable n'a de sens qu'au sein
# sein du bloc où elle a été déclarée, dans le stack)
# - être interne : un linking est créé pour qu'au sein
# du même fichier, la variable puisse continuer
# d'être accédée.
# - être externe : un linking est créé pour que la
# variable puisse être accédée par d'autres fichiers
# (compilés en même temps, ou #include)
MEMORY ALLOCATION ==> #Une valeur (et non une variable) peut avoir une memory
#allocation :
# - static : cf dessus.
# - automatic : cf dessus.
# - dynamic : la mémoire (taille) et l'allocation
# précise de la zone mémoire ont lieu lors du runtime
# (quand la variable est requise), dans le heap
# (static lifetime donc). Une variable n'est jamais
# dynamic, c'est sa valeur qui l'est. Il s'agit de
# pointeurs (eux-mêmes auto, register, static ou
# extern) que l'on fait pointer vers des data dynamic
# créées via malloc() par exemple.
RESUME DES CLASS #Il est interdit de spécifier le class storage des
STORAGES ==> #paramètres d'une fonction (car nécessairement auto) :
[auto] TYPE VAR #Déclare une variable auto.
#Ne peut être utilisé en dehors d'un bloc, c'est le
#type par défaut des variables au sein d'un bloc : est
#donc par nature redondant.
register TYPE VAR #Comme auto, mais indique au compilateur d'essayer
#d'utiliser les registres plutôt que le stack (le
#compilateur peut refuser).
#Par ailleurs, l'opération &VAR est impossible.
#Ne marche pas pour les types complexes.
static TYPE [FONC_]VAR #Deux cas :
# - au sein d'un bloc (in-bloc) : portée locale, mais
# lifetime static. Ainsi, à la fin du bloc où elle
# a été déclarée, son contenu n'est plus accessible,
# mais lors d'un nouvel appel de ce même bloc, son
# contenu est toujours là, et accessible.
# Utile pour des foncions récursives par exemple.
# Prudent aussi lorsque l'une fonction renvoie un
# pointeur vers la valeur d'une variable déclarée en
# son sein (sinon cette valeur peut être écrasée en
# mémoire ultérieurement)
# - en dehors d'un bloc (out-bloc) :
# comme extern, sauf que le linking extern est
# impossible (seul la portée globale au sein du
# fichier est utilisée)
#Initialisé à 0, puis garde ses valeurs jusqu'à fin du
#programme.
#Ne peut pas être initialisée à partir de la valeur
#d'une static ou extern VAR.
#Un static ne peut être déclaré que dans un seul
#fichier, de même pour la définition. Ainsi, s'il est
#déclaré dans un header, il ne peut être initialisé
#que dans un fichier et non plusieurs.
#Il ne faut pas initialiser une static VAR en accédant
#à une autre static VAR présente dans un autre
#fichier, car on est jamais sûr que cette dernière
#sera construite avant.
[extern] TYPE [FONC_]VAR#La variable, ou fonction, a une portée globale, une
#lifetime static et un linking extern est possible.
#Par défaut si en dehors d'un bloc pour une fonction :
#ne doit donc être explicité que dans un bloc, ou en
#dehors d'un bloc pour certaines variables.
#Deux utilités donc :
# - peut être utilisée dans tout le fichier courant
# - permet de faire un linking extern.
#Le mécanisme du linking extern est le suivant :
# - dans un premier fichier, la variable extern est
# définie.
# - dans un second fichier (compilé en même temps ou
# #include), elle est à nouveau déclarée ce qui
# permet de pouvoir l'utiliser dans ce fichier
# également (fait référence à la même valeur dans la
# mémoire), mais ne pourra être exécutée qu'au sein
# d'un bloc. C'est l'utilité des headers notamment.
#Une fonction devant être en dehors de tout bloc, est
#par défaut extern donc.
#Ne peut pas être initialisée à partir de la valeur
#d'une static ou extern VAR.
#Initialisé à 0, puis garde ses valeurs jusqu'à fin du
#programme.
inline TYPE FONC_VAR #Ce dernier specifier est apporté par C99, et ne
#s'applique qu'à une fonction. Il signifie que lors de
#la compilation, il n'y aura pas de linking, mais que le
#contenu de la fonction sera incorporée en dur dans
#l'exécutable, ce qui permet d'éviter un appel vers la
#fonction (gain de performance), mais conduit à un temps
#de compilation plus long, et un exécutable plus gros.
#En général, éviter et préférer l'optimisation du
#compilateur, qui choisit lui-même si une fonction doit
#être inline ou non.
┌────────────────────────┐
│ TYPES DE VARIABLES │
└────────────────────────┘
TYPES DE VARIABLES ==> #Voici les types de variables simples. Les valeurs
#dépendent de l'architecture 16 / 32 / 64 bits. Le
#RANGE est celui d'un OS 64 bits. Les TOUINT et CHAR
#sont signed par défaut.
#%d et %i désignent des signed et %u, %o, %x et %X des
#unsigned TOUINT. Les deux RANGE pour chaque TOUINT
#dépend de sa signedness.
NOM SIZE LETTRE RANGE
16 bits 32 Bits 64 bits
[[un]signed] %hhd, %hhi, %hhu +/-127 (ASCII)
char 1 octet %hhx, %hhX, %hho 255 (ASCII)
%c
[[un]signed] 1 octet 2 octets %hd, %hi, %hu -/+32767
short [int] %hx, %hX, %ho 65535
[[un]signed] 2 octets 4 octets %d, %i, %u, -/+2 147 483 647
[int] %x, %X, %o 4 294 967 295
[[un]signed] 4 octets 8 octets %ld, %li, %lu -/+9 223 372 036 854
long [int] %lx, %lX, %lo 775 807
18 446 744 073 709
551 615
[[un]signed] 8 octets 8 octets %lld, %lli, %llu -/+9 223 372 036 854
long long [int] %llx, %llX, %llo 775 807
%qd, %qi, %qu 18 446 744 073 709
%qx, %qX, %qo 551 615
float 4 octets %f, %F, %e 3.4*10^(+/-38) (7
%E, %g, %G chiffres exacts)
double 8 octets %lf, %lF, %le 1.7*10^(+/-308) (15
%lE, %lg, %lG chiffres exacts)
long double 10 octets %Lf, %LF, %Le 3.4*10^(+/-4932) (19
%LE, %Lg, %LG chiffres exacts)
TYPE* (en fait
un int ou un
long suivant
l'architecture) 2 octets 4 octets 8 octets %p 0xFFFF FFFF FFFF FFFF
CHAR ==> #char est en fait un simplement un TOUINT d'1 octet,
#mais est représenté par les fonctions comme un
#caractère ASCII.
VOID ==> #Le type void a trois utilités :
# - TYPE d'un FONC_VAR ne retournant rien.
# - préciser qu'une argument ne prend pas d'argument.
# Mettre seulement FONC_VAR() laisse sinon
# l'interprétation au compiler.
# - utilisé sur les pointeurs. L'opérateur & renvoie
# un pointeur de type VOID *. Un pointeur initialisé
# donc ainsi :
# - void *VOID_ADR = &VAR
# ne peut pas être exécuté sous la forme :
# - *VOID_ADR
# et ne peut pas avoir d'arithmétique des pointeurs
# mais a deux utilités :
# - être converti en un pointeur de n'importe quel
# type, par transtypage :
# - TYPE *ADR = VOID_ADR
# (TYPE *ADR = (TYPE *)VOID_ADR est
# redondant et à éviter)
# Utilisé égalament dans le corps d'une
# fonction, après avoir passé le VOID_ADR en
# argument à la fonction, via par exemple :
# - FONC_VAR((void *)&VAR)
# - être utilisé sous la forme printf("%p",
# VOID_ADR) par exemple
ARRAYS ET STRINGS ==> #Les arrays sont des pointeurs (voir plus loin).
#Les strings sont des arrays de CHAR (correspond à %s)
BOOLEEN ==> #Il n'y a pas de type booléen. Faux est noté 0, et vrai
#différent de 0.
SIGNEDNESS ==> #Elle est obtenue par complément de 2. Une conversion de
#de signedness est transparente, à condition que la
#valeur se trouve dans le range de la signedness opposée
POINTEUR ==> #Le statut de pointeur fait partie du type de la
#variable.
CONVERSION DE TYPE ==> #Les types peuvent être convertis entre eux :
#Conversions à éviter :
# - touint -> touint inférieur : TOUINT %
# MAX(TOUINT_INFERIEUR)
# - toufloat -> toufloat inférieur : perte de
# précision possible
# - toufloat -> touint : troncature
# - touint -> toufloat : peut provoquer des pertes de
# précision, quand taille de touint > taille du
# significand de toufloat
# - toufloat -> toufloat moins précis : troncature de
# la précision
#Conversion sans problème :
# - toufloat -> toufloat supérieur
# - touint -> touint supérieur
#Pour les convertir :
# - transtypage implicite :
# - VAR = VAL, où VAL a un type différent.
# - aussi donc le cas lorsque l'on passe des
# arguments à une fonction, avec un type
# différent.
# - après une opération arithmétique :
# - deux nombres de même TYPE donne TYPE. Ainsi :
# - DOUBLE_VAR = 5 / 9
# devrait être écrit :
# - DOUBLE_VAR = 5.0 / 9
# - les CHAR et les SHORT sont toujours promus en
# INT : cela s'appelle le widening. Si seulement
# des SHORT ou des INT, donne donc au moins un
# signed INT, puis unsigned INT si cela dépasse
# - deux TOUINT donnent le TOUINT dont le type est
# le plus large, ou un type supérieur encore si
# cela dépasse les limites
# - même chose pour les TOUFLOAT
# - un TOUINT et un TOUFLOAT donne TOUFLOAT
# - widening : les CHAR et SHORT passés en argument
# d'une fonction, ou return value sont toujours
# promus en INT à la compilation x86, car le stack
# est aligné sur 4 bits
# - transtypage explicite (cast) :
# - (TYPE)VAL : Exemple de (TYPE) :
# - (int)
# - (int *)
# - (int[30]) pour une array
# - (int (*)()) pour un pointeur de fonction
NOTATION ==> #Voici les notations précises :
# - TOUINT_LIT : [0][0x]NOMBRE (0 dénote un octal et 0x
# un hexadécimal)
# - TOUFLOAT_LIT : - NOMBRE[.NOMBRE]
# - NOMBRE / NOMBRE
# - NOMBRE[.NOMBRE]e|E-|+NOMBRE
# - Un TOUINT_LIT ou un TOUFLOAT_LIT peut être terminé
# par un caractère pour spécifier :
# - L ou l : qu'il s'agit d'un long
# - U ou u : qu'il s'agit d'un unsigned
# - CHAR_LIT : 'LETTRE'
# - STR_LIT : "STR"[ "STR"]... Il s'agit d'un char const*
# - pour un CHAR_LIT ou un STR_LIT, les séquences
# d'échappement backslash suivantes sont
# disponibles :
# - \xN, \xNN, \0N et \0NN. Les séquences \xNNN et
# \0NNN buguent, de sorte que si un caractère non
# whitespace ou ', ou " ne suit pas, cela produit
# un bug. On peut clore la STR_LIT et la rouvrir
# pour pallier le problème.
# - \a, \b, \f, \n, \t, \v
# - \e produit le caractère ESC, 0x1b
# - \r, retour au début de la ligne mais n'écrase
# pas
# - \ échappe \, ', " et ? (si trigraphes)
# - \uXXXX pour un caractère Unicode, en fonction de la locale courante (UTF8 ou 16)
# Ces séquences sont transformées vers le control
# character correspondant pendant le compile-time :
# ainsi, les caractères fournis par l'utilisateur
# durant le runtime ne sont pas transformés
┌─────────────────────┐
│ TYPES COMPLEXES │
└─────────────────────┘
TYPES COMPLEXES ==> #TYPE peut aussi désigner :
# - struct STKT_VAR
# - union UNIO_VAR
# - enum ENUM_VAR
#à condition que ces derniers doivent être déclarés
#avant que TYPE ne soit utilisé
STRUCTURES ==> #Voici :
struct STKT #Déclaration d'une structure (utile seulement pour le
#linking externe)
struct [STKT] { #Déclaration et initialisation d'une structure.
[TYPE STKTMBR;]... #Crée une structure avec des membres STKTMBR,
[[un]signed #hétérogènes en son sein, de manière contigüe dans la
[STKTMBR3] #mémoire (attention : un short prendra 4 octets). Les
:TOUINT_LIT]... #membres peuvent être de TYPE différents.
} [STKT_VAR] ; #
#STKTMBR peut être également :
# - un pointeur (il faudra allouer la mémoire)
# - TYPE *STKTMBR;
# - dont un pointeur de fonction :
# - TYPE (*STKTMBR)()
# - un array :
# - TYPE STKTMBR[TOUINT_VAL]
#Il faut allouer de la mémoire et affecter une ADR aux
#pointeurs pour pouvoir les utiliser (notamment une
#FONC_ADR pour un pointeur de fonction, qu'on exécute
#ensuite sous la forme (*STKT_VAR.STKTMBR)( ... )
#
#Trois sortes de membres possibles :
# - Membre avec un TYPE simple.
# - TYPE peut être complexe (comme struct STKT2),
# ce qui permet d'inclure des unions, enum et d'avoir
# des nested structures (la structure enfant doit
# être définie et déclarée avant la structure
# parente). Exemple :
# - struct STKT {
# struct STKT2 STKTMBR;
# union UNIO STKTMBR2;
# ...
# };
# Il est possible d'initialiser ce type complexe en
# même temps, par exemple :
# - struct STKT {
# struct [STKT2] {
# TYPE1 STKTMBR1;
# ...
# } [STKTMBR];
# };
# Auquel cas si STKT2 et STKTMBR sont omis, il s'agit
# d'une anonymous structure, et on pourra accéder aux
# membres de la nested structure directement, sans
# passer par STKTMBR :
# - struct STKT STKT_VAR;
# STKT_VAR.STKTMBR1 = VAL;
# Pour inclure une structure étant la même que celle
# déclarée (créant ainsi un struct récursif), il faut
# la déclarer en tant que pointeur de structure :
# - struct STKT {
# struct STKT *STKTMBR;
# ...
# };
# On doit allouer la mémoire à chaque STKTMBR dans
# la récursion (en faisant dans l'ordre, ce qui
# signifie que pour accéder à la troisième récursion,
# il faut allouer de la mémoire au premier pointeur
# de structure dans la récursion, puis au deuxième,
# puis à ce dernier)
# Autre possibilité :
# Pour faire que deux struct s'incluent mutuellement,
# il faut que la première déclarée déclare l'autre
# comme pointeur de structure (car la deuxième struct
# n'est pas encore déclarée). Il est d'usage que la
# deuxième struct inclue également la première sous
# forme de pointeur de structure (penser à allouer la
# mémoire)
# - Il est également possible de déclarer des bitfields
# signed ou unsigned, ils n'ont pas de type
# particulier, mais occupe une place de TOUINT_LIT
# bits (et non octets).
# Ils sont initialisés et exécutés via STKTMBR comme
# les membres normaux d'une structure. On peut leur
# affecter tout type de data, mais les data seront :
# 1) converties dans la signedness du field
# 2) tronquées si pas assez de place.
# STKTMBR est optionnel (mais le bitfield sera alors
# inacessible).
# Sert en général dans avec une union englobant la
# structure pour associer la structure avec un stream
# sériel de data, pour accéder à ce flux de manière
# non sérialisé. Parfois non-portable
#
#Ne pas oublier le point-virgule final.
#Déclarer avant main() si possible, dont dans un header.
#La structure elle-même ne peut pas être un array en
#même temps, mais ses images STKT_VAR peuvent l'être
#(voir plus bas)
#Il est possible d'initialiser des STKT_VAR en même
#temps : STKT peut alors être omis, et ne pourra plus
#être accédé en dehors des STKT_VAR initialisées (utile
#parfois si cette déclaration de STKT est nested)
struct STKT STKT_VAR #Déclaration d'une STKT_VAR comme étant de type struct
#STKT. Déclare donc en même temps tous ses STKTMBR_VAR.
#"struct STKT" est en fait un TYPE complexe.
#STKT_VAR peut être un array.
STKT_VAR = STKT_VAR2 #Initialisation des membres d'une STKT_VAR comme copies
#des membres d'une autre STKT_VAR, qui doit être du
#même type struct STKT.
STKT_VAR[.STKT_VAR2]...
= STKT_VAR3 #Initialisation des membres d'une nested STKT_VAR
struct STKT STKT_VAR #Déclaration + initialisation d'une STKT_VAR (marche
= STKT_VAR2 #aussi pour les nested STKT_VAR)
struct STKT STKT_VAR #Déclaration + initialisation d'une STKT_VAR (marche
= { VAL1[, VAL2]... } #aussi pour les nested STKT_VAR). { } possible seulement
#dans ce cas. VAL peut être une autre STKT_VAR. Elle
#peut aussi être une autre { } s'il s'agit d'une
#STKT_VAL imbriquée.
STKT_VAR.STKTMBR_VAR #Initialisation de STKTMBR_VAR (qui est du TYPE tel que
= VAL #décrit par sa struct STKT).
STKT_VAR[.STKT_VAR2]... #Initialisation de STKTMBR_VAR, au sein de nested
.STKTMBR_VAR = VAL #structures.
STKT_VAR.STKTMBR_VAR #Exécution de STKTMBR_VAR
STKT_VAR[.STKT_VAR2]...
.STKTMBR_VAR #Exécution de STKTMBR_VAR, au sein de nested structures
POINTEURS DE MEMBRES #Si STKTMBR a été déclaré dans STKT comme pointeur, pour
DE STRUCTURES ==> #y accéder, il faut faire :
*STKT_VAR[.STKT_VAR2]...
.STKTMBR_VAR #Et non la même chose sans le *.
STKT_VAR[.STKT_VAR2]...
.STKTMBR_VAR = malloc #De plus, il faut allouer la zone mémoire vers laquelle
(sizeof(TYPE *)) #pointe ce pointeur (sinon segfault)
POINTEUR DE
STRUCTURES ==> #Voici :
struct STKT *STKT_ADR #Exemple de déclaration + initialisation de pointeur de
= &STKT_VAR #structure
(*[STKT_VAR.]... #Exécution de STKTMBR_VAR après déréférencement de
STKT_ADR).STKTMBR_VAR #*STKT_ADR (les parenthèses importent)
[STKT_VAR.]...STKT_ADR
->STKTMBR_VAR #Autre syntaxe pour la même chose.
#Il est possible de déclarer des pointeurs dans
#structures imbriqués. Ainsi :
# - (*(*STKT_VAR.STKT_ADR).STKT_ADR).STKT_VAR
#équivaut à :
# - STKT_VAR.STKT_ADR->STKT_ADR->STKT_VAR
STKT_VAR[.STKT_VAR2]...
.STKTMBR_ADR = malloc #De plus, il faut allouer la zone mémoire vers laquelle
(sizeof(TYPE *)) #pointe ce pointeur de structure (sinon segfault)
UNION ==> #Voici :
union UNIO #Déclaration d'une union (utile seulement pour le
#linking externe)
union UNIO { #Déclaration et définition d'une union.
TYPE UNIOMBR;... #Des TYPE différents sont possibles.
} [UNIO_VAR] #UNIOMBR peut être un pointeur ou un array, comme pour
[, UNIO_VAR2]... ; #STKTMBR.
#"union UNIO" est un TYPE complexe.
#Contrairement à une structure, une union utilise la
#même adresse pour tous ses membres (juxtaposés et non
#contigus).
#Ainsi, s'ils ont la même taille mémoire par exemple,
#en modifier un modifie les autres.
#Faire des imbrications d'unions n'a donc pas de sens.
#Ne pas oublier le point-virgule final. Déclarer avant
#main() si possible, dont dans un header.
#Il est possible de déclarer des UNIO_VAR en même temps,
#comme pour les structures.
#La syntaxe des unions et des structures est en fait la
#même.
union UNIO UNIO_VAR #Déclaration d'une UNION_VAR de type union UNIO.
UNIO_VAR = UNIO_VAR2 #Initialisation d'une UNIO_VAR comme copie d'une autre
#UNIO_VAR, qui doit être du même type union UNIO.
union UNIO UNIO_VAR
= UNIO_VAR2 #Déclaration + initialisation d'une UNIO_VAR
UNIO_VAR.UNIOMBR_VAR #Initialisation de UNIOMBR_VAR (qui est du type tel que
= VAL #décrit par l'union UNIO). Rajouter un * devant UNIO_VAR
#si UNIOMBR désigne un pointeur (même chose que struct
#pour l'allocation de la mémoire pour les pointeurs)
UNIO_VAR.UNIOMBR_VAR #Exécution de UNIOMBR_VAR. Même pour pour le *
ARITHMETIQUE DE #Les STKT_VAR et UNIO_VAR sont bien des variables et
POINTEURS DE STRUCTURES #non des adresses. On peut assigner un pointeur de
ET D'UNIONS ==> #structure ou d'union à un autre pointeur de type TYPE *
#ce qui permettra d'utiliser une arithmétique de
#pointeur, selon sizeof(TYPE) cependant, et non selon
#l'ordre des membres de la structure par exemple :
# - TYPE *ADR = (TYPE *)&STKT_VAR
# *ADR++ = VAL
#Il est nécessaire de faire un (TYPE *), sinon on ne
#peut pas assigner de STKT_ADR à une ADR d'autre type.
#C'est cependant risquer de faire cela car les unions et
#les structures rajoutent parfois des gaps entre les
#membres (par exemple après les short ints).
#Pour calculer les gaps, utiliser offsetof()
#(<stddef.h>)
EXEMPLE DE SERIALIATION #Voici la déclaration des types :
DESERIALISATION PAR # - struct STKT {
UNION ==> # TYPE STKTMBR1;...
# } ;
# union UNIO {
# struct STKT STKTMBR2;
# char STKTMBR3[sizeof(struct STKT)];
# } ;
#Ensuite la déclaration et initialisation :
# - union UNIO UNIO_VAR;
# UNION_VAR.STKTMBR3_VAR = VAL
#STKT doit être de même taille que VAL (un special file,
#etc.)
#Et exécution non-sérielle :
# - UNIO_VAR.STKTMBR2_VAR
#Exécution sérielle :
# - UNIO_VAR.STKTMBR3_VAR
ENUM ==> #Voici :
enum ENUM #Déclaration d'un enum (utile seulement pour le linking
#externe)
enum ENUM { #Déclare et définit une enum ENUM. Chaque MOT... est
MOT[=INT_VAL], #associé à une INT_VAL... (par défaut la valeur
... #précédente + 1 (0 pour la première valeur))
} ; #Ne pas oublier le point-virgule. Séparer par des
#virgules, non des point-virgules.
#L'enum doit être défini dans le fichier l'utilisant :
#le définir donc toujours dans un header. La
#substitution a lieu compile-time.
enum ENUM ENUM_VAR #Déclare une ENUM_VAR.
#"enum ENUM" est en fait un TYPE complexe.
ENUM_VAR = VAL; #Si VAL contient un des MOT... du type ENUM, l'INT_VAL
#correspondante est renvoyée à la place de VAL.
#Sinon VAL est bien sûr renvoyé, mais en converti en
#INT_VAL.
enum ENUM [{