diff --git a/Exercices/Programmes/prog-3.rst b/Exercices/Programmes/prog-3.rst index 90ec44815efec915143a920ef399e5126054ff81..5a66a98e9d200bed4b5a2cbf0c50a9261cb0a6bc 100644 --- a/Exercices/Programmes/prog-3.rst +++ b/Exercices/Programmes/prog-3.rst @@ -64,128 +64,172 @@ Questions complémentaires .. only:: staff - .. note:: + .. note:: - L'adresse de ``global`` ne change pas, car elle fait partie du segment texte du programme. Les autres sont soit sur la pile (stack), ou sur le tas (heap). + L'adresse de ``global`` ne change pas, car elle fait partie du segment texte du programme. Les autres sont soit sur la pile (stack), ou sur le tas (heap). #. Un étudiant a fait l'implémentation d'un sous-ensemble des fonctions définies dans string.h, mais il rencontre quelques problèmes avec son code :download:`/Programmes/src/string.c`. Utilisez `gdb <http://sites.uclouvain.be/SystInfo/notes/Outils/html/gdb.html>`_ pour corriger son code. Utilisez le flag ``-g`` de ``gcc`` pour ajouter les informations de debug dans votre executable. Pour rappel, voici quelques commandes importantes de `gdb <http://sites.uclouvain.be/SystInfo/notes/Outils/html/gdb.html>`_: - - ``run [ARGS]`` permet de lancer l'execution du programme avec les arguments ARGS si spécifiés. - - ``break string.c:9`` met un point d'arrêt à la ligne 9 du fichier string.c - - ``next`` permet d'executer la ligne courante et de s'arrêter à la ligne suivante - - ``print var`` affiche la valeur de la variable ``var`` - - ``backtrace`` affiche la pile d'appel des fonctions courantes - - ``quit`` quitte `gdb <http://sites.uclouvain.be/SystInfo/notes/Outils/html/gdb.html>`_ + - ``run [ARGS]`` permet de lancer l'execution du programme avec les arguments ARGS si spécifiés. + - ``break string.c:9`` met un point d'arrêt à la ligne 9 du fichier string.c + - ``next`` permet d'executer la ligne courante et de s'arrêter à la ligne suivante + - ``print var`` affiche la valeur de la variable ``var`` + - ``backtrace`` affiche la pile d'appel des fonctions courantes + - ``quit`` quitte `gdb <http://sites.uclouvain.be/SystInfo/notes/Outils/html/gdb.html>`_ - .. only:: staff + .. only:: staff .. note:: 4 erreurs: strlen ne check pas NULL, strlen appelé à chaque itération de strcat, argc pas vérifié, concat_2 pas initialisé - -#. Vous travaillez sur un programme qui doit manipuler des vecteurs. Afin de pouvoir supporter des vecteurs de taille quelconque, vous décidez de réimplémenter ces vecteurs vous même en utilisant des pointeurs. Votre programme définit la structure ``struct vector_t`` et les fonctions ci-dessous. -Implémentez ces fonctions sans jamais utiliser la notation des tableaux en C (``[`` et ``]``). +#. Vous travaillez sur un programme qui doit manipuler des vecteurs. Afin de pouvoir supporter des vecteurs de taille quelconque, vous décidez de réimplémenter ces vecteurs vous même en utilisant des pointeurs. Votre programme définit la structure ``struct vector_t`` et les fonctions ci-dessous. + Implémentez ces fonctions sans jamais utiliser la notation des tableaux en C (``[`` et ``]``). - .. literalinclude:: /Programmes/src/vector.c - :encoding: utf-8 - :language: c - :start-after: ///AAA - :end-before: ///BBB - -.. only:: staff + .. literalinclude:: /Programmes/src/vector.c + :encoding: utf-8 + :language: c + :start-after: ///AAA + :end-before: ///BBB - #. Faites l'exercice relatif aux `linked lists <https://inginious.info.ucl.ac.be/course/LSINF1252/linked_lists_1>`_ sur INGInious. -.. only:: staff + .. only:: staff - #. Expliquez la différence entre `malloc(3)`_ et `calloc(3)`_. D'après vous, quel appel sera le plus lent ? + #. Faites l'exercice relatif aux `linked lists <https://inginious.info.ucl.ac.be/course/LSINF1252/linked_lists_1>`_ sur INGInious. -.. only:: staff - #. Dans la fonction ``push`` du programme de manipulation d'un pile :download:`/../Theorie/C/S3-src/stack.c`, faut-il remplacer l'appel à `malloc(3)`_ par un appel à `calloc(3)`_ + .. only:: staff - .. note:: + #. Expliquez la différence entre `malloc(3)`_ et `calloc(3)`_. D'après vous, quel appel sera le plus lent ? - Non. La zone mémoire est initialisée directement après. + .. only:: staff -.. only:: staff + #. Dans la fonction ``push`` du programme de manipulation d'un pile :download:`/../Theorie/C/S3-src/stack.c`, faut-il remplacer l'appel à `malloc(3)`_ par un appel à `calloc(3)`_ - #. Le prototype de la fonction ``push`` du programme de manipulation d'une pile :download:`/../Theorie/C/S3-src/stack.c`, est ``void push(struct fraction_t *)``. Serait-il possible d'écrire une function push ayant comme prototype ``void push(struct fraction_t)`` ? Qu'est-ce qui changerait dans ce cas ? + .. note:: - .. note:: + Non. La zone mémoire est initialisée directement après. - Pas possible. + .. only:: staff -.. only:: staff + #. Le prototype de la fonction ``push`` du programme de manipulation d'une pile :download:`/../Theorie/C/S3-src/stack.c`, est ``void push(struct fraction_t *)``. Serait-il possible d'écrire une function push ayant comme prototype ``void push(struct fraction_t)`` ? Qu'est-ce qui changerait dans ce cas ? - #. Les fonctions ``push`` et ``pop`` définies dans l'exemple de manipulation d'une pile :download:`/../Theorie/C/S3-src/stack.c` utilisent une pile qui est définie par un pointeur qui est une variable globale. Est-il possible de réécrire ces fonctions de façon à ce qu'elles prennent comme argument un pointeur vers la pile ? Leurs prototypes deviendraient : + .. note:: - - ``void push(struct node_t *, struct fraction_t *);`` - - ``struct fraction_t * pop(struct node_t *);`` + Pas possible. - .. note:: + .. only:: staff - Oui, idéalement dans ce cas, il faudrait définir une fonction init qui renverrait un ``struct node_t *``. + #. Les fonctions ``push`` et ``pop`` définies dans l'exemple de manipulation d'une pile :download:`/../Theorie/C/S3-src/stack.c` utilisent une pile qui est définie par un pointeur qui est une variable globale. Est-il possible de réécrire ces fonctions de façon à ce qu'elles prennent comme argument un pointeur vers la pile ? Leurs prototypes deviendraient : -.. only:: staff - + - ``void push(struct node_t *, struct fraction_t *);`` + - ``struct fraction_t * pop(struct node_t *);`` - #. Considérons la structure suivante: + .. note:: - .. code-block:: c + Oui, idéalement dans ce cas, il faudrait définir une fonction init qui renverrait un ``struct node_t *``. - typedef struct { - char c; - long l; - short s; - } test_t; + .. only:: staff - Combien de bytes seront utilisés en mémoire pour représenter cette structure? Représentez graphiquement la position en mémoire de chaque élément (utilisez `printf(3)`_ et ``%p``), observez-vous des trous ? Expliquez. - Serait-il possible d'utiliser moins de bytes pour représenter cette structure ? Si oui, comment ? + #. Considérons la structure suivante: - .. note:: + .. code-block:: c - La structure prend 16 bytes d'espace (sur une machine 64-bits - 12 bytes sur une machine 32 bits). C'est dû au fait que les champs sont alignés à des multiples de 64 bits (resp. 32 bits) pour éviter d'avoir des parties de variables copié sur plusieurs registres. Pour optimiser, il suffit de reordonner les champs. Par exemple: + typedef struct { + char c; + long l; + short s; + } test_t; - .. code-block:: c - typedef struct { - char c; - short s; - long l; - } test_t; + Combien de bytes seront utilisés en mémoire pour représenter cette structure? Représentez graphiquement la position en mémoire de chaque élément (utilisez `printf(3)`_ et ``%p``), observez-vous des trous ? Expliquez. + Serait-il possible d'utiliser moins de bytes pour représenter cette structure ? Si oui, comment ? -.. only:: staff + .. note:: - .. note:: + La structure prend 16 bytes d'espace (sur une machine 64-bits - 12 bytes sur une machine 32 bits). C'est dû au fait que les champs sont alignés à des multiples de 64 bits (resp. 32 bits) pour éviter d'avoir des parties de variables copié sur plusieurs registres. Pour optimiser, il suffit de reordonner les champs. Par exemple: - Elle bypasse l'alignement décrit dans la question précédente. L'avantage est que la structure prend l'espace minimale n'importe soit l'ordonnancement des champs. Le désavatage est que la CPU doît faire plus de travail pour lire la variable, car des parties de la variable sont sur plusieurs registres, et donc la CPU doît faire des bit-shifts. + .. code-block:: c + typedef struct { + char c; + short s; + long l; + } test_t; + .. only:: staff + .. note:: + + Elle bypasse l'alignement décrit dans la question précédente. L'avantage est que la structure prend l'espace minimale n'importe soit l'ordonnancement des champs. Le désavatage est que la CPU doît faire plus de travail pour lire la variable, car des parties de la variable sont sur plusieurs registres, et donc la CPU doît faire des bit-shifts. #. Expliquez à quoi sert l'attribut ``packed`` des structures dans `gcc(1)`_ (regardez la manpage). Appliquez cet attribut à la structure de l'exercice précédent. Qu'observez-vous comme différence ? Quel sont les avantages et désavantages d'utiliser cet attribut ? Dans quel cas est-il intéressant de l'utiliser ? + .. only:: staff - .. note:: + .. note:: - Il bypasse l'alignement décrit dans la question précédente. L'avantage est que la structure prend l'espace minimal n'importe soit l'ordonnancement des champs. Le désavantage est que la CPU doît faire plus de travail pour lire la variable, car des parties de la variable sont sur plusieurs registres, et donc la CPU doît faire des bit-shifts. + Il bypasse l'alignement décrit dans la question précédente. L'avantage est que la structure prend l'espace minimal n'importe soit l'ordonnancement des champs. Le désavantage est que la CPU doît faire plus de travail pour lire la variable, car des parties de la variable sont sur plusieurs registres, et donc la CPU doît faire des bit-shifts. -#. Exécutez plusieurs fois le code suivant. Expliquez les différents résultats obtenus. - .. code-block:: c + .. only:: staff - int global; - -int main (int argc, char** argv) - { - int local; - int *ptr1 = (int *)malloc(sizeof(*ptr1)); - int *ptr2 = (int *)malloc(sizeof(*ptr2)); + #. Exécutez plusieurs fois le code suivant. Expliquez les différents résultats obtenus. + .. code-block:: c - printf("global %p loc %p p1 %p p2 %p\n", &global, &local, ptr1, ptr2); - return EXIT_SUCCESS; - } + int global; + int main (int argc, char** argv) + { + int local; + int *ptr1 = (int *)malloc(sizeof(*ptr1)); + int *ptr2 = (int *)malloc(sizeof(*ptr2)); + + printf("global %p loc %p p1 %p p2 %p\n", &global, &local, ptr1, ptr2); + return EXIT_SUCCESS; + } + + +Questions de bilan final +------------------------ + +#. Lisez attentivement le code suivant et essayez de deviner ce qui sera affiché + sur la sortie standard. Ensuite, compilez et exécutez le code. Avez-vous bien + deviné ? Comment expliquez-vous les lignes affichées par le programme ? + + .. code-block:: c + + int f() { + uint32_t zero = 0; + char a = 'a'; + char b = 'b'; + char str[8] = "Hello !"; + printf("1) str = \"%s\",\t\tzero = %d,\ta = %c,\tb = %c\n", str, zero, a, b); + strcpy(str, "I love sour!"); + printf("2) str = \"%s\",\tzero = %d,\ta = %c,\tb = %c\n", str, zero, a, b); + a = 'a'; + b = 'b'; + printf("3) str = \"%s\",\tzero = %d,\ta = %c,\tb = %c\n", str, zero, a, b); + return 0; + } + + int main(int argc, char *argv[]) { + return f(); + } + +#. *Question ouverte*. Soit la structure ``pair_t`` suivante : + + .. code-block:: c + + typedef struct pair { + int a; + int b; + } pair_t; + + + Comment feriez-vous pour stocker dans les + variables ``pair_t *p1, *p2`` les pointeurs vers deux instances de ``pair_t`` + allouées sur le heap de manière contiguë (i.e. les deux structures se suivent + directement dans la mémoire) ?