Skip to content
Snippets Groups Projects
Verified Commit 3ca29947 authored by orestis.malaspin's avatar orestis.malaspin
Browse files
parents ef39b810 2a432611
Branches
No related tags found
No related merge requests found
......@@ -4,7 +4,7 @@ PDFOPTIONS += --highlight-style my_highlight.theme
PDFOPTIONS += --pdf-engine pdflatex
PDFOPTIONS += -V theme:metropolis
PDFOPTIONS += -V themeoptions:numbering=none -V themeoptions:progressbar=foot
PDFOPTIONS += -V fontsize=smaller
# PDFOPTIONS += -V fontsize=smaller
PDFOPTIONS += -V urlcolor=blue
REVEALOPTIONS = -t revealjs
......
......@@ -74,6 +74,68 @@ if (x) { // si x s'évalue à `vrai`
}
```
# Structures de contrôle: `continue`{.C}, `break`{.C}
- `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);
}
```
# La fonction `main()` (1/2)
## Généralités
- Point d'entrée du programme.
- Retourne le code d'erreur du programme:
- 0: tout s'est bien passé.
- Pas zéro: problème.
- La valeur de retour peut être lue par le shell qui a exécuté le programme.
- `EXIT_SUCCESS`{.C} et `EXIT_FAILURE`{.C} (de `stdlib.h`) sont des valeurs de retour **portables** de programmes C.
# La fonction `main()` (2/2)
## Exemple
```C
int main() {
// ...
if (error)
return EXIT_FAILURE;
else
return EXIT_SUCCESS;
}
```
- Le code d'erreur est lu dans le shell avec `$?`{.bash}
```bash
$ ./prog
$ echo $?
0 # tout s'est bien passé par exemple
$ if [ $? -eq 0 ]; then echo "OK" ; else echo "ERROR"; fi
ERROR # si tout s'est mal passé
```
# Entrées/sorties: `printf()`{.C} (1/2)
## Généralités
......@@ -135,65 +197,29 @@ int main() {
}
```
# Structures de contrôle: `continue`{.C}, `break`{.C}
- `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);
}
```
# La fonction `main()` (1/2)
## Généralités
- Point d'entrée du programme.
- Retourne le code d'erreur du programme:
- 0: tout s'est bien passé.
- Pas zéro: problème.
- La valeur de retour peut être lue par le shell qui a exécuté le programme.
- `EXIT_SUCCESS`{.C} et `EXIT_FAILURE`{.C} (de `stdlib.h`) sont des valeurs de retour **portables** de programmes C.
# Nombres aléatoires: `rand()`, `srand()`{.C} (2/2)
# La fonction `main()` (2/2)
```
$ man 3 rand
The rand() function returns a pseudo-random integer
in the range 0 to RAND_MAX inclusive (i.e., the
mathematical range [0, RAND_MAX]).
```
## Exemple
```C
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
// ...
if (error)
return EXIT_FAILURE;
else
return EXIT_SUCCESS;
srand(0); // every run will be identical
srand(time(NULL)); // every run will be be different
for (int i = 0; i < 50; ++i) {
printf("%d\n", rand() % 50);
}
return EXIT_SUCCESS;
}
```
- Le code d'erreur est lu dans le shell avec `$?`{.bash}
```bash
$ ./prog
$ echo $?
0 # tout s'est bien passé par exemple
$ if [ $? -eq 0 ]; then echo "OK" ; else echo "ERROR"; fi
ERROR # si tout s'est mal passé
```
Source diff could not be displayed: it is too large. Options to address this: view the blob.
---
title: "Structures"
date: "2021-10-19"
patat:
wrap: true
margins:
left: 10
right: 10
---
# Types composés: `struct`{.C} (1/6)
## Fractions
* Numérateur: `int num`;
* Dénominateur: `int denom`.
## Addition
```C
int num1 = 1, denom1 = 2;
int num2 = 1, denom2 = 3;
int num3 = num1 * denom2 + num2 * denom1;
int denom3 = denom1 * denom2;
```
## Pas super pratique....
# Types composés: `struct`{.C} (2/6)
## On peut faire mieux
* Plusieurs variables qu'on aimerait regrouper dans un seul type: `struct`{.C}.
```C
struct fraction { // déclaration du type
int32_t num, denom;
};
struct fraction frac; // déclaration de frac
```
# Types composés: `struct`{.C} (3/6)
## Simplifications
- `typedef`{.C} permet de définir un nouveau type.
```C
typedef unsinged int uint;
typedef struct fraction fraction_t;
typedef struct fraction {
int32_t num, denom;
} fraction_t;
```
- L'initialisation peut aussi se faire avec
```C
fraction_t frac = {1, -2}; // num = 1, denom = -2
fraction_t frac = {.denom = 1, .num = -2};
fraction_t frac = {.denom = 1}; // argl! .num non initialisé
fraction_t frac2 = frac; // copie
```
# Types composés: `struct`{.C} (4/6)
## Pointeurs
- Comme pour tout type, on peut avoir des pointeurs vers un `struct`{.C}.
- Les champs sont accessible avec le sélecteur `->`{.C}
```C
fraction_t *frac; // on crée un pointeur
frac->num = 1; // seg fault...
frac->denom = -1; // mémoire pas allouée.
```
![La représentation mémoire de
`fraction_t`.](figs/pointer_struct.svg){width=50%}
# Types composés: `struct`{.C} (5/6)
## Initialisation
- Avec le passage par **référence** on peut modifier un struct *en place*.
- Les champs sont accessible avec le sélecteur `->`{.C}
```C
void fraction_init(fraction_t *f,
int32_t num, int32_t denom)
{
// f a déjà été allouée
f->num = num;
f->denom = denom;
}
int main() {
fraction_t frac; // on alloue une fraction
fraction_init(&frac, 2, -1); // on l'initialise
}
```
# Types composés: `struct`{.C} (6/6)
## Initialisation version copie
* On peut allouer une fraction, l'initialiser et le retourner.
* La valeur retournée peut être copiée dans une nouvelle structure.
```C
fraction_t fraction_create(int32_t num, int32_t denom) {
fraction_t f;
f.num = num; f.denom = denom;
return f;
}
int main() {
// on crée une fraction et on l'initialise
// en copiant la fraction créé par fraction_create
// deux allocation et une copie
fraction_t frac = fraction_create(2, -1);
}
```
---
title: "Tableaux et fonctions"
date: "2021-10-12"
patat:
wrap: true
margins:
left: 10
right: 10
---
# Les tableaux statiques
## Qu'est-ce qu'un tableau statique?
. . .
* Une **liste** ou un **ensemble**
. . .
* d'éléments du **même type**
. . .
* alloués de façon **contigüe** (en bloc) en mémoire
. . .
* sur la **pile**
. . .
* dont la taille **ne peut pas** être changée.
. . .
## Remarques
- Les éléments d’un tableau sont accédés avec `[i]`{.C} où `i`{.C} est l’index de l’élément.
- Le premier élément du tableau à l’index `0`{.C}!
- Lorsqu’un tableau est déclaré, la taille de celui-ci doit toujours être spécifiée, sauf s’il est initialisé lors de sa déclaration.
- Un tableau local à une fonction ne doit **jamais être retourné**!
# Syntaxe des tableaux
```C
float tab1[5]; // tableau de floats à 5 éléments
// ses valeurs sont indéfinies
int tab2[] = {1, 2, 3}; // tableau de 3 entiers,
// taille inférée
int val = tab2[1]; // val vaut 2 à présent
int w = tab1[5]; // index hors des limites du tableau
// comportement indéfini!
// pas d'erreur du compilateur
```
<!-- TODO QUIZ:
```C
int a1[5]; // OK
int a2[] = { 1, 2, 3 }; // OK
int a3[4][5]; // OK
int [] a4; // Erreur
int a5[]; // Erreur
int[] function(void) { // Erreur
int array[5]; // OK
return array; // Erreur
}
void foo(int a[]) { // OK
a[3] = 0; // OK
}
void bar(void) {
int a[5]; // OK
foo(a); // OK
a = a[5]; // Erreur
}
``` -->
<!-- ```C
#include <stdio.h>
int main(void) {
char i;
char a1[] = { 100,200,300,400,500 };
char a2[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
a2[10] = 42;
for (i = 0; i < 5; i++) {
printf("a1[%d] = %d\n", i, a1[i]);
}
return 0;
}
``` -->
# Itérer sur les éléments d'un tableau
```C
int x[10];
for (int i = 0; i < 10; ++i) {
x[i] = 0;
}
int j = 0;
while (j < 10) {
x[j] = 1;
j += 1;
}
int j = 0;
do {
x[j] = -1;
j += 1;
} while (j < 9)
```
# Représentation des tableaux en mémoire
## La mémoire est :
- ... contigüe,
- ... accessible très rapidement
## Exemple:
```C
char tab[4] = {79, 91, 100, 88};
```
+------+------+------+------+------+------+------+
| char | 79 | 91 | 100 | 88 | .... | .... |
+======+======+======+======+======+======+======+
| addr | 2000 | 2001 | 2002 | 2003 | .... | .... |
+------+------+------+------+------+------+------+
## Qu'est-ce que `tab`?
```C
tab; // 2000, l'adress du 1er élément
&tab[0]; // 2000 == tab
tab[0]; // 79
sizeof(tab); // 4
```
# Les tableaux comme arguments de fonctions
- Un tableau en argument est le pointeur vers sa première case.
- Pas moyen de connaître sa taille: `sizeof()`{.C} inutile.
- Toujours spécifier la taille d'un tableau passé en argument.
```C
void foo(int tab[]) { // équivalent à int *tab
// que vaut sizeof(tab)?
for (int i = 0; i < ?; ++i) { // taille?
printf("tab[%d] = %d\n", i, tab[i]);
}
}
void bar(int n, int tab[n]) { // [n] optionnel
for (int i = 0; i < n; ++i) {
printf("tab[%d] = %d\n", i, tab[i]);
}
}
```
# Quels sont les bugs dans ce code?
```C
#include <stdio.h>
int main(void) {
char i;
char a1[] = { 100,200,300,400,500 };
char a2[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
a2[10] = 42;
for (i = 0; i < 5; i++) {
printf("a1[%d] = %d\n", i, a1[i]);
}
return 0;
}
```
# Quels sont les bugs dans ce code?
```C
#include <stdio.h>
int main(void) {
char i;
// 200, .., 500 char overflow
char a1[] = { 100,200,300,400,500 };
char a2[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
a2[10] = 42; // [10] out of bounds
for (i = 0; i < 5; i++) {
printf("a1[%d] = %d\n", i, a1[i]);
}
return 0;
}
```
<!-- TODO quiz: -->
<!-- que retourne sizeof(tab[]) -->
---
title: "Variables et fonctions"
date: "2021-10-05"
patat:
wrap: true
margins:
left: 10
right: 10
---
# Les variables
## Qu'est-ce qu'une variable?
. . .
* Un **identifiant**
. . .
* pour un **espace de stockage**
. . .
* contenant un **valeur**.
## En général, une variable possède:
* Un **type** (`int`, `double`, ...);
* une **adresse mémoire** (voir ci-après).
# Représentation des variables en mémoire (1/3)
## La mémoire est :
- ... un ensemble de bits,
- ... accessible via des adresses,
+------+----------+----------+------+----------+------+------+
| bits | 00110101 | 10010000 | .... | 00110011 | .... | .... |
+======+==========+==========+======+==========+======+======+
| addr | 2000 | 2001 | .... | 4000 | .... | .... |
+------+----------+----------+------+----------+------+------+
- ... gérée par le système d'exploitation.
- ... séparée en deux parties: **la pile** et **le tas**.
## Pile vs tas
* Pile structurée, tas non-structuré;
* Pile ordonnée, tas amas informe;
* Pile taille connue à la compilation, tas dynamique.
# Représentation des variables en mémoire (2/3)
## Une variable, `type a = valeur`{.C}, possède:
- un type (`char`{.C}, `int`{.C}, ...),
- un contenu (une séquence de bits qui encode `valeur`{.C}),
- une adresse mémoire (accessible via `&a`{.C}),
- une portée.
## En fonction du **type** les bits ne représentent pas la même chose!
# Représentation des variables en mémoire (3/3)
![Les variables en mémoire.](figs/memory.svg){width=100%}
# Les fonctions (1/7)
- Les parties indépendantes d'un programme.
- Permettent de modulariser et compartimenter le code.
- Syntaxe:
```C
type identificateur(paramètres) {
// variables optionnelles
instructions;
// type expression == type
return expression;
}
```
# Les fonctions (2/7)
## Exemple
```C
int max(int a, int b) {
if (a > b) {
return a;
} else {
return b;
}
}
int main() {
int c = max(4, 5);
}
```
# Les fonctions (3/7)
- Il existe un type `void`{.C}, "sans type", en C.
- Il peut être utilisé pour signifier qu'une fonction ne retourne rien, ou qu'elle n'a pas d'arguments.
- `return`{.C} utilisé pour sortir de la fonction.
- Exemple:
```C
void show_text(void) { // second void optionnel
printf("Aucun argument et pas de retour.\n");
return; // optionnel
}
void show_text_again() { // c'est pareil
printf("Aucun argument et pas de retour.\n");
}
```
# Les fonctions (4/7)
## Prototypes de fonctions
- Le prototype donne la **signature** de la fonction, avant qu'on connaisse son implémentation.
- L'appel d'une fonction doit être fait **après** la déclaration du prototype.
```C
int max(int a, int b); // prototype
int max(int a, int b) { // implémentation
if (a > b) {
return a;
} else {
return b;
}
}
```
# Les fonctions (5/7)
## Arguments de fonctions
- Les arguments d'une fonction sont toujours passés **par copie**.
- Les arguments d'une fonction ne peuvent **jamais** être modifiés.
```C
void set_to_two(int a) { // a: nouvelle variable
// valeur de a est une copie de x
// lorsque la fonction est appelée, ici -1
a = 2; // la valeur de a est fixée à 2
} // a est détruite
int main() {
int x = -1;
set_to_two(x); // -1 est passé en argument
// x vaudra toujours -1 ici
}
```
# Les fonctions (6/7)
## Arguments de fonctions: pointeurs
- Pour modifier un variable, il faut passer son **adresse mémoire**.
- L'adresse d'une variable, `x`{.C}, est accédé par `&x`{.C}.
- Un **pointeur** vers une variable entière a le type, `int *x`{.C}.
- La syntaxe `*x`{.C} sert à **déréférencer** le pointeur (à accéder à la mémoire pointée).
# Les fonctions (7/7)
## Exemple
```C
void set_to_two(int *a) {
// a contient une copie de l'adresse de la
// variable passée en argument
*a = 2; // on accède à la valeur pointée par a,
// et on lui assigne 2
} // le pointeur est détruit, pas la valeur pointée
int main() {
int x = -1;
set_to_two(&x); // l'adresse de x est passée
// x vaudra 2 ici
}
```
# Quiz: Les fonctions
## [Quiz: Les fonctions](https://cyberlearn.hes-so.ch/mod/evoting/view.php?id=1038560)
<!-- TODO quiz;
```C
void set_to_two(int *a) {
a = 2;
}
int main() {
int x = -1;
set_to_two(&x);
}
void add_two(int *a) {
*a += 2;
}
int main() {
int x = -1;
add_two(&x);
}
void add_two(int a) {
a += 2;
printf("%d", a);
}
int main() {
int x = -1;
add_two(&x);
}
``` -->
# Comment lire une signature?
Que peut-on déduire de la signature de la fonction suivante:
```C
int32_t bissect(double a1, double b1, double epsilon,
double *zero);
```
. . .
Une fonction prenant trois `double` en argument, et un pointeur de
`double` (il est donc modifiable) et retournant un entier.
Un site permettant de faire ce genre de traductions:
<https://cdecl.org/>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment