Aller au contenu

Un soupçon de C

Ces exercices doivent être utilisés pour vous entraîner à comprendre/travailler avec un nouveau langage de programmation. Les questions sont accompagnés de leur solution pour vous permettre de progresser.
Cependant, il ne vous sera pas demandé de savoir coder en C cette année (ni l'année prochaine d'ailleurs).

Ne vous précipitez pas sur ces solutions dès la première difficulté, vous êtes capable de trouver par vous-mêmes.

Le langage C est un langage important créé au début des années 1970, fondamental dans les systèmes UNIX. De nombreux langages héritent, d'une façon ou d'une autre, de ce langage C.

Nous allons utiliser ce langage pour travailler sur la représentation des entiers relatifs en machine.

ExerciceA03.51 - Les entiers en C

En Python, les nombres entiers sont de type int et ... c'est tout ! Le langage gère « tout seul » la mémoire nécessaire pour travailler sur ces entiers.

En C, ils existent plusieurs types différents pour les entiers, selon la place en mémoire qu'ils vont occuper :

Type Description Taille en mémoire
signed char Entier signé 1 octet
unsigned char Entier positif 1 octet
signed short Entier standard signé 2 octets
unsigned short Entier standard positif 2 octets
signed long Entier long signé 4 octets
unsigned long Entier long positif 4 octets

Les entiers de type signed short ou unsigned short sont codés sur deux octets.

  1. Combien d'entiers distincts peut-on coder sur deux octets ?

    Une piste

    Un octet (en anglais : byte) correspond à 8 bits.
    Un bit peut prendre deux valeurs (0 ou 1).

    Réponse

    Sur deux octets, c'est à dire 16 bits, on dispose de 2^{16} = 65536 codes différents. On peut donc a priori coder 65536 entiers sur ces deux octets.

  2. Un entier de type signed short a une valeur comprise entre -2^{15} = -32768 et 2^{15}-1 = 32767.
    A-t-on codé 65536 entiers distincts sur ces deux octets ?

    Réponse

    32768 entiers sont strictement négatifs et 32767 entiers sont strictement positifs. En prenant en compte 0, cela fait bien un total de 65536 entiers.

ProgA03.52 - Exécuter du C

Pour éviter d'avoir à installer sur vos machines le nécessaire pour compiler du C, on utilisera des compilateurs en ligne.

Par exemple:

Questions

  1. Connectez-vous sur le site online_c_compiler.
  2. Ce site s'ouvre sur un court exemple de programme. Testez-le.
  3. Copiez/collez le code C ci-dessous dans le compilateur en ligne :

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #include <stdio.h>
    
    int main() {
        /* On déclare une variable x de type signed char
        puis on lui affecte la valeur 10. */
        signed char x = 10 ; 
    
        /* On déclare une variable y de type signed char
        puis on lui affecte la valeur 25. */
        signed char y = 25 ;
    
        /* On déclare une variable z de type signed char
        puis on lui affecte la valeur x+y. */
        signed char z = x + y ;
    
        /* On affiche le contenu de z : */
        printf("Somme x + y = %hd", z) ;
    }
    
  4. Anticipez le texte qui sera affiché à l'écran puis exécutez le code afin de confirmer votre réponse.

    Réponse

    On obtient, sans surprise :

    Somme x + y = 35
    
  5. Modifiez le code pour ajouter les nombres 112 et 25.
    Anticipez le texte qui sera affiché à l'écran puis exécutez le code afin de confirmer votre réponse.

    Réponse

    On obtient, avec surprise :

    Somme x + y = -119
    

    En fait, ce n'est pas si surprenant si vous vous souvenez le travail réalisé sur le dépassement de capacité dans une plage de représentation binaire par complément à 2 sur un octet.

    Rappel

    retenues 1 1 1
    0 1 1 1 0 0 0 0
    + 0 0 0 1 1 0 0 1
    1 0 0 0 1
    0 0 1

    Or (1000 \; 1001)_2 est la représentation binaire en complément à deux d'un entier négatif.

    1. L'entier positif qui correspond à cette représentation binaire « usuelle » est 137.
    2. On soustrait à cet entier 2^8 : 137 - 256 = -119.
    3. Dès lors, la somme de la représentation par complément à 2 sur un octet des entiers 112 et 25 donne -119.

    En effet, on a dépassé la valeur du plus grand entier représenté par complément à 2 sur un octet donc on a continué sur la « roue »

    Remarque

    On aurait pu s'attendre à un message d'erreur du type « dépassement de capacité ».
    Sans un tel message, on voit que c'est au programmeur de gérer ce type de dépassement ce qui n'est pas sans poser problèmes (bugs...).

ProgA03.53 - max+1 = min

  1. Connectez-vous sur le site online_c_compiler.
  2. Copiez/collez le code C de l'exercice précédent puis modifiez ce code pour ajouter les nombres 127 et 1.

    Réponse

    Le code modifié (sans les commentaires) :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main() {
        signed char x = 127;
    
        signed char y = 1;
    
        signed char z = x + y;
    
        printf("Somme x + y = %hd", z);
    }
    
  3. Anticipez le texte qui sera affiché à l'écran avant d'exécutez le code pour confirmer votre réponse.

    Réponse

    On obtient:

    Somme x + y = -128
    

    Vous avez bien vu : il y a un signe « - » devant le résultat !

  4. Pouvait-on s'attendre au résultat obtenu ?

    Réponse

    Bien sûr !
    Il y a un nouveau dépassement de capacité : l'entier relatif qui suit 127 sur la roue dans une représentation par complément à 2 sur un octet est -128.

ProgA03.54 - Et sur deux octets ?

On rappelle que les signed short du langage C sont des entiers codés en complément à deux sur 16 bits.

  1. Connectez-vous sur le site online_c_compiler.
  2. Copiez/collez puis exécutez le code C ci-dessous dans le compilateur en ligne :

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #include <stdio.h>
    
    int main() {
        /* On déclare une variable x de type signed short
        puis on lui affecte la valeur 10. */
        signed short x = 10 ; 
    
        /* On déclare une variable y de type signed short
        puis on lui affecte la valeur 25. */
        signed short y = 25 ;
    
        /* On déclare une variable z de type signed short
        puis on lui affecte la valeur x+y. */
        signed short z = x + y ;
    
        /* On affiche le contenu de z : */
        printf("Somme x + y = %hd", z) ;
    }
    
  3. Modifiez le code pour ajouter les nombres 112 et 25.
    Anticipez le texte qui sera affiché à l'écran puis exécutez le code afin de confirmer votre réponse.

    Réponse

    On obtient, sans surprise :

    Somme x + y = 137
    
  4. Modifiez le code pour ajouter les nombres 32767 et 1.
    Anticipez le texte qui sera affiché à l'écran puis exécutez le code afin de confirmer votre réponse.

    Réponse

    On obtient :

    Somme x + y = -32768
    

    A nouveau, on a fait apparaître un dépassement de capacité !

  5. Justifiez, sur le cahier, le résultat obtenu à la question précédente.

    Réponse

    Les entiers de types signed short sont compris entre -2^{15} = -32768 et 2^{15}-1 = 32767.

    La représentation en complément à 2 sur 16 bits de 32767 est donc (0111 \; 1111 \; 1111 \; 1111)_2.

    La représentation de 1 est (0000 \; 0000 `\; 0000 \; 0001)_2.

    Effectuons la somme :

    retenues 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
    0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
    + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
    1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

    On obtient (1000 \; 0000 \; 0000 \; 0000)_2.

    Or (1000 \; 0000 \; 0000 \; 0000)_2 = 2^{15} = 32768 et l'entier maximal représenté en complément à deux sur 16 bit est 32767.

    Puisque 32768 - 2^{16} = -32768, le code (1000 \; 0000 \; 0000 \; 0000)_2 est donc la représentation de l'entier -32768.

  6. Modifiez le code pour ajouter les nombres -11256 et -25343.
    Anticipez le texte qui sera affiché à l'écran puis exécutez le code afin de confirmer votre réponse.

    Réponse

    On obtient :

    Somme x + y = 28937
    
  7. Justifiez ce résultat sur votre cahier.

    Réponse

    La somme (en base 10) de -11256 et -25343 donne -36599.

    Or le plus petit entier représenté par complément à 2 sur deux octets est -32768. Il y a donc un dépassement de capacité.

    Lorsqu'on prend « à rebours » la roue des représentations sur deux octets, le précédent de -32768 est 32767.

    Puisque la différence entre -32768 et -36599 est 3831, il faut effectuer 3831 pas « à rebours » sur cette roue, soit 3830 pas depuis 32767.

    Or 32767 - 3830 = 28937, ce qui confirme le résultat renvoyé par le programme rédigé en C.