Entiers

Un programme sert à manipuler un ordinateur, mais un ordinateur sert essentiellement à manipuler des données. La forme de données la plus simple que nous pouvons trouver dans un programme est la constante littérale : une valeur qu'il suffit de lire pour savoir combien elle vaut.

printf("Hello");
Dans cet exemple, "Hello" est une constante littérale : il suffit de la regarder pour comprendre le texte qu'elle représente.

La façon dont une constante est écrite est également importante. Il existe de nombreux types de données, mais le fait que cette constante soit entourée de guillemets nous indique qu'il s'agit d'une chaîne de caractères.

Un autre type de données très fréquemment rencontré dans les programmes est le type entier. Il a même un nom officiel : int, la contraction de integer, qui veut dire entier en anglais.

Remarque On retrouve ce mot-clé dans la ligne au début de chaque programme, mais cette ligne reste identique même si nous n'utilisons aucun entier par la suite.

int main(void) {
  ...
}

Une constante littérale peut représenter un entier, avec les conventions de notation qui vous sont familières :

12
+589712
0
-75
Une telle constante commence par un signe optionnel (elle est positive si vous n'indiquez pas de signe), puis un chiffre entre 1 et 9, puis éventuellement des chiffres supplémentaires entre 0 et 9. On ne peut pas laisser d'espace pour séparer les milliers.

Alternativement, vous pouvez exprimer un entier positif en base 8 (pas très utile pour le commun des mortels, mais intéressant pour les informaticiens).

012
07
0
02277541
Dans ce cas, la notation commence toujours par un zéro, suivi d'un chiffre compris entre 1 et 7, puis éventuellement des chiffres entre 0 et 7. Les chiffres 8 et 9 ne sont jamais utilisés en base 8.

Pour les mêmes raisons, on peut également écrire un entier positif en base 16.

0x12
0x7a
0x0
0xCAFE
Le préfixe dans ce cas est 0x. Comme précédemment, le premier chiffre ne peut pas être un zéro puisqu'il ne serait pas significatif. Les lettres entre A et F sont employées pour représenter les chiffres de 10 à 15.

Remarque Peu importe la notation que vous adoptez, la valeur sera stockée en binaire dans la machine (généralement sur 32 bits consécutifs). Donc la base choisie dans le fichier source n'a aucune influence sur l'exécutable produit par le compilateur.

La fonction printf n'est pas directement disponible en langage C ; elle fait partie de la bibliothèque standard. Pour pouvoir l'utiliser il vaut mieux demander au préprocesseur cpp d'inclure le fichier d'en-tête stdio.h. Ce fichier contient une partie de la définition nécessaire, tandis que le fichier objet qui contient le reste de la définition sera automatiquement inclus par l'éditeur de lien ld.

Le rôle de la fonction printf est d'envoyer une série de caractères sur la sortie standard. Dans le cas le plus fréquent, il s'agit de la console, et les caractères envoyés s'y afficheront donc.

Nous avons déjà vu des exemples d'utilisation de printf où nous lui donnons une chaîne de caractères en argument. Dans ce cas, le travail de printf consiste simplement à envoyer un par un tous les caractères de la chaîne à la console.

printf("abcdefghijklmnopqrstuvwxyz");

Pour afficher un entier, c'est moins direct. Un entier n'est pas une série de caractères, il faut donc le convertir en caractères avant de pouvoir l'afficher. Pour cela, on utilise printf un peu différemment :

printf("La valeur est %d", 12);
Ici, printf prend deux arguments. Le premier, qui est toujours une chaîne de caractères, contient un indicateur de conversion. Le % marque le début de l'indicateur. Le d marque la fin de l'indicateur et signifie que printf doit convertir un entier vers sa représentation en base 10. C'est le deuxième argument qui donne la valeur à convertir.

D'autres lettres sont disponibles pour obtenir diverses conversions :

printf("%o", 12);
printf("%x", 12);
o indique une conversion en représentation octale, et x donne la représentation héxadécimale d'un entier. s convertit une chaîne de caractères... en chaîne de caractères. Nous l'utilisons ici uniquement pour harmoniser notre façon d'invoquer printf.

Remarque printf est une fonction capable de beaucoup plus, et nous verrons petit à petit ce qu'elle peut nous offrir. Si vous souhaitez la découvrir en détail dès maintenant, sa page manuelle est une bonne ressource (lancez la commande man 3 printf).

Une donnée n'est pas toujours exprimée comme une constante littérale. Très souvent, une valeur est obtenue en évaluant une expression, c'est à dire en effectuant un calcul.

printf("%d", 12 + 7);
Pour exécuter cette instruction, la machine va tout d'abord calculer la somme de 12 et 7, puis fournir le résultat à printf pour affichage.

Dans une expression, les calculs les plus fréquents sont les opérations arithmétiques. Leur notation est très proche de ce dont vous avez l'habitude.

printf("%d", 12 - 7);
printf("%d", 12 * 7);
printf("%d", 12 / 7);
Le symbole de la multiplication est *, car la croix n'est pas disponible facilement au clavier, et le point sert à autre chose.

Il est important de garder à l'esprit qu'un calcul entre entiers produit toujours un entier. La division n'est donc pas celle que vous propose une calculette (où 12/7 donnerait 1,714288), mais le quotient de la division euclidienne.

12/7 donne donc 1, car dans douze il va une fois sept et il reste cinq. Vous pouvez d'ailleurs obtenir ce reste à l'aide d'un autre opérateur :

printf("%d", 12 % 7);

On peut combiner plusieurs opérations, avec les règles usuelles de priorité. Les parenthèses permettent de contourner ces règles au besoin.

printf("%d",  1 + 2  * 3);
printf("%d", (1 + 2) * 3);

Toutes les données stockées dans la machine sont formées de bits. Pour manipuler une donnée, on doit donc manipuler les bits qui la composent.

Un bit possède deux états distincts. On les note souvent 0 et 1, ou alternativement faux et vrai. Les manipulations les plus courantes sont empruntées au vocabulaire de la logique :

et
vraivrai ≡ vrai
vraifaux ≡ faux
fauxvrai ≡ faux
fauxfaux ≡ faux
ou
vraivrai ≡ vrai
vraifaux ≡ vrai
fauxvrai ≡ vrai
fauxfaux ≡ faux
non
¬vrai ≡ faux
¬faux ≡ vrai

Un programme C ne manipule pas les bits individuellement, mais il peut manipuler (par exemple) des entiers, qui ne sont rien d'autre que des groupes de bits.

Il existe des opérateurs qui imitent les opérations vues plus haut, mais sur de tels groupes de bits. Ce sont les opérateurs bit-à-bit.

printf("%d", 12 & 7);

La valeur 12 est représentée dans la machine par le groupe de bits :

00000000000000000000000000001100
Tandis que la valeur 7 est représentée par :
00000000000000000000000000000111
Le & est un «et bit-à-bit». Pour chaque position (ici, pour chaque colonne), on effectue un «et», avec pour résultat :
00000000000000000000000000000100
Ce qui correspond à l'entier 4.

Il en va de même pour le «ou bit-à-bit» et le «non bit-à-bit».

printf("%d", 12 | 7);
printf("%d", ~12);

Enfin, on peut également déplacer les bits au sein d'un groupe. On dispose pour cela d'un décalage à gauche (<<) et d'un décalage à droite (>>).

printf("%d", 12 << 2);
Tous les bits sont décalés de deux positions vers la gauche. Les deux bits qui sortent du groupe (à gauche) sont ignorés. Les deux bits qui sont créés (à droite) sont automatiquement mis à zéro. Le résultat du décalage ressemble à ceci :
00000000000000000000000000110000
Ce qui correspond à l'entier 48.

Remarque Pour les entiers positifs, un décalage d'une position vers la gauche est équivalent à une multiplication par deux.

  1. Bases. Écrivez le programme suivant dans un fichier nommé bases.c :

    #include <stdio.h>
    #include <stdlib.h>
     
    int main(void) {
      printf("%d\n", 72);
      printf("%d\n", 0110);
      printf("%d\n", 0x48);
      return EXIT_SUCCESS;
    }
    Compilez puis exécutez ce programme. Vous pouvez constater que chacune des trois expressions désigne en fait le même entier.

    Sans le compiler ni l'exécuter, pouvez-vous prédire ce que produirait le programme suivant ?
    #include <stdio.h>
    #include <stdlib.h>
     
    int main(void) {
      printf("%d\n", 72);
      printf("%o\n", 72);
      printf("%x\n", 72);
      return EXIT_SUCCESS;
    }

  2. Arithmétique. Dans un fichier nommé reponses.txt, écrivez ce que vous pensez que le programme suivant va afficher.
    #include <stdio.h>
    #include <stdlib.h>
     
    int main(void) {
      printf("%d\n", 100/6);
      printf("%d\n", 100%6);
      printf("%d\n", 0x1A*015);
      printf("%d\n", -3/5);
      printf("%d\n", -31/5);
      printf("%d\n", -31%5);
      printf("%d\n", 100*(3/5));
      printf("%d\n", 100*3/5);
      printf("%d\n", 2-3-5);
      printf("%d\n", 2-(3-5));
      return EXIT_SUCCESS;
    }
    Compilez puis exécutez ce programme, et vérifiez l'exactitude de vos prédictions.
  3. Limites. Par tâtonnements, écrivez un programme qui affiche le plus grand entier possible. Affichez cette valeur en représentation octale et en représentation hexadécimale. Que pouvez-vous constater ?
  4. Multiplication. Écrivez un programme qui calcule 73×16 et affiche le résultat, mais sans utiliser d'opérateur arithmétique.
  5. Opérations bit-à-bit. Que fait le programme suivant ?

    #include <stdio.h>
    #include <stdlib.h>
     
    int main(void) {
      int n = 12;
     
      printf("%d", (n>>7)&1);
      printf("%d", (n>>6)&1);
      printf("%d", (n>>5)&1);
      printf("%d", (n>>4)&1);
      printf("%d", (n>>3)&1);
      printf("%d", (n>>2)&1);
      printf("%d", (n>>1)&1);
      printf("%d", n&1);
      printf("\n");
      return EXIT_SUCCESS;
    }
    La première ligne du programme mérite une explication : ici nous définissons une variable nommée n, de type int, et de valeur initiale 12.

    Pouvez-vous deviner ce que vous verrez si vous remplacez dans le code la valeur 12 par 35 ? Et sachant cela, pouvez-vous deviner ce qui se passera si vous remplacez 35 par 12|35 ?

    Modifiez le programme pour qu'il affiche 32 chiffres (utilisez le copier-coller). Choisissez une valeur à mettre à la place du 12 pour que ce programme affiche :

    01010101010101010101010101010101

retour à la page d'accueil

retour au sommet