-
orestis.malaspin authoredorestis.malaspin authored
- Représentation des variables en mémoire (1/N)
- La mémoire
- Une variable
- Représentation des variables en mémoire (2/N)
- Les pointeurs (1/N)
- Les pointeurs (2/N)
- Les pointeurs (3/N)
- Quiz: Les pointeurs
- Quiz: Les pointeurs
- La fonction sizeof() (1/N)
- La fonction sizeof() (2/N)
- Les fonctions (1/N)
- Les fonctions (2/N)
- Exemple
- Les fonctions (3/N)
- Les fonctions (4/N)
- Prototypes de fonctions
- Les fonctions (5/N)
- Arguments de fonctions
- Les fonctions (6/N)
- Arguments de fonctions: pointeurs
- Les fonctions (7/N)
- Exemple
- Quiz: Les fonctions
- Quiz: Les fonctions
- La fonction main() (1/N)
- Généralités
- La fonction main() (2/N)
- Exemple
- Les tableaux (1/N)
- Généralités
- Les tableaux (2/N)
- Exemple
- Les tableaux (3/N)
- Itérer sur les éléments d'un tableau
- Les tableaux (4/N)
- Les tableaux comme argument
- Les tableaux (5/N)
- Quels sont les bugs dans ce code?
- Les tableaux (6/N)
- Quels sont les bugs dans ce code?
% Base II % Inspirés des slides de F. Glück % 25 septembre 2019
Représentation des variables en mémoire (1/N)
La mémoire
-
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.
Une variable
- 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.
- un type (
Représentation des variables en mémoire (2/N)
Les pointeurs (1/N)
-
Un pointeur est une adresse mémoire.
type *id;
-
Pour interpréter le contenu de ce qu'il pointe, il doit être typé.
-
Un pointeur n'est rien d'autre qu'un entier (64bit sur x86-64, soit 8 octets).
-
Un pointeur peut être déréférencé: on accède à la valeur située à l'adresse mémoire sur laquelle il pointe.
char *c; // déclaration pointeur de char *c = 'a'; // assign. 'a' à valeur pointée par c c = 1000; // on modifie l'adresse pointée par c char d = *c; // on lit la valeur pointée par c. UB!
-
NULL
{.C} (ou0
{.C}) est la seule adresse toujours invalide.
Les pointeurs (2/N)
Les pointeurs (3/N)
-
Permettent d'accéder à une valeur avec une indirection.
int a = 2; int *b = &a; *b = 7; // on met 7 dans la case pointée par b // ici a == 7 aussi a = -2; // ici *b == -2 aussi
-
Permettent d'avoir plusieurs chemins d'accès à une valeur.
-
Lire et écrire en même temps dans un bout de mémoire devient possible: danger.
Quiz: Les pointeurs
Quiz: Les pointeurs
sizeof()
(1/N)
La fonction - La fonction
sizeof()
{.C} permet de connaître la taille en octets:- d'une valeur,
- d'un type,
- d'une variable.
- Soit
int a = 2
{.C}, sur l'architecture x86_64 que vaut:-
sizeof(a)
{.C}? -
sizeof(&a)
{.C}?
-
- Soit
char b = 2
{.C},-
sizeof(b)
{.C}? -
sizeof(&b)
{.C}?
-
sizeof()
(2/N)
La fonction - Réponses:
-
sizeof(a) == 4
{.C},int
{.C} entier 32 bits. -
sizeof(&a) == 8
{.C}, une adresse est de 64 bits. -
sizeof(b) == 1
{.C},char
{.C} entier 8 bits. -
sizeof(&b) == 8
{.C}, une adresse est de 64 bits.
-
Les fonctions (1/N)
-
Les parties indépendantes d'un programme.
-
Permettent de modulariser et compartimenter le code.
-
Syntaxe:
type identificateur(paramètres) { // variables optionnelles instructions; // type expression == type return expression; }
Les fonctions (2/N)
Exemple
int max(int a, int b) {
if (a > b) {
return a;
} else {
return b;
}
}
int main() {
int c = max(4, 5);
}
Les fonctions (3/N)
-
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:
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/N)
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.
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/N)
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.
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/N)
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/N)
Exemple
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
main()
(1/N)
La fonction 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} etEXIT_FAILURE
{.C} (destdlib.h
) sont des valeurs de retour portables de programmes C.
main()
(2/N)
La fonction Exemple
int main() {
// ...
if (error)
return EXIT_FAILURE;
else
return EXIT_SUCCESS;
}
- Le code d'erreur est lu dans le shell avec
$?
{.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é
Les tableaux (1/N)
Généralités
-
C
offre uniquement des tableaux statiques- Un tableau est un "bloc" de mémoire contiguë associé à un nom
- taille fixe déterminée à la déclaration du tableau
- la taille ne peut pas être changée.
- Pas d’assignation de tableaux.
- Un tableau déclaré dans une fonction ou un bloc est détruit à la sortie de celle/celui-ci
- \Rightarrow Un tableau local à une fonction ne doit jamais être retourné (aussi valable pour toute variable locale)!
- Un tableau est un "bloc" de mémoire contiguë associé à un nom
- 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.
Les tableaux (2/N)
Exemple
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
Les tableaux (3/N)
Itérer sur les éléments d'un tableau
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)
Les tableaux (4/N)
Les tableaux comme argument
-
Un tableau 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.
void foo(int tab[]) { // sans taille... for (int i = 0; i < ?; ++i) { // on sait pas quoi mettre pour ? printf("tab[%d] = %d\n", i, tab[i]); } } // n doit venir avant tab, [n] optionnel void bar(int n, int tab[n]) { for (int i = 0; i < n; ++i) { printf("tab[%d] = %d\n", i, tab[i]); } }
Les tableaux (5/N)
Quels sont les bugs dans ce code?
#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;
}
Les tableaux (6/N)
Quels sont les bugs dans ce code?
#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;
}