Programmation C source/Types de base
Apparence
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main(void)
{
printf("----------------------------------------\n");
printf(" Entiers\n");
printf("----------------------------------------\n");
// Il y a cinq types de variables entières (integer en anglais) :
char a;
short int b1; short b2;
int c;
long int d1; long d2;
long long int e1; long long e2; // (ce type a été ajouté depuis la norme C99)
/* les modificateurs signed et unsigned permettent respectivement
d'obtenir un type signé ou non signé */
signed int f;
unsigned int g;
/* Domaines de valeurs minimaux des types entiers
signed char -127 à +127
unsigned char 0 à +255
short -32 767 à +32 767
unsigned short 0 à +65 535
int -32 767 à +32 767
unsigned int 0 à +65 535
long -2 147 483 647 à +2 147 483 647
unsigned long 0 à +4 294 967 295
long long -9 223 372 036 854 775 807 à +9 223 372 036 854 775 807
unsigned long long 0 à +18 446 744 073 709 551 615 */
// domaine(char) ≤ domaine(short) ≤ domaine(int) ≤ domaine(long) ≤ domaine(long long)
/* un utilisateur peut connaître les domaines de valeurs exacts
de sa machine en utilisant l'en-tête <limits.h> */
printf("\n----------------------------------------\n");
printf(" Portabilité apportée par C99\n");
printf("----------------------------------------\n");
/* certains programmes peuvent nécessiter un type de données de taille fixe
et cependant être destinés à être portables
la norme ISO C99 décide une bonne fois pour toute de définir,
dans l'en-tête stdint.h, plusieurs nouveaux types
où N représente un nombre entier définissant la taille requise en bit
types implémentés optionnellement sur certaines architectures (à éviter ?)
uintN_t et intN_t entiers signés ou non de longueur exacte
intptr_t et uintptr_t entiers pouvant contenir un pointeur
types requis par toutes les architectures respectant la norme C99
int_leastN_t et uint_leastN_t
entiers devant être plus grand que N bits au moins
int_fastN_t et uint_fastN_t
entiers rapides à calculer et plus grand que N bits au moins
intmax_t et uintmax_t
plus grand entier
cet en-tête (stdint.h) définit aussi des constantes
pour les valeurs minimales et maximales de chaque type.
inttypes.h définit les constantes symboliques à utiliser
pour imprimer ces nouveaux types avec les fonctions de la famille
de printf (PRIxxx)
et les lire avec celles de scanf (SCNxxx) */
int32_t nombre_de_32_bits_avec_signe = 0;
printf("Saisir un nombre : ");
scanf("%" SCNd32, &nombre_de_32_bits_avec_signe);
printf("Le nombre est %" PRId32 "\n", nombre_de_32_bits_avec_signe);
printf("\n----------------------------------------\n");
printf(" Constantes numériques entières\n");
printf("----------------------------------------\n");
#if 0
123
// un nombre en notation décimale : une suite de chiffres (0-9)
0123
/* le caractère « 0 » suivi d'un nombre en notation octale :
une suite de chiffres compris entre 0 et 7 */
0x123abc
/* les caractères « 0x » suivi d'un nombre en notation hexadécimale :
une suite de chiffres et des lettres a, b, c, d, e, f (ou A, B, C, D, E, F). */
/* Par défaut, une constante numérique entière est de type int
si sa valeur est trop grande pour le type int,
elle prend celle du type « plus grand » suffisant
Comme les domaines de valeurs des types peuvent varier suivant la machine,
le type effectif d'une constante peut lui aussi varier.
Cela peut s'avérer problématique lors de passage de paramètres à des fonctions
à nombre variable d'arguments
il est recommandé de forcer le type de la constante */
123U
// la constante est non-signée
123L
// la constante est de type long
123LL
// (C99) la constante est de type long long
123UL
123ULL
/* Lorsqu'une constante est suffixée, mais que sa valeur est trop grande
pour le type demandé, le processus de recherche de type « assez grand »
est utilisé */
#endif
printf("\n----------------------------------------\n");
printf(" Débordement\n");
printf("----------------------------------------\n");
/* Si on essaye d'enregistrer une valeur hors domaine
dans une variable de type signé, la conversion n'est pas définie par le langage.
Cela signifie que tout peut arriver. */
/* Si on essaye d'enregistrer une valeur hors domaine
dans une variable de type non signé (unsigned long, par exemple),
la conversion se fait modulo la valeur maximale représentable par ce type + 1 */
/* On suppose que le type unsigned char est codé sur 8 bits.
Alors une valeur de ce type peut aller de 0 à 2^8 -1, soit 255 */
unsigned char h = 300; // 300 - (255 + 1) = 44
printf("La variable h vaut %hhu.\n", h);
// %hhu sert à dire à printf() qu'on veut afficher un unsigned char
h = -5; // -5 + (255 + 1) = 251
printf("La variable h vaut %hhu.\n", h);
signed char i = 300; // 300 - (127 + 1) - (127 + 1) = 44
printf("La variable i vaut %hhd.\n", i);
// %hhd sert à dire à printf() qu'on veut afficher un signed char
i = -300; // -300 + (127 + 1) + (127 + 1) = -44
printf("La variable i vaut %hhd.\n", i);
/* Sur une telle machine,
les types signés sont traités de la même manière que les types non signés. */
/* votre compilateur pourra détecter les débordements
et vous en avertir (GCC le fait) */
printf("\n----------------------------------------\n");
printf(" Réels\n");
printf("----------------------------------------\n");
/* Les réels sont approximés par des nombres à virgule flottante
En voici la liste triée par précision croissante
float
double
long double */
printf("\n----------------------------------------\n");
printf(" Constantes réelles\n");
printf("----------------------------------------\n");
/* une constante à virgule flottante est
une suite de chiffres séparée par un caractère point
cette séparation pouvant s'effectuer à n'importe quel endroit de la suite
0.0 .0 0. */
/* ou un nombre suivi d'un caractère e suivi d'un entier
le nombre peut être soit un entier, soit un réel
30e-2
1.002e2 */
/* Les constantes sont de type double par défaut
pour un floaf 3.4f ou 3.4F
pour un long double 3.4l ou 3.4L */
printf("\n----------------------------------------\n");
printf(" Arithmétique\n");
printf("----------------------------------------\n");
float j = 0;
int k;
for (k = 0; k < 1000; k++)
{
j += 0.1;
}
printf("j = %f\n", j);
/* Le résultat est 99,999046,
ce qui montre que la précision du type float est en général mauvaise,
d'autant plus que le nombre 0,1 n'est pas représentable en binaire.
Il est ainsi conseillé d'utiliser le type double à la place de float autant que
possible.
Dans ce cas de figure, il est préférable d'éviter les accumulations d'erreurs
infinitésimales, en réécrivant le code de la manière suivante */
for (k = 1; k < 1001; k++)
{
j = k * 0.1;
}
printf("j = %f\n", j);
printf("\n----------------------------------------\n");
printf(" Caractères\n");
printf("----------------------------------------\n");
/* Le type permettant de représenter un caractère est char
sa taille définit l'unité de calcul pour les quantités de mémoire
(et donc pour les tailles des autres types du langage)
sizeof(char), vaut toujours 1
Il occupera au minimum 8 bits, mais il existe des architectures,
relativement spécialisées il est vrai,
ayant des char de 9 bits, de 16 bits, voire plus
Un piège de ce type est qu'il peut être de base signed ou unsigned,
au choix du compilateur, ce qui peut s'avérer dangereux.
Ce code peut ne pas fonctionner avec certains compilateurs
si char est implicitement unsigned, la condition i >= 0 sera toujours vraie */
char l;
for (l = 100; l >= 0; l--)
{
// ...
}
printf("\n----------------------------------------\n");
printf(" Constantes représentant un caractère\n");
printf("----------------------------------------\n");
/* exemple 'a' 'b' 'c'
En fait pour le C, les caractères ne sont ni plus ni moins que
des nombres entiers. Il est tout à fait autorisé d'écrire 'a' * 2 ou 'a' - 32
La valeur représentée par cette constante est néanmoins dépendante du système,
même si dans une écrasante majorité des cas, on se retrouvera avec un jeu de
caractére compatible avec ASCII, qui définit précisément les glyphes des
caractères de 0 à 127
Cet ensemble est à peine suffisant pour couvrir certaines langues latines,
seule optique visée à l'époque où il a été défini, si bien que de nombreuses
extensions sont par la suite apparues
Il s'agit de l'internationalisation. Le langage C reste relativement agnostique
à ce niveau : les caractères sont des nombres et les chaînes, une simple suite
de caractères terminée par 0.
Au système d'interpréter les nombres comme il se doit. */
printf("Sur votre machine, la lettre 'a' a pour code %d.\n", 'a');
printf("\n----------------------------------------\n");
printf(" Caractères spéciaux\n");
printf("----------------------------------------\n");
/* '\'' une apostrophe
'\"' un guillemet
'\?' un point d'interrogation
'\\' un backslash
'\a' un signal sonore (ou visuel)
'\b' un espace arrière
'\f' saut au début de la page suivante
'\n' saut de ligne
'\r' un retour chariot
'\t' une tabulation
'\v' une tabulation verticale */
/* De plus, on peut écrire n'importe quelle valeur de caractère
avec les expressions suivantes :
'\xHH', où chaque H représente un chiffre hexadécimal
correspondant au code du caractère.
Par exemple, '\x61' représente le caractère 'a' (minuscule) en ASCII
(car 97 = 6 * 16 + 1) ;
'\oOO', où chaque O représente un chiffre octal
correspondant au code du caractère. */
printf("\n----------------------------------------\n");
printf(" Trigramme\n");
printf("----------------------------------------\n");
/* Un trigramme est simplement une suite de trois caractères dans le code source
qui sera remplacée par un seul ; cette fonctionnalité a été ajoutée au C pour
supporter les architectures dont l'alphabet ne dispose pas de certains caractères
qui sont nécessaires dans la syntaxe du C, comme les dièses ou les accolades */
/* ??= #
??( [
??) ]
??< {
??> }
??/ \
??' ^
??! |
??- ~ */
printf("\n----------------------------------------\n");
printf(" Chaîne de caractères\n");
printf("----------------------------------------\n");
#if 0
"Ceci est une chaîne de caractère"
"" // Chaîne vide
"Ceci est une chaîne de caractère, " // pas de ;
"déclarée sur plusieurs lignes."
"une " "chaine"
"Une chaîne avec des \"guillemets\" et une barre oblique (\\)\n"
"\x00abcdefghijklmnopqrstuvzxyz"
/* il faut faire attention avec la notation hexadécimale,
car la définition peut s'étendre sur plus de 2 caractéres
Ce code contient un effet de bord inattendu */
"\x00" "abcdefghijklmnopqrstuvzxyz"
"Ceci est une chaîne de caractère,\
déclarée sur plusieurs lignes";
// Ce code est obsolete et a eviter
#endif
printf("\n----------------------------------------\n");
printf(" Booléens\n");
printf("----------------------------------------\n");
// Le langage (jusqu'à la norme C99) ne fournit pas de type booléen.
/* on avait jusqu'à présent
0 faux
autres valeurs vrai */
/* en C99 on a le type _Bool, une variable de type _Bool contient 0 ou 1
l'entete stdbool.h définit bool un raccourci pour _Bool
ainsi que false et true */
printf("\n----------------------------------------\n");
printf(" Vide\n");
printf("----------------------------------------\n");
/* void qui représente rien, le vide.
Il n'est pas possible de déclarer une variable de type void.
Nous verrons l'utilité de ce type lorsque nous parlerons
de fonctions et de pointeurs. */
return 0;
}