Spaceuit


Informatique Appliquée

Fillière Intelligence Artificielle

Automne 2024

Ali El Hourch
[email protected]


Guide
Nouveau
Assistant AI
Visual Studio Code Google Classroom Whatsapp Whatsapp Discord Discord
Module 1: Probabilités Et Statistiques Module 2: Architecture Des Ordinateurs Module 3: Structure De Données En C Module 4: Système d'exploitation 1 Module 5: Programmation Web 1 Module 6: Langues Etrangéres Module 7: Compétences Culturelles & Artistiques

Semestres

Automne 2024

Tronc Communs

Informatique Appliquée

Rappel


Hexadécimal


  • L’hexadécimal est un système de comptage qui comporte 16 valeurs. Elles sont les suivantes :
  •   0 1 2 3 4 5 6 7 8 9 a b c d e f
    
  • Remarquez que F représente 15
  • L'hexadécimal est également connu sous le nom de base-16.
  • Lorsque l'on compte en hexadécimal, chaque chiffre est une puissance de 16.
  • Le chiffre 0 est représenté par 00.
  • Le chiffre 1 est représenté par 01.
  • Le chiffre 2 est représenté par 02.
  • Le chiffre 9 est représenté par 09.
  • Le chiffre 10 est représenté par 0A.
  • Le chiffre 15 est représenté par 0F.
  • Le chiffre 16 est représenté par 10.
  • Le nombre 255 est représenté par FF, car 16 x 15 (ou F) est égal à 240. Ajoutez 15 de plus pour obtenir 255. C'est le nombre le plus élevé que vous puissiez compter en utilisant un système hexadécimal à deux chiffres.
  • L'hexadécimal est utile parce qu'il peut être représenté avec moins de chiffres. L'hexadécimal nous permet de représenter les informations de manière plus brève

Mémoire


  • On peut imaginer que la mémoire est constituée de blocs de mémoire simultanés. En appliquant une numérotation hexadécimale à chacun de ces blocs de mémoire.
  • Tous les nombres hexadécimaux sont souvent représentés par le préfixe 0x comme suit :
  • Dans votre editeur de texte, créez une fichier adresses.c et écrivez votre code comme suit :
  • #include <stdio.h>
    
    int main(void)
    {
        int n = 50;
        printf("%i\n", n);
    }
    
  • Remarquez que n est stocké en mémoire avec la valeur 50.
  • Vous pouvez visualiser la façon dont ce programme stocke cette valeur comme suit :
  • Le langage C dispose de deux opérateurs puissants liés à la mémoire :
  •   & Fournit l'adresse d'un élément stocké en mémoire.       
      * Indique au compilateur d'aller à un endroit de la mémoire.
    
  • Nous pouvons tirer parti de ces connaissances en modifiant notre code comme suit :
  • #include <stdio.h>
    
    int main(void)
    {
        int n = 50;
        printf("%p\n", &n);
    }
    
  • Remarquez le %p, qui nous permet de visualiser l'adresse d'un emplacement en mémoire. &n peut être traduit littéralement par « l'adresse de n. ».
  • L'exécution de ce code renverra une adresse de mémoire commençant par 0x.

Pointeurs


  • Un pointeur est une variable qui contient l'adresse d'une certaine valeur. Plus succinctement, un pointeur est une adresse dans la mémoire de votre ordinateur.
  • Considérons le code suivant :
  • int n = 50;
    
    int *p = &n;
    
  • Notez que p est un pointeur qui contient l'adresse d'un entier n.
  • Modifiez votre code comme suit :
  • #include <stdio.h>
    
    int main(void)
    {
        int n = 50;
        int *p = &n;
        printf("%p\n", p);
    }
    
  • Remarquez que ce code a le même effet que notre code précédent. Nous avons simplement mis à profit notre nouvelle connaissance des opérateurs & et *.
  • Pour illustrer l'utilisation de l'opérateur *, considérons ce qui suit :
  • #include <stdio.h>
    
    int main(void)
    {
        int n = 50;
        int *p = &n;
        printf("%i\n", *p);
    }
    
  • Remarquez que la ligne printf imprime l'entier à l'emplacement de p. int *p crée un pointeur dont la fonction est de stocker l'adresse mémoire d'un entier.
  • Vous pouvez visualiser notre code comme suit :
  • Remarquez que le pointeur semble plutôt grand. En effet, un pointeur est généralement stocké sous la forme d'une valeur de 8 octets. p stocke l'adresse du 50.
  • Il est plus facile de se représenter un pointeur comme une adresse qui pointe vers une autre :

Chaînes de caractères


  • Rappelons qu'une chaîne de caractères est simplement un tableau de caractères. Par exemple, char s[] = "HI!" peut être représentée comme suit :
  • Mais qu'est-ce que le s en réalité ? Où est stocké le s dans la mémoire ? Comme vous pouvez l'imaginer, s doit être stocké quelque part. Vous pouvez visualiser la relation entre s et la chaîne de caractères de la manière suivante :
  • Remarquez qu'un pointeur appelé s indique au compilateur où se trouve le premier octet de la chaîne de caractères dans la mémoire.
  • Modifiez votre code comme suit
  • #include <stdio.h>
    
    int main(void)
    {
        char s[] = "HI!";
        printf("%p\n", s);
        printf("%p\n", &s[0]);
        printf("%p\n", &s[1]);
        printf("%p\n", &s[2]);
        printf("%p\n", &s[3]);
    }
    
  • Remarquez que le code ci-dessus affiche les emplacements en mémoire de chaque caractère de la chaîne s. Le symbole & est utilisé pour afficher l'adresse de chaque élément de la chaîne. Lors de l'exécution de ce code, vous remarquerez que les éléments 0, 1, 2 et 3 se trouvent les uns à côté des autres dans la mémoire.
  • De même, vous pouvez modifier votre code comme suit :
  • #include <stdio.h>
    
    int main(void)
    {
        char *s = "HI!";
        printf("%s\n", s);
    }
    
  • Notez que ce code présentera la chaîne de caractères qui commence à l'emplacement de s.

Malloc


  • En programmation, il est souvent nécessaire de copier une chaîne de caractères dans une autre.
  • Dans votre editeur de texte, créez une fichier copier.c et écrivez votre code comme suit :
  • #include <ctype.h>
    #include <stdio.h>
    #include <string.h>
    
    int main(void)
    {
        char s[] = "HI!";
    
        // Copier l'adresse de la chaîne s
        string t = s;
    
        // Mise en majuscule de la première lettre de la chaîne de caractères
        t[0] = toupper(t[0]);
    
        // Afficher s et t
        printf("s: %s\n", s);
        printf("t: %s\n", t);
    }
    
  • Remarquez que la chaîne t = s copie l'adresse de s dans t. Cela n'accomplit pas ce que nous souhaitons. La chaîne n'est pas copiée, seule l'adresse l'est.
  • Vous pouvez visualiser le code ci-dessus comme suit :
  • Remarquez que s et t pointent toujours vers les mêmes blocs de mémoire. Il ne s'agit pas d'une copie authentique d'une chaîne de caractères. Il s'agit plutôt de deux pointeurs pointant sur la même chaîne.
  • Avant de relever ce challenge, il est important de s'assurer que notre code ne présente pas d'erreur de segmentation, en tentant de copier la chaîne s dans la chaîne t, alors que la chaîne t n'existe pas. Pour cela, nous pouvons utiliser la fonction strlen comme suit :
  • #include <ctype.h>
    #include <stdio.h>
    #include <string.h>
    
    int main(void)
    {
        string s = get_string("s: ");
    
        // Copier l'adresse de la chaîne s
        string t = s;
    
        // Mise en majuscule de la première lettre de la chaîne de caractères
        if (strlen(t) > 0)
        {
            t[0] = toupper(t[0]);
        }
    
        // Afficher les deux chaînes
        printf("s: %s\n", s);
        printf("t: %s\n", t);
    }
    
  • Remarquez que strlen est utilisé pour s'assurer que la chaîne t existe. Si ce n'est pas le cas, rien ne sera copié.
  • Pour pouvoir faire une copie authentique de la chaîne de caractères, nous devrons introduire deux nouveaux blocs de construction. Premièrement, malloc vous permet, en tant que programmeur, d'allouer un bloc de mémoire d'une taille spécifique. Deuxièmement, free vous permet de dire au compilateur de libérer le bloc de mémoire que vous avez précédemment alloué.
  • Nous pouvons modifier notre code pour créer une copie authentique de notre chaîne comme suit :
  • #include <ctype.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(void)
    {
        char s[] = "HI!";
    
        // Allouer de la mémoire pour une autre chaîne de caractères
        char *t = malloc(strlen(s) + 1);
    
        // Copier la chaîne de caractères dans la mémoire, y compris '\0'
        for (int i = 0; i <= strlen(s); i++)
        {
            t[i] = s[i];
        }
    
        // Mise en majuscule de la première lettre de la chaîne de caractères
        t[0] = toupper(t[0]);
    
        // Afficher les deux chaînes
        printf("s: %s\n", s);
        printf("t: %s\n", t);
    }
    
  • Remarquez que malloc(strlen(s) + 1) crée un bloc de mémoire de la longueur de la chaîne s plus un. Cela permet d'inclure le caractère null \0 dans notre chaîne finale copiée. Ensuite, la boucle for parcourt la chaîne s et assigne chaque valeur au même endroit de la chaîne t.
  • Il apparaît qu'il y a une inefficacité dans notre code. Modifiez votre code comme suit :
  • #include <ctype.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(void)
    {
        char s[] = "HI!";
    
        // Allouer de la mémoire pour une autre chaîne de caractères
        char *t = malloc(strlen(s) + 1);
    
        // Copier la chaîne de caractères dans la mémoire, y compris '\0'
        for (int i = 0, n = strlen(s); i <= n; i++)
        {
            t[i] = s[i];
        }
    
        // Mise en majuscule de la première lettre de la chaîne de caractères
        t[0] = toupper(t[0]);
    
        // Afficher les deux chaînes
        printf("s: %s\n", s);
        printf("t: %s\n", t);
    }
    
  • Remarquez que n = strlen(s) est maintenant défini dans la partie gauche de la boucle for. Il est préférable de ne pas appeler de fonctions inutiles dans la condition centrale de la boucle for, car elle s'exécutera encore et encore. En déplaçant n = strlen(s) vers le côté gauche, la fonction strlen ne s'exécute qu'une seule fois.
  • Le langage C dispose d'une fonction intégrée pour copier des chaînes de caractères appelée strcpy. Elle peut être mise en œuvre comme suit :
  • #include <ctype.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(void)
    {
        char s[] = "HI!";
    
        // Allouer de la mémoire pour une autre chaîne de caractères
        char *t = malloc(strlen(s) + 1);
    
        // Copier la chaîne de caractères dans la mémoire, y compris '\0'
        strcpy(t, s);
    
        // Mise en majuscule de la première lettre de la chaîne de caractères
        t[0] = toupper(t[0]);
    
        // Afficher les deux chaînes
        printf("s: %s\n", s);
        printf("t: %s\n", t);
    }
    
  • Remarquez que strcpy effectue le même travail que notre boucle for précédemment.
  • malloc renvoie NULL, une valeur spéciale dans la mémoire, en cas de problème. Vous pouvez écrire un code qui vérifie cette condition NULL de la manière suivante :
  • #include <ctype.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(void)
    {
        char s[] = "HI!";  
    
        // Allouer de la mémoire pour une autre chaîne de caractères
        char *t = malloc(strlen(s) + 1);
        if (t == NULL)
        {
            return 1;
        }
    
        // Copier la chaîne de caractères dans la mémoire, y compris '\0'
        strcpy(t, s);
    
        // Mise en majuscule de la première lettre de la chaîne de caractères
        if (strlen(t) > 0)
        {
            t[0] = toupper(t[0]);
        }
    
        // Afficher les deux chaînes
        printf("s: %s\n", s);
        printf("t: %s\n", t);
    
        // Libérer la mémoire
        free(t);
        return 0;
    }
    
  • Notez que si la chaîne obtenue est de longueur 0 ou si malloc échoue, NULL est renvoyé. De plus, notez que free indique à l'ordinateur que vous en avez fini avec ce bloc de mémoire que vous avez créé via malloc.
  • Nous pouvons également créer un tableau de 3 entiers en utilisant malloc :
  • #include <stdio.h>
    #include <stdlib.h>
    
    int main(void)
    {
        int *x = malloc(3 * sizeof(int));
        x[0] = 72;
        x[1] = 73;
        x[2] = 33;
        free(x);
    }
    
  • Remarquez que nous avons utilisé sizeof(int) pour un tableau d'entiers et non sizeof(char), c'est parce que les caractères prennent 1 octet et que les entiers peuvent prendre 1 octet, 2, 3 ou 4 octets en fonction de l'ordinateur. Il est donc préférable d'utiliser sizeof(int) pour éviter ce problème.

Valeurs poubelles


  • Lorsque vous demandez au compilateur un bloc de mémoire, rien ne garantit que cette mémoire sera vide.
  • Il est très possible que la mémoire que vous avez allouée ait déjà été utilisée par l'ordinateur. Par conséquent, il se peut que vous voyiez des valeurs inutiles ou des valeurs poubelles. Cela résulte du fait que vous avez obtenu un bloc de mémoire mais que vous ne l'avez pas initialisé. Par exemple, considérez le code suivant pour poubelle.c:
  • #include <stdio.h>
    
    int main(void)
    {
        int entiers[1024];
        for (int i = 0; i < 1024; i++)
        {
            printf("%i\n", entiers[i]);
        }
    }
    
  • Notez que l'exécution de ce code allouera 1024 emplacements dans la mémoire pour votre tableau, mais la boucle for montrera probablement que toutes les valeurs qui s'y trouvent ne sont pas 0. Il est toujours préférable d'être conscient du potentiel de valeurs poubelles lorsque vous n'initialisez pas les blocs de mémoire à une autre valeur, comme zéro ou autre.
Crédits à CS50x Harvard pour avoir fourni les diapositives et le cours. Je l'ai simplement traduite et j'ai légèrement modifié le contexte pour qu'elle corresponde mieux aux besoins des étudiants. Je ne prétends pas avoir écrit et créé les diapositives. Lien de cours en Anglais.