Programmation C source/Types de base

Un livre de Wikilivres.
Programmation C source
Programmation C++
Programmation C++
Sommaire
Modifier ce modèle
#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;
}