Newer
Older
# Historique (1/2)
- Conçu initialement pour la programmation des systèmes d’exploitation (UNIX).
- Créé par Dennis Ritchie à Bell Labs en 1972 dans la continuation de CPL, BCPL et B.
- Standardisé entre 1983 et 1988 (ANSI C).
- La syntaxe de C est devenue la base d’autres langages comme C++, Objective-C, Java, Go, C#, Rust, etc.
- Révisions plus récentes, notamment C99, C11, puis C18.
# Historique (2/2)
- Développement de C lié au développement d’UNIX.
- UNIX a été initialement développé en assembleur:
- Les instructions assembleur sont de très bas niveau
- Les instructions assembleur sont spécifiques à l’architecture du processeur.
- Pour rendre UNIX portable, un langage de *haut niveau* (en 1972) était nécessaire.
- Comparé à l’assembleur, le C est :
- Un langage de "haut niveau": C offre des fonctions, des structures de données, des constructions de contrôle de flots (`while`{.C}, `for`{.C}, etc).
- Portable: un programme C peut être exécuté sur un *très grand nombre* de plateformes (il suffit de recompiler le *même code* pour l’architecture voulue).
# Qu'est-ce que le C?
- Langage compilé, statiquement (et faiblement) typé, procédural, portable, très efficace.
- Langage "bas niveau" (en 2020): management explicite et manuelle de la mémoire (allocation/désallocation), grande liberté pour sa manipulation.
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
- Pas de structures de haut niveau: chaînes de caractères, vecteurs dynamiques, listes, ...
- Aucune validation ou presque sur la mémoire (pointeurs, overflows, ...).
# Exemple de programme
```C
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("Enter n: "); // affichage
int n = 0; // déclaration et initialisation de n
scanf("%d", &n); // entrée au clavier
int sum = 0; // déclaration et initialisation de sum
for (int i = 0; i <= n; ++i) { // boucle
sum += i;
}
printf("Sum of the %d first integers: %d\n", n, sum);
if (sum != n * (n+1) / 2) { // branchement cond.
printf("Error: the answer is wrong.\n");
return EXIT_FAILURE; // code d'erreur
}
return EXIT_SUCCESS; // code de réussite
}
```
# Génération d'un exécutable
- Pour pouvoir être exécuté un code C doit être d'abord compilé (avec `gcc` ou `clang`).
- Pour un code `prog.c` la compilation "minimale" est
```bash
$ clang prog.c
$ ./a.out # exécutable par défaut
```
- Il existe une multitude d'options de compilation:
1. `-std=c11` utilisation de C11.
2. `-Wall et -Wextra` activation des warnings.
3. `-fsanitize=…` contrôles d’erreurs extensifs à l’exécution (au prix d’un coût en performance).
4. `-g` symboles de débogages sont gardés.
5. `-o` défini le fichier exécutable à produire en sortie.
<!-- 6. `-O1`, `-O2`, `-O3`: activation de divers degrés d'optimisation -->
```bash
$ clang -std=c11 -Wall -Wextra -g porg.c -o prog
-fsanitize=address -fsanitize=leak -fsanitize=undefined
```
# La simplicité de C?
- 32 mots-clé et c'est tout
---------------- -------------- ---------------- ---------------
`auto`{.C} `double`{.C} `int`{.C} `struct`{.C}
`break`{.C} `else`{.C} `long`{.C} `switch`{.C}
`case`{.C} `enum`{.C} `register`{.C} `typedef`{.C}
`char`{.C} `extern`{.C} `return`{.C} `union`{.C}
`const`{.C} `float`{.C} `short`{.C} `unsigned`{.C}
`continue`{.C} `for`{.C} `signed`{.C} `void`{.C}
`default`{.C} `goto`{.C} `sizeof`{.C} `volatile`{.C}
`do`{.C} `if`{.C} `static`{.C} `while`{.C}
---------------- -------------- ---------------- ---------------
# Déclaration et typage
En C lorsqu'on veut utiliser une variable (ou une constante), on doit déclarer son type
```C
const double two = 2.0; // déclaration et init.
int x; // déclaration (instruction)
char c; // déclaration (instruction)
x = 1; // affectation (expression)
c = 'a'; // affectation (expression)
int y = x; // déclaration et initialisation en même temps
int a, b, c; // déclarations multiples
## Numériques
Type Signification (**gcc pour x86-64**)
---------------------------------- ---------------------------------------------
`char`{.C}, `unsigned char`{.C} Entier signé/non-signé 8-bit
`short`{.C}, `unsigned short`{.C} Entier signé/non-signé 16-bit
`int`{.C}, `unsigned int`{.C} Entier signé/non-signé 32-bit
`long`{.C}, `unsigned long`{.C} Entier signé/non-signé 64-bit
`float`{.C} Nombre à virgule flottante, simple précision
`double`{.C} Nombre à virgule flottante, double précision
---------------------------------- ---------------------------------------------
**La signification de `short`{.C}, `int`{.C}, ... dépend du compilateur et de l'architecture.**
Voir `<stdint.h>` pour des représentations **portables**
Type Signification
---------------------------------- ---------------------------------------------
`int8_t`{.C}, `uint8_t`{.C} Entier signé/non-signé 8-bit
`int16_t`{.C}, `uint16_t`{.C} Entier signé/non-signé 16-bit
`int32_t`{.C}, `uint32_t`{.C} Entier signé/non-signé 32-bit
`int64_t`{.C}, `uint64_t`{.C} Entier signé/non-signé 64-bit
---------------------------------- ---------------------------------------------
## Booléens
- Le ANSI C n'offre pas de booléens.
- L'entier `0`{.C} signifie *faux*, tout le reste *vrai*.
- Depuis C99, la librairie `stdbool` met à disposition un type `bool`{.C}.
- En réalité c'est un entier:
- $1 \Rightarrow$ `true`{.C}
- $0 \Rightarrow$ `false`{.C}
- On peut les manipuler comme des entier (les sommer, les multiplier, ...).
# Quiz: booléens
## [Quiz: booléens](https://cyberlearn.hes-so.ch/mod/evoting/view.php?id=1032492)
<!-- TODO Quiz en ligne -->
<!-- ```C
if (42) { /* vrai */ }
int x = 100;
if (x == 4) { /* faux */ }
if (x) { /* vrai */ }
while (x−−) { /* répète tant que x est différent de 0 */ }
if (0) { /* faux */ }
if (i = 4) { /* vrai */ }
if (i = 0) { /* faux */ }
#include <stdbool.h>
bool x = true;
if (x) { /* vrai */ }
``` -->
## Conversions
- Les conversions se font de manière:
- Explicite:
```C
int a = (int)2.8;
double b = (double)a;
int c = (int)(2.8+0.5);
```
- Implicite:
```C
int a = 2.8; // warning, si activés, avec clang
double b = a + 0.5;
char c = b; // pas de warning...
int d = 'c';
```
# Quiz: conversions
## [Quiz: conversions](https://cyberlearn.hes-so.ch/mod/evoting/view.php?id=1033446)
<!-- TODO Quiz en ligne -->
<!-- ```C
int a = (int)2.8; // 2
double b = 2.85;
int c = b + 0.5; // 3
int d = a + 0.5; // 2
bool d = 2.78; // 0
bool e = 1.0; // 1
``` -->
Une expression est tout bout de code qui est **évalué**.
## Expressions simples
- Pas d'opérateurs impliqués.
- Les littéraux, les variables, et les constantes.
```C
const int L = -1; // 'L' est une constante, -1 un littéral
int x = 0; // '0' est un litéral
int y = x; // 'x' est une variable
int z = L; // 'L' est une constante
```
## Expressions complexes
- Obtenues en combinant des *opérandes* avec des *opérateurs*
```C
int x; // pas une expression (une instruction)
x = 4 + 5; // 4 + 5 est une expression
// dont le résultat est affecté à 'x'
```
## Opérateurs relationnels
Opérateurs testant la relation entre deux *expressions*:
- `(a opérateur b)` retourne `1`{.C} si l'expression s'évalue à `true`{.C}, `0`{.C} si l'expression s'évalue à `false`{.C}.
| Opérateur | Syntaxe | Résultat |
|-----------|--------------|----------------------|
| `<`{.C} | `a < b`{.C} | 1 si a < b; 0 sinon |
| `>`{.C} | `a > b`{.C} | 1 si a > b; 0 sinon |
| `<=`{.C} | `a <= b`{.C} | 1 si a <= b; 0 sinon |
| `>=`{.C} | `a >= b`{.C} | 1 si a >= b; 0 sinon |
| `==`{.C} | `a == b`{.C} | 1 si a == b; 0 sinon |
| `!=`{.C} | `a != b`{.C} | 1 si a != b; 0 sinon |
## Opérateurs logiques
| Opérateur | Syntaxe | Signification |
|-----------|--------------|----------------------|
| `&&`{.C} | `a && b`{.C} | ET logique |
| `||`{.C} | `a || b`{.C} | OU logique |
| `!`{.C} | `!a`{.C} | NON logique |
# Quiz: opérateurs logiques
## [Quiz: opérateurs logiques](https://cyberlearn.hes-so.ch/mod/evoting/view.php?id=1033629)
<!-- TODO: Quiz -->
<!-- ```C
1 && 0 == 0
7 && 3 == 1
4 || 3 == 1
!34 == 0
!0 == 1
Soit n un unsigned char initialisé à 127:
!n == 0
``` -->
## Opérateurs arithmétiques
| Opérateur | Syntaxe | Signification |
|-----------|--------------|----------------------|
| `+`{.C} | `a + b`{.C} | Addition |
| `-`{.C} | `a - b`{.C} | Soustraction |
| `*`{.C} | `a * b`{.C} | Multiplication |
| `/`{.C} | `a / b`{.C} | Division |
| `%`{.C} | `a % b`{.C} | Modulo |
## Opérateurs d'assignation
| Opérateur | Syntaxe | Signification |
|-----------|--------------|---------------------------------------------|
| `=`{.C} | `a = b`{.C} | Affecte la valeur `b` à la variable `a` |
| | | et retourne la valeur de `b` |
| `+=`{.C} | `a += b`{.C} | Additionne la valeur de `b` à `a` et |
| | | assigne le résultat à `a`. |
| `-=`{.C} | `a -= b`{.C} | Soustrait la valeur de `b` à `a` et |
| | | assigne le résultat à `a`. |
| `*=`{.C} | `a *= b`{.C} | Multiplie la valeur de `b` à `a` et |
| | | assigne le résultat à `a`. |
| `/=`{.C} | `a /= b`{.C} | Divise la valeur de `b` à `a` et |
| | | assigne le résultat à `a`. |
| `%=`{.C} | `a %= b`{.C} | Calcule le modulo la valeur de `b` à `a` et |
| | | assigne le résultat à `a`. |
## Opérateurs d'assignation (suite)
| Opérateur | Syntaxe | Signification |
|-----------|--------------|---------------------------------------------|
| `++`{.C} | `a++`{.C} | Incrémente la valeur de `a` de 1 et |
| | | retourne le résultat (`a += 1`). |
| `--`{.C} | `a--`{.C} | Décrémente la valeur de `a` de 1 et |
| | | retourne le résultat (`a -= 1`). |
| `++`{.C} | `++a`{.C} | Retourne `a`{.C} et incrémente `a` de 1. |
| `--`{.C} | `--a`{.C} | Retourne `a`{.C} et décrémente `a` de 1. |
# Structures de contrôle: `if`{.C} .. `else if`{.C} .. `else`{.C} (1/2)
## Syntaxe
```C
if (expression) {
instructions;
} else if (expression) { // optionnel
// il peut y en avoir plusieurs
instructions;
} else {
instructions; // optionnel
}
```
```C
if (x) { // si x s'évalue à `vrai`
} else if (y == 8) { // si y vaut 8
# Structures de contrôle: `if`{.C} .. `else if`{.C} .. `else`{.C} (2/2)
printf("y vaut 3 mais cela ne sera jamais affiché.\n");
x = -1; // toujours évalué
```
# Structures de contrôle: `switch`{.C} .. `case`{.C} (1/2)
```C
switch (expression) {
case constant-expression:
instructions;
break; // optionnel
case constant-expression:
instructions;
break; // optionnel
// ...
default:
instructions;
}
```
**Que se passe-t-il si `break`{C} est absent?**
# Structures de contrôle: `switch`{.C} .. `case`{.C} (2/2)
```C
int x = 0;
switch (x) {
case 0:
case 1:
printf("0 ou 1\n");
break;
case 2:
printf("2\n");
break;
default:
printf("autre\n");
}
```
**Dangereux, mais c'est un moyen d'avoir un "ou" logique dans un case.**
# Quiz: `if ... else, switch ... case`{.C}
## [Quiz: `if ... else, switch ... case`{.C}](https://cyberlearn.hes-so.ch/mod/evoting/view.php?id=1033916)
# Structures de contrôle: `for`{.C}, `while`{.C}, `do ... while`{.C} (1/4)
## La boucle `for`{.C}
```C
for (expression1; expression2; expression3) {
instructions;
}
```
## La boucle `while`{.C}
```C
while (expression) {
instructions;
}
```
## La boucle `do ... while`{.C}
```C
do {
instructions;
} while (expression);
```
# Structures de contrôle: `for`{.C}, `while`{.C}, `do ... while`{.C} (2/4)
## La boucle `for`{.C}
```C
int sum = 0; // syntaxe C99
for (int i = 0; i < 10; i++) {
sum += i;
}
for (int i = 0; i != 1; i = rand() % 4) { // ésotérique
printf("C'est plus ésotérique.\n");
}
```
# Structures de contrôle: `for`{.C}, `while`{.C}, `do ... while`{.C} (3/4)
## La boucle `while`{.C}
```C
int sum = 0, i = 0;
while (i < 10) { // pas assuré·e·s de faire un tour
sum += i;
i += 1;
}
```
## La boucle `do ... while`{.C}
```C
int sum = 0, i = 0;
do { // assuré·e·s de faire un tour
sum += i;
i += 1;
} while (i < 10);
```
# Structures de contrôle: `continue`{.C}, `break`{.C} (4/4)
- `continue`{.C} saute à la prochaine itération d'une boucle.
```C
int i = 0;
while (i < 10) {
if (i == 3) {
continue;
}
printf("%d\n", i);
i += 1;
}
```
- `break`{.C} quitte le bloc itératif courant d'une boucle.
```C
for (int i = 0; i < 10; i++) {
if (i == 3) {
break;
}
printf("%d\n", i);
}
```
## Variables et portée
- Une variable est un identifiant, qui peut être liée à une valeur (un expression).
- Une variable a une **portée** qui définit où elle est *visible* (où elle peut être accédée).
- La portée est **globale** ou **locale**.
- Une variable est **globale** est accessible à tout endroit d'un programme et doit être déclarée en dehors de toute fonction.
- Une variable est **locale** lorsqu'elle est déclarée dans un **bloc**, `{...}`{.C}.
- Une variable est dans la portée **après** avoir été déclarée.
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
## Exemple
```C
int bar() { // x, y pas visibles ici, max oui }
int foo() {
int x = 1; // x est locale à foo
{
// x est visible ici, y pas encore
int y = 2;
bar(); // ni x ni y sont visible dans bar()
} // y est détruite à la sortie du bloc
} // x est à la sortie de foo
float max; // variable globale accessible partout
int main() {
int z; // locale, à main
} // z est détruite ici, max aussi
```
<!-- TODO: quiz, compile, compile pas -->
<!-- ```C
int main() {
global = 1;
} // COMPILE PAS
```
```C
int main() {
int global = 1;
{
printf("global = %d", global);
}
} // COMPILE
```
```C
int local;
int main() {
local = 1;
{
printf("local = %d", local);
}
} // COMPILE
```
```C
#include <stdio.h>
int local = 0;
int main() {
int local = -1;
{
int local = 1;
printf("local = %d\n", local);
}
} // COMPILE
``` -->
# Quiz: compile ou compile pas?
## [Quiz: compile ou compile pas](https://cyberlearn.hes-so.ch/mod/evoting/view.php?id=1033948)
- La fonction `printf()`{.C} permet d'afficher du texte sur le terminal:
```C
int printf(const char *format, ...);
```
- Nombre d'arguments variables.
- `format`{.C} est le texte, ainsi que le format (type) des variables à afficher.
- Les arguments suivants sont les expressions à afficher.
```C
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("Hello world.\n");
int val = 1;
printf("Hello world %d time.\n", val);
printf("%f squared is equal to %f.\n", 2.5, 2.5*2.5);
return EXIT_SUCCESS;
}
```
- La fonction `scanf()`{.C} permet de lire du texte formaté entré au clavier:
```C
int scanf(const char *format, ...);
```
- `format`{.C} est le format des variables à lire (comme `printf()`{.C}).
- Les arguments suivants sont les variables où sont stockées les valeurs lues.
```C
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("Enter 3 numbers: \n");
int i, j, k;
scanf("%d %d %d", &i, &j, &k);
printf("You entered: %d %d %d\n", i, j, k);
return EXIT_SUCCESS;
}
```