Questions INGINIOUS

  1. Le premier exercice INGINIOUS porte sur le heap et le stack : https://inginious.info.ucl.ac.be/course/LSINF1252/stack_vs_heap
  2. malloc(3) est une fonction clé en C puisqu’elle permet d’allouer une zone de mémoire. Elle a l’inconvénient de ne pas initialiser cette mémoire, contrairement à calloc(3). Lisez les pages de manuel de ces deux fonctions et implémentez vous-même la fonction calloc(3) en utilisant malloc(3) : https://inginious.info.ucl.ac.be/course/LSINF1252/calloc2
  3. Lorsque l’on utilise les fonctions de la librairie ou les appels systèmes, il est nécessaire de vérifier chaque fois leur valeur de retour pour éviter tout problème. Dans cet exercice, vous écrivez une variante de malloc(3) qui essaye plusieurs fois d’allouer de la mémoire pour pallier à un problème temporaire de manque de mémoire : https://inginious.info.ucl.ac.be/course/LSINF1252/sleep_malloc
  4. La question suivante porte sur les déclarations de types de données (faites seulement les 7 premières sous-questions) : https://inginious.info.ucl.ac.be/course/LSINF1252/types
  5. strcpy(3) est une fonction de la librairie standard qui permet de copier une chaîne de caractères. Cet exercice vous propose d’écrire une variante de cette fonction : https://inginious.info.ucl.ac.be/course/LSINF1252/strcpy
  6. Lorsque l’on travaille avec les pointeurs, il est possible d’accéder à n’importe quel endroit de la mémoire. Cet exercice vous permet de tester vos compétences de manipulation des pointeurs: https://inginious.info.ucl.ac.be/course/LSINF1252/pointer_types
  7. Un exercice classique pour montrer que l’on comprend bien les pointeurs est de manipuler des listes chainées: https://inginious.info.ucl.ac.be/course/LSINF1252/basic_linked_list
  8. Un exercice sur le parcours simple d’un arbre binaire de recherche https://inginious.info.ucl.ac.be/course/LSINF1252/BST
  9. Un exercice où vous devez analyser l’information reçue d’un modem : https://inginious.info.ucl.ac.be/course/LSINF1252/modem_read
  10. Maintenant que vous avez écrit de nombreuses fonctions sur INGINIOUS, il est temps pour vous d’écrire votre premier programme directement en C. Utilisez un éditeur de texte pour écrire le fichier test.c qui implémente un sous-ensemble du programme standard test(1). Pensez à structurer votre code en utilisant des sous-fonctions. Compilez votre programme sur votre ordinateur avant de le soumettre sur INGINIOUS. https://inginious.info.ucl.ac.be/course/LSINF1252/commandetest

Verifiez vos réponses

Questions complémentaires

  1. En C, on peut définir des tableaux à deux dimensions avec une déclaration comme int a[3][3];. Ecrivez un petit programme qui utilise des pointeurs pour déterminer si un tel tableau à deux dimensions est stocké ligne par ligne ou colonne par colonne.

  2. Exécutez plusieurs fois le code suivant. Expliquez les différents résultats obtenus.
    int global;
    void main(void)
    {
            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);
    }
    
  3. 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 /Programmes/src/string.c. Utilisez gdb 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:

    • 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

  4. 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 ]).

    struct vector_t {
      int size;
      float *v;
    };
    // initialise le vecteur à la valeur du réel
    struct vector_t * init(int, float) ;
    // récupère le nième élément
    float get(struct vector_t *, int) ;
    // fixe la valeur du nième élément
    void set(struct vector_t *, int , float);
    // supprime un vecteur
    void destroy(struct vector_t *);
    
    
  5. 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 ?

Questions de bilan final

  1. Lisez attentivement le code suivant et essayez de deviner ce qui sera affiché sur la sortie standard. Ensuite, compilez le code en activant l’option -fno-stack-protector de gcc(1) et exécutez le code. Avez-vous bien deviné ? Comment expliquez-vous les lignes affichées par le programme ?

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdint.h>
    
    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();
    }
    
  2. Question ouverte. Soit la structure pair_t suivante :

    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) ?