0% ont trouvé ce document utile (0 vote)
4 vues72 pages

Notes de cours CSC209 : C et programmation

Les notes de CSC209 couvrent divers aspects de la programmation en C, y compris l'entrée/sortie, la compilation, les types de données, les structures de contrôle, les fonctions, et la gestion de la mémoire. Chaque section aborde des concepts clés et des exemples pratiques pour aider à la compréhension des principes fondamentaux du langage C. Le document est structuré en chapitres détaillés, facilitant l'apprentissage progressif des compétences en programmation.

Transféré par

ScribdTranslations
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
4 vues72 pages

Notes de cours CSC209 : C et programmation

Les notes de CSC209 couvrent divers aspects de la programmation en C, y compris l'entrée/sortie, la compilation, les types de données, les structures de contrôle, les fonctions, et la gestion de la mémoire. Chaque section aborde des concepts clés et des exemples pratiques pour aider à la compréhension des principes fondamentaux du langage C. Le document est structuré en chapitres détaillés, facilitant l'apprentissage progressif des compétences en programmation.

Transféré par

ScribdTranslations
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Notes de CSC209

Jenci Wei
Hiver 2022

1
Contenu
1 Entrée Sortie et compilation 5
1.1 Printing Values (printf) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2 Compiling À partir de la ligne de commande . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3 Reading Entrée (scanf) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2 Types Vavariables et instructions d'affectation 7


2.1 Programme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 Variables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.3 Utilisation Variables dans les expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.4 Double Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.5 Programming Style. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.6 Stockage Characters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

3 booléens Expressions et Conditionnelles 10


3.1 Si Déclarations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.2 Conditionnel Opérateurs10
3.3 Structuration Instructions conditionnelles 11
3.4 Mise en œuvre of Rational and Conditional Operators . . . . . . . . . . . . . . . . . . . . . . 11

4Functions 12
4.1 Calling une fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.2 Écriture Fonctions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.3 Fonction Exécution. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

5 itération 14
5.1 Pour Boucles14
5.2 Pendant Boucles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
5.3 Pause et Continuer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

6 Types et conversions de type 15


6.1 Numérique Types15
6.2 Casting15

7 Tableaux 16
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
7.1 Introduction
7.2 Accès Éléments de tableau 16

8 points 18
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
8.1 Introduction
8.2 Attribution à des pointeurs déréférencés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
8.3 Pointers as Parameters to Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
8.4 Pointeur Arithmétique. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
8.5 Pointeur aux pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

9 °C Modèle de mémoire 21
9.1 Code et segments de pile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
9.2 Tas et Segments Mondiaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

Page 2
10 Mémoire Dynamique 22
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
10.1 Introduction
10.2 Libération Mémoire allouée dynamiquement 22
10.3 Retour Une adresse avec un pointeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
10.4 Imbriqué Structures de données . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

11 Arguments de ligne de commande 24


11.1 Conversion Chaînes en entiers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
11.2 Ligne de commande Arguments24

12 Cords 25
12.1 Introduction.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
12.2 Initialisation Chaînes et littéraux de chaînes .......................... 25
12.3 Taille et Longueur. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
12.4 Copie Chaines26
12,5 Concaténation Chansons. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
12.6 Recherche Avec des cordes27

13 Structures 28
13.1 Intro .............................................. 28
13.2 Utilisation Structures dans les Fonctions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

14 Structures liées et itération 30


14.1 Intro 30
14.2 Parcours une liste 30

15 Flux 32
15.1 Introduction.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
15.2 Redirection 32

16 Fichiers 33
16.1 Introduction33
16.2 Lecture Depuis les fichiers33
16.3 Le fscanfFonction33
16.4 Écriture vers les fichiers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

17 Entrées/Sorties de bas niveau 35


17.1 Binaire Fichiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
17.2 Écriture Fichiers binaires 35
17.3 Lecture Fichiers binaires35
17,4wavFichiers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
17.5 Déménagement Autour des fichiers 36

18 Compilation 37
18.1 Le Chaîne d'outils de compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
18.2 En-tête Fichiers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
18.3 En-tête Variables de fichier38
18.4 Fichiers Make . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

19 Utiles CFeatures 41
19.1 Type défini 41
19.2 Macros 41

20 Le préprocesseur C 43

Page 3
21FuPointeurs de fonction 45

22 Appel système 46

23 Erreurs et Errno 47

24 processus 48
24.1 Processus Modèles48
24.2 Création Processus avec Fork . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
24.3 Processus Relation et Résiliation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
24.4 Zombies and Orphans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
24,5 Course Différents programmes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

25 Tuyaux 52
25.1 Non tamponné I/O52
aux tuyaux. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
25.2 Introduction
25.3 Concurrence et des tuyaux. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
25.4 Redirection Entrée et Sortie avecdup2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

26 Signaux 54
26.1 Introduction.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
26.2 Signal Manipulation54

27 Manipulation des bits et drapeaux 56


Opérations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
27.1 Opérateur binaire
27.2 Équipe Opérateurs56
27,3 bits Manipulation et Drapeaux 56
27,4 bits Vecteurs57

28 Multiplexage I/O 59
28.1 Le Problème avec les lectures bloquantes 59
28,2sélectionner59

29 Prises 60
29.1 Intro 60
29.2 Prise Configuration. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
29.3 Cadre Établir une connexion63
29.4 Douille Communication. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

30 Programmation Shell 67

Page 4
1 Entrée, Sortie et Compilation
1.1 Printing Values (printf)
Tobeabelotusepnrif:taddthesatndardnipu-otuptutlbirary

#include <stdio.h>

Pour imprimer du texte à l'écran :

printf("Bonjour, le monde !\n");

•Guillemets autour de la chaîne


•La chaîne se termine par

Pour inclure un int dans printf :

intn = 50;
printf("%d\n", n);// 50

•%dis un spécifieur de format

•Le nombre de paramètres après la chaîne doit être égal au nombre de spécificateurs de format

To include a floating point number insideprintf:

doublen = 1.0 / 3.0;


printf("%f\n", n);// 0.333333
printf("%.3f\n");// 0.333

1.2 Compilation depuis la ligne de commande


Pour compiler hello.c, nous tapons gcc hello.c dans la ligne de commande

L'exécutable produit par gcc est enregistré sous [Link]

•To execute the program, type./[Link]


Deux façons de déterminer quels fichiers sont exécutables :

1. Listez les fichiers en utilisant ls -F, et les fichiers exécutables auront un astérisque à la fin, par exemple [Link]*

2. Listez les fichiers en utilisant ls -l, et les fichiers exécutables auront x comme permission d'exécution

Options pour gcc


•gcc -Wall affiche des messages d'avertissement supplémentaires

•gcc -o permet de spécifier le nom du fichier exécutable (c'est-à-dire au lieu de [Link])

•Par exemple : gcc -Wall -o hello hello.c

Page 5
1.3 Lecture de l'entrée (scanf)
Incluez la bibliothèque standard io pour pouvoir utiliser scanf :

#include <stdio.h>

les spécificateurs de format scanftakes

doublen
scanf("%lf", &n);

%lf : flottant long

Le nombre de paramètres doit correspondre au nombre de spécificateurs de format.

•donne l'adresse mémoire de la variable n, puis scanf place la valeur que l'utilisateur saisit dans celle-ci
emplacement
•Peut avoir plusieurs spécificateurs de format dans un appel ascanf

Page 6
2 Types, Variables et Instructions d'Attribution
2.1 Program
Programme : Une séquence d'instructions pour un ordinateur

Exemple d'un programme :

#include <stdio.h>

intmain() {
printf("Bonjour le monde!");
retour0;
}

•int main(): la fonction principale est exécutée lorsque nous exécutons un programme

•Le corps de la fonction est à l'intérieur des accolades

•printf("Bonjour, le monde!") affiche la chaîne littérale à l'intérieur des guillemets

Un point-virgule ; signifie la fin d'une instruction


•return 0 : l'instruction de retour indique à l'ordinateur de terminer l'exécution de la fonction

•#include <stdio.h> spécifie que nous voulons que notre programme puisse utiliser l'entrée et la sortie

–Cette ligne n'est pas une instruction, elle ajoute du code d'un autre fichier à notre code
Rend des instructions telles que printf disponibles pour notre programme.
Les instructions sont envoyées au processeur de l'ordinateur de manière séquentielle dans l'ordre spécifié par notre code

Compilateur : convertit le code C en instructions machines que l'ordinateur peut comprendre


Le code C doit être compilé avant que nous puissions l'exécuter.

2.2 Variables
L'information est stockée dans la mémoire, qui a des millions de « cellules », et chaque cellule a une adresse et contient un
information
Un programme stocke une valeur en réservant une petite section de mémoire et en lui donnant un nom

•Variable : un morceau de mémoire nommé, ou en d'autres termes, un espace réservé pour ce morceau de mémoire

Création d'une variable : déclaration de l'instruction usevariable

intn;

•D'abord spécifiez le type, puis spécifiez le nom


•Le nom de la variable peut contenir des lettres, des chiffres et des underscores, ne peut pas commencer par un chiffre, et est sensible à la casse.

sensible
Donner une valeur à une variable : utiliser une déclaration d'assignation

n = 200;

Page 7
Prenez la valeur du Membre de Droite et stockez-la dans la variable du Membre de Gauche

•Le symbole égal = en C est l'opérateur d'assignation

–Différent de ==, qui est le symbole d'égalité mathématique

Expressions : façons d'effectuer des calculs, en utilisant des opérateurs arithmétiques et logiques

2.3 Utiliser des variables dans les expressions

Opérations arithmétiques en C :
addition +
soustraction -
multiplication *
division /
modulo %

La priorité des opérateurs (c'est-à-dire 'BEDMAS') s'applique

Par exemple.

intx, y;
x = 2;// 2
y = (x + 2) * (x + 5); // 28

2.4 Variables doubles


Le type de données double peut stocker des résultats en virgule flottante

doublen = 32;// 32
n = 99.5;// 99.5

Si je devais être un entier, la deuxième ligne serait évaluée à 99

•le double a une précision limitée

La division entre deux entiers donne un entier

doubleq = 9 / 4 ; // 2.00000 car le côté droit donne 2 et est converti en double

L'opérateur modulo % donne le reste d'une opération de division entière.

intn = 9 % 4;// 1

2.5 Style de programmation


Les opérateurs doivent avoir de l'espace de chaque côté

•Les déclarations doivent être sur des lignes séparées

Noms de variables significatifs

snake_case
camelCase

Page 8
Évitez les longues files d'attente

Si nous utilisons une deuxième ligne pour une déclaration, ajoutons une indentation.

Commentaire
Les commentaires ne sont pas exécutés comme des instructions.

/* Comment qui peut avoir


multiple
lignes */

// Commentaire en ligne

2.6 Stockage des caractères


Un caractère peut être stocké dans le type de données inchardata.

charc

Deux façons d'assigner


1. Attribution à un caractère entouré de simples apostrophes

2. Attribuer une valeur numérique


Chaque personnage se voit attribuer un numéro
La plupart des ordinateurs utilisent l'ASCII, qui attribue à chaque caractère une valeur dans la plage de 0 à 255.

charc =un;//a
c = 98;//'b'
c = c + 1;//'c'

Page 9
3 Expressions booléennes et conditionnels
3.1 Instructions conditionnelles

Bloc if-else
•Si la condition est vraie, le bloc if est exécuté
Si la condition est fausse, le bloc else est exécuté

si(3 > 0) {
printf("1");
}else{
printf("2");
}

Opérateurs de comparaison
moins que <
plus grand que >
égal à ==
supérieur ou égal à >=
moins que ou égal à <=
pas égal à !=

3.2 Opérateurs conditionnels


Opérateur logique ET
•&&
• Évalue à vrai lorsque le gauche et le droit de l'opérateur sont tous deux vrais

si(gpa >= 0.0 && gpa <= 4.0) {


printf("La moyenne pondérée cumulative est valide\n");
}

Opérateur logique OU
•||
•Évalue vrai quand au moins un côté de l'opérateur est vrai

si(gpa1 == 4.0 || gpa2 == 4.0) {


printf("Un ou les deux GPA sont de 4.0");
}

Opérateur NON
•!
•Négation de la condition

si(!(gpa < 0.0 || gpa > 4.0)) {


printf("Le GPA est valide\n");
}

Logical/Boolean operators:&&, ||, !


Relational operators:<, <=, >, >=, !=

Page 10
3.3 Structuration des instructions If
Si seulement le bloc (pas de bloc else)

Si la condition est fausse, ignorer ce code et passer à autre chose.

Instruction if imbriquée

•Instruction if à l'intérieur d'une instruction if

Sinon si
•Comporte de la même manière qu'une instruction if imbriquée, mais plus lisible

Les conditions sont vérifiées une à une en commençant par le haut

•La première fois qu'une condition est vraie, le bloc correspondant est exécuté, puis plus aucune condition.
sont vérifiés

si(gpa == 4.0) {
printf("A+ ou A\n");
} else if (gpa >= 3.7) {
printf("A-\n");
}else{
printf("Pas A\n");
}

3.4 Mise en œuvre des opérateurs rationnels et conditionnels


Les opérateurs relationnels retournent 1 pour vrai et 0 pour faux

intx, y, z;
x = 4 < 5; // vrai
y = 5 < 4; // faux
z = 2 < 3 || 5 < 4;// vrai
printf("%d %d %d\n", x, y, z);// 1 0 1

Toute valeur numérique excepté 0 est considérée comme vraie

si(0) {
printf("0\n");
} // ne s'exécutera pas
si(1) {
printf("1\n");
}
si(2) {
printf("2\n");
}// 2

Page 11
4Fonctions
4.1 Appeler une fonction
Pour utiliser une fonction, nous l'appelons en utilisant son nom, suivi d'un ensemble de parenthèses. À l'intérieur des parenthèses, nous
fournir les arguments (entrées) à la fonction.

Il faut dire à l'ordinateur d'où provient une fonction.


•Par exemple, pour utiliser fmax, nous avons besoin d'#include <math.h>

fmax retourne la plus grande des deux valeurs

doublelarger_num = fmax(2, 3);// 3

printf imprime le premier argument et insère les valeurs des arguments suivants aux spécificateurs de format
dans le premier argument

Le plus grand nombre est %f

Nous pouvons fournir des expressions en arguments de fonction, par exemple.

doublelarger_num = fmax(2 * 8.1, 10 * 19.177);// 191.770000

•Peut avoir des variables

•Peut imbriquer des appels de fonctions

4.2 Écriture de fonctions


Tout d'abord, déclarez la fonction comme un prototype de fonction (déclaration de fonction)

Le prototype comprend un nom, un type de retour et une liste d'arguments.

•Function signature: combination of the name, return type, and list of argument types

Par exemple.

doublemy_fmax(doublex, doubley);

Le nom de chaque argument (c'est-à-dire xandy) peut être omis dans la déclaration de la fonction

Définissez ensuite la fonction, c'est-à-dire écrivez les instructions qui seront exécutées lorsque nous appellerons la fonction

•En-tête de définition de fonction similaire à un prototype de fonction

–Suivi par des parenthèses au lieu d'un point-virgule


Les paramètres doivent avoir des noms

•Documenter ce que la fonction fera en utilisant des commentaires

•À l'intérieur des accolades se trouve le corps de la fonction

Les paramètres dans l'en-tête sont des variables

•Instruction de retour qui renvoie la valeur correcte

Page 12
/* Renvoie la plus grande des deux valeurs données, x ou y. */
doublemy_fmax(doublex,doubley) {
si(x > y) {
returnx;
}else{
retour;
}
}

Alternativement, nous pouvons déclarer et définir la fonction en même temps avant de l'utiliser.

•Pour ne pas avoir à écrire un prototype de fonction séparé


• Analogue à la déclaration d'une variable et à son initialisation en même temps

4.3 Exécution de Fonction


Lorsqu'un programme C commence à s'exécuter

La fonction principale est exécutée


•Les déclarations de fonction et #include sont des instructions pour le compilateur, et ne sont pas exécutées

Le cadre de pile pour la fonction principale est empilé sur la pile

2. Un appel de fonction est atteint

Son cadre de pile est poussé sur la pile


Les variables introduites dans la fonction n'existent que dans le cadre de cette fonction, c'est-à-dire créées
lorsque la fonction est appelée et désallouée lorsque la fonction se termine
–These variables arelocalto the function
Ces variables vont dans la pile d'exécution pour la fonction
La fonction renvoie la valeur à celui qui l'a appelée, puis la pile de l'exécution pour cette fonction.
est retiré de la pile, et toutes les variables locales sont désallouées
•Lorsque nous utilisons une variable comme argument d'une fonction :

Le programme obtiendra la valeur de la variable, puis copiera cette valeur dans l'espace.
alloué pour les paramètres
Passer par valeur
Nous avons deux ensembles de variables : l'un provenant de la fonction principale et l'autre de la fonction appelée.
Les deux ensembles sont complètement séparés l'un de l'autre

Page 13
5 Itération
5.1 Boucles For
Structure de boucle For :

pour(INITIALISATION; CONDITION; MISE À JOUR) {


CORPS DE LA BOUCLE

La section d'initialisation est exécutée avant le début de la boucle.

–Habituellement utilisé pour définir une variable qui est mise à jour à travers la boucle

La section de mise à jour s'exécute après chaque itération de la boucle.

La condition est vérifiée avant que le corps de la boucle ne soit exécuté

Si la condition est vraie, alors le corps de la boucle s'exécute


–Si c'est faux, alors la boucle se termine
Les boucles peuvent être imbriquées

Les variables de boucle ne devraient pas avoir le même nom

5.2 Boucles While


Structure de boucle while :

tant que(CONDITION) {
CORPS DE LA BOUCLE

•La condition se comporte de la même manière que la boucle for

•Si la condition est évaluée à vrai, alors le corps de la boucle s'évalue à nouveau

Structure de boucle do-while :

faire{
CORPS DE LA BOUCLE

}tant que(CONDITION)

Le corps de la boucle est exécuté avant que la condition ne soit vérifiée

5.3 Interrompre et Continuer


break termine l'itération actuelle de la boucle
continuecauses the rest of the loop body to be skipped

Page 14
6 Types et Conversions de Type
6.1 Types numériques
Lorsque nous assignons une valeur double à un entier, la partie fractionnaire est éliminée ou tronquée.

doubled = 4.8;
inti = d;// 4

Lorsque nous assignons une valeur entière à un double, alors la valeur correcte peut être représentée.

inti = 17;
doublé = i;// 17.000000

sizeof donne combien d'octets sont utilisés par le compilateur pour une certaine variable

•La valeur résultante peut être imprimée avec le spécificateur de format %lu

•Par exemple, la taille d'un int est de 4, la taille d'un double est de 8

Le plus grand entier qu'un int peut représenter est la constante INT MAX

Si nous ajoutons à cette constante, le programme va entraîner un débordement.

Essayer de représenter un grand entier avec un flottant entraînera une perte de précision.

inti = 21247000000;
floatf = i;// 2147000064.000000

C'est une estimation de la valeur qu'il tente de représenter.

Si un type peut contenir toute valeur qu'un autre type peut représenter, alors le premier type est plus large que le second.

•Par exemple, un entier plus large qu'un caractère

Lorsqu'on convertit un type en un type plus large, on peut obtenir la valeur attendue.

Lorsqu'on convertit un type plus large en un autre type, les octets de haut ordre peuvent être perdus.

6.2 Casting
Lorsque nous effectuons une division sur deux entiers, le type du résultat est entier

inti = 5;
intj = 10;
doublek = i / j;// 0,000000 car l'int 5/10 évalue d'abord à 0

Façons de résoudre cela :

1. Change soit à double

[Link] un double avant la division

doublek = (double) i / j; // 0.500000

Page 15
7 Tableaux
7.1 Introduction

Pour déclarer un tableau, spécifiez le type, le nom, suivi de crochets.

floatgpa[10];

•All elements of the array must have the same type


•À l'intérieur des crochets, nous spécifions le nombre d'éléments dans le tableau

Nous pouvons accéder à un élément d'un tableau en fournissant le nom du tableau suivi de l'index spécifique.
accéder entre crochets

gpa[0] = 3.7;

Les indices commencent à 0

Nous assignons 3.7 au premier élément de gpa


Pas besoin de fournir le type
•“gpaat 0 est assigné 3.7”
Pour accéder aux valeurs du tableau, utilisez la même notation entre crochets.

printf("%f", gpa[0]); // 3.7

Utile depuis que l'index peut être une variable

7.2 Accéder aux éléments du tableau


Déclarer un tableau et l'initialiser avec des valeurs :

intarr[3] = {1, 2, 3};

Les adresses mémoire pour chaque élément de ce tableau sont séparées par 4 octets.

Lorsque ce tableau est déclaré, le compilateur réserve 12 octets contigus.

L'espace pour un tableau est alloué lorsque le tableau est déclaré, et tous les éléments sont alloués en un seul
lieu
Par conséquent, les tableaux ne peuvent pas changer de taille.

Peut alternativement créer un nouvel array plus grand et copier tous les éléments de l'array original dedans.
Une fois que nous connaissons l'adresse où le tableau commence et la taille de chaque élément, nous pouvons calculer le
adresse de chaque élément
L'adresse du tableau est l'adresse de l'élément-0
–Prenant le dernier exemple :

adresse de arr[1]
= adresse de arr + 4
= adresse de arr + 1 *sizeof(int)

Page 16
–En général:

Adresse de arr[i] = adresse de arr + (i * taille d'un élément de arr)

Si nous tentons d'accéder à un élément du tableau dont l'indice dépasse la taille du tableau, nous obtenons
quelque chose d'inattendu

C ne vérifie généralement pas si un accès au tableau est dans les limites du tableau
Nous obtenons ce qui est conservé dans la mémoire après la fin du tableau, cela pourrait être n'importe quelles données.

•Si nous essayons d'assigner ce qui est en dehors d'un tableau à une valeur, cela pourrait remplacer la valeur d'un autre
la variable est utilisée, ou provoquer un défaut de segmentation (c'est-à-dire que l'adresse accédée n'était pas légale)

Page 17
8 Indications
8.1 Introduction

Pour accéder à l'adresse d'un objet, utilisez l'opérateur commercial &.

inti = 5;
printf("Adresse de i : %p\n", &i)

Si une variable est un pointeur, alors sa valeur est une adresse mémoire

Lorsqu'on déclare un pointeur, nous devons spécifier le type de la valeur stockée à cette adresse mémoire.

int*pt;
pt = &i;

ptis est une variable qui contiendra l'adresse d'un int


Le type de ptis « int star » ou « pointeur vers int »

•En utilisant l'opérateur &, l'adresse de i est assignée à p.

ptpoints toi

Lorsque l'opérateur * est appliqué à un pointeur, il s'évalue à la valeur de la mémoire à laquelle le pointeur pointe.
à

printf("%d\n", *pt);// 5

•Déréférencer le pointeur

*inside a declaration is part of the type,*inside an expression is the dereference operator

8.2 Attribution aux pointeurs désindirigés


Lorsque un pointeur déséréférencé est du côté gauche d'une instruction d'affectation, la valeur du côté droit devrait être
stocké dans l'emplacement auquel le pointeur fait référence, et non pas dans le pointeur lui-même

Le pointeur ne change pas, mais la valeur à laquelle il pointe change.

La variable originale et le pointeur déséréférencé sont des alias.

inti = 7;
int*pt = &i;// i = *pt = 7
*pt = 9;// i = *pt = 9

•*pt = *pt + 1 fait la même action que asi = i + 1

int *pt = q; est équivalent à int *pt; pt = q;


•Notint *pt; *pt = q;

Page 18
8.3 Pointers as Parameters to Functions
Les variables de fonction sont des variables locales

Changer cela n'a aucun effet sur l'argument qui lui a donné la valeur initiale.

Pour muter la valeur, avoir un pointeur comme argument


Déréférencer le pointeur pour modifier la valeur

Utilisez des parenthèses pour vous assurer que l'ordre des opérations est conforme à vos souhaits.

Lorsque nous avons l'intention de passer un tableau à une fonction, nous pourrions déclarer int sum(int arr[]) ou int sum(int
*arr)
•Ce dernier représente mieux ce que fait le compilateur

•If the size of the array is not fixed, the size should also be passed in as a parameter
–Puisque l'utilisation de sizeof sur le tableau donne la taille du pointeur

8.4 Arithmétique des pointeurs


Lorsque nous ajoutons un entier à un pointeur dont le type a des tailles, le résultat est une adresse plus grande que l'originale.
pointeur par octets

taper k;
type *p = &k;
intn;
p = p + n; // p + (sizeof(type) * n)

Avec cela, nous pourrions accéder aux éléments du tableau en utilisant l'arithmétique des pointeurs

intarr[3] = {1, 4, 9};


int*p = arr;
printf("%d %d\n", *p, *(p + 1));// 1 4

Nous pourrions également traiter un pointeur comme un tableau

printf("%d %d\n", p[0], p[1]);// 1 4

•p[k] == *(p + k)

8.5 Pointeur vers des pointeurs


Nous pourrions stocker l'adresse d'un pointeur

inti = 1;
int*pt = &i;
int**pt_ptr = &pt;

•pthas typeint*, donc pt ptr devrait avoir le typeint**, c'est-à-dire un pointeur versint*

Lorsque nous désinférons ptr, nous obtenons le type int*

Pour obtenir la valeur originale, déréférencer le pointeur deux fois.

Page 19
int*r = *pt_ptr;// &i
intk = **pt_ptr;// 1, équivalent à int k = *r

Nous pourrions avoir des pointeurs de ordre supérieur

int***pt_ptr_ptr = &pt_ptr;
intx = ***pt_ptr_ptr;// 1

Page 20
Modèle de mémoire C 9

9.1 Segments de code et de pile


La mémoire peut être considérée comme un tableau qui stocke toutes les données

•Ce tableau de mémoire est divisé en segments, où chaque segment stocke un type particulier de données.
•Couches :
Tampon
Code
Données mondiales
Tas
Pile
SO
Une fois que le code est compilé, il est stocké dans le segment de code de la mémoire.
•Au fur et à mesure que le code s'exécute, il appelle diverses fonctions.

•Each function invocation is allocated space in the stack segment to store local variables
Le segment de pile
L'appel de fonction le plus récent est en haut de la pile
Les fonctions sont supprimées dans l'ordre dernier entré, premier sorti.

L'espace pour les fonctions est alloué sous forme de frames de pile, qui ont suffisamment de mémoire pour stocker toutes les variables locales.

variables
Une fois qu'une fonction a terminé son exécution, nous retirons son cadre de pile de la pile et retournons une valeur à
l'appelant
Le sommet de la pile est toujours la fonction actuellement en cours d'exécution.

9.2 Segments de tas et globaux


Si nous assignons une variable en dehors de main, alors c'est une variable globale qui existe partout.
Les variables globales sont stockées dans le segment des variables globales

•Pas connecté à une fonction particulière


Le segment de données global contient également d'autres valeurs :

•Littéraux de chaîne, par exemple, lorsque nous écrivons char *ptr = "Hi", alors "Hi" est stocké dans le segment de données global.

malloc nous permet d'allouer de la mémoire pendant que le programme s'exécute

•Ceci est de la mémoire allouée dynamiquement

Les données allouées dynamiquement sont stockées dans le segment heap

Chaque fois que nous libérons un morceau de mémoire alloué, il est marqué comme étant disponible pour allocation.

La pile et le tas ont des tailles maximales


Si un programme dépasse la taille maximale de la pile ou du tas, il y aura une erreur de mémoire insuffisante, ou
ENOMEM
Si nous tentons d'accéder à la mémoire du système d'exploitation, nous obtiendrons une faute de segmentation, ou segfault.

•Par exemple, tenter d'accéder à la mémoire du système d'exploitation

•Par exemple, accéder à une variable ou un pointeur non initialisé, qui pointe vers l'adresse zéro

Page 21
10 Mémoire Dynamique
10.1 Intro
À l'intérieur d'une fonction, nous pouvons allouer de l'espace pour des variables sur le tas afin qu'elles puissent durer au-delà du retour
déclaration des fonctions dans lesquelles elles sont déclarées

La fonction malloc alloue de la mémoire sur le tas

void*malloc(size_t taille);

Le paramètre de taille indique combien d'octets de mémoire doivent être alloués


Son type est size t, qui est un type retourné par sizeof
–size est un unsigned int
Renvoie un pointeur qui contient l'adresse de la mémoire allouée par malloc sur le tas
Un pointeur nul est utilisé pour retourner un pointeur de type générique
Lorsque nous stockons cette adresse dans un pointeur, nous aurions besoin d'un type explicite pour le pointeur, par exemple.

int*ptr = malloc(sizeof(int));

La mémoire heap reste disponible jusqu'à ce que le programmeur l'attribue explicitement.

10.2 Libération de la mémoire allouée dynamiquement


Si la mémoire dynamique est allouée dans une fonction et que l'adresse n'est pas renvoyée, nous n'aurions aucun moyen d'y accéder.
encore une telle mémoire de tas car nous n'avons pas de pointeur pour cela

Cela s'appelle une fuite de mémoire

•Si la fuite de mémoire continue, le programme rencontrera finalement une erreur hors mémoire : ENOMEM

Pour désallouer de la mémoire, utilisez la fonction free

voidlibérer(void* ptr);

Cela désalloue l'intégralité du bloc qui a été alloué dans l'appel malloc qui a renvoyé cette adresse.

Un pointeur qui pointe vers une mémoire qui a déjà été libérée est un pointeur pendu.

Ce pointeur est dangereux

10.3 Retourner une adresse avec un pointeur


Lorsqu'un pointeur est passé dans une fonction en tant que paramètre, la fonction crée une variable de pointeur locale.

Lorsque nous modifions ce pointeur dans la fonction, le pointeur à l'extérieur n'est pas affecté.

Dans le cas où nous voulons modifier un pointeur, nous passons un pointeur de deuxième niveau à la fonction

Page 22
10.4 Structures de données imbriquées
Lorsque nous avons un tableau imbriqué dans le tas, par exemple

int**pointers = malloc(sizeof(int*) * 2);


pointers[0] = malloc(sizeof(int));
pointeurs[1] = malloc(sizeof(int) * 3);

nous devons être prudents lorsque nous libérons de la mémoire

Il faut d'abord libérer les pointeurs internes.

•Si nous libérons d'abord le pointeur extérieur, alors les pointeurs intérieurs deviennent des pointeurs pendants.

Page 23
11 Arguments de ligne de commande

11.1 Conversion des chaînes en entiers


Les chaînes C sont des tableaux spéciaux d'éléments de caractères

•Nous pouvons déclarer et initialiser une variable de chaîne littérale directement :

char*s ="bonjour le monde";

Nous pouvons avoir une chaîne de chiffres :

char*s ="17";

qui peut être interprété comme un tableau de caractères : un, sept

Nous pouvons utiliser la fonction strtol pour convertir une chaîne en l'entier qu'elle représente.

•Syntax:

long int strtol(const char* str, char** endptr, int base);

–Premier paramètre : la chaîne que nous voulons convertir


–Deuxième paramètre : supposons qu'il y ait des caractères superflus dans la chaîne, ce paramètre
indique où commence le morceau « restant » de la chaîne
–Troisième paramètre : la base du système numérique (généralement 10)

•Peut gérer les espaces de début et le signe plus/moins de début

11.2 Arguments de ligne de commande


main peut avoir deux arguments

intmain(intargc,char**argv)

argcholds le nombre d'arguments de ligne de commande

Ceci est toujours un de plus que le nombre d'arguments passés en.


Le premier argument est le nom du programme.
Les autres éléments du tableau sont les arguments de la ligne de commande

•argv : vecteur d'arguments, stocke un tableau de chaînes de caractères

Les arguments de ligne de commande sont toujours des chaînes.

–Utilisez strtol si nous voulons les utiliser comme des entiers

Page 24
12 cordes
12.1 Introduction

Avantages des chaînes par rapport aux tableaux de caractères

Peut manipuler le contenu sans utiliser de boucles explicites

•Ne voudrait imprimer rien d'autre qui suit le texte stocké en mémoire
Chaîne C : un tableau de caractères qui a un caractère nul immédiatement après le dernier caractère du texte

Le caractère nul marque la fin du texte

Le caractère nul est écrit comme '\0'


Pour imprimer une chaîne, utilisez le spécificateur de format %s

12.2 Initializing Strings and String Literals


Nous pouvons fournir un initialiseur de tableau avec chacun des caractères de la chaîne, par exemple.

chartext[20] = {'h', e, l', l', 'o', \u0000};

•Produit un tableau avec les cinq premiers caractères contenant "bonjour" et les caractères restants comme null
personnages

Nous pouvons également donner les caractères de la chaîne entre guillemets, par exemple

chartext[20] ="hello";

•Abréviation de la méthode précédente


Pour éviter les bogues, le nombre de caractères doit être strictement inférieur à la taille spécifiée.

Nous pouvons également omettre la taille de la chaîne, par exemple.

chartext[] ="hello";

Le compilateur allouera une taille de mémoire égale à la longueur de la chaîne plus un caractère supplémentaire.
pour le terminateur nul

Une fois que le tableau est déclaré, sa taille est fixe

Pour créer un littéral de chaîne, utilisez la notation de pointeur au lieu de la notation de tableau :

bonjour

"bonjour" est une littérale de chaîne, qui est une constante qui ne peut pas être modifiée

•texte pointe au premier caractère de cette chaîne

Page 25
12.3 Taille et Longueur
L'utilisation de sizeof sur une chaîne donne le nombre d'octets occupés par le tableau.

sizeof est une opération à la compilation, elle ne regarde pas le contenu


De nombreuses fonctions de chaîne C ont besoin du fichier d'en-tête chaîne :

#include <string.h>

strlen renvoie le nombre de caractères dans la chaîne, sans inclure le terminateur nul
Prototype pour strlen :

size_t strlen(const char*s)

•size est un type entier non signé qui peut être traité comme un entier

12.4 Copier des chaînes


Une chaîne peut être copiée en utilisant strcpy

Prototype:

char*strcpy(char*s1,const char*s2)

•Copie les caractères de s2 au début du tableau s1

s1 n'est pas requis d'être une chaîne, mais s2 doit être une chaîne
•strcpy est une fonction non sécurisée, c'est-à-dire que si nous avons s1 qui n'a pas assez de taille pour contenir le contenu de s2, différent
les machines pourraient produire des résultats différents

La version sûre de strcpy est strncpy

•Prototype :

char*strncpy(char*s1,const char*s2,intn)

•nindique le nombre maximum de caractères que cela peut contenir, y compris les caractères nuls.

• Pourrait encore être dangereux, puisque le premier caractère de s2 pourrait ne pas se terminer par un terminateur nul.

Pour garantir que cette fonction est sûre, nous ajoutons explicitement un caractère nul à la fin de s1

12.5 Concatenating Strings


Nous pouvons concaténer des chaînes en utilisant strcat

•Prototype:

char*strcat(char*s1,const char*s2)

Les deux s1 et s2 doivent être des chaînes de caractères

Ajoute s2 à la fin de s1

Page 26
•strcat est une fonction non sécurisée car s1 peut ne pas avoir assez d'espace pour stocker tout le contenu

Version sécurisée de strcatisstrncat

•Prototype:

char*strncat(char*s1,const char*s2,intn)

•nis le nombre maximum de caractères, sans inclure le terminateur nul, qui doit être copié à partir de
s2 à la fin de s1
•strncatalways ajoute un terminateur nul à s1
•habituellement réglé sur sizeof(s1) - strlen(s1) - 1

12.6 Recherche avec des chaînes


Recherche un caractère unique : usestrchr

Prototype :

char*strchr(const char*s,intc)

•sis la chaîne à rechercher, c'est le caractère à rechercher

•Renvoie le pointeur vers le caractère qui est trouvé, ou null si le caractère n'est pas trouvé
L'indice peut être déterminé par l'arithmétique des pointeurs

Rechercher une sous-chaîne : usestrstr

Prototype :

char*strstr(const char*s1,const char*s2)

• Si s2 est trouvé dans s1, alors retourne un pointeur vers le caractère de s1 qui commence la correspondance avec s2

Page 27
13 Structures
13.1 Introduction

Les structures, c'est-à-dire les structures, stockent des collections de données associées.

Différences entre les tableaux et les structures :


Tableau Structure
Données du même type Oui Pas nécessaire
Détails de la déclaration Type et nombre d'éléments (notation de tableau) Types de membres (mot-clé struct)
Accès via... Notation d'index Notation par points

Exemple de structure :

structétudiant {
charfirst_name[20];
charlast_name[20];
intyear;
gpa flottant;
};

Utilisez le mot-clé struct pour déclarer une structure

•Le tag de structure donne un nom au type de structure afin que nous puissions définir plus tard des variables de ce type

–Tag de structure dans l'exemple : étudiant

Le corps de la déclaration de la structure déclare les membres de la structure

Dans l'exemple, nous avons quatre membres

Nous pouvons déclarer des variables de type struct student, par exemple.

structstudent bon_étudiant;

strcpy(good_student.first_name, "Jo");
strcpy(good_student.last_name, "Smith");
good_student.year = 2;
good_student.gpa = 3.2;

•The wordstructis required whenever we declare a variable of a structure type


•Pour accéder aux membres des structures, utilisez le nom d'une variable de structure, un point, puis le nom de la structure
membre auquel nous voulons accéder

13.2 Utilisation des structures dans les fonctions

Lorsque nous passons une structure à une fonction, la fonction reçoit une copie de la structure.

Tout changement que la fonction effectue est seulement un changement sur la copie, pas sur la structure originale.

•Tout tableau à l'intérieur d'une structure est copié

Deux façons de conserver les modifications d'une structure par une fonction :

1. Faites en sorte que la fonction retourne la structure modifiée

Cette méthode copie la structure deux fois, c'est-à-dire lorsque la fonction est appelée et à l'instruction de retour.

Page 28
•Pas préférable car les structures peuvent être grandes

2. Passez un pointeur vers la structure en tant que paramètre

•Preferred since nothing is copied


•Pour accéder aux membres à l'aide d'un pointeur, d'abord déréférencez le pointeur, puis utilisez l'opérateur point.
accéder au membre, par exemple.(*p).gpa
•L'opérateur flèche est identique à la syntaxe ci-dessus en termes de signification, par exemple p->gpa

Page 29
Structures liées et itération
14.1 Intro
Différences entre les tableaux et les structures liées :

Tableau Structure liée


Mise en œuvre Intégré dans le langage C User-defined
Nécessite une fonction "traverse" pour
Accès et stockage Utilisez des indices pour récupérer et stocker
parcourir les éléments dans la structure
Taille Fixe Taille dynamique

Liste chaînée
•Stocke une séquence d'éléments

•Has a front pointer which holds the address of the first node in the list
•Has nodes which are analogous to elements of array
Chaque nœud contient les données stockées et le pointeur suivant, qui pointe vers le nœud suivant

Le pointeur suivant pour le dernier nœud a une valeur nulle

En C, les nœuds sont représentés à l'aide de structures.

structnode {
intvalue;
structnode *suivant;
};

14.2 Parcourir une liste


Création d'une liste chaînée

•Commencez à partir d'une liste vide, c'est-à-dire que le pointeur avant est nul

•Create nodes one at a time


•Allouer des nœuds sur le tas

structnode *node_x = malloc(sizeof(structnode));

Peut-on utiliser typedeft pour raccourcir le nom de type :

typedef structnode {
...
} Noeud;

Alors il ne serait pas nécessaire d'utiliser la structure node chaque fois que nous faisons référence à ce type.

•Au lieu de cela, nous pouvons utiliser Node

Parcourir la liste
•Commencez à l'avant puis suivez une trace de pointeurs suivants
La valeur nulle au pointeur du dernier nœud nous indique quand arrêter.

Page 30
•Modèle de parcours :

Node *curr = front;


tant que(curr != NULL) {
// some action
curr = curr->next;
}

Insertion d'un nœud :

1. Créer un nouveau nœud

2. Duplicate the link to the new node after the insertion point
3. Remplacez le lien original par le lien vers le nouveau nœud

Testing
•Avoir quatre cas : milieu, début, fin, index illégal
•Écrivez le code pour les trois derniers cas limites en conséquence

Page 31
15 Ruisseaux
15.1 Intro
flux d'entrée : source de données qui fournit des entrées à notre programme

•scanf lit depuis l'entrée standard, qui est un flux d'entrée réglé sur le clavier

Flux de sortie
•printf écrit sur la sortie standard, qui est un flux de sortie réglé sur l'écran
–Utilisé pour la sortie normale du programme

L'erreur standard est un flux de sortie référé à l'écran


–Utilisé pour la sortie d'erreur

L'entrée standard, la sortie standard et l'erreur standard s'ouvrent automatiquement lorsqu'un programme s'exécute

15.2 Redirection
Pour rediriger l'entrée standard, utilisez le <symbole lors de l'exécution du programme, par exemple ../[Link] < [Link]

•Nous redirigeons l'entrée standard pour lire à partir de [Link]


Pour rediriger la sortie standard, utilisez le symbole > lors de l'exécution du programme, par exemple ../[Link] > [Link]

•Si un fichier existe déjà, et que nous utilisons la redirection de sortie avec ce nom de fichier, alors ce fichier serait
écrasé
Limitation : un seul fichier peut être utilisé pour la redirection d'entrée ou de sortie

Page 32
16 Fichiers
16.1 Introduction

fopen ouvre un fichier et le rend disponible en tant que flux


Prototype :

FICHIER *fopen(const char*nom_de_fichier,const char*mode)

•moderequires une chaîne qui indique ce que nous voulons faire avec le fichier :
Mode chaîne Emplacement par défaut
r Fichier ouvert pour lecture
“w” Fichier ouvert en écriture
a Fichier ouvert en mode ajout
•Renvoie un pointeur de fichier que nous utiliserons lorsque nous voudrons fermer le fichier, lire à partir du fichier ou écrire à partir de
le fichier
• Si l'ouverture échoue, elle retourne null
Pour fermer un fichier, utilisez fclose

Passer le pointeur qui a été précédemment retourné par fopen


•Retourne 0 si l'appel a réussi, et une valeur non nulle si cela a échoué

16.2 Lecture à partir de fichiers

Lors de la lecture de texte ou de lignes complètes de données, utilisez fgets

Prototype :

char*fgets(char*s,intn, FILE *stream)

•stream est la source de données

•sis un pointeur où le texte peut être stocké


•Returnsson success, and returns null when failed
•nis le nombre maximum de caractères que fgets est autorisé à mettre dans s, y compris le terminator nul

La valeur est généralement le nombre de caractères souhaité + 1


Avant de lire quoi que ce soit, le curseur de fichier est avant le premier caractère du fichier.

•Après un appel réussi, le curseur de fichier se déplace au début de la ligne suivante


Pour lire depuis l'entrée standard, utilisez stdinasstream

16.3 La fonction fscanf


scanf ne peut lire que depuis l'entrée standard, tandis que fscanf peut lire depuis n'importe quel flux d'entrée

Prototype forfscanf:

intfscanf(FILE *flux, const char* format, ...)

Retourne le nombre d'éléments lus avec succès


• A un argument supplémentaire par rapport à scanf, à savoir le flux, qui indique de quel flux lire.

Page 33
16.4 Écriture dans des fichiers

Lorsque nous avons l'intention d'écrire dans un fichier, utilisez 'w' ou 'a' comme mode pour fopen.

•Mode’w’ supprime le fichier s'il existe déjà

•Mode’a’appends à la fin du fichier

fprintf est similaire à printf, mais permet de spécifier le flux où la sortie doit aller

Lorsque notre programme écrit dans un flux, il écrit d'abord dans le tampon de fichier, un emplacement en mémoire contrôlé par
le système d'exploitation

Cette mémoire est périodiquement écrite dans le fichier sur le disque

•Nous ne savons pas ce qui arrivera au contenu écrit lorsque l'ordinateur perdra de l'électricité en plein milieu.
du programme
L'entrée/sortie pour le débogage n'est pas recommandée pour la raison ci-dessus

Pour s'assurer que des modifications ont été apportées à un flux, utilisez fflush

•Prototype:

intfflush(*FICHIER *flux)

•Demandes que le système d'exploitation écrive toutes les modifications qui sont dans son tampon

Page 34
17 Entrées/Sorties à bas niveau

17.1 Fichiers binaires


Peut être ouvert avec fopen, mais ajouter au mode

•Par exemple fopen("[Link]", "rb");

Extensions pour les fichiers binaires : aucune extension, dat, jpg, mp3, etc.

Des fonctions comme fprintf, fgets, fscanf, etc. ne sont pas utiles pour les fichiers binaires, car les fichiers binaires n'ont pas de notion
de « ligne », et ces fonctions lisent et produisent du texte, pas de données binaires

17.2 Écriture de fichiers binaires


Utilisez fwrite pour écrire des données binaires dans un fichier

Prototype :

size_t fwrite(const void*ptr, size_t size, size_t nmemb, FILE *stream)

•ptris est un pointeur vers les données que nous voulons écrire dans le fichier

•taille est la taille de chaque élément que nous écrivons dans le fichier

•nmemb est le nombre d'éléments que nous écrivons dans le fichier (c'est-à-dire 1 pour une variable individuelle, ou un nombre)
d'éléments pour un tableau)
•stream est le pointeur de fichier vers lequel nous écrirons (doit se référer à un flux ouvert en mode binaire)

Renvoie le nombre d'éléments écrits avec succès dans le fichier, ou 0 en cas d'erreur

17.3 Lecture de fichiers binaires


Utilisez fread pour lire des données binaires

Prototype :

size_t fread(void*ptr, size_t size, size_t nmemb, FILE *stream)

•ptris est un pointeur vers la mémoire où les données du fichier seront stockées

•size est la taille d'un élément

•nmemb est le nombre d'éléments à lire


• le flux est le flux à partir duquel lire

Renvoie le nombre d'éléments lus avec succès à partir du fichier, ou 0 si aucun élément n'est lu avec succès.
Utiliser fwrite sur un ordinateur et tenter de fread sur un autre peut ne pas fonctionner correctement.

Différents ordinateurs peuvent représenter les données de différentes manières

Page 35
17.4fichiersWav
Avoir deux parties :

1. Header
44 bits de données
•Contains information about thewavfile, including parameters required to properly play the file
dans un programme de musique

2. Une ou plusieurs valeurs de deux octets

Chaque valeur de deux octets est un échantillon

Pour afficher des fichiers binaires, utilisez od, qui imprime les valeurs trouvées dans un fichier binaire.

c'est-à-dire

od -A d -j 44 -t d2 [Link]

•-A d traduit de la base-8 à la base-10


•-j 44 saute les 44 premiers octets du fichier (c'est-à-dire l'en-tête)

•-t d2 indique que le fichier se compose de valeurs de deux octets

17.5 Se déplacer dans les fichiers


Pour se déplacer dans un fichier, utilisez fseek

Chaque fichier ouvert maintient sa position actuelle


Chaque appel de lecture ou d'écriture déplace la position du fichier

•Cette position peut être changée par fseek

•Prototype pour fseek :

intfseek(FILE *flux,long decalage,int origine)

•l'offset est un compte d'octets indiquant de combien la position du fichier doit changer

•l'origine détermine comment le deuxième paramètre est interprété

–RECHERCHER SET: depuis le début du fichier


–RECHERCHER CUR : depuis la position actuelle du fichier

–RECHERCHER FIN : fin de fichier

• Avec une entrée invalide (par exemple, se déplacer vers une position négative), l'appel fseek peut réussir, mais un appel ultérieur
la tentative de lecture/écriture échouera

Pour se déplacer au début d'un fichier, utilisez rewind

•Prototype :

void rewind(FILE *stream)

Page 36
18 Compilation
18.1 La chaîne d'outils de compilation

compiler courir
Code source.c−−−−−−→exé[Link]−−−→Exécution du programme

Le compilateur fonctionne en 3 phases :

1. Front end
obtenirProchainJeton
Analyse lexicale )−−−−−−−−−−−−−−−−−−−−−−* Analyse syntaxique
NextToken
Le code source est préparé pour le front-end par le préprocesseur
•Traduit le code source en une représentation intermédiaire indépendante du langage
2. Milieu de gamme

AST
•Analyse syntaxique−−−→Analyse sémantique
Optimise le code
3. Arrière-plan

AST modifié Assemblée


•Analyse sémantique−−−−−−−−−−−→Génération de code−−−−−−−→Programme exécutable
•Traduire le langage intermédiaire en langage assembleur
Utilisez gcc -S pour compiler en langage d'assemblage

•Vous devez assembler le code d'assemblage en code objet, qui devient l'exécutable
Nous pouvons invoquer l'assembleur en utilisant la commande
–La sortie n'est pas lisible par un humain – c'est un fichier objet qui contient des instructions de code machine.
et données
–Besoin d'une étape supplémentaire pour produire l'exécutable : liaison

•L'éditeur de liens prend un ou plusieurs fichiers objets compilés et assemblés et les combine pour créer un
fichier au format exécutable
Le fichier exécutable final est un package qui contient toutes les instructions du programme.
–Cet exécutable n'est pas portable – nous ne pouvons pas le copier sur une autre machine et attendre la même chose.
comportement
4. L'exécutable doit être chargé en mémoire avant que nous ne l'exécutons.
Le chargeur fait ce travail
compiler assembler lien courir
Code source (.c)−−−−−−→assemblage (.s)−−−−−−−→fichier objet (.o)−−−→exécutable (.out)−−−→programme

18.2 Fichiers d'en-tête


Lors de la compilation du programme, nous devons répertorier tous les fichiers qui contiennent le code utilisé par le programme principal.

Chaque fichier est compilé en fichier objet séparément, puis ils sont combinés lors de l'étape de liaison.

Nous pouvons également utiliser la compilation séparée, où nous compilons les codes sources en fichiers objets séparément.
alors utilisez gccto les relier

Pour compiler le code source en fichiers objets, utilisez gcc -c

Page 37
–Par exemple.

$gcc -c un.c
$gcc -c two.c
$gcc un.o deux.o

Cela peut être avantageux lorsque nous voulons modifier un fichier dans un grand projet.
– Peut également être dangereux car le fichier objet modifié peut ne pas être compatible avec les autres fichiers (par exemple.
les types de paramètres de fonction deviennent incompatibles) et le lissage pourrait encore réussir

Nous pouvons améliorer l'organisation de nos projets en utilisant des fichiers d'en-tête.

Un fichier d'en-tête (.h) est une interface

•Un en-tête doit déclarer ce que les fonctions font et quels types elles nécessitent, sans définir comment elles le font.
sont en réalité mises en œuvre

•Contient des prototypes de fonction

Pour utiliser le fichier d'en-tête, incluez-le dans le code source, c'est-à-dire #include "header.h"

Les guillemets doubles signifient que nous voulons utiliser le fichier d'en-tête dans le répertoire courant.

Lorsque la dclaration dans le fichier d'en-tte et la dfinition dans le fichier source ne correspondent pas, le type
un décalage sera détecté par le compilateur
•Nous n'avons pas besoin de fournir le nom du fichier d'en-tête à gcc

L'instruction #include indique au préprocesseur d'insérer le corps du fichier d'en-tête dans le code source.
code

Fichier d'en-tête Variables


Nous pouvons séparer la déclaration et la définition des variables

•Lorsque nous déclarons des variables dans des fichiers d'en-tête, nous devons ajouter le mot-clé extern (qui signifie « externe »
défini)
Lorsque nous voulons qu'une variable globale existe uniquement dans un fichier, utilisons le mot-clé static.

Rend la variable locale (uniquement dans le fichier qui la définit)

Nous pouvons ajouter une condition de garde lors de l'importation de fichiers d'en-tête afin de ne pas faire d'importations doubles (ce qui entraîne
erreurs concernant les doublons)
•Par exemple.

#ifndef TEST_H
#define TEST_H
// ...quelques déclarations...
#endif

Dépendance : fichiers sources s'appuyant sur le contenu du fichier d'en-tête

Si static est utilisé sur une variable locale, elle conserve alors sa valeur à travers les exécutions de la fonction.

Page 38
18.4 Fichiers Make
Lorsqu'un fichier d'en-tête change, le fichier source qui en dépend doit être recompilé.

Les Makefiles sont composés d'une séquence de règles

Chaque règle a la structure suivante :

target: dependencies...
recette

•Cible : le fichier à construire


•Recipe: the command or list of commands to execute that creates the target
•If dependencies (or prerequisites) are present, the recipes are executed if one or more of the dependencies
sont plus récents que la cible

•S'il n'y a pas de dépendances, les actions ne sont exécutées que si la cible n'existe pas.
L'espace blanc avant la recette est une tabulation

•C.-à-d.

test: test_1.c test_2.c


gcc test_1.c test_2.c -o test

•Le makefile a le nom de fichier Makefile

Lorsque nous exécutons la commande make, le système d'exploitation recherche le fichier Makefile et vérifie les règles qu'il contient.

S'il n'y a pas de fichier nommé test, alors l'action pour la règle test s'exécute
Si make est exécuté à nouveau, puisque le test n'a pas besoin d'être reconstruit, rien n'est fait.
- Si make a été exécuté et si l'un des tests 1. le test de cœur 2. c est modifié depuis la dernière construction, l'action
exécute
Nous pourrions utiliser des makefiles pour tirer parti de la compilation séparée.

•Par exemple

test_1.o : test_1.c test_1.h


gcc -c test_1.c -o test_1.o

test_2.o : test_2.c test_2.h


gcc -c test_2.c -o test_2.o

test : test_1.o test_2.o


gcc test_1.o test_2.o -o test

L'exécutable dépend des fichiers objets


Chaque fichier objet dépend des fichiers sources et des fichiers d'en-tête correspondants.

Chaque fois que timemake évalue une règle, il vérifie d'abord toutes les dépendances ; si une dépendance est également une cible dans le
makefile, il évaluera cette règle en premier avant de vérifier les dépendances

Makefile prend en charge les caractères génériques

Page 39
•Par exemple.

%.o: %.c %.h


gcc -c$< -o$@

• Le signe pourcentage signifie que chaque fichier objet qui doit être construit dépend d'un fichier source (et d'un en-tête
fichier) du même nom
•$<est une variable contenant le prénom dans la liste des dépendances

$@ est une variable contenant le nom de la cible


Nous pouvons ajouter une règle qui nettoie

Par exemple

.PHONY: clean
clean:
rm test *.o

•.PHONY indique que clean n’est pas un fichier

•make clean exécute la commande remove


Peut déclarer une variable dans le makefile

•Par exemple.

OBJFILES = test_1.o test_2.o

test:$(OBJFILES)
gcc$(OBJFILES)

• Lorsque un nouveau fichier est ajouté au projet, nous pouvons simplement mettre à jour la variable

Page 40
19 fonctionnalités C utiles

19.1 Typedef
Le typedef permet de créer des alias pour les types et est évalué à la compilation
•Par exemple, la taille est définie dans [Link]

typedef unsigned int size_t;

•typedef permet de définir un nom (c'est-à-dire size_t) qui fait référence à un type existant (c'est-à-dire unsigned int)

•Cela fournit un nouveau nom pour un type existant


Généralement utilisé pour les structures

•Par exemple.

typedef structstudent {
...
} Student;

permet d'utiliser Studentau lieu de struct student

•Le premier étudiant peut être omis

19.2 Macros
Les macros créent des alias qui sont évalués pendant la prétraitement
•Par exemple

#define MAX_NAME_LENGTH 40

•#définir demande au préprocesseur de remplacer les occurrences de LONGUEUR MAXIMALE DU NOM par 40

•Par convention, tous les macros définis sont en majuscules

Les macros sont utiles pour les constantes

Améliore la lisibilité
Les constantes peuvent être facilement mises à jour

Le langage macro n'est pas du C

Nous n'avons pas besoin de signes égaux ni de points-virgules

Les macros peuvent prendre des paramètres

•[Link].

#define AVEC_TAXE(x) ((x) * 1.13)

Utilisation :

printf("%f\n", AVEC_TAXE(9.99));// remplacé par printf("%f\n", ((9.99) * 1.13));

Page 41
•Se comporte comme une fonction, mais de manière plus efficace car cela se produit avant la compilation

•Ne peut pas y avoir d'espace entre le nom de la macro et les parenthèses

•Dans la définition de la macro, placez le nom du paramètre (c'est-à-dire x) et la définition entière entre parenthèses

–Il faut s'assurer que le paramètre est complètement évalué avant d'autres opérations.
Par exemple, si 1 + 1 est substitué pour x, alors nous devons évaluer cette addition en premier.

Page 42
5 Itération
5.1 Boucles For
Structure de boucle For :

pour(INITIALISATION; CONDITION; MISE À JOUR) {


CORPS DE LA BOUCLE

La section d'initialisation est exécutée avant le début de la boucle.

–Habituellement utilisé pour définir une variable qui est mise à jour à travers la boucle

La section de mise à jour s'exécute après chaque itération de la boucle.

La condition est vérifiée avant que le corps de la boucle ne soit exécuté

Si la condition est vraie, alors le corps de la boucle s'exécute


–Si c'est faux, alors la boucle se termine
Les boucles peuvent être imbriquées

Les variables de boucle ne devraient pas avoir le même nom

5.2 Boucles While


Structure de boucle while :

tant que(CONDITION) {
CORPS DE LA BOUCLE

•La condition se comporte de la même manière que la boucle for

•Si la condition est évaluée à vrai, alors le corps de la boucle s'évalue à nouveau

Structure de boucle do-while :

faire{
CORPS DE LA BOUCLE

}tant que(CONDITION)

Le corps de la boucle est exécuté avant que la condition ne soit vérifiée

5.3 Interrompre et Continuer


break termine l'itération actuelle de la boucle
continuecauses the rest of the loop body to be skipped

Page 14
•Peut être utilisé pour définir des constantes spécifiques au système et pour inclure des bibliothèques spécifiques au système.

Nous pouvons définir des macros dans la ligne de commande en utilisant le drapeau -D

•Par exemple

$gcc -D DEBUG=3 test.c

où le code C contient

#ifdef DEBUG
printf("Exécution en mode de débogage au niveau %d\n", DEBUG);
#endif

Lorsque nous utilisons #include, le fichier d'en-tête est copié dans le fichier source

Nous pourrions rencontrer des problèmes lorsque la même variable est définie dans différents fichiers d'en-tête, ou lorsqu'un en-tête
le fichier est inclus deux fois (par exemple, A et B sont inclus, et B ordonne au préprocesseur d'inclure A (à nouveau))

Presque tout ce qui peut être fait avec des macros peut être fait au sein du système C

Les fonctions C sont préférées aux macros de type fonction.

Page 44
21 Pointeurs de fonction
Nous pouvons passer des fonctions en arguments ou stocker des fonctions dans des structures

Le type d'une fonction est son type de retour et ses types de paramètres
•Par exemple

void...(int*,int)

Pour en faire un pointeur vers une fonction :

void(*nom_func)(int*,int)

• Lorsque nous appelons une fonction via son pointeur, nous n'avons pas besoin de le déréférencer.

• Lorsque nous passons des fonctions en arguments, nous ne faisons que passer son nom, comme s'il s'agissait d'une variable normale.

Le nom d'une fonction est traité comme un pointeur vers la fonction.

Page 45
22 Appel système
Appel système : une fonction qui demande un service du système d'exploitation

•Par exemple.

voidexit(intstatus);

•printf est une fonction de bibliothèque, qui est de haut niveau

–printf→analyser la chaîne de format et construire la chaîne de sortie→configurer le tampon et copier la sortie


chaîne à tampon → écrire
–write est un appel système
•Lorsqu'un appel système se produit, le contrôle est donné au système d'exploitation et celui-ci exécute du code en faveur de l'utilisateur.

programme
•Les fonctions de bibliothèque, par exemple printf, scanf, fopen, fgets, appellent des appels système dans le cadre de leur fonctionnement

Page 46
23 Erreurs et Errno
Par exemple, lorsque nous utilisons fopen, le fichier peut ne pas exister ou avoir les mauvaises permissions, auquel cas le
le programme pourrait planter

•To indicate whether an error occurred, return a special value

−1 pour entier
Pointer nul
Pour indiquer le type d'erreur, utilisez la variable globale errnow qui stocke le type de l'erreur

•errnoest unint
Des codes numériques différents sont définis pour différents types d'erreurs.

•Par exemple, si malloc échoue, cela renvoie nul et définit errno sur ENOMEM

Nous pouvons utiliser perror

•Prototype:

voidperror(const char*s)

•perror imprime un message sur l'erreur standard

Le message inclut les arguments, suivis d'un deux-points, puis du message d'erreur correspondant.
à la valeur actuelle de errno
•Le but principal est d'afficher un message d'erreur basé sur la valeur actuelle de errno

•Ne devrait pas être utilisé comme une fonction générique de rapport d'erreur, utilisez plutôt fprintf pour l'erreur standard.

Lors de l'utilisation des appels système ou des fonctions de bibliothèque, vérifiez les erreurs (en vérifiant la valeur de retour) avant de passer à autre chose.

sur
• Lorsqu'une erreur est rencontrée, des messages d'erreur informatifs sont imprimés (plutôt qu'une faute de segmentation)
ou un résultat aléatoire)

Page 47
24 Processus
24.1 Modèles de Processus
Programme : Les instructions exécutables d'un programme
•Par exemple, code source, code machine compilé

Processus : exécution d'une instance d'un programme, y compris :


•Code machine du programme

•Informations sur l'état actuel du processus, par exemple.

–Quelles instructions exécuter ensuite


–Valeurs actuelles des variables en mémoire

Processus
Chaque processus a un identifiant de processus (PID)

Chaque processus est également associé à une structure de données appelée un bloc de contrôle de processus (BCP) (ou tâche
bloc de contrôle) qui stocke
Valeurs actuelles des registres importants
Inclure le pointeur de pile (SP), qui identifie le sommet de la pile
Inclure le compteur de programme (PC), qui identifie la prochaine instruction à exécuter.
–Ouvrir des descripteurs de fichiers

–Autres états que l'OS gère


Pour voir les processus actifs sur la machine, exécutez la commande top

Nous pouvons voir le PID et le programme de commande que le processus exécute.


Le processus avec l'ID de processus 1 associé à la commande est un processus spécial du système d'exploitation.

Le nombre de processeurs (c'est-à-dire de CPU) détermine combien de processus peuvent exécuter une instruction.
en même temps
Les processus qui s'exécutent actuellement sont dans l'état en cours d'exécution.

Les processus qui pourraient s'exécuter si un CPU était disponible sont dans l'état prêt.

Les processus qui attendent qu'un événement se produise sont dans l'état bloqué (ou état de sommeil)
•L'ordonnanceur du système d'exploitation est responsable de décider quel processus doit être exécuté et quand.

24.2 Création de Processus avec Fork


Lorsqu'un processus appelle fork, il passe le contrôle à l'OS

Pour dupliquer un processus, le système d'exploitation copie l'espace d'adressage du processus original, ses données et le PCB.

•En conséquence, le nouveau processus créé exécute le même code, a les mêmes valeurs pour toutes les variables.
dans la mémoire, et a la même valeur pour le compteur de programme et le pointeur de pile

•Différences entre les deux processus :

Le processus nouvellement créé a un PID différent.

Page 48
La valeur de retour de fork est différente dans les deux processus

Le processus original et la copie nouvellement créée sont liés

Le processus original est le processus parent


–Whenforkis called, achild processis created
Lorsque le processus fils s'exécute, il commence à s'exécuter après le retour de fork.

Nous ne savons pas si le processus parent ou le processus enfant s'exécute en premier.

La valeur de retour de fork :

PID du processus enfant pour le processus parent


-0 pour le processus enfant
-1 si la création du processus échoue, et le nouveau processus n'est pas créé

•fork peut échouer s'il y a déjà trop de processus en cours d'exécution pour l'utilisateur, ou sur l'ensemble du système

Les processus parent et enfant ne partagent pas la mémoire.

24.3 Processus de Relation et de Résiliation


getpid renvoie le PID du processus actuel

getppid renvoie le PID du processus parent

usleepsleeps le processus actuel

Le système d'exploitation traite le processus parent et le processus enfant de la même manière et ne privilégie aucun d'eux.

Lorsqu'on exécute un programme, le shell attend que le processus soit terminé puis affiche une invite pour le suivant.
ordre
Lorsque le processus parent d'origine se termine, un invite de commande s'affiche.

•Puisque le shell est un processus que le système d'exploitation doit planifier, quelques processus fils pourraient imprimer une certaine sortie.
avant que le shell n'affiche son invite
•Au moment où l'invite de commande du shell est affichée, il est possible que certains processus enfants ne soient pas encore terminés.
alors ils impriment plus ensuite
Nous pouvons utiliser l'appel waitsystem pour forcer le processus parent à attendre jusqu'à ce que l'un de ses enfants se termine.

Le shell utilise l'appel système wait pour se suspendre jusqu'à ce que son enfant se termine.

Nous devons appeler waitforeachchild qui a été créé pour attendre tous les processus enfants.
•Valeur de retour de wait :

PID de l'enfant qui a terminé avec succès


-1 en cas d'échec

La valeur de retour du processus enfant fait partie de la valeur d'état dans wait

Le statut 0 représente une exécution réussie du processus.


Un statut non nul représente diverses terminaisons anormales

Page 49
Il y a d'autres aspects au statut, par exemple la partie qui représente la façon dont les processus se sont terminés, que ce soit
normalement ou par un signal (c'est-à-dire Ctrl-C)

Pour sortir anormalement, utilisez abort()

Nous pouvons utiliser waitpid pour spécifier quel processus enfant attendre.

Ne peut attendre que les processus enfants, pas les processus non liés comme l'enfant de l'enfant.

24.4 Zombies et Orphelins


Lorsque le processus enfant se termine avant que le parent n'appelle wait, alors l'état du processus enfant est Z (ce qui
représente zombie)
Le processus enfant a déjà appelé exit donc il est mort
•Le système d'exploitation garde ces informations de sortie au cas où le parent appellerait wait pour obtenir cette valeur, donc le système d'exploitation ne peut pas

supprimez le PCB du processus terminé jusqu'à ce qu'il sache qu'il est sûr de le nettoyer

Un processus zombie est un processus qui est mort, mais qui est toujours là en attendant que le parent le collecte.
état de résiliation
Un processus zombie est exorcisé lorsque son statut de terminaison a été collecté.

La tâche principale du processus init est d'appeler wait dans une boucle qui collecte le statut de terminaison
de tout processus qu'il a adopté
Après qu'Afterinithas a collecté l'état de terminaison d'un processus orphelin, toutes les données du processus
les structures peuvent être supprimées, et le zombie disparaît

Lorsque le processus parent a été terminé mais qu'un processus enfant ne l'a pas été, alors le processus enfant est un orphelin.

•Lorsque nous utilisons getppid sur les processus orphelins, nous obtenons 1

Le processus avec PID 1 est le processus init, c'est-à-dire le premier processus que le système d'exploitation lance.

Lorsqu'un processus devient un orphelin, il est adopté par le processus init.

24.5 Exécution de Différents Programmes


Nous voulons charger et exécuter d'autres programmes

La famille de fonctions exec remplace le processus en cours d'exécution par un exécutable différent.
Les variantes des fonctions exec accomplissent toutes la même tâche, mais diffèrent par la manière dont les arguments sont passés.
les fonctions sont interprétées

•execl : le premier argument est le chemin vers un exécutable, et les arguments restants sont la commande
arguments de ligne à l'exécutable
Si nous n'avons pas d'arguments en ligne de commande, nous passons NULL comme deuxième argument
Lorsque un programme appelle exec, le système d'exploitation fait ce qui suit :

Le code (machine) est chargé dans la région de code de l'espace d'adresses


Le compteur de programme pointe vers la fonction execl
Le pointeur de pile pointe vers le cadre de pile de la fonction principale

–Lors de l'exécution de l'execl, le système d'exploitation trouve le fichier contenant le programme exécutable et le charge.
dans la mémoire où se trouve le segment de code

Page 50
Une nouvelle pile est également initialisée

Le compteur de programme et le pointeur de pile sont mis à jour afin que la prochaine instruction à exécuter soit
ce processus retourne au niveau utilisateur est la première instruction dans le nouveau programme
Lorsque le contrôle revient au processus de niveau utilisateur, le code original qui a appelé execl a disparu, donc il
ne devrait jamais revenir et les lignes suivantes execl ne devraient jamais s'exécuter
∗ execl retournera si une erreur se produit et qu'il est incapable de charger le programme
Le système d'exploitation ne crée pas un nouveau processus, au lieu de cela, le processus appelant est modifié.

Le processus a le même PID après execl


Il conserve un certain état du processus original, comme les descripteurs de fichiers ouverts.

execvariants
execl
–l – liste : arguments de ligne de commande passés sous forme de liste d'arguments à exécuter

•execv
–v – vecteur : arguments de ligne de commande passés sous forme de tableau de chaînes

•execlp execvp

–p – path: the PATH variable is searched for the executable


Sans p, le premier argument doit être un chemin complet vers l'exécutable
•execle execvpe

–e – environnement : passez également un tableau spécifiant les variables d'environnement

Processus Shell
Lorsque nous tapons une commande à l'invite de commande, le shell appelle d'abord fork pour créer un nouveau processus, puis
callsexecto load a different program into the memory of the child process
Le processus shell appelle ensuite wait et se bloque jusqu'à ce que le processus fils ait fini d'exécuter.

Lorsqu'il retourne, le délai d'attente imprime un message indiquant qu'il est prêt à recevoir la prochaine commande

Page 51
25 tuyaux
25.1 Entrée/Sortie non tamponnée

Nous pouvons utiliser la commande strace pour voir quels appels système nous avons effectués.

De nombreuses impressions sont combinées en un grand appel système d'écriture.

Dans l'appel système write, 3 est passé en tant que premier paramètre (paramètre de descripteur de fichier)

Les 3 fichiers qui s'ouvrent automatiquement sont l'entrée standard, la sortie standard et l'erreur standard.
Chacun d'eux a les descripteurs de fichier 0, 1, 2, respectivement.

Le descripteur de fichier est contenu dans la structure FILE

25.2 Introduction aux tuyaux

Les tuyaux peuvent être utilisés pour envoyer des données entre des processus liés.

Un pipe est spécifié par un tableau de deux descripteurs de fichiers

•Un pour lire les données du tube


Un pour écrire des données dans le tuyau

Lorsqu'un programme appelle l'appel système pipe, le système d'exploitation crée les structures de données de pipe et les descripteurs de fichiers ouverts.

sur le tuyau
Ces deux descripteurs de fichier sont stockés dans le tableau de deux entiers que nous passons dans le tube

L'indice 0 du tableau est le descripteur de fichier utilisé pour lire depuis le tube

L'index 1 du tableau est utilisé pour écrire dans le tuyau

•Après le retour de l'appel de pipe, le processus peut lire et écrire sur le pipe

Lorsque fork crée une copie d'un processus existant, il crée également une copie de tous les descripteurs de fichiers ouverts.

Après l'appel à fork, les deux processus ont des descripteurs de fichiers de lecture et d'écriture sur le tube.

Les tuyaux sont unidirectionnels

Un processus écrit dans le tuyau et un autre processus lit à partir du tuyau


•Besoin de fermer les descripteurs de fichiers qui ne sont pas utilisés

•Lorsque les descripteurs de fichier d'écriture sur le tuyau sont fermés, une lecture sur le tuyau renverra 0, indiquant que
il n'y a plus de données à lire depuis le tuyau
Lorsqu'un processus se termine, tous ses descripteurs de fichiers ouverts sont fermés.

25.3 Concurrence et tuyaux


Problèmes de producteur-consommateur

Le producteur ajoute à la file d'attente lorsqu'elle est pleine

2. Le consommateur retire de la file d'attente lorsqu'elle est vide

3. Le producteur et le consommateur fonctionnent simultanément sur la file d'attente

Page 52
Le tube est une structure de données en file d'attente dans le système d'exploitation

L'écriture dans le tuyau est le producteur


Le processus de lecture depuis le tuyau est le consommateur

Problèmes de tubes et de producteurs-consommateurs

Le cas 3 ne se produira pas car le système d'exploitation ne permet d'opérer qu'un seul à la fois.

Le système d'exploitation bloque l'appel de lecture lorsque le tube est vide.

•Le système d'exploitation bloque l'appel d'écriture lorsque le tuyau est plein

25.4 Redirection de l'entrée et de la sortie avec dup2

dup2 : un appel système qui crée une copie d'un descripteur de fichier ouvert
•Prototype de fonction :

intdup2(int oldfd, int newfd)

• Lorsqu'un processus enfant est créé, même si sa table de descripteurs de fichiers est séparée de celle de son parent, ils
peut pointer vers les mêmes objets
•dup2 prend 2 indices dans la table des descripteurs de fichiers et réinitialise l'un d'eux pour faire référence au même objet fichier que le

autre

Page 53
26 Signaux
26.1 Introduction

Chaque signal est identifié par un numéro entre 1 et 31, et des constantes définies sont utilisées pour leur donner des noms.

Lorsque nous tapons Ctrl-C, le signal SIGINT est envoyé, et l'action par défaut est que le processus se termine.

•Lorsque nous tapons Ctrl-Z, le signal SIGSTOP est envoyé, et l'action par défaut est que le processus soit suspendu.
exécution
Nous pourrions utiliser une autre fenêtre de terminal pour envoyer un signal à un programme.

–Suspendre le processus :

kill -STOP (PID)

–Pour continuer le processus :

tuer -CONT (PID)

–Pour terminer le processus :

tuer -INT (PID)

Nous pourrions utiliser lekilllibrary et envoyer des signaux d'un programme C à un autre.

26.2 Gestion des Signaux


Le PCB contient un tableau de signaux

Chaque entrée dans la table des signaux contient un pointeur vers le code qui sera exécuté lorsque le système d'exploitation livrera
le signal au processus
Ce code est appelé la fonction de gestion des signaux
Nous pouvons changer le comportement d'un signal en installant une nouvelle fonction de gestion des signaux.

• L'appel du système d'actions Thesig modifie la table des signaux afin que la fonction désirée soit appelée à la place de
l'action souhaitée

–Prototype :

intsigaction(intsignum, const structsigaction *act,


structsigaction *oldact)

–signum : le signal étant modifié


–act : pointeur vers une structure que nous devons initialiser avant d'appeler sigaction
–oldact : pointeur vers une structure, mais sa valeur est remplie par l'appel système comme l'état actuel de la
gestionnaire de signal avant que nous ne le changions

struct sigaction

Page 54
structsigaction {
void(*sa_handler)(int);
void(*sa_sigaction)(int, siginfo_t *,void*);
sigset_t sa_mask;
intsa_flags;
void(*sa_restorer)(void);
}

•sa handler : le pointeur de fonction pour le gestionnaire de signal que nous installons

Deux signaux qui ne peuvent pas être changés : SIGKILL et SIGSTOP

•SIGKILL provoque la terminaison du processus

•SIGSTOP suspend le processus

Page 55
27 Manipulation de bits et drapeaux
27.1 Opérations Bitwise
Effectue des opérations logiques en examinant chaque bit.

ET binaire

1 et 1 == 1
1 & 0 == 0
0 & 1 == 0
0 & 0 == 0

OU bit à bit

1 | 1 == 1
1 | 0 == 1
0 | 1 == 1
0 | 0 == 0

XOR bit à bit

1 ^ 1 == 0
1 ^ 0 == 1
0 ^ 1 == 1
0 ^ 0 == 0

Négation/complément bit à bit

~0 == 1
~1 == 0

Nous pouvons stocker des constantes binaires en préfixant la valeur avec 0b

chara = 0b00010011;// 0x13 in hex

Nous pouvons stocker des constantes hexadécimales en préfixant la valeur avec 0x

unsigned charb = 0x14;// 0001 0100 en binaire

27.2 Opérateurs de décalage


Deux opérateurs de décalage

•<<déplacer à gauche

•>>décaler à droite

•Par exemple, x << y

–xis la valeur à décaler


–yis how many places to shift

27.3 Manipulation des bits et indicateurs


Permissions de fichier

Page 56
Chaque fichier a un propriétaire et un groupe

•Peut être vérifié en utilisant ls -l, par exemple.

-rwxr-xr-x l reid instrs 9710 Sep 30 2014 sb*

•La première colonne dans la sortie est la chaîne de permission, représentant qui peut lire, écrire ou exécuter le
fichier
Par exemple, le propriétaire peut lire, écrire et exécuter le fichier ; le groupe peut lire et exécuter le fichier ;
tout le monde peut lire et exécuter le fichier
La troisième colonne est le propriétaire du fichier

La quatrième colonne est le groupe

Nous pouvons définir des autorisations distinctes pour le propriétaire, le groupe et chaque autre utilisateur du système.

•Un annuaire aurait eu la première lettre à la place de - et un lien aurait eu un l


•Les 9 caractères représentant les permissions peuvent être représentés par des bits

Exemple
Numéro de bit 8 7 6 5 4 3 2 1 0
Bit 1 1 1 1 0 1 1 0 1
Caractère de permission r w x r - x r - x

L'appel système chmod


Modes pour les permissions décrites en base-8 (octal)

–Base-8 is convenient for permissions since each digit can be represented by 3 bits
Un octal en C s'écrit avec un zéro précédant
Pour définir les bits, nous pouvons utiliser l'opérateur binaire OU

Pour vérifier les bits, nous pouvons utiliser l'opération binaire ET.

27.4 Vecteurs de bits


Une Implémentation de Set

Stocke un ensemble contenant de petits entiers positifs

Aucun doublon autorisé


•Peut être représenté par un tableau de bits, où les éléments de l'ensemble sont les indices dans le tableau,
and the value of each location tells us whether an element is present
•Pour ajouter un élément au jeu :

bit_array = bit_array | (1 << n);

•To remove an elementnfrom the set:

bit_array = bit_array & ~(1 << n);

Page 57
Cette technique s'appelle le masquage de bits

•Pour avoir un ensemble suffisamment grand, nous pouvons utiliser un tableau d'entiers non signés, où chaque élément (c'est-à-dire un entier non signé)

int) peut stocker 32 valeurs


Opérations sur l'implémentation avec un tableau d'entiers non signés

•Pour définir un bit à l'index n à 1 :

1. Déterminez quel élément du tableau d'entiers non signés

intindex = n / 32

2. Déterminez quel bit modifier

intbit = n % 32

3. Effectuer l'opération

bit_array[index] = bit_array[index] | 1 << bit

Nous pouvons encapsuler le tableau dans une structure

•D'autres opérations peuvent être mises en œuvre de manière similaire en utilisant des opérations binaires

Page 58
28 Multiplexage d'E/S
28.1 Le problème des lectures bloquantes
Lorsque le parent lit d’un tuyau alors qu’il n’y a rien sur le tuyau encore, l’appel read est bloqué jusqu'à ce que le
l'enfant écrit au tuyau

Si le parent lit à partir de deux tubes, et que ses appels à read sont l'un après l'autre dans une boucle :

Pourrait bien fonctionner

•Si le premier enfant n'écrit rien dans le tuyau, tandis que le deuxième enfant écrit beaucoup dans le tuyau,
le parent attendrait le premier enfant pour toujours

28.2sélectionner

Appel selectSystam
•Prototype:

intselect(numfd, read_fds, write_fds, error_fds, timeout)

•L'appelant spécifie un ensemble de descripteurs de fichiers (c'est-à-dire des fds de lecture) à surveiller

•sélectionner les blocs jusqu'à ce que l'un de ces descripteurs de fichier ait des données à lire ou jusqu'à ce que la ressource ait été

fermé
Un descripteur de fichier avec des données à lire ou avec une ressource fermée est prêt

•select modifie l'ensemble des descripteurs afin que, lorsqu'il retourne, l'ensemble ne contienne que les descripteurs de fichier qui
sont prêts à lire
Il pourrait y avoir plus d'un descripteur de fichier qui est prêt

•lire fdshas type*fd défini


•numfd doit être réglé sur la valeur du descripteur de fichier le plus élevé dans l'ensemble +1

•écrire fdsanderror fdsare également des ensembles de descripteurs de fichiers que nous pouvons utiliser pour vérifier quels descripteurs de fichiers

sont prêtes à écrire ou ont des conditions d'erreur, respectivement

•timeout est un pointeur vers struct timeval qui indique combien de temps select bloquera avant de retourner,
même si aucun descripteur de fichier n'est prêt

•Sinceread fdsis modifié par ceslectcall, nous ne pouvons pas simplement réutiliser l'appel de fonction, au lieu de cela, nous
besoin de réinitialiser l'ensemble avant d'appeler select à nouveau

ensemble fd

•FD ZÉRO prend l'adresse de anfd et définit tous ses éléments à 0


•FD SET ajoute le premier paramètre (descripteur de fichier) au second paramètre (pointeur vers l'ensemble de descripteurs de fichiers)

•FD ISSET vérifie si le premier paramètre (descripteur de fichier) se trouve dans le second paramètre (pointeur vers
fd ensemble)

Page 59
29 Prises
29.1 Introduction

Internet
Chaque machine dispose d'un protocole Internet, ou adresse IP, qui est utilisé pour lui envoyer un message depuis n'importe où.
d'autres machines connectées à Internet
Une machine peut exécuter de nombreux processus différents qui doivent communiquer via Internet.
Pour spécifier le programme, en plus de l'adresse IP, nous avons également besoin du port.

•L'emplacement complet d'un programme s'exécutant sur une machine connectée à Internet est l'adresse de la machine plus
le port
•Les messages envoyés d'une machine sont encapsulés dans des paquets, qui contiennent à la fois l'adresse et la charge utile.
(c'est-à-dire le contenu)

Lorsque le paquet quitte la machine, il est reçu par le routeur, qui facilite le transfert des paquets.
entre les réseaux
Les routeurs sont connectés à plusieurs réseaux et savent à quel réseau le paquet doit être envoyé.
ordre de l'approcher de sa destination finale

Client et Serveur
Un serveur est un programme fonctionnant sur un port spécifique d'une certaine machine, attendant qu'un autre programme...
envoyer un message

Ils ont généralement des ports définis


Les pages Web sont généralement servies sur le port 80
Les pages web sécurisées utilisent le port 443

La personne qui gère le serveur publie l'adresse de la machine et le port.

•A user runs aclientprogram when they want to start interacting with a server
–Le client envoie soit un message unique, soit commence une connexion (c'est-à-dire une conversation entre le
deux machines qui impliquent plusieurs messages
Une fois que les programmes ont établi un canal de communication, alors soit la machine peut envoyer des données à l'autre.
autre
Pour établir un canal de communication, nous utiliserons des sockets

Prises
A de nombreux types

Sockets de datagramme
Sockets de flux
–Sockets bruts

Sockets de flux
•Construit sur le protocole TCP

Sockets orientés connexion

Page 60
•Garantir que le message ne sera pas perdu en transit, et que les messages seront livrés dans l'ordre dans
qu'ils sont envoyés
ThesocketSystem Call
Prototype :

intsocket(intdomaine,inttype,intprotocol)

•Utilisé pour créer un point de terminaison pour la communication

•Lorsque tout est configuré, nous avons besoin d'un point de terminaison dans le programme client et d'un point de terminaison dans le serveur, donc

les deux programmes invoqueront indépendamment cet appel système


La valeur de retour est -1 en cas d'erreur

•En cas de succès, la valeur retournée sera l'indice d'un élément dans la table des descripteurs de fichiers

• domain définit le protocole (c'est-à-dire l'ensemble de règles) utilisé pour la communication

– Généralement défini sur PF INET ou AF INET, qui sont définis comme étant les mêmes

•type
Pour les sockets de flux, définissez ce paramètre sur SOCK STREAM

protocole
–TCP est le seul protocole disponible pour les sockets de flux, définissez ce paramètre à 0 (ce qui indique
que nous utilisons le protocole par défaut pour ce type de socket)

Configuration du socket 29.2


Pour définir l'adresse, utilisez l'appel système bind

•Prototype:

intbind(intsocket, const structsockaddr *address, socklen_t address_len)

•socket : le socket que nous souhaitons configurer

adresse
Le pointeur vers la structure sockaddr bind fonctionne pour toutes les familles d'adresses différentes

–Pour notre famille particulière AF INET, nous définissons ce paramètre en utilisant une struct sockaddr in (où
in signifie internet)
Définition de sockaddr dans :

structsockaddr_in {
shortsin_family;
u_short sin_port;
structin_addr sin_addr;
charsin_zero[8];
};

Famille Setsin à AF INET


-sin portis où nous définissons le numéro de port

Page 61
Les numéros de port varient de 0 à 65535
Les numéros de port 0-1023 sont réservés aux services bien connus
*Le numéro de port 1024-49151 est un port enregistré, c'est-à-dire que nous pouvons nous enregistrer auprès de l'IANA (Internet Assigned Numbers Authority).

Autorité des Numéros Signés


L'IANA s'occupe également d'assigner des noms de domaine au plus haut niveau.
∗ Les numéros de port 49152-65535 sont des ports dynamiques. Si nous écrivons un serveur sur notre propre machine,
n'importe quel port de cette plage est bon
∗ Si nous écrivons un serveur pour fonctionner sur une machine partagée, alors évitez de configurer un socket sur un
port que d'autres programmes utilisent déjà
Dans quel cas avons-nous besoin d'un plan sur qui utilise quel port
∗ Si nous utilisons portnon sur une machine partagée, nous définirons thesin porttohtons(n)
·htons : hôte vers réseau court
·Puisque différentes machines stockent les octets qui composent un entier dans des ordres différents, nous
il est nécessaire de s'assurer que les deux machines qui communiquent peuvent se comprendre
Les deux machines doivent transmettre et attendre des données particulières dans un format spécifique.
ces accords s'appellent des protocoles
·htonsconvertit l'entier de l'ordre des octets de la machine hôte à l'ordre du réseau
(telle que définie par le protocole)
–dans addris une structure, et nous avons seulement besoin de définir son champ addr

Définir à INADDR_ANY, ce qui configure le socket pour accepter les connexions de n'importe quelle adresse
de la machine
Une machine peut avoir plusieurs cartes d'interface réseau et peut être branchée sur des prises séparées.
réseaux, résultant en ayant des adresses IP différentes dans chaque réseau
La machine a également une adresse pour elle-même, [Link] (c'est-à-dire localhost)
–sin zéro est un rembourrage supplémentaire, rendant le sockaddr de la même longueur que la structure sockaddr
∗ Lorsque nous allouons de l'espace pour cette structure, ces octets ne sont d'aucune manière réinitialisés

Nous pouvons utiliser memset pour définir ces 8 octets à 0 (pour des raisons de sécurité)

–Puisque bindexpects un pointeur vers sockaddr (pas un pointeur vers sockaddr in), nous devons faire 2 choses
1. Take the address of thesockaddr instruct
2. Cast vers struct sockaddr *
•l'adresse lenis la longueur de l'adresse que nous passons

–Définir sizeof(struct sockaddr in)


La valeur de retour est destinée à la vérification des erreurs

–Retourne 0 en cas de succès

Renvoie -1 en cas d'échec


–bind pourrait échouer si le port que nous avons choisi n'est pas disponible

•Configuration générale

intlisten_soc = socket(AF_INET, SOCK_STREAM, 0);


si(listen_soc == -1) {
perror("socket");
exit(1);
}

structsockaddr_in addr;
addr.sin_family = AF_INET;

Page 62
addr.sin_port = htons(54321);
addr.sin_addr.s_addr = INADDR_ANY;
memset(&(addr.sin_zero), 0, 8);

if(bind(listen_soc, (structsockaddr *) &addr,sizeof(structsockaddr_in)) ==


-1) {
perror("bind");
fermer(listen_soc);
exit(1)
}

L'appel système listen dit à la machine de commencer à chercher des connexions.

•Prototype:

intlisten(intsocket,intbacklog);

•le socket est le socket que nous configurons


La valeur de retour est destinée à la vérification des erreurs

•backlog
Il se peut qu'il y ait un cas où plusieurs utilisateurs tentent de se connecter presque en même temps, entraînant
une file d'attente pour les demandes de connexion

–écoute met en place la structure de données nécessaire pour stocker ces connexions partielles
–le backlog est le nombre maximum de connexions partielles qu'il peut contenir
–Pas le nombre maximum de connexions qu'il peut contenir

29.3 Configuration d'une connexion


L'appel système accept
•Prototype:

intaccept(intsockfd,structsockaddr *adresse, socklen_t *addrlen)

•sockfdis le socket d'écoute

•address

–acceptuses ce paramètre pour communiquer au rappeleur, l'adresse du client


Lors de l'acceptation des retours, l'adresse pointera vers une structure qui contient les informations d'adresse du client.
–Vous devez allouer de la mémoire pour cette structure avant d'appeler accept

Nous passerons un pointeur à struct sockaddr et le convertirons en struct sockaddr *


*Le seul champ que nous devons définir est la famille sin, que nous définissons sur AF INET

•accepte un appel système de blocage - il attend qu'une connexion soit établie


La valeur de retour est -1 lorsque l'acceptation échoue

•En cas de succès, la valeur de retour est un entier représentant un nouveau socket que nous utiliserons pour communiquer
avec le client

Page 63
•addrlen est la longueur de l'adresse
Nous définissons la longueur à la taille de notre adresse et passons l'adresse de cette valeur.

Configuration générale

structsockaddr_in client_addr;
client_addr.sin_family = AF_INET;
unsigned int client_len = sizeof(struct sockaddr_in);

int return_value = accept(listen_soc, (structsockaddr *) &client_addr,


&longueur_client);

Pour établir une connexion via un socket à un serveur, utilisez l'appel système connect.

•Prototype:

intconnect(intsockfd, const structsockaddr *address, socklen_t addrlen)

•sockfdis the socket


•l'adresse est l'adresse de la socket sur le serveur à laquelle nous voulons nous connecter

Utilisez le type struct sockaddr dans

–Définir le champ pour la famille forsin à AF INET

–réinitialiser les champs à 0


–Configure le port au port souhaité et convertit en ordre des bytes réseau avec htons
–l'adresse du sin doit faire référence à l'adresse IP du serveur
Utilisez l'appel système getaddrinfo pour rechercher l'adresse Internet d'une machine en fonction de son
name
∗ Prototype forgetaddrinfo :

intgetaddrinfo(char* hôte, char* service, struct addrinfo


*indices, structaddrinfo **résultat)

∗ serviceetlesindicespeuventêtre définis sur NULL

∗ hôte est le nom de la machine hôte, par exemple "[Link]"


∗ result est l'adresse d'un pointeur vers une liste chaînée de structures
Il pourrait y avoir plus d'une adresse qui satisfait la demande d'informations sur l'adresse
Chaque élément de la liste est une information sur l'une de ces adresses valides
Nous devons déclarer un pointeur de type struct addrinfo * et passer son adresse à cela.
champ
L'appel système alloue de la mémoire pour la liste chaînée dans le tas et fournit une fonction
nous appelons à libérer cette mémoire quand nous avons fini
Pour libérer cette mémoire, utilisez freeaddrinfo
∗ Nous pourrions examiner la première structure d'information d'adresse à partir du résultat

La première adresse est directement pointée par le résultat.


Il a un champ addr, dont le type est sockaddr, qui contient les informations dont nous avons besoin.
Nous pouvons le convertir en typestruct sockaddr dans *
À partir de that sockaddr in, nous examinons le champ sin addr et l'assignons à sin addr.
champ de la structure que nous mettons en place pour notre appel de connexion

Page 64
Nous castons la structure sockaddr résultante en *tostruct sockaddr *

•addrlen est la longueur de l'adresse

Définissez-le sur sizeof(struct sockaddr in)

Retourne 0 en cas de succès, et -1 en cas d'échec

Configuration générale

intsoc = socket(AF_INET, SOCK_STREAM, 0);

struct sockaddr_in serveur;


server.sin_family = AF_INET;
memset(&server.sin_zero, 0, 8);
server.sin_port = htons(54321);

structaddrinfo *result;
intgetaddrinfo("[Link]", NULL, NULL, &result);
server.sin_addr = ((structsockaddr_in *) result->ai_addr)->sin_addr;
freeaddrinfo(result);

int return_code = connecter(soc, (struct sockaddr *) &server, sizeof(struct


sockaddr_in));

29.4 Communication par socket


Côté serveur :
•Créer un socket sur lequel le serveur écoute les connexions

écouter_soc = socket(...);

•Liez ce socket à un port particulier et à l'adresse de la machine

lier(listen_soc, ...);

Dites au socket de commencer à écouter les connexions partielles

écoute(écoute_soc, ...);

•Callaccept, qui bloque, ne renvoyant que s'il y a une erreur ou lorsqu'une connexion est établie

intclient_socket = accepter(listen_soc, ...);

si(client_socket == -1) {
perror(accept);
sortir(1);
}

Lorsque accept retourne, il renvoie le descripteur d'un nouveau socket

•Le socket d'écoute est toujours en écoute - nous pourrions l'appeler à nouveau.

–To handle multiple clients simultaneously, use eitherforkorselectsystem calls

Page 65
•Nous pouvons utiliser le descripteur de socket tout comme un descripteur de fichier

–Peut écrire au client, par exemple.

écrire(client_socket, "bonjour\r\n", 7);

–\rand\nare chacun considéré comme 1 caractère


– est le réseau nouvelle ligne
–Peut également lire depuis le client

Fermer la prise lorsque nous avons terminé

fermer(listen_soc);

Côté client :
•Créer une socket pour se connecter au serveur

soc = socket(...);
si((connect(soc, ...)) == -1) {
perror(connect);
exit(1);
}

Allouer de la mémoire pour contenir les valeurs que nous allons lire

charbuf[10];

Lire depuis le serveur

lire(soc, buf, 7);


buf[7] = '';

• Peut également écrire sur le serveur

•Close the socket when we are done

fermer(soc);

Lire sur Internet


Il n'y a aucune garantie que tout le contenu puisse être lu par un appel de lecture unique.

•Vous devez utiliser la valeur de retour de read pour déterminer combien d'octets ont été lus, et appeler read à nouveau si
nécessaire
La valeur de retour de read peut également déterminer si l'autre extrémité du socket est fermée.

Page 66
30 Programmation Shell
Le shell est une grande boucle qui effectue les actions suivantes dans l'ordre

•Prints a prompt
•Lit une commande
Analyse la commande
•Exécute la commande
Variétés de langages de programmation de shell

•sh
Les versions incluent la version 7 shell, ksh, ash, bash, dash, etc.
Ce sont des implémentations du langage de programmation sh de base, avec des fonctionnalités supplémentaires.
mented by the author
•csh

Les variétés incluent csh et tcsh


Lorsque le shell analyse la ligne de commande, il effectue diverses substitutions de ligne de commande

•Par exemple, les caractères génériques de nom de fichier sont remplacés par la liste correspondante des noms de fichiers par le shell avant l'exécution

le commandement

-Par exemple, cat *.c est remplacé par cat a.c b.c
La commande theechocommand peut être utilisée pour voir les arguments de ligne de commande substitués.

Pour exécuter les commandes écrites dans un fichier, utilisez h

Les substitutions en ligne de commande sont également utilisées pour les variables.

–Exemple de déclaration d'une variable :

$i=3

∗ Pas d'espace entre les opérateurs


–When we write dollar signs in front of the variable name, then the shell substitutes it with the
valeur de la variable, par exemple

$ écho$i
3

Pour faire des calculs dans le shell, utilisez expr

•Par exemple

$ expr2 + 2
4

Pour effectuer des calculs arithmétiques sur des variables, utilisez l'accent grave ‘

Ce qui est à l'intérieur des accents graves est interprété comme une commande et est exécuté.

Page 67
Si la sortie est capturée, la sortie est substituée dans la ligne de commande - moins une nouvelle ligne à la fin
caractère à la fin
–Par exemple.

$i=`expr4 + 1`
$ écho$i
5
$i=`expr$i + 1
$ écho$i
6

Pour lire l'entrée, utilisez read

•lire suivi des noms de variables dans lesquelles l'entrée est stockée

•Each input token goes into its corresponding variable


•S'il y a plus de jetons d'entrée que de variables, alors tout le reste va dans la dernière variable
•Par exemple.

$ lisez y
foo bar baz
$ écho$x
foo
$ écho$y
bar baz

•S'il y a plus de variables que de jetons, alors les variables suivantes sont définies sur des chaînes vides

PATH est une variable spéciale pour le shell


C'est une liste de répertoires séparés par des deux-points

Lorsque une commande est exécutée, le shell cherche la variable PATH pour la commande
Pour obtenir le statut de sortie de la dernière commande exécutée, utilisez $ ?

•Can useechoto print this value


0 est un état de sortie de succès

•Tout autre chose est un statut de sortie d'échec

Instructions conditionnelles

•Structure

ifcondition
alors
commande
sinon
ordre
fi

•Si la condition réussit (c'est-à-dire a un statut de sortie 0), alors la commande then est exécutée

Sinon, la commande else est exécutée

Page 68
•Pour avoir plusieurs conditions, utilisez elif, par exemple.

si condition 1
alors
commande1
elifcondition2
alors
commande2
sinon
commande3
fi

•Si nous ne voulons rien faire pour l'une des branches, utilisons :

–Analogique topassin Python


La commande test peut être utilisée pour comparer des valeurs

•Par exemple.

$ test2 -lt 3
$ echo $?
0
$ test3 -lt 2
$ écho$?
1

Au lieu de chiffres, nous pourrions utiliser des variables en mettant un signe dollar devant.

•Opérateurs de comparaison numériques pour le test :

–-eq: equal
–-ne: not equal
–-gt: plus grand que
–ge : supérieur ou égal
–lt: moins que
–-le : inférieur ou égal
Ces opérateurs de comparaison numériques sont issus du langage de programmation Fortran.
•Opérateurs de comparaison de chaînes pour le test :

égal
–!=: pas égal
Motivation d'avoir deux ensembles d'opérateurs de comparaison :

–test 03 = 3 est faux


–test 03 -eq 3 est vrai
•Tester les opérateurs de fichier pour le test :

--f fichier : le fichier existe et est un fichier ordinaire

--d fichier : le fichier existe et est un répertoire


--s fichier : le fichier existe et a une taille non nulle

Page 69
–Etc.

Boucles While
•Structure

condition de boucle
faire
commande
fait

glande testiculaire utile pour la condition

Utile pour lire un fichier avec read, qui s'arrête à la fin du fichier

–La fin de fichier peut être signalée depuis le terminal en utilisant Ctrl-V

–/dev/null est un fichier spécial qui est toujours vide


Lorsque nous écrivons dans /dev/null, les données sont jetées

Constantes Booléennes

•vrai : quitter avec 0

•faux : quitter avec 1


Pour combiner des instructions booléennes, utilisez && et || comme en C

•Ils ont le comportement de court-circuit comme C

Citant en sh
•Dans sh, ls et "ls" sont les mêmes puisqu'ils sont tous deux des chaînes.

Nous devons être en mesure de supprimer l'interprétation de certains caractères.

•Pour supprimer le sens d'un seul caractère, utilisez \ suivi par le caractère
•Les guillemets doubles suppriment tout sauf le signe dollar, le backquote, le backslash ou le guillemet double de fermeture.
citation
•Single quotes suppress everything except for the closing single quote
•L'espace est préservé dans des guillemets doubles et des guillemets simples

–Par exemple, cat ’a b’ cherche le fichier nommé “a espace b”

•Exemple :

$filename='a b'
$chat"$filename"

Pour le chat, des guillemets sont nécessaires pour éviter l'interprétation de l'espace, et nous avons besoin de guillemets doubles.
pour encore interpréter le signe dollar

Pour boucle

•Structure

Page 70
pourvariabledansliste
faire
commande
fait

•Plus comme la boucle for en Python


•Peut parcourir n'importe quel nombre de chaînes, par ex.

pourin*.c
faire
écho$i
fait

•La commande seq est similaire à range en Python, par exemple

$seq 1 4
1
2
3
4

•seq peut être utilisé dans une boucle for, par exemple

sum=0
pourinséquence 1 100
faire
somme=`expr$somme +$i`
fait

•Peut utiliser une variation de la boucle for qui parcourt tous les arguments de la ligne de commande

fori
faire
un argument écho$i
Fait

Déclaration de cas

•Structure

expressiondecas
modèle1)
commande1
;;
motif2)
commande2
;;
motif3)
commande3
;;
*)
commande4
;;

Page 71
esac

•Matchesexpressionto eachpattern
•Chaque cas se termine par;;
Les cas sont testés dans l'ordre

•Cas par défaut :*)


Les motifs peuvent être des expressions régulières

Descripteurs de fichiers

Lorsqu'un programme démarre, il a trois fichiers ouverts.

-0 : entrée standard
-1 : sortie standard
-2 : erreur standard
•L'entrée et la sortie standard sont toutes deux le terminal

• L'entrée et la sortie peuvent être redirigées

–< fichier : rediriger l'entrée standard vers un fichier

–> fichier : rediriger la sortie standard vers un fichier

–>> fichier : ajouter la sortie standard au fichier (au lieu de remplacer)


–<< fichier : prendre l'entrée à partir du fichier

appelé "texte ici"


Ce qui se trouve à l'intérieur du texte ici est interprété comme s'il était entre guillemets.
*Pour avoir le comportement des guillemets simples, utilisez <<\

L'erreur standard contourne le tube/la redirection et s'imprime sur le terminal


•Nous pouvons rediriger le descripteur de fichier 2 en utilisant 2> fichier

•Pour rediriger un descripteur de fichier vers un autre, utilisez >&2 où le côté gauche de >& est le descripteur de fichier à
rediriger et le côté droit est le descripteur de fichier vers lequel rediriger

Cet exemple particulier redirige la sortie standard vers l'erreur standard.


Caractères spéciaux à l'intérieur d'un fichier

•$1,$2 sont les 1er et 2ème arguments de ligne de commande, respectivement

•$#est le nombre d'arguments de ligne de commande


•$* s'étend à tous les arguments de ligne de commande

•$@est identique à$*en dehors des guillemets doubles ; à l'intérieur des guillemets doubles, les guillemets doubles sont considérés comme
cesser de fonctionner entre les arguments tout en continuant à fonctionner dans un argument donné
• Lorsque nous voulons passer tous les arguments de ligne de commande à un autre programme, utilisez "$@" entre guillemets.

shiftcommand moves all of the command-line arguments down one place, i.e. $3becomes$2, etc.,$1is
écarté

Pour une variable, nous pourrions mettre des accolades autour du nom de la variable, par exemple ${var}

Cela permet d'avoir un autre argument qui vient immédiatement après la variable (sans espaces).
Commentaires dans sh est#

Page 72

Vous aimerez peut-être aussi