Outils d'utilisateurs

Outils du Site


types_en_detail

Ceci est une ancienne révision du document !


Chapitre précédent Sommaire principal Chapitre suivant

Les types en profondeur

Taille des variables

Quand on a vue la logique booléenne, l'inverse d'un nombre s'écrit en 32 bits :

main.cpp
#include <iostream>
 
int main() {
    std::cout << std::hex << std::showbase;
    std::cout << ~0b1 << std::endl;
}

affiche :

0xfffffffe

32 bits = 4 octets = la taille de la variable en mémoire. A chaque fois que l'on écrit une variable de type int, le compilateur réserve 4 octets en mémoire pour stocker la valeur.

Pour connaître la taille d'un type ou d'une variable, utilisation de sizeof :

main.cpp
#include <iostream>
#include <string>
 
int main() {
    std::cout << "sizeof(int) = " << sizeof(int) << std::endl;
    std::cout << "sizeof(double) = " << sizeof(double) << std::endl;
    std::cout << "sizeof(bool) = " << sizeof(bool) << std::endl;
    std::cout << "sizeof(std::string) = " << sizeof(std::string) << std::endl;
    std::cout << "sizeof(char) = " << sizeof(char) << std::endl;
}

affiche (sur Coliru, peut changer selon le système) :

sizeof(int) = 4
sizeof(double) = 8
sizeof(bool) = 1
sizeof(std::string) = 8
sizeof(char) = 1

Avec des variables :

main.cpp
#include <iostream>
#include <string>
 
int main() {
    int x { 123 };
    double d { 12.34 };
    bool b { true };
    std::string s { "hello, world!" };
    char c { 'a' };
 
    std::cout << "sizeof(int) = " << sizeof(x) << std::endl;
    std::cout << "sizeof(double) = " << sizeof(d) << std::endl;
    std::cout << "sizeof(bool) = " << sizeof(b) << std::endl;
    std::cout << "sizeof(std::string) = " << sizeof(s) << std::endl;
    std::cout << "sizeof(char) = " << sizeof(c) << std::endl;
}

affiche :

sizeof(int) = 4
sizeof(double) = 8
sizeof(bool) = 1
sizeof(std::string) = 8
sizeof(char) = 1

Problème : occupation mémoire parfois inutile. Par exemple, bool qui pourrait être représenté par 1 bit est représenté par 1 octet (8 bits). Si on a besoin de compter de 0 à 3, int sur 4 octets est trop gros (il faut que 2 bits pour cela : 0b00, 0b01, 0b10 et 0b11).

Les modificateurs de type

Les nombres entiers

Possible de modifier les types de base en ajouter des modificateurs, de façon à réduire leurs taille en mémoire ou leur plage de valeur. Par exemple, pour modifier la taille : short, long, long long.

main.cpp
#include <iostream>
 
int main() { 
    std::cout << "sizeof(char) = " << sizeof(char) << std::endl;
    std::cout << "sizeof(short int) = " << sizeof(short int) << std::endl;
    std::cout << "sizeof(int) = " << sizeof(int) << std::endl;
    std::cout << "sizeof(long int) = " << sizeof(long int) << std::endl;
    std::cout << "sizeof(long long int) = " << sizeof(long long int) << std::endl;
}

affiche :

sizeof(char) = 2
sizeof(short int) = 2
sizeof(int) = 4
sizeof(long int) = 8
sizeof(long long int) = 8

Remarque : char est en fait un entier sur 1 octet, affiché par cout comme un caractère, selon le code ASCII. Mais peut être manipulé comme un entier

Pour modifier le signe : signed (par défaut, pas nécessaire de la mettre) et unsigned (que des positifs)

main.cpp
#include <iostream>
 
int main() { 
    std::cout << "sizeof(char) = " << sizeof(char) << std::endl;
    std::cout << "sizeof(unsigned char) = " << sizeof(unsigned char) << std::endl;
    std::cout << "sizeof(short int) = " << sizeof(short int) << std::endl;
    std::cout << "sizeof(unsigned short int) = " << sizeof(unsigned short int) << std::endl;
    std::cout << "sizeof(int) = " << sizeof(int) << std::endl;
    std::cout << "sizeof(unsigned int) = " << sizeof(unsigned int) << std::endl;
    std::cout << "sizeof(long int) = " << sizeof(long int) << std::endl;
    std::cout << "sizeof(unsigned long int) = " << sizeof(unsigned long int) << std::endl;
    std::cout << "sizeof(long long int) = " << sizeof(long long int) << std::endl;
    std::cout << "sizeof(unsigned long long int) = " << sizeof(unsigned long long int) << std::endl;
}

affiche

sizeof(char) = 1
sizeof(unsigned char) = 1
sizeof(short int) = 2
sizeof(unsigned short int) = 2
sizeof(int) = 4
sizeof(unsigned int) = 4
sizeof(long int) = 8
sizeof(unsigned long int) = 8
sizeof(long long int) = 8
sizeof(unsigned long long int) = 8

La différence est que le signed ira de -Max à +Max et que unsigned ira de 0 à 2*Max

En fait, dans la norme, pas de garantie sur la taille, juste des garantie sur l'ordre char < short < int < long < long long. ( Fixed width integer types ? )

Les nombres réels

Pour les nombres réels, un peu différent :

main.cpp
#include <iostream>
 
int main() { 
    std::cout << "sizeof(float) = " << sizeof(float) << std::endl;
    std::cout << "sizeof(double) = " << sizeof(double) << std::endl;
    std::cout << "sizeof(long double) = " << sizeof(long double) << std::endl;
}

affiche :

sizeof(float) = 4
sizeof(double) = 8
sizeof(long double) = 16

Ici, les noms changent, pas simplement des modificateurs de type (pour des raisons historiques : au début, que les float - qui veut dire floattant, pour nombre à virgule flottante - puis ensuite les réels 2 fois plus gros que les floats = appelé double. Ensuite taille au dessus, choisit long comme pour les entier).

Par de modificateurs pour les autres types

Tester les types

digits ? digits10 ? max_digits10 ? radix ?

  • is_signed
  • is_integer
  • is_exact
  • min
  • max
  • lowest
  • epsilon

Tester les valeurs

  • is_nan
  • is_finite
  • is_infinite

Les littérales modifiées

utiliation de u, ul, ull pour modifier une littérale entier et f et l pour une littérale réelle.

Limites des types numériques

max, min, epsilon, etc

Créer ses types

Définir des types

Partir d'un code simple :

int x {};
int y {};
int z {};
int a {};
int b {};
int c {};

On décide de changer d'avis et d'utiliser des réels. Nécessite de tout remplacer

double x {};
double y {};
double z {};
double a {};
double b {};
double c {};

Même problème que pour les variables : nécessite modification de plusieurs lignes, code non facilement maintenable, non respect qualité logiciel.

Pour éviter cela, définir un type, le nommer et l'utiliser.

using local_type = int;
local_type x {};
local_type y {};
local_type z {};
local_type a {};
local_type b {};
local_type c {};

Pour changer de type, besoin que de changer une ligne. Code plus facilement maintenable

Nom des types = même règle que pour les noms des variables. Souvent, pour montrer que c'est un type, on ajoute _t a la fin (C++, STL, boost) ou on commence par une majuscule (Qt)

Type et sémantique

sémantique simple : donner un sens aux types.

ex:

int temp1 { 12 }; // heure
int temp2 { 40 }; // minutes
temps1 + temps2; // n'a pas de sens

Nommer les types :

using heure = int;
using minute = int;
heure temp1 { 12 };
minute temp2 { 40 };

Le type exprimer à quoi il correspond, c'est plus précis que “int”. Mais pas suffisant, on peut toujours écrire :

temps1 + temps2; // n'a pas de sens

idéallement, faudrait que compilateur préviennent. Pourquoi fait-il pas ? Parce que “heure” et “minute” ont un sens pour lecteur. Mais pour compilateur, ce sont des int, donc pas d'interdiction d'addition. Sémantique incomplète

Dans la suite, apprendre à créer ses types et créer une sémantique (des “règles” d'utilisation).

Représentation des nombres

représentation des nombres entier négatif

limitation de représentation d'un nombre “réel” par une représentation limité en mémoire

Typage fort et faible ?

expression : de même type, notion de typage fort (on ne mélange pas les types). Mais conversion possible

limite des nombres réels

Chapitre précédent Sommaire principal Chapitre suivant
types_en_detail.1401750910.txt.gz · Dernière modification: 2014/06/03 01:15 par gbdivers