-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathSynthèseC.tex
1571 lines (1427 loc) · 63.5 KB
/
SynthèseC.tex
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
\documentclass[a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage[cyr]{aeguill}
\usepackage[english, francais]{babel}
\usepackage{amsmath}
\usepackage{layout}
\usepackage{hyperref}
\usepackage{listings}
\usepackage{color}
\usepackage[top=2cm, bottom=2.5cm, left=2.5cm, right=2cm]{geometry}
\title{OS: Synthèse C}
\author{Bruno}
\date\today
\begin{document}
\definecolor{mygreen}{rgb}{0,0.6,0}
\definecolor{mygray}{rgb}{0.5,0.5,0.5}
\definecolor{mymauve}{rgb}{0.58,0,0.82}
\lstset{
literate={á}{{\'a}}1 {é}{{\'e}}1 {í}{{\'i}}1 {ó}{{\'o}}1 {ú}{{\'u}}1 {Á}{{\'A}}1 {É}{{\'E}}1 {Í}{{\'I}}1 {Ó}{{\'O}}1 {Ú}{{\'U}}1 {à}{{\`a}}1 {è}{{\`e}}1 {ì}{{\`i}}1 {ò}{{\`o}}1 {ù}{{\`u}}1 {À}{{\`A}}1 {È}{{\'E}}1 {Ì}{{\`I}}1 {Ò}{{\`O}}1 {Ù}{{\`U}}1 {ä}{{\"a}}1 {ë}{{\"e}}1 {ï}{{\"i}}1 {ö}{{\"o}}1 {ü}{{\"u}}1 {Ä}{{\"A}}1 {Ë}{{\"E}}1 {Ï}{{\"I}}1 {Ö}{{\"O}}1 {Ü}{{\"U}}1 {â}{{\^a}}1 {ê}{{\^e}}1 {î}{{\^i}}1 {ô}{{\^o}}1 {û}{{\^u}}1 {Â}{{\^A}}1 {Ê}{{\^E}}1 {Î}{{\^I}}1 {Ô}{{\^O}}1 {Û}{{\^U}}1 {œ}{{\oe}}1 {Œ}{{\OE}}1 {æ}{{\ae}}1 {Æ}{{\AE}}1 {ß}{{\ss}}1 {ű}{{\H{u}}}1 {Ű}{{\H{U}}}1 {ő}{{\H{o}}}1 {Ő}{{\H{O}}}1 {ç}{{\c c}}1 {Ç}{{\c C}}1 {ø}{{\o}}1 {å}{{\r a}}1 {Å}{{\r A}}1 {€}{{\euro}}1 {£}{{\pounds}}1 {«}{{\guillemotleft{}}}1 {»}{{\guillemotright{}}}1 {ñ}{{\~n}}1 {Ñ}{{\~N}}1 {¿}{{?`}}1,
language=C,
morekeywords={size_t}
}
% \lstset{language=c}
\maketitle
\tableofcontents
% je mets des TODO: quand j'ai encore quelque chose a faire, car dans mon éditeur de texte je les vois très facillement, si vous n'avez pas fini une section, je vous invite a faire de même, ou alors écrire autre chose, mais que ça puisse ce voir. Si vous voulez des choses en plus, je vous invite également a les mettre dans la section TODO, ce seras plus simple.
\section{Intro}
Programme minimal:
\begin{lstlisting}
int main (void)
{
return 0;
}
\end{lstlisting}
Pour afficher quelque chose sur la sortie standard (souvent l'écran), on peut utiliser la fonction $puts$ qui prend comme paramètre un pointeur vers une chaîne de caractère:
\begin{lstlisting}
#include <stdio.h>
int puts (char const *);
\end{lstlisting}
Les différents types et objets possibles + glossaire:
\begin{description}
\item [Bit] Le bit (BInary digiT) ou chiffre binaire est l'unité de mesure d'information. Il peut prendre 2 valeurs: 0 ou 1.
\item [Byte] Le byte (ou multiplet) est le plus petit objet adressable pour une implémentation donnée. En langage C, il fait au moins huit bits.
\item [Caractère] Un caractère est une valeur numérique qui représente un glyphe visible ('A', '5', '*' etc.) ou non (CR, LF etc.). Un caractère est de type int. Cependant, sa valeur tient obligatoirement dans un type char.
\item [Chaîne de caractères] Une chaîne de caractères est une séquence de caractères encadrée de double quotes. \newline
\begin{lstlisting}
"Hello world!"
\end{lstlisting}
La représentation interne d'une chaîne de caractères est spécifiée. C'est un tableau de char terminé par un \guillemotleft{}\textbackslash0\guillemotright{}, le caractère de fin de chaine.
\begin{lstlisting}
char s[] = "Hello";
\end{lstlisting}
est équivalent à:
\begin{lstlisting}
char s[] = {'H','e','l','l','o','\0'}; // ou alors on met un 0 à la place de '\0'
\end{lstlisting}
\item [\guillemotleft{}Suite d'objets\guillemotright{}]
% vu qu'on est en c, on parle vraiment d'une suite d'objets ?
\item [Tableau] Un tableau est une suite d'objets identiques consécutifs.\newline
Exemple:
\begin{lstlisting}
#include <stddef.h>
size_t size_of_tab = sizeof (long); // type size_t
long tab[5] = {12.34, 56.78};
size_t size_of_tab = sizeof tab / sizeof tab[0];
// donne le nombre d'éléments dans le tableau tab
\end{lstlisting}
Pour un tableau à 2 dimensions:
\begin{lstlisting}
p = (int *)t; // pas sur a 100 %
<type> t[N][M];
t[i][j] = *(p + N*i + j) /* ou encore p[N*i + j] */
// permet de parcourir le tableau
\end{lstlisting}
\item [Types:] valeur minimal / valeur maximal les termes entre \guillemotleft{} () \guillemotright{} sont facultatifs.
\item [char] 0 127
\item [unsigned char] 0 255
\item [signed char] -127 127
\item [(signed) short (int)] -32767 32767
\item [unsigned short (int)] 0 65535
\item [(signed) int] -32767 32767
\item [unsigned (int)] 0 65535
\item [(signed) long (int)] -2147483647 2147483647
\item [unsigned long (int)] 0 4294967295
\end{description}
\subsubsection{Le mot-clé const}
Il permet de déclarer une variable constante par exemple:
\begin{lstlisting}
const int n = 10;
\end{lstlisting}
\subsection{printf}
Formateur de base Rôle Type attendu Types compatibles
\begin{description}
\item [\%] affiche le glyphe du caractère \% Type attendu: int Types compatibles: short, char
\item [c] affiche le glyphe d'un caractère imprimable Type attendu: int Types compatibles: short, char
\item [d] affiche la valeur d'un entier en décimal Type attendu: int Types compatibles: short, char
\item [f] affiche la valeur d'un réel en décimal avec virgule fixe Type attendu: double Type compatibles: float
\item [s] affiche les glyphes d'une chaîne de caractères Type attendu: char *
\end{description}
% pour le programme exemple ci-dessous ce serait pas mieux de mettre à côté des instructions l'output attendu en commentaire ? (gain de place)
\begin{lstlisting}
#include <stdio.h>
int main (void)
{
printf ("%c\n", 'A'); // Affiche "A" puis retour à la ligne
printf ("%d\n", 123); // Affiche "123" puis retour à la ligne
printf ("%d, '%c'\n", 'A', 'A'); // Affiche "65", "A" puis retour à la ligne
printf ("Hello world\n"); // Affiche "Hello world" puis retour à la ligne
printf ("Hello %s\n", "world"); // idem
printf ("%s\n", "Hello world"); // idem
return 0;
}
\end{lstlisting}
\subsection{Fonction}
Une fonction est une séquence d'instructions dans un bloc nommé. Une fonction est constituée de la séquence suivante:
\begin{description}
\item type (ou le mot clé void)
\item identificateur (le nom de la fonction, ici main)
\item parenthèse ouvrante \guillemotleft{} (\guillemotright{}
\item liste de paramètres ou une liste vide (dans ce cas, on écrit void)
\item parenthèse fermante \guillemotleft{}) \guillemotright{}
\item accolade ouvrante \guillemotleft{}\{\guillemotright{}
\item liste des instructions terminées par un point virgule \guillemotleft{};\guillemotright{}, ou rien
\item accolade fermante \guillemotleft{}\}\guillemotright{}.
\end{description}
L'utilisateur peut créer ses propres fonctions. Une fonction peut appeler une autre fonction. Généralement, une fonction réalisera une opération bien précise. L'organisation hiérarchique des fonctions permet un raffinement en partant du niveau le plus élevé (main()) en allant au niveau le plus élémentaire (atomique).\newline
On gardera cependant en tête que la multiplication des niveaux introduit une augmentation de la taille du code et du temps de traitement.\newline
Une fonction ne peut être appelée qu'à partir d'une autre fonction.\newline
Il faut savoir qu'en C, un paramètre ne fait que transmettre une valeur. Modifier la valeur d'un paramètre n'aura jamais d'effet sur la valeur originale:
\begin{lstlisting}
#include <stdio.h>
void fonction (int x)
{
printf ("x = %d (dans la fonction)\n", x); /* x = 123 */
x = 456;
printf ("x = %d (dans la fonction)\n", x); /* x = 456 */
}
int main (void)
{
int a = 123;
printf ("a = %d\n", a); /* a = 123 */
fonction (a);
printf ("a = %d\n", a); /* a = 123 */
return 0;
}
\end{lstlisting}
\subsubsection{Déclaration / implémentation}
Comme en C++\newline
Exemple de déclaration
\begin{lstlisting}
int function (void);
\end{lstlisting}
\subsubsection{Les macros}
\begin{lstlisting}
#define <macro> <le texte de remplacement>
\end{lstlisting}
Peut également être utilisé comme petite fonction, par exemple:
\begin{lstlisting}
#define carre(x) ((x) * (x))
carre(3) // sera remplacé par 3 * 3
\end{lstlisting}
\subsubsection{Typedef}
Le C dispose d'un mécanisme très puissant permettant au programmeur de créer de nouveaux types de données en utilisant le mot clé typedef. Par exemple:
\begin{lstlisting}
typedef int ENTIER;
ENTIER a, b;
\end{lstlisting}
Définit le type ENTIER comme n'étant autre que le type int. Bien que dans ce cas, un simple \#define aurait pu suffire, il est toujours recommandé d'utiliser typedef qui est beaucoup plus sûr.
\subsection{Compilation}
TODO:
\subsection{Les pointeurs}
Le langage C dispose d'un opérateur \guillemotleft{}\&\guillemotright{} permettant de récupérer l'adresse en mémoire d'une variable ou d'une fonction quelconque. Par exemple, si n est une variable, \&n désigne l'adresse de n. \newline
Le C dispose également d'un opérateur \guillemotleft{}*\guillemotright{} permettant d'accéder au contenu de la mémoire dont l'adresse est donnée. Par exemple, supposons qu'on ait:
\begin{lstlisting}
n = 10;
// est tout a fait identique à
* ( &n ) = 10;
\end{lstlisting}
Les pointeurs en langage C sont typés et obéissent à l'arithmétique des pointeurs que nous verrons un peu plus loin. Supposons que l'on veuille créer une variable \guillemotleft{} p \guillemotright{} destinée à recevoir l'adresse d'une variable de type int. \guillemotleft{} p \guillemotright{} s'utilisera alors de la façon suivante:
\begin{lstlisting}
int n;
int * p;
p = &n;
*p = 5;
\end{lstlisting}
Exemple dans une fonction qui permute deux variables:
\begin{lstlisting}
#include <stdio.h>
void permuter(int * addr_a, int * addr_b);
int main(){
int a = 10, b = 20;
permuter(&a, &b);
printf("a = %d\nb = %d\n", a, b);
return 0;
}
void permuter(int * addr_a , int * addr_b)
/***************\
* addr_a <-- &a *
* addr_b <-- &b *
\***************/
{
int c;
c = *addr_a;
*addr_a = *addr_b;
*addr_b = c;
}
\end{lstlisting}
\subsubsection{Les pointeurs générique}
Le type des pointeurs génériques est void *. Comme ces pointeurs sont génériques, la taille des données pointées est inconnue et l'arithmétique des pointeurs ne s'applique donc pas à eux. De même, puisque la taille des données pointées est inconnue, l'opérateur d'indirection * ne peut être utilisé avec ces pointeurs, un cast est alors obligatoire. Par exemple:
\begin{lstlisting}
int n;
void * p;
p = &n;
*((int *)p) = 10;
/* p étant désormais vu comme un int *, on peut alors lui appliquer l'opérateur *.
*/
\end{lstlisting}
Étant donné que la taille de toute donnée est multiple de celle d'un char, le type char * peut également être utilisé en tant que pointeur universel. En effet, une variable de type char * est un pointeur sur octet autrement dit il peut pointer n'importe quoi. Cela s'avère pratique de temps en temps (lorsqu'on veut lire le contenu d'une mémoire octet par octet par exemple) mais dans la plupart des cas, il vaut mieux toujours utiliser les pointeurs génériques. Par exemple, la conversion d'une adresse de type différent en char * et vice versa nécessite toujours un cast, ce qui n'est pas le cas avec les pointeurs génériques.\newline
Dans printf, le spécificateur de format \%p permet d'imprimer une adresse (void *) dans le format utilisé par le système.\newline
Et pour terminer, il existe une macro à savoir NULL, définie dans stddef.h, permettant d'indiquer qu'un pointeur ne pointe nulle part. Son intérêt est donc de permettre de tester la validité d'un pointeur.Il est conseillé de toujours initialiser un pointeur à NULL.
\subsection{lvalue et rvalue}
Dois-je vraiment rappeler comment ça fonctionne?
En gros, une lvalue est quelque chose qui peut ce situer à gauche du \guillemotleft{=}\guillemotright{} et rvalue a droite. Une rvalue possède une valeur mais pas d'adresse.
\href{https://www.internalpointers.com/post/understanding-meaning-lvalues-and-rvalues-c}{Très bon article (en anglais) sur les lvalues et rvalues}
\subsection{Opérateurs usuels et logiques}
\textbf{usuels}: %mis en gras
\begin{description}
\item [<] Inférieur à
\item [>] Supérieur à
\item [==] Égal à
\item [<=] Inférieur ou égal à
\item [>=] Supérieur ou égal à
\item [!=] Différent de
\end{description}
\textbf{logiques}:
\begin{description}
\item [\&\&] ET
\item [||] OU
\item [!] NON
\end{description}
Dans une opération ET, l'évaluation se fait de gauche à droite. Si l'expression à gauche de l'opérateur est fausse, l'expression à droite ne sera plus évaluée car on sait déjà que le résultat de l'opération sera toujours FAUX.\newline
Dans une opération OU, l'évaluation se fait de gauche à droite. Si l'expression à gauche de l'opérateur est vrai, l'expression à droite ne sera plus évaluée car on sait déjà que le résultat de l'opération sera toujours VRAI.
On peut séparer plusieurs expressions à l'aide de l'opérateur virgule. Le résultat est une expression dont la valeur est celle de l'expression la plus à droite. L'expression est évaluée de gauche à droite.
\begin{lstlisting}
(a = -5, b = 12, c = a + b) * 2 ; // renvoie 14.
\end{lstlisting}
\subsubsection{l'opérateur ternaire \guillemotleft{}? : \guillemotright{}}
Une expression conditionnelle est une expression dont la valeur dépend d'une condition. L'expression:
\begin{lstlisting}
p ? a : b ;
\end{lstlisting}
vaut a si p est vrai et b si p est faux.
\subsection{Cast}
\begin{lstlisting}
float f;
f = (float)3.1416;
\end{lstlisting}
\section{Les instructions}
\subsection{if}
\begin{lstlisting}
if ( <expression> )
{
les instructions
}
else if (<expression>)
{
les instructions
}
else
{
les instructions
}
\end{lstlisting}
\subsection{do ... while}
\begin{lstlisting}
do
{
les instructions
}
while ( <expression> );
\end{lstlisting}
\subsection{while}
\begin{lstlisting}
while ( <expression> )
{
les instructions
}
\end{lstlisting}
\subsection{for}
\begin{lstlisting}
for ( <init> ; <condition> ; <step>)
les instructions
\end{lstlisting}
\subsection{break}
%https://msdn.microsoft.com/fr-fr/library/wt88dxx6.aspx
L'instruction break termine l'exécution de l'instruction $do$, $for$, $switch$ ou $while$ englobante la plus proche dans laquelle elle figure. Le contrôle est transmis à l'instruction qui suit l'instruction terminée.\subsection{switch}
\begin{lstlisting}
#include <stdio.h>
int main()
{
int n;
printf("Entrez un nombre entier : ");
scanf("%d", &n);
switch(n)
{
case 0:
printf("Cas de 0.\n");
break;
case 1:
printf("Cas de 1.\n");
break;
case 2: case 3:
printf("Cas de 2 ou 3.\n");
break;
case 4:
printf("Cas de 4.\n");
break;
default:
printf("Cas inconnu.\n");
}
return 0;
}
\end{lstlisting}
\subsection{continue}
Dans une boucle, permet de passer immédiatement à l'itération suivante. Par exemple, modifions le programme table de multiplication de telle sorte qu'on affiche rien pour n = 4 ou n = 6.
\begin{lstlisting}
#include <stdio.h>
int main()
{
int n;
for(n = 0; n <= 10; n++)
{
if ((n == 4) || (n == 6))
continue; // termine la boucle actuelle du for
printf("5 x %2d %2d\n", n, 5 * n);
}
return 0;
}
\end{lstlisting}
\section{L'allocation dynamique de mémoire}
\subsection{Les fonctions malloc et free}
L'intérêt d'allouer dynamiquement de la mémoire se ressent lorsqu'on veut créer un tableau dont la taille dont nous avons besoin n'est connue qu'à l'exécution par exemple. On utilise généralement les fonctions malloc et free.
\begin{lstlisting}
int t[10];
...
/* FIN */
\end{lstlisting}
Peut être remplacé par:
\begin{lstlisting}
int * p;
p = malloc(10 * sizeof(int));
...
free(p); /* libérer la mémoire lorsqu'on n'en a plus besoin */
/* FIN */
\end{lstlisting}
Les fonctions malloc et free sont déclarées dans le fichier stdlib.h. malloc retourne NULL en cas d'échec. Voici un exemple qui illustre une bonne manière de les utiliser:
\begin{lstlisting}
#include <stdio.h>
#include <stdlib.h>
int main()
{
int * p;
/* Creation d'un tableau assez grand pour contenir 10 entiers */
p = malloc(10 * sizeof(int));
if (p != NULL)
{
printf("Succes de l'operation.\n");
p[0] = 1;
printf("p[0] = %d\n", p[0]);
free(p); /* Destruction du tableau. */
}
else
printf("Le tableau n'a pas pu etre cree.\n");
return 0;
}
\end{lstlisting}
\subsection{La fonction realloc}
\begin{lstlisting}
void * realloc(void * memblock, size_t newsize);
\end{lstlisting}
Permet de « redimensionner » une mémoire allouée dynamiquement (par malloc par exemple). Si memblock vaut NULL, realloc se comporte comme malloc. En cas de réussite, cette fonction retourne alors l'adresse de la nouvelle mémoire, sinon la valeur NULL est retournée et la mémoire pointée par memblock reste inchangée.
\begin{lstlisting}
#include <stdio.h>
#include <stdlib.h>
int main() {
int * p = malloc(10 * sizeof(int));
if (p != NULL) {
/* Sauver l'ancienne valeur de p au cas ou realloc echoue. */
int * q = p;
/* Redimensionner le tableau. */
p = realloc(p, 20 * sizeof(int));
if (p != NULL) {
printf("Succes de l'operation.\n");
p[0] = 1;
printf("p[0] = %d\n", p[0]);
free(p);
}
else {
printf("Le tableau n'a pas pu etre redimensionne.\n");
free(q);
}
}
else
printf("Le tableau n'a pas pu etre cree.\n");
return 0;
}
\end{lstlisting}
\subsection{Les structs}
Les structures permettent de remédier à cette lacune des tableaux, en regroupant des objets (des variables) de types différentes au sein d'une entité repérée par un seul nom de variable. \newline
Lors de la déclaration de la structure, on indique les champs de la structure, c'est-à-dire le type et le nom des variables qui la composent.
\begin{lstlisting}
struct Nom_Structure {
type_champ1 Nom_Champ1;
type_champ2 Nom_Champ2;
type_champ3 Nom_Champ3;
type_champ4 Nom_Champ4;
type_champ5 Nom_Champ5;
...
};
// son utilisation
struct Nom_Structure Nom_Variable_Structuree;
\end{lstlisting}
Exemple:
\begin{lstlisting}
struct MaStructure {
int Age;
char Sexe;
char Nom[12];
float MoyenneScolaire;
struct AutreStructure StructBis;
/* en considérant que la structure AutreStructure est définie */
};
//
struct MaStructure Pierre, Paul;
Pierre.Age=42;
\end{lstlisting}
\section{Les caractères}
\subsection{Le jeu de caractères du C}
Voir \href{http://emmanuel-delahaye.developpez.com/tutoriels/c/bonnes-pratiques-codage-c/#LI-A}{Bonne pratique de codage en C}
\subsection{Commentaire}
Préférez les commentaires en dessous ou au dessus d'une ligne plutôt qu'en bout de ligne, souvent ils rendent la ligne trop longue. \newline
Pour les commentaires de plusieurs lignes, utiliser /* */.\newline
S'il faut isoler provisoirement une portion de code, le mieux est de ne pas utiliser les commentaires (il pourrait y avoir des commentaires imbriqués), mais plutôt les directives de préprocesseur: \#ifdef .. \#endif ou \#if .. \#endif
\begin{lstlisting}
#if 0
/* Compteur */
int cpt ;
#endif
\end{lstlisting}
Commentez le moins possible. Le principe est de ne commenter que ce qui apporte un supplément d'information. Il est d'usage d'utiliser en priorité l'auto-documentation, c'est à dire un choix judicieux des identificateurs qui fait que le code se lit 'comme un livre'…
\section{Les threads}
On sait tous ce qu'est un thread, si tu ne sais pas, tu es dans la merde pour ton oral et va d'abord (re)voir ton cours!
\subsection{Compilation}
Toutes les fonctions relatives aux threads sont incluses dans le fichier d'en-tête <pthread.h> et dans la bibliothèque libpthread.a (soit -lpthread à la compilation).\newline
Exemple:\newline
Voici la ligne de commande qui vous permet de compiler votre programme sur les threads constitué d'un seul fichier.\newline
gcc -lpthread <nom du fichier>.c -o <Output>\newline
Et n'oubliez pas d'ajouter \#include <pthread.h> au début de vos fichiers.
Bon, tout ça on aura pas à l'examen, c'est juste pour le projet de C.
\subsection{Les threads en eux-mêmes}
\subsubsection{Créer un thread}
Pour créer un thread, il faut déjà déclarer une variable le représentant.Celle-ci sera de type pthread\textunderscore{}t (qui est, sur la plupart des systèmes, un typedef d'unsigned long int). Ensuite, pour créer la tâche elle-même, il suffit d'utiliser la fonction:
\begin{lstlisting}
#include <pthread.h>
int pthread_create(pthread_t * thread, pthread_attr_t * attr,
void *(*start_routine) (void *), void *arg);
\end{lstlisting}
Ce prototype est un peu compliqué, c'est pourquoi nous allons récapituler ensemble.
\begin{description}
\item La fonction renvoie une valeur de type int: 0 si la création a été réussie ou une autre valeur s'il y a eu une erreur.
\item Le premier argument est un pointeur vers l'identifiant du thread (valeur de type pthread\textunderscore{}t).
\item Le second argument désigne les attributs du thread. Vous pouvez choisir de mettre le thread en état joignable (par défaut) ou détaché, et choisir sa politique d'ordonnancement (usuelle, temps-réel\ldots). Dans nos exemples, on mettra généralement NULL.
\item Le troisième argument est un pointeur vers la fonction à exécuter dans le thread. Cette dernière devra être de la forme \guillemotleft{}void *fonction(void* arg)\guillemotright{} et contiendra le code à exécuter par le thread.
\item Enfin, le quatrième et dernier argument est l'argument à passer au thread.
\end{description}
\subsubsection{Supprimer un thread}
\begin{lstlisting}
#include <pthread.h>
void pthread_exit(void *ret);
\end{lstlisting}
Elle prend en argument la valeur qui doit être retournée par le thread, et doit être placée en dernière position dans la fonction concernée.
\newpage
\subsubsection{Exemple simple}
\begin{lstlisting}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void *thread_1(void *arg) {
printf("Nous sommes dans le thread.\n");
/* Pour enlever le warning */
(void) arg; // le warning enlevé est celui-ci:
/* format '%s' expects argument of type 'char*',
* but argument 2 has type 'void*' [-Wformat=]
* en gros, il faut reconvertir l'argument
* s'il y en a un pour pas avoir d'erreur de format */
pthread_exit(NULL);
}
int main(void) {
pthread_t thread1; // thread1 contiendra le thread
printf("Avant la création du thread.\n");
/* appelle la fonction thread_1 dans la condition du if
* (pour direct renvoyer une erreur si besoin) */
if(pthread_create(&thread1, NULL, thread_1, NULL) == -1) {
perror("pthread_create");
return EXIT_FAILURE;
}
printf("Après la création du thread.\n");
return EXIT_SUCCESS;
}
\end{lstlisting}
On a bien sur un soucis, c'est que ici, le programme s'arrête avant que le thread ait pu finir son calcul (bon, ici, ce n'est qu'afficher quelque chose, mais quand-même). Pour éviter ce soucis, on peut utiliser \guillemotleft{} pthread\textunderscore{}join \guillemotright{}
\subsubsection{Attendre un thread avec pthread\textunderscore{}join}
\begin{lstlisting}
#include <pthread.h>
int pthread_join(pthread_t th, void **thread_return);
\end{lstlisting}
Elle prend donc en paramètre l'identifiant du thread et son second paramètre, un pointeur, permet de récupérer la valeur retournée par la fonction dans laquelle s'exécute le thread (c'est-à-dire l'argument de pthread\textunderscore{}exit).\newline
Du coup dans l'exemple précédent, on peut rajouter ceci(après la création du thread, bien sûr):
\begin{lstlisting}
if (pthread_join(thread1, NULL)) {
perror("pthread_join");
return EXIT_FAILURE;
}
\end{lstlisting}
Donc, pour ceux qui n'ont pas suivi:
\begin{enumerate}
\item créer une fonction qui va permettre de faire tous les calculs qu'on veut dans les autres threads que le thread principal;
\item initialiser une variable de type pthread\textunderscore{}t pour chaque thread qu'on veut créer;
\item appeler cette fonction (dans un if, souvent, ça permet de gérer les éventuelles erreurs plus facillement);
\item appeler la fonction pthread\textunderscore{}join pour "fusionner" les deux threads (en réalité, attendre le thread en retard sur l'autre)
\end{enumerate}
\subsection{Mutex et toute ces belles petites choses}
Bon, c'est sympa tout ça, mais il y a encore un soucis. Toutes les variables sont partagées, et on se rend vite compte que ça peut poser problème, si on veut faire plusieurs calculs sur une même variable, mais l'un à la suite de l'autre, ça risque de coincer, on ne sait pas dans quel ordre vont se faire les calculs. Ce qui veut dire que nous risquons de modifier une valeur dans un thread alors qu'un autre thread en avait besoin sans modifications.(je ne sais pas si j'en ai pas perdu par hasard, si c'est le cas, sachez juste que les mutexes sont vachement utiles pour garder visible une variable qu'à un seul thread et la cacher à tous les autres).\newline
Il faut aussi de nouveau créer une variable, mais cette fois avec un type \guillemotleft{}pthread\textunderscore{}mutex\textunderscore{}t\guillemotright{}.\newline
Le problème, c'est qu'il faut que le mutex soit accessible en même temps que la variable et dans tout le fichier (vu que différents threads s'exécutent dans différentes fonctions). La solution la plus simple consiste à déclarer les mutexes en variable globale. Pour le faire plus proprement possible, on peut utiliser une structure avec la donnée à protéger. Allez, encore un exemple:
\begin{lstlisting}
typedef struct data {
int var;
pthread_mutex_t mutex;
} data;
\end{lstlisting}
Ainsi, nous pourrons passer la structure en paramètre à nos threads quand nous les créons.\newline
Du coup, voilà comment on initialise un mutex en prenant en compte la convention qui veut qu'on initialise le mutex avec la valeur de la constante PTHREAD\textunderscore{}MUTEX\textunderscore{}INITIALIZER, déclarée dans pthread.h.
\begin{lstlisting}
#include <stdlib.h>
#include <pthread.h>
typedef struct data {
int var;
pthread_mutex_t mutex;
} data;
int main(void){
data new_data;
new_data.mutex = PTHREAD_MUTEX_INITIALIZER;
return EXIT_SUCCESS;
}
\end{lstlisting}
\subsubsection{Pour verrouiller un mutex de nom mut}
\begin{lstlisting}
int pthread_mutex_lock(pthread_mutex_t *mut);
\end{lstlisting}
\subsubsection{Pour déverrouiller un mutex de nom mut}
\begin{lstlisting}
int pthread_mutex_unlock(pthread_mutex_t *mut);
\end{lstlisting}
\subsubsection{Pour détruire un mutex de nom mut}
\begin{lstlisting}
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mut);
\end{lstlisting}
\subsection{Les conditions (permettent d'attendre un autre thread)}
Quand un thread est en attente d'une condition, il reste bloqué tant que celle-ci n'est pas réalisée par un autre thread.
\subsubsection{Initialisation} %tu voulais pas dire initialisation ? si si complètement
\begin{lstlisting}
static pthread_cond_t cond_stock = PTHREAD_COND_INITIALIZER;
\end{lstlisting}
Les conditions reposent essentiellement sur deux fonctions.
l'une permet de mettre en attente un thread et la seconde permet de signaler que la condition est remplie ce qui réveille alors le thread qui est en attente de cette condition.
Plusieurs threads peuvent surveiller la même condition.
\newpage
\subsubsection{Exemple complet de mutex}
\begin{lstlisting}
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
pthread_t tid[2];
int counter;
pthread_mutex_t lock; // init le mutex
void* doSomeThing(void *arg){
pthread_mutex_lock(&lock);
unsigned long i = 0;
counter += 1;
printf("\n Job %d started\n", counter);
for(i=0; i<(0xFFFFFFFF);i++);
printf("\n Job %d finished\n", counter);
pthread_mutex_unlock(&lock);
return NULL;
}
int main(void){
int i = 0;
int err;
if (pthread_mutex_init(&lock, NULL) != 0){
printf("\n mutex init failed\n");
return 1;
}
while(i < 2){
err = pthread_create(&(tid[i]), NULL, &doSomeThing, NULL);
if (err != 0)
printf("\ncan't create thread :[%s]", strerror(err));
i++;
}
pthread_join(tid[0], NULL);
pthread_join(tid[1], NULL);
pthread_mutex_destroy(&lock);
return 0;
}
\end{lstlisting}
\href{https://www.thegeekstuff.com/2012/05/c-mutex-examples/}{ma source pour cet exemple}
\newpage
\subsubsection{Le thread attend la condition}
\begin{lstlisting}
int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex);
\end{lstlisting}
Cette fonction permet de mettre le thread appelant en attente de la condition, il suspend donc son exécution temporairement.
Ses deux arguments sont:
\begin{description}
\item [L'adresse de la variable] condition de type pthread\textunderscore{}cond\textunderscore{}t.
\item [L'adresse d'un mutex] Une condition est en effet, toujours associée à un mutex!
\end{description}
\subsubsection{Réveiller un thread}
pthread\textunderscore{}cond\textunderscore{}signal est la fonction qui permet de signaler la condition au thread qui l'attend.
Elle prend en paramètre l'adresse de la variable-condition surveillée. Cette fonction ne permet de réveiller qu'un seul thread.
\begin{lstlisting}
int pthread_cond_signal (pthread_cond_t *cond);
\end{lstlisting}
\subsubsection{Réveiller plusieurs threads}
Cette fonction permet de réveiller tous les threads qui surveillent la condition cond. Tout comme\newline pthread\textunderscore{}cond\textunderscore{}signal, elle prend en paramètre l'adresse de la variable-condition surveillée.
\begin{lstlisting}
int pthread_cond_broadcast (pthread_cond_t *cond);
\end{lstlisting}
\href{http://franckh.developpez.com/tutoriels/posix/pthreads/}{cours complet sur les threads}
\newpage
\subsection{Exemple avec les conditions et les mutexes}
\begin{lstlisting}
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
// Création de la condition
pthread_cond_t condition = PTHREAD_COND_INITIALIZER;
//Création du mutex
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* threadAlarme (void* arg);
void* threadCompteur (void* arg);
int main (void){
pthread_t monThreadCompteur;
pthread_t monThreadAlarme;
pthread_create (&monThreadCompteur, NULL, threadCompteur, (void*)NULL);
// Création des threads
pthread_create (&monThreadAlarme, NULL, threadAlarme, (void*)NULL);
pthread_join (monThreadCompteur, NULL);
// Attente de la fin des threads
pthread_join (monThreadAlarme, NULL);
return 0;
}
void* threadCompteur (void* arg) {
int compteur = 0, nombre = 0;
srand(time(NULL));
// Boucle infinie
while(1) {
// On tire un nombre entre 0 et 10
nombre = rand()%10;
// On ajoute ce nombre à la variable compteur
compteur += nombre;
printf("\n%d", compteur);
// Si compteur est plus grand ou égal à 20
if(compteur >= 20) {
// On verrouille le mutex
pthread_mutex_lock (&mutex);
// On délivre le signal : condition remplie
pthread_cond_signal (&condition);
// On déverrouille le mutex
pthread_mutex_unlock (&mutex);
// On remet la variable compteur à 0
compteur = 0;
}
// On laisse 1 seconde de repos
sleep (1);
}
// Fin du thread
pthread_exit(NULL);
}
void* threadAlarme (void* arg) {
// Boucle infinie
while(1) {
// On verrouille le mutex
pthread_mutex_lock(&mutex);
// On attend que la condition soit remplie
pthread_cond_wait (&condition, &mutex);
printf("\nLE COMPTEUR A DÉPASSÉ 20.");
// On déverrouille le mutex
pthread_mutex_unlock(&mutex);
}
// Fin du thread
pthread_exit(NULL);
}
\end{lstlisting}
\href{https://openclassrooms.com/courses/la-programmation-systeme-en-c-sous-unix/les-threads-3#/id/r-1515355}{Lien de l'exemple}
\section{Gestion des processus}
Pour cette partie je me suis plus inspiré des slides des tp.
\subsection{Création des processus}
\subsubsection{fork}
Fork permet de cloner intégralement le processus courant avec un pid différent pour son fils.
Du coup, fork est la seule fonction qui renvoie deux valeur:
\begin{enumerate}
\item dans le processus fils (le clone), fork renvoie 0
\item dans le processus père (l'original), fork renvoie le pid du fils
\end{enumerate}
Exemple:
\begin{lstlisting}
#include <sys/types.h>
#include <unistd.h>
pid_t fork();
// renvoie 0 si c'est l'enfant
// < 0 si il y a une erreur dans le fork
// > 0 si c'est le parent
\end{lstlisting}
\newpage
\textbf{Exemple complet:}
\begin{lstlisting}
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
int var_glb; /* A global variable*/
int main(void) {
pid_t childPID;
int var_lcl = 0;
childPID = fork();
if(childPID >= 0) { // fork was successful
if(childPID == 0) { // child process
var_lcl++;
var_glb++;
printf("\n Child :: var_lcl = [%d], var_glb[%d]\n", var_lcl, var_glb);
}
else { //Parent process
var_lcl = 10;
var_glb = 20;
printf("\n Parent :: var_lcl = [%d], var_glb[%d]\n", var_lcl, var_glb);
}
}
else { // fork failed
printf("\n Fork failed, quitting!!!!!!\n");
return 1;
}
return 0;
}
\end{lstlisting}
Les variables var\textunderscore{}lcl et var\textunderscore{}glb sont utilisé pour montrer que le père et le fils travail bien sur des variables séparé.\newline
Output:\newline
Parent :: var\textunderscore{}lcl = [10], var\textunderscore{}glb[20]\newline
Child :: var\textunderscore{}lcl = [1], var\textunderscore{}glb[1]\newline
\href{https://www.thegeekstuff.com/2012/05/c-fork-function/}{Liens~de~l'exemple}\newline
\href{http://www.commentcamarche.net/faq/10611-que-fait-un-fork}{Fork~pas~a~pas}\newline
\href{http://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html#}{Reference~anglais~pour~fork}\newline
\subsubsection{getpid()}
Comment savoir le pid du processus courant?
\begin{lstlisting}
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
\end{lstlisting}
Et pour le pid du père:
\begin{lstlisting}
#include <sys/types.h>
#include <unistd.h>
pid_t getppid(void);
\end{lstlisting}
\subsection{Execution d'un programme depuis un programme C, "systeme calls"}
Utilise la famille exec\newline
Lorsqu'un processus fait appel à exec, le processus appelant est remplacé par le programme passé en paramètre. Si cela fonctionne, l'appel à exec ne retourne pas.\newline
les différentes variantes:
\begin{lstlisting}
// permet de passer les paramètres du programme
// en utilisant une liste d'arguments terminée par NULL
int execl(const char *program, const char *arg, ...);
execl("/bin/ls", "/bin/ls", "-l", "-a", NULL );
// cherche le programme dans la variable PATH
int execlp(const char *program, const char *arg, ...);
execlp("ls", "ls", "-l", "-a", NULL );
// avec un vecteur pour les paramètres et dois donner le chemin absolu
int execv(const char *program, const char *argv [ ]);
char *arguments [4];
arguments[0] = "/bin/ls";
arguments[1] = "-l";
arguments[2] = "-a";
arguments[3] = NULL;
execv("/bin/ls", arguments);
// avec un vecteur pour les paramètres et dois donner le chemin absolu
int execvp(const char *program, const char *argv [ ]);
char *arguments [4];
arguments[0] = "ls";
arguments[1] = "-l";
arguments[2] = "-a";
arguments[3] = NULL;
execvp("ls", arguments);
\end{lstlisting}
\href{http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html}{Référence en anglais pour approfondir}
\subsubsection{Attendre la fin d'un processus}
Attendre la fin d'un processus peut être intéressant quand on attend de ce processus un résultat.\newline
Utilise la famille exec\newline
\begin{lstlisting}
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
\end{lstlisting}
wait: attent la fin d'un des processus fils, retourne son PID et stocke dans l'entier pointé par status (si status est différent de NULL), le code de retour du processus fils (ce qui est renvoyé par la fonction main du processus fils).\par{}
waitpid: comme wait mais attend uniquement le processus fils précisé par le paramètre PID. Le paramètre entier \guillemotleft{}option\guillemotright{} permet de rentre l'appel à waitpid non bloquant avec la constante WNOHANG, si cela n'est pas nécessaire, on utilise la valeur 0.
\par{}
Pour savoir si le processus c'est terminé correctement, on utile la macro WIFEXITED(status) qui renvoie vrai si le processus fils s'est terminé normalement.\newline
Ensuite pour avoir le résultat que le processus a renvoyé (ce que son main a renvoyé), on utilise la macro WEXITSTATUS(status).\newpage
Et voilà, encore bien sur un exemple:
\begin{lstlisting}
/* CELEBW02
The following function suspends the calling process using &waitpid.
until a child process ends.
*/
#define _POSIX_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
main() {
pid_t pid;
time_t t;
int status;
if ((pid = fork()) < 0)
perror("fork() error");
else if (pid == 0) {
sleep(5);
exit(1);
}
else do {
if ((pid = waitpid(pid, &status, WNOHANG)) == -1)
perror("wait() error");
else if (pid == 0) {
time(&t);
printf("child is still running at %s", ctime(&t));
sleep(1);
}
else {
if (WIFEXITED(status))
printf("child exited with status of %d\n", WEXITSTATUS(status));
else puts("child did not exit successfully");
}
}
while (pid == 0);
}
\end{lstlisting}
Output:\newline
child is still running at Mon Jan 8 11:05:43 2018\newline
child is still running at Mon Jan 8 11:05:44 2018\newline
child is still running at Mon Jan 8 11:05:45 2018\newline
child is still running at Mon Jan 8 11:05:46 2018\newline
child is still running at Mon Jan 8 11:05:47 2018\newline
child is still running at Mon Jan 8 11:05:48 2018\newline
child is still running at Mon Jan 8 11:05:49 2018\newline
child exited with status of 1\newline
\href{https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.1.0/com.ibm.zos.v2r1.bpxbd00/rtwaip.htm}{Liens de l'exemple avec des explications plus poussées (anglais)}
\subsection{Les pipes}
Les pipes permettent a deux processus sur la même machine de communiquer de manière unidirictionnels entre eux. (pour pouvoir communiquer entre des machines différentes, on va utiliser des serveurs, mais on verra ça plus tard).\newline
Il en existe deux type:
\begin{enumerate}
\item anonymes: communication parentale, en famille quoi (père -> fils; fils -> fils)
\item nommés: utilisés pour la communication entre deux processus indépendants (pas de lien de parenté direct entre les deux), du coup il faudras le nommé d'où le nom.
\end{enumerate}
\subsubsection{Comment les créer?}
\textbf{Les pipes anonymes:}
\begin{lstlisting}
#include <unistd.h>
int pipe(int fd[2]);
\end{lstlisting}
C'est quoi tout ça?\newline
\begin{description}
\item L'appel à pipe crée une paire de \guillemotleft{}file descriptors\guillemotright{} stockée dans fd.\newline
fd[0] sert à la lecture du pipe et fd[1] sert à l'écriture dans le pipe.
\item La fonction pipe renvoie 0 si elle réussie, autre chose sinon
\item Pour établir une communication (unidirectionelle!) à l'aide d'un pipe, il faut que le processus père crée un pipe avant de faire un (ou plusieurs) appel(s) à fork (sinon l'autre processus ne pourras pas connaître l'entré ou la sortie du pipe). Dès lors, après le fork, le processus père et le(s) processus fils peuvent accéder aux pipe en utilisant fd.
\item Pour une communication bidirectionelle, il faut bien sur créer deux pipes.
\end{description}
Pour écrire dans un tube (ce n'est pas dans les tp, mais ça me semble plutôt utile):
\begin{lstlisting}
ssize_t write(int entreeTube, const void *elementAEcrire, size_t nombreOctetsAEcrire);
\end{lstlisting}
La fonction prend en paramètre l'entrée du tube (on lui enverra fd[1]), un pointeur générique vers la mémoire contenant l'élément à écrire, ainsi que le nombre d'octets de cet élément.\newline
Elle renvoie une valeur de type ssize\textunderscore{}t correspondant au nombre d'octets effectivement écrits.\par
Et pour lire dedans:
\begin{lstlisting}
ssize_t read(int sortieTube, void *elementALire, size_t nombreOctetsALire);