Les variables

Introduction et commentaires

Dans cette série d’articles nous allons voir des bases de programmation. Il est recommandé d’avoir consulté au préalable l’article traitant des données numériques. Les sujets traités seront les variables (types, constantes, tableaux, les pointeurs ou références), les structures de contrôle (boucle, switch, conditions) et les fonctions (déclaration, utilisation). Au delà, les détails propres à chaque langages seront traités dans des articles dédiés à chacun d’entre eux.

Les différents langages traités ici, ne pas du même niveau. En effet, le langage C est un langage bas niveau qui doit être compilé (comprendre transformé) en instruction machine lui donnant ainsi une rapidité d’exécution sans pareil. Les autres langages (Perl, PHP et Javascript) sont pour leur part des langages dits de haut niveau. Chacune de leur instruction est interprété par un programme appelé interpréteur qui peut être au choix, votre navigateur web pour javascript, ou un programme spécifique au langage dans les cas du Perl et de PHP.

Pour information dans les parties code définies comme ci-dessous, vous trouverez des commentaires aidant à la bonne compréhension des codes. Il seront représentés comme suit:

// Ceci est un commentaire (Javascript, PHP, C)
 
/* Il s'agit également d'un commentaire
* Pouvant être rédigé
* Sur plusieurs lignes (Javascript, PHP, C) */
 
# C'est un commentaire aussi (Perl, Shell)

Les commentaires sont des chaînes de caractères (mots, phrases) insérés dans du code de manière à ne pas être interprété. Ils n’ont de valeur que pour les programmeurs, lecteurs du code. Un commentaire n’influencera pas le déroulement d’un programme. Il pourra par contre aider un programmeur reprenant le code d’un autre.

Présentation des variables

En programmation, on appelle variable l’association entre un mot clé (nom de variable) et une (ou plusieurs) valeur pouvant être modifiée au fur et à mesure du déroulement du programme.

Pour ceux qui se sentent déjà perdus, voici un exemple simple en PHP.

<?php
// En PHP les noms variables doivent être précédés du symbole $
// Je renseigne ici ma variable $nomprenom avec un nouveau contenu
$nomprenom='Henry Dupont';
 
// J'utilise ici le contenu de cette variable
// La fonction echo permet d'afficher du texte et le contenu (ASCII, affichable) de certaines variables simples
echo "Salutations $nomprenom";
?>

Le code ci-dessus affichera: Salutations Henry Dupont

Si la valeur de la variable $nomprenom venait à être modifié, le même morceau de code affichera le nouveau contenu de $nomprenom.

Cela pourrait permettre par exemple de créer des salutations pour une liste de prénom-nom sans avoir à réécrire le code pour chaque cas de figure.

Une variable peut avoir un contenu bien plus complexe qu’un simple texte et c’est ce que nous allons voir ci-dessous.

Les constantes

En opposition avec les variables, on trouve les constantes qui, comme leur nom l’indique ont un contenu qui ne change pas.

Par exemple, PI est une constante, sa valeur ne changera pas au cours de l’exécution d’un programme. (en tout cas pas sans remettre en cause un certain nombre de choses)

Exemple en Javascript

// Définition de la constante PI
const PI=3.14159265359;
 
// Définition de la variable r (pour rayon)
r=12;
 
// Utilisation de la constante PI avec la variable r
console.log('Circonférence d’un cercle de rayon r: ' + r + '=' + (2*PI*r);

L’exemple ci-dessus affichera: Circonférence d’un cercle de rayon r: 12 = 75.39822368616001

Exemple en PHP

<?php
// Définition de la constante PI
define("PI", 3.14159265359);
 
// Définition de la variable r (pour rayon)
$r=12;
 
// Utilisation de la constante PI avec la variable r
echo "Circonférence d’un cercle de rayon r: $r = ".(2*PI*$r);
?>

L’exemple ci-dessus affichera: Circonférence d’un cercle de rayon r: 12 = 75,39822368616

Exemple en C

#include <stdio.h>
#include <stdlib.h>
 
/* Pour compiler ce code source:
*       gcc constante.c -o constante.exe
* Pour éxécuter ce programme une fois compilé ./constante.exe (sous UNIX/Linux) ou constante.exe (sous Windows)
*/
 
/* Définition de la constante PI */
#define PI 3.14159265359
 
int main(void) {
        /* Définition de la variable r (pour rayon)
        short int r=12;
        /* Utilisation de la constante PI */
        printf("Circonférence d’un cercle de rayon r: %d = %f\n", r, (float) (2*PI*r));
 
        return EXIT_SUCCESS;
}

L’exemple ci-dessus affichera une fois compilé et éxécuté: Circonférence d’un cercle de rayon r: 12 = 75.398224

Exemple en Perl

#!/usr/bin/perl
 
use strict;
use warnings;
# Définition de la constante PI
use constant PI => 3.14159265359;
 
# En Perl, comme en PHP les noms de variable doivent être précédés du symbole $
# Définition de la variable r (pour rayon)
my $r=12;
 
# Utilisation de la constante PI avec la variable r
print "Circonférence d'un cercle de rayon r: $r = ".(2*PI*$r)."\n";

L’exemple ci-dessus affichera une fois exécuté: Circonférence d’un cercle de rayon r: 12 = 75.39822368616

Pour une constante il est important que quelqu’un ne puisse pas faire quelque chose comme ça: PI=8.53!!! C’est pour cela que les constantes sont en général définies d’une manière différente des variables.

Typage

Le typage correspond à la définition du type des données à stocker. Il existe différents niveaux de typage dépendants du langage utilisé.

Par exemple on parlera de typage statique un langage bas niveau comme C où le programmeur doit définir à l’avance (début de fonction ou de programme) le type de données qui sera stocké dans la variable (char, int, cf: Article sur les données numériques). Au contraire on parlera plutôt de typage dynamique pour des langages de plus haut niveau (PHP par exemple) qui ne demanderont pas au programmeur de définir à l’avance ses variables ni de spécifier le type des données qui seront stockées.

3 est un nombreNombre est un type.

3.1415 est un nombre à virguleNombre à virgule est un type.

C est une lettreLettre est également un type.

Bonjour est un motMot est aussi un type (en fonction des langages).

-8 est un nombre négatifNombre négatif a lui aussi un type pour le représenter.

Déclarez vos variables

Dans l’oeuvre de Douglas Adams Le guide du voyageur intergalactique il est dit que la réponse à la Grande Question sur la Vie, l’Univers et le Reste est 42. Essayons de rentrer le nombre 42 dans une variable et ceci dans différents langages.

En C (typage statique)

 
/* On précise un type numérique adapté */
short int reponse=42;
/* On pourrait également utiliser un type plus large */
int reponse=42;
/* Ou encore plus petit car le nombre est inférieur à 127 */
/* Attention! Dans ce cas de figure le nombre n'est pas stocké sous forme de caractère mais bien sous sa forme numérique */
/* Ce qui donne en hexadécimal: 2A */
/* Le caractère ASCII associé à 2A est * ce qui vous vous en doutez n'a rien à voir avec le contenu que nous venons d'entrer dans cette variable */
char reponse=42;
/* Ici il sera stocké sous forme d'une chaîne de caractère ASCII soit 2 caractères de 8 bits chacun */
/* Soit en hexadécimal:  34 32 */
/* N'étant pas de type numérique, il sera alors impossible de l'utiliser dans des calculs en l'état. */
char reponse[]="42";
/* B est un caractère donc de type char et sera stocké avec son code ASCII : 42 */
char lettre='B';
/* Bonjour est un mot, en C un mot est un tableau de caractères (appelé aussi string)
* Ce tableau (que nous verrons plus en détails dans un autre article) contiendra respectivement les codes ASCII des lettres
* B o n j o u r soit 42 6F 6E 6A  6F 75 72 */
char mot[]="Bonjour";

Le langage C est très précis et propose de nombreux types de données pour définir les variables.

En PHP (typage dynamique)

<?php
/* PHP déterminera qu'il s'agit d'un numérique et adaptera le type de la variable en conséquence */
$reponse=42;
 
// Affiche integer ce qui correspond à un entier
echo gettype($reponse)
 
/* Si 42 est entouré de double quotes, PHP l’interprétera alors comme une chaîne de caractères, c'est une forme de typage en PHP */
$reponse="42";
// Affiche string ce qui correspond à une chaîne de caractères
echo gettype($reponse)
 
// Toutefois, si on utilise $reponse défini avec les doubles quotes dans un calcul, celui-ci sera alors interprété comme un nombre */
echo ($reponse + $reponse);    // Affichera 84
?>

En Perl (typage dynamique)

/* Perl détermine qu'il s'agit d'une valeur numérique */
my $reponse=42;
 
/* Perl détermine qu'il s'agit d'une chaîne de caractères */
my $reponse="42";
 
// Comme en PHP, utiliser $reponse défini comme une chaîne de caractère dans un calcul interprétera ce dernier comme un nombre.
print ($reponse + $reponse);    # Affichera 84

Enfin le cas particulier du Javascript

/* Javascript détermine qu'il s'agit d'une valeur numérique */
var reponse=42;
console.log(typeof(reponse));    // Affichera number
 
/* Javascript détermine qu'il s'agit d'une chaîne de caractères */
my $reponse="42";
console.log(typeof(reponse));    // Affichera string
 
// A la différence de Perl de PHP, Javascript traitera un string en tant que tel, même dans un calcul
console.log((reponse + reponse));    // Affichera 4242, on parle alors de concaténation (à suivre dans un autre article)

Les signes

Le mot clé unsigned permet de préciser que le type n’est pas signé et ne pourra contenir par conséquent que des valeurs positives allant de 0 à 2x (où x vaut le nombre de bits du type). Un type signé permet lui de contenir des valeurs négatives comme positives allant de -2(x-1) à 2(x-1)-1. Pourquoi la valeur positive est diminuée de 1? C’est simple, la valeur 0 est une valeur qu’il faut prendre en compte dans nos calculs car elle existe bien.

Par exemple, un type unsigned char permet de coder des valeurs allant de 0 à 255 soit 256 valeurs. Dans le cas d’un char signé, on ne peut coder que de 256 valeurs également allant de -128 à 127 ce qui nous fait bien 256 valeurs en comptabilisant le 0. Le signe du nombre contenu utilise 1 bit dont la valeur est soit à 0 indiquant que le nombre est positif, soit à 1 indiquant que le nombre est négatif. Pour mieux comprendre, voici un petit exemple utilisant deux signed char de même valeur avec avec un signe différent. Attention! Cet exemple ne tient pas compte de l’architecture endianness un PC ne codera pas la valeur comme dans l’exemple ci-dessous, mais plutôt 111010012 soit e916 (l’ordre des bits est inversé).

Positions des bitsSIGNE0123456
Valeur binaire de 2300010111
Valeur hexa de 2317
Positions des bitsSIGNE0123456
Valeur binaire de -2310010111
Valeur hexa de -2397

Les flottants

Voici un type dédié au stockage des nombre décimaux.

En C il existe plusieurs types de flottant

/* Le flottant à précision simple */
float valeur=12.75;
/* Le flottant à précision double */
double valeur=3.14159265358979323846264338327950288419716939937510582097494459230781640628620899;

Voici comment les données sont rangées en fonction du type. Une fois encore, ce schéma ne tient pas compte de l’architecture. Garder une lecture de gauche à droite permet de simplifier la présentation de ces types plus complexes..

Le type float

Positions des bits012345678910111213141516171819202122232425262728293031
Répartition des valeursSigne
+ ou –
Exposant
×10puissance y
Fraction
314159265358979323846264338327950288419716939937510582097494459230781640628620899

Le type double

Positions des bits012345678910111213141560616263
Répartition des valeursSigne
+ ou –
Exposant
×10puissance y
Fraction
314159265358979323846264338327950288419716939937510582097494459230781640628620899

Les autres langages traités ici (Perl, Javascript et PHP) ne feront pas de différence. Javascript nous répondra number quoi qu’il arrive, PHP stockera décrétera qu’il s’agit d’un double que nous souhaitions stocker 2.75 ou un nombre avec 50 décimales.

Nous venons de balayer dans cet article les variables simples avec la plupart des types de base utilisés en programmation. Il en existe d’autres plus complexes et plus spécifique dont nous étudierons (pour certain) les détails dans d’autres articles à venir.

Liens et références

En attendant le prochain article, si vous souhaitez vous documenter un peu sur le sujet, voici quelques liens intéressants:

Ma documentation préférée reste celle du PHP que je trouve particulièrement bien présentée et facile à comprendre. Pour le reste, je vous souhaite une bonne lecture.

Les données numériques

Vous retrouverez les liens et références à la fin de cet article.

1 – Les bits

Le bit est la plus petite unité numérique définissable. Le bit correspond l’état d’un signal électrique (arrêt ou marche). Un bit peut se résumer à un interrupteur contrôlant une ampoule qui est soit en à l’arrêt (0) soit en fonctionnement (1). Seul, il ne représente rien et on ne peut pas définir de donnée plus complexe que OUI ou NON. Pour créer l’informatique il aura été nécessaire de regrouper les bits en blocs nommés octets.

Représentation d’une séquence de bits sous forme de signal électrique
La ligne horizontale représente le temps, et la ligne vertical l’état du signal

2 – Les octets

Stockage des octets

Sur les premiers systèmes d’exploitations publics (GECOS créé en 1962 par la General Electric) les octets étaient des assemblages de 6 bits stockés sur des fiches en carton appelées “cartes perforées” et regroupés par “mots” de 36 bits. La première carte perforée a été créée vers 1725 pour automatiser des métiers à tisser.

Plus tard, avec l’apparition du standard ASCII, les octets sont devenus des assemblages de 8 bits.

Un bit pouvant avoir 2 valeurs (0 ou 1), un octet comportant 8 bits permet donc de coder 28 = 2×2×2×2×2×2×2×2 = 256 valeurs (de 0 à 255).

Un octet peut être représenté de différentes manières:

  • En binaire allant de 00000000 à 11111111
  • En hexadécimal (base 16) de 00 à FF (aussi écrit: 0x00 à 0xFF)
  • En décimal non signé de 0 à 255
  • En décimal signé de -128 à 127

Voici une liste de supports pour stocker nos octets:

  • Sur des cartes perforées par “mots” de 80 caractères de 8 bits (mais aussi les codes bar ou QR codes)
  • Sur des puces électroniques PROM – EPROM – EEPROM (cartes SD, ou disques SSD, barettes de mémoire RAM)
  • Sur des bandes magnétiques, cassettes ou cartouches, puis disquettes (cartouches LTO, cassettes DAT, et aussi sur nos veilles cassettes audio à l’époque des Amstrad et ORIC)
    • Ces données stockées sous forme de signaux électriques étaient décodées par des modem intégrés à nos vieux ordinateurs
  • Sur des disques de métal au format binaire proche des cartes perforées (CDROM, DVDROM, BluRay)
  • Sur des disques magnétiques au format binaire par impulsions électriques (disques dur)

A présent que nous avons défini les octets, nous savons les stocker sur différents types de supports. C’est bien beau tout ça, mais qu’allons nous faire de ces données numériques?

Traitement des octets

Les microprocesseurs ont évolué depuis l’aube de l’informatique et leur aptitude à traiter des mots de plus en plus long et de plus en plus rapidement ont permis aux ordinateurs d’évoluer jusqu’aux capacités époustouflantes qu’on leur connait aujourd’hui. Mais cela n’a pas toujours été ainsi. Aux premières heures de l’informatique grand public, les microprocesseurs ne savaient traiter qu’un nombre de données restreint et pire encore, ils le faisaient beaucoup plus lentement qu’aujourd’hui.

Voici l’évolution de la taille des mots traités par les microprocesseurs (CPU) grand public:

  • 8 bits (Z80, Intel 8008) – Années 1970 – 1980
  • 16 bits (Intel 8086, Motorola 68000) – Années 1980 – 1990
  • 32 bits (Intel 80386, Motorola 68020) – Années 1990 – 2000
  • 64 bits (Intel core 2 duo, MIPS R4000) – Années 1990 – 2000

La taille des mots en bits qu’un microprocesseur sait traiter lui permet également de définir des plages d’adresses pour stocker des données en mémoire. Il faut se représenter la mémoire RAM comme un damier avec des coordonnées pour chaque case. Plus le damier est grand, plus vous aurez un nombre de case important et donc vous aurez besoin plus de bits pour définir ce nombre. Si vous rangez vos données dans le damier mais que vous n’êtes pas en mesure de noter les coordonnées où elles sont stockées, comment allez vous les retrouver?

Voici la quantité de mémoire adressable en fonction de la taille du bus mémoire en bits:

  • 8 bits ⇒ 16 Ko (2 puissance 8)
  • 16 bits ⇒ 64 Ko (2 puissance 16)
  • 32 bits ⇒ 4 Go (2 puissance 32)
  • 64 bits ⇒ 16 777 216 To ou 16 Eo (2 puissance 64)

Bien sûr, il existe des astuces de programmation qui permettent d’adresser plus de mémoire que la limite du bus ne le permet. Mais ce sera au détriment des performances.

3 – Les types de données de base

Maintenant, avec les connaissances que nous avons, nous savons traiter et stocker des données…

Oui mais au fait, quoi comme données???

C’est vrai! Comment faire la différence entre des numéros de série et des vraies données numériques utilisées dans des calculs?

En somme, comment différencier des chiffres utilisés dans du texte et des chiffres utilisés pour des calculs? Comment faire comprendre ça à un microprocesseur?

Le standard ASCII nous indique que chaque caractères imprimables (a, B, c, D, 0, 1, 2, @ etc…) correspond à un code (dit code ASCII) numérique lui donnant une position bien définie dans la table du même nom. Ainsi, avec seulement des chiffres, nous sommes capables de stocker du texte et bien d’autres choses encore.

Par exemple: Le nombre 32768 stocké sous forme de caractères nous coûterait 5 caractères ⇒ 6|5|5|3|5 soit en hexadécimal 33 32 37 36 38.

Le même nombre stocké avec un type binaire adapté (short int par exemple) ne coûterait que 2 caractères ⇒ 80 00

Il existe plusieurs types de données en fonction de l’usage qui leur sera réservé (affichage ou calcul, petits nombres ou très grands nombres, nombres à virgule flottante)

Les types de données présentées ci-dessous sont les types de base tels qu’ils sont utilisés par les microprocesseurs (en langage C, les noms changent en fonction des langages, mais le contenu et le stockage restent les mêmes).

(signed) char

  • Type de donnée: Caractère (character) / Entier signé de faible valeur
  • Taille: 1 octet (8 bits)
  • En C il est recommandé de calculer la taille avec la fonction sizeof(char)
  • Une chaîne de caractère est en fait un tableau de caractères comme suit: nombre de caractères × sizeof(char)
  • Peut contenir: ‘a’ ou ‘z’ ou ‘8’ ou ‘+’
  • Valeur décimale: de -128 à 127
  • Valeur hexadécimale: de 00 à FF
  • Stockage: Non affecté

unsigned char

  • Type de donnée: Caractère (character) non signé / Entier de faible valeur positive
  • Taille: 1 octet (8 bits) / caractère en général (ASCII)
  • En C il est recommandé de calculer la taille avec la fonction sizeof(char)
  • Peut contenir: ‘a’ ou ‘z’ ou ‘8’ ou ‘+’
  • Valeur décimale: de 0 à 255
  • Valeur hexadécimale: de 00 à FF
  • Stockage: Non affecté

(signed) short int

  • Type de donnée: Entier numérique
  • Taille: 2 octets (16 bits)
  • En C il est recommandé de calculer la taille avec la fonction sizeof(short int)
  • Peut contenir: de -32768 à 32767
  • Valeur décimale: de 0 à 65535
  • Valeur hexadécimale: de 00 00 à FF FF
  • Stockage: Dépendant de l’architecture BIG ENDIAN/LITTLE ENDIAN

unsigned short int

  • Type de donnée: Entier numérique positif
  • Taille: 2 octets (16 bits)
  • En C il est recommandé de calculer la taille avec la fonction sizeof(short int)
  • Peut contenir: de 0 à 65535
  • Valeur décimale: de 0 à 65535
  • Valeur hexadécimale: de 00 00 à FF FF
  • Stockage: Dépendant de l’architecture BIG ENDIAN/LITTLE ENDIAN

(signed) int (16 bits/32 bits)

  • Type de donnée: Entier numérique
  • Taille: 2 octets (16 bits) / 4 octets (32 bits)
  • En C il est recommandé de calculer la taille avec la fonction sizeof(int)
  • Peut contenir: de -2 147 483 648 à 2 147 483 647
  • Valeur décimale: de 0 à 4 294 967 295
  • Valeur hexadécimale: de 00 00 00 00 à FF FF FF FF
  • Stockage: Dépendant de l’architecture BIG ENDIAN/LITTLE ENDIAN

unsigned int (16 bits/32 bits)

  • Type de donnée: Entier numérique positif
  • Taille: 2 octets (16 bits) / 4 octets (32 bits)
  • En C il est recommandé de calculer la taille avec la fonction sizeof(int)
  • Peut contenir: de 0 à 4 294 967 295
  • Valeur décimale: de 0 à 4 294 967 295
  • Valeur hexadécimale: de 00 00 00 00 à FF FF FF FF
  • Stockage: Dépendant de l’architecture BIG ENDIAN/LITTLE ENDIAN

(signed) long int (16 bits/32 bits)

  • Type de donnée: Entier numérique
  • Taille: 4 octets (32 bits) / 8 octets (64 bits)
  • En C il est recommandé de calculer la taille avec la fonction sizeof(long int)
  • Peut contenir: de -9 223 372 036 854 775 808 à -9 223 372 036 854 775 807
  • Valeur décimale: de 0 à 18 446 744 073 709 551 615
  • Valeur hexadécimale: de 00 00 00 00 00 00 00 00 à FF FF FF FF FF FF FF FF
  • Stockage: Dépendant de l’architecture BIG ENDIAN/LITTLE ENDIAN

unsigned lont int (16 bits/32 bits)

  • Type de donnée: Entier numérique positif
  • Taille: 4 octets (32 bits) / 8 octets (64 bits)
  • En C il est recommandé de calculer la taille avec la fonction sizeof(long int)
  • Peut contenir: de 0 à 18 446 744 073 709 551 615
  • Valeur décimale: de 0 à 18 446 744 073 709 551 615
  • Valeur hexadécimale: de 00 00 00 00 00 00 00 00 à FF FF FF FF FF FF FF FF
  • Stockage: Dépendant de l’architecture BIG ENDIAN/LITTLE ENDIAN

(signed) long long int

  • Type de donnée: Entier numérique de grande taille
  • Taille: 8 octets (64 bits)
  • En C il est recommandé de calculer la taille avec la fonction sizeof(long long int)
  • Peut contenir: de -9 223 372 036 854 775 808 à -9 223 372 036 854 775 807
  • Valeur décimale: de 0 à 18 446 744 073 709 551 615
  • Valeur hexadécimale: de 00 00 00 00 00 00 00 00 à FF FF FF FF FF FF FF FF
  • Stockage: Dépendant de l’architecture BIG ENDIAN/LITTLE ENDIAN

unsigned long long int

  • Type de donnée: Entier numérique positif de grande taille
  • Taille: 8 octets (64 bits)
  • En C il est recommandé de calculer la taille avec la fonction sizeof(long long int)
  • Peut contenir: de 0 à 18 446 744 073 709 551 615
  • Valeur décimale: de 0 à 18 446 744 073 709 551 615
  • Valeur hexadécimale: de 00 00 00 00 00 00 00 00 à FF FF FF FF FF FF FF FF
  • Stockage: Dépendant de l’architecture BIG ENDIAN/LITTLE ENDIAN

float

  • Type de donnée: Flottant numérique à précision simple
  • Taille: 4 octets (32 bits)
  • En C il est recommandé de calculer la taille avec la fonction sizeof(float)
  • Peut contenir: de -3.4e-38 à 3.4e38 avec une précision à 7 décimales
  • Valeur décimale: de 0 à 4 294 967 295
  • Valeur hexadécimale: de 00 00 00 00 à FF FF FF FF
  • Stockage: Dépendant de l’architecture BIG ENDIAN/LITTLE ENDIAN

double

  • Type de donnée: Flottant numérique à précision double
  • Taille: 8 octets (64 bits)
  • En C il est recommandé de calculer la taille avec la fonction sizeof(double)
  • Peut contenir: de -1.7e-308 à 1.7e308 avec une précision à 15 décimales
  • Valeur décimale: de 0 à 18 446 744 073 709 551 615
  • Valeur hexadécimale: de 00 00 00 00 00 00 00 00 à FF FF FF FF FF FF FF FF
  • Stockage: Dépendant de l’architecture BIG ENDIAN/LITTLE ENDIAN

long double (32 bits / 64 bits)

  • Type de donnée: Flottant numérique de grande taille à forte précision
  • Taille: 10 octets (80 bits) / 16 octets (128 bits)
  • En C il est recommandé de calculer la taille avec la fonction sizeof(long double)
  • Peut contenir: ????
  • Valeur décimale: de 0 à 18 446 744 073 709 551 615
  • Valeur hexadécimale: de 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 à FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
  • Stockage: Dépendant de l’architecture BIG ENDIAN/LITTLE ENDIAN

void * (dépendant de l’architecture)

  • Type de donnée: Pointeur (adresse mémoire)
  • Taille: (complètement dépendant de l’architecture)
  • En C il est recommandé de calculer la taille avec la fonction sizeof(void *)
  • Peut contenir: Toute la plage adressable par l’architecture
  • Valeur décimale: (complètement dépendant de l’architecture)
  • Valeur hexadécimale: (complètement dépendant de l’architecture)
  • Stockage: Dépendant de l’architecture BIG ENDIAN/LITTLE ENDIAN

4 – L’histoire du petit et du grand indien…

Il existe différents ordres de stockage des données numérique de grande taille (16 bits et au delà) dont les plus connus sont nommés LITTLE ENDIAN, BIG ENDIAN ou encore BI-ENDIAN (plus rare). Pour des raisons de culture, de conception et de logique différentes, les deux principales architectures stockent et traitent les données dans un ordre qui lui est propre.

Processeurs LITTLE-ENDIAN: Intel et compatibles Intel (x86 IA32, IA64 ou amd64)

Le petit boutiste (LITTLE-ENDIAN) range l’octet de poid faible à l’adresse la plus petite

Processeurs BIG-ENDIAN: SPARC, ARM, PowerPC

Le grand boutiste (BIG-ENDIAN) range l’octet de poid fort à l’adresse la plus petite

En résumé, on peut dire que les données sont rangées en mémoire en fonction de l’architecture matérielle qui les traite.

Vous trouverez ci-dessous un petit bout de code en C qui, une fois compilé (avec gcc) et éxécuté, va générer un fichier par type (au sens C du terme) en stockant une valeur à l’intérieur afin de vous donner un aperçu de la manière dont les données numériques sont rangées en mémoire. Le binaire enregistre les données dans des fichiers telles qu’elles sont stockées en mémoire. Je vous recommande de vous équiper d’un éditeur hexadécimal pour comparer les contenus des fichiers (Editeur hexadécimal).

#include <stdio.h>
#include <stdlib.h>

/* Pour compiler ce code source:
* gcc sizeof-types.c -o sizeof-types
* Pour éxécuter ce programme une fois compilé
* ./sizeof-types (sous UNIX/Linux)
* ou
* sizeof-types.exe (sous Windows)
*/

int main(void) {
	FILE *fd;
	char char_val='c';
	short int sint_val=25315;
	int int_val=-1123315;
	long int lint_val=-3223372036854775807L;
	float float_val=3.1415926F;
	double double_val=3.14159265358979323846264338;
	void *void_val=(void * ) &int_val;
	/* Codes couleur pour affichage */
	char btitle[]="\033[04;34;43m";
	char ecol[]="\033[0m";
	char bnom[]="\033[04;33;40m";
	char btaille[]="\033[01;33;40m";

	printf("%sType\t\tTaille\t\tType C%s\n", btitle, ecol);
	printf("%sEntier court\t%s%d octets\t%sshort int%s\n", bnom, ecol, sizeof(short int), btaille, ecol);
	printf("%sEntier\t\t%s%d octets\t%sint%s\n", bnom, ecol, sizeof(int), btaille, ecol);
	printf("%sEntier long\t%s%d octets\t%slong int%s\n", bnom, ecol, sizeof(long int), btaille, ecol);
	printf("%sEntier long long\t%s%d octets\t%slong long int%s\n", bnom, ecol, sizeof(long long int), btaille, ecol);
	printf("%sFlottant\t%s%d octets\t%sfloat%s\n", bnom, ecol, sizeof(float), btaille, ecol);
	printf("%sCaractere\t%s%d octets\t%schar%s\n", bnom, ecol, sizeof(char), btaille, ecol);
	printf("%sSans type\t%s%d octets\t%svoid%s\n", bnom, ecol, sizeof(void), btaille, ecol);
	printf("%sDouble\t\t%s%d octets\t%sdouble%s\n", bnom, ecol, sizeof(double), btaille, ecol);
	printf("%sDouble long\t%s%d octets\t%slong double%s\n", bnom, ecol, sizeof(long double), btaille, ecol);
	printf("%sPointeur\t%s%d octets\t%svoid *%s\n", bnom, ecol, sizeof(void *), btaille, ecol);

	/* Ecriture de données typées sur le disque */
	/* Lire les fichiers avec un éditeur hexadécimal */
	/* char */
	fd=fopen("./char", "w");
	if (fd) {
		fwrite(&char_val, sizeof(char), (size_t) 1, fd);
		printf("Le caractère (char) 'c' est écrit dans le fichier './char'\n");
		fclose(fd);
	}
	/* short int */
	fd=fopen("./short_int", "w");
	if (fd) {
		fwrite(&sint_val, sizeof(short int), (size_t) 1, fd);
		printf("La valeur (short int) 25315 est écrite dans le fichier './short_int'\n");
		fclose(fd);
	}
	/* int */
	fd=fopen("./int", "w");
	if (fd) {
		fwrite(&int_val, sizeof(int), (size_t) 1, fd);
		printf("La valeur (int) -1 123 315 est écrite dans le fichier './int'\n");
		fclose(fd);
	}
	/* long int */
	fd=fopen("./long_int", "w");
	if (fd) {
		fwrite(&lint_val, sizeof(long int), (size_t) 1, fd);
		printf("La valeur (long int) -3 223 372 036 854 775 807 est écrite dans le fichier './long_int'\n");
		fclose(fd);
	}
	/* float */
	fd=fopen("./float", "w");
	if (fd) {
		fwrite(&float_val, sizeof(float), (size_t) 1, fd);
		printf("La valeur (float) 3.1415926 est écrite dans le fichier './float'\n");
		fclose(fd);
	}
	/* double */
	fd=fopen("./double", "w");
	if (fd) {
		fwrite(&double_val, sizeof(double), (size_t) 1, fd);
		printf("La valeur (double) 3.14159265358979323846264338 est écrite dans le fichier './double'\n");
		fclose(fd);
	}
	/* void * */
	fd=fopen("./void", "w");
	if (fd) {
		fwrite(&void_val, sizeof(void *), (size_t) 1, fd);
		printf("Le pointeur (void *) %p est écrit dans le fichier './pointer'\n", (void *) void_val);
		fclose(fd);
	}

	return EXIT_SUCCESS;
}

J’ai exécuté ce code sur mon PC actuel (CPU Intel Quad Core 64 Bits) ainsi que sur ma vieille station SUN Ultra Sparc 64 bits elle aussi et je vous ai préparé un petit tableau ci-dessous avec les résultats à comparer. La différence saute aux yeux dès que l’on arrive sur des types plus complexes qu’un simple caractère.

Type de baseValeurLITTLE-ENDIAN (mon PC)BIG-ENDIAN (ma vieille station Sun Ultra SPARC 64)
char (8 bits / 1 octet)c6363
short int (16 bits)25315E3 6262 E3
int (32 bits)-11233150D DC EE FFFF EE DC 0D
float (32 bits)3.1415926DA 0F 49 4040 49 0F DA
long int (64 bits)-322337203685477580701 00 58 EC 35 48 44 D3D3 44 48 35 EC 58 00 01
double (64 bits)PI (26 décimales)40 09 21 FB 54 44 2D 1818 2D 44 54 FB 21 09 40

 

Liens et références

Cet article n’a pas pour vocation d’être complet. Si vous souhaitez en savoir plus, voici une liste d’articles traitant en détails des différents sujets soulevés dans cet article. Bonne lecture!