đ Retour au Sommaire
Dans la section prĂ©cĂ©dente, nous avons vu que le type int peut stocker des valeurs dâenviron -2 milliards Ă +2 milliards. Mais que se passe-t-il si vous nâavez besoin que de nombres positifs ? Câest lĂ quâintervient la distinction entre types signĂ©s (signed) et non signĂ©s (unsigned).
Cette distinction est fondamentale en C, car elle double la plage positive des types entiers tout en sacrifiant les valeurs négatives.
Un type signé peut représenter des nombres positifs et négatifs.
Par défaut, tous les types entiers en C sont signés :
int temperature = -15; // OK : int est signé par défaut
short altitude = -200; // OK : short est signé par défaut
char lettre = 'A'; // char peut ĂȘtre signĂ© ou non selon le compilateur
Le bit de poids fort (le bit le plus Ă gauche) sert de bit de signe :
0 = nombre positif1 = nombre négatifLe C utilise la méthode du complément à deux pour représenter les nombres négatifs.
Exemple avec un char signé (8 bits) :
| Valeur décimale | Représentation binaire | Explication |
|---|---|---|
| 0 | 0000 0000 |
Zéro |
| 1 | 0000 0001 |
Un positif |
| 127 | 0111 1111 |
Maximum positif |
| -1 | 1111 1111 |
Complément à deux de 1 |
| -2 | 1111 1110 |
Complément à deux de 2 |
| -128 | 1000 0000 |
Minimum négatif |
Plage dâun char signĂ© : -128 Ă +127 (256 valeurs au total)
Note : Le complĂ©ment Ă deux simplifie les opĂ©rations arithmĂ©tiques en Ă©lectronique. Vous nâavez pas besoin de comprendre les dĂ©tails mathĂ©matiques pour utiliser le C, mais sachez que câest la mĂ©thode standard.
Un type non signé ne peut représenter que des nombres positifs ou nuls.
On utilise le mot-clé unsigned devant le type :
unsigned int population = 67000000;
unsigned short port = 8080;
unsigned char octet = 255;
Tous les bits servent à représenter la magnitude du nombre (pas de bit de signe).
Exemple avec un unsigned char (8 bits) :
| Valeur décimale | Représentation binaire | Explication |
|---|---|---|
| 0 | 0000 0000 |
Zéro |
| 1 | 0000 0001 |
Un |
| 127 | 0111 1111 |
Ancien maximum signé |
| 128 | 1000 0000 |
Maintenant positif ! |
| 255 | 1111 1111 |
Maximum |
Plage dâun unsigned char : 0 Ă 255 (256 valeurs au total)
Voici lâimpact du modificateur unsigned sur les types courants :
| Type | Taille | Plage (signed) | Plage (unsigned) |
|---|---|---|---|
char / unsigned char |
1 octet | -128 Ă 127 | 0 Ă 255 |
short / unsigned short |
2 octets | -32 768 Ă 32 767 | 0 Ă 65 535 |
int / unsigned int |
4 octets | -2 147 483 648 Ă 2 147 483 647 | 0 Ă 4 294 967 295 |
long long / unsigned long long |
8 octets | ±9.2 Ă 10Âčâž | 0 Ă 1.8 Ă 10Âčâč |
Observation clé : Le type unsigned double la valeur positive maximale, mais ne peut plus représenter de nombres négatifs.
Il existe plusieurs façons dâĂ©crire les types non signĂ©s :
// Forme complÚte (recommandée pour la lisibilité)
unsigned int compteur = 1000;
unsigned short port = 443;
unsigned long long id = 123456789012345ULL;
// Forme raccourcie (équivalente)
unsigned compteur2 = 1000; // "int" est implicite
// Avec les types Ă largeur fixe (C99+)
#include <stdint.h>
uint8_t octet = 200; // unsigned 8 bits
uint16_t port2 = 8080; // unsigned 16 bits
uint32_t timestamp = 1704067200U; // unsigned 32 bits
uint64_t grande_valeur = 18446744073709551615ULL; // unsigned 64 bits
Suffixes de littéraux :
U ou u : indique un littéral unsigned intUL : unsigned longULL : unsigned long longunsigned ?unsigned quand :Les valeurs sont toujours positives par nature
unsigned int age = 25; // Un Ăąge ne peut pas ĂȘtre nĂ©gatif
unsigned short annee = 2025; // Une année non plus
unsigned long long taille_fichier = 5000000000ULL; // Taille en octets
Vous manipulez des bits ou des masques
unsigned int flags = 0x00FF; // Masques de bits
unsigned char pixel = 0xFF; // Valeur RGB (0-255)
Vous avez besoin de la plage étendue
unsigned int compteur = 3000000000U; // Dépasse les 2 milliards d'un int signé
Interfaçage avec du matériel ou des protocoles
uint32_t adresse_ip = 0xC0A80001; // 192.168.0.1 en hexadécimal
uint16_t port_reseau = 80;
unsigned quand :Vous faites des soustractions pouvant ĂȘtre nĂ©gatives
// DANGER : comportement inattendu
unsigned int a = 5;
unsigned int b = 10;
unsigned int resultat = a - b; // Résultat : 4294967291 (underflow !)
Vous utilisez des boucles décroissantes
// ERREUR : boucle infinie !
for (unsigned int i = 10; i >= 0; i--) {
printf("%u\n", i);
// i ne sera jamais < 0 car unsigned !
}
Par défaut, sans raison spécifique
int comme type par défaut pour les entiersunsigned que si vous en avez vraiment besoinunsigned int x = 5;
unsigned int y = 10;
unsigned int diff = x - y;
printf("Résultat : %u\n", diff); // Affiche : 4294967291 (pas -5 !)
Explication : En unsigned, -5 « boucle » au maximum (UINT_MAX + 1 - 5).
Solution : Utilisez des types signés si vous pouvez avoir des résultats négatifs.
int a = -1;
unsigned int b = 1;
if (a < b) {
printf("a est plus petit\n");
} else {
printf("a est plus grand\n"); // C'est ici qu'on arrive !
}
Explication : Le -1 signé est converti en unsigned, devenant 4294967295.
Solution : Ăvitez de mĂ©langer signed et unsigned dans les comparaisons.
// ERREUR : boucle infinie
for (unsigned int i = 10; i >= 0; i--) {
printf("%u\n", i);
}
// i passe de 0 Ă 4294967295 au lieu de devenir -1
Solution :
// Version correcte
for (unsigned int i = 10; i > 0; i--) {
printf("%u\n", i);
}
printf("0\n");
// Ou utilisez un int signé
for (int i = 10; i >= 0; i--) {
printf("%d\n", i);
}
size_t taille = strlen(chaine); // size_t est unsigned
// DANGER si taille < 5
for (size_t i = 0; i < taille - 5; i++) {
// Si taille = 3, alors taille - 5 = trĂšs grand nombre !
}
Solution : Vérifiez les bornes avant la soustraction.
if (taille >= 5) {
for (size_t i = 0; i < taille - 5; i++) {
// Maintenant c'est sûr
}
}
printfUtilisez les bons spécificateurs de format :
| Type | Spécificateur | Exemple |
|---|---|---|
int |
%d |
printf("%d", x); |
unsigned int |
%u |
printf("%u", x); |
long |
%ld |
printf("%ld", x); |
unsigned long |
%lu |
printf("%lu", x); |
long long |
%lld |
printf("%lld", x); |
unsigned long long |
%llu |
printf("%llu", x); |
| En hexadécimal | %x ou %X |
printf("0x%X", x); |
#include <stdio.h>
int main(void) {
int signe = -42;
unsigned int non_signe = 42;
printf("Signé : %d\n", signe); // Signé : -42
printf("Non signé : %u\n", non_signe); // Non signé : 42
// Attention : mauvais spécificateur
printf("Signé avec %%u : %u\n", signe); // Affiche un grand nombre !
// Hexadécimal
printf("Hexa : 0x%X\n", non_signe); // Hexa : 0x2A
return 0;
}
charLe type char est spécial : le standard C laisse le choix au compilateur de le rendre signé ou non signé par défaut.
char c = 200; // Peut ĂȘtre -56 (signĂ©) ou 200 (non signĂ©) selon le compilateur !
Recommandation : Soyez explicite si cela importe :
signed char temperature = -10; // Garanti signé
unsigned char pixel = 200; // Garanti non signé
Cas dâusage :
char : pour les chaĂźnes de caractĂšres (texte)signed char : pour de petits entiers signĂ©s (-128 Ă 127)unsigned char : pour des octets bruts (0 Ă 255), pixels, donnĂ©es binairesLes types <stdint.h> Ă©liminent lâambiguĂŻtĂ© :
#include <stdint.h>
int8_t temperature = -10; // Signé 8 bits : -128 à 127
uint8_t pixel = 255; // Non signé 8 bits : 0 à 255
int32_t balance = -50000; // Signé 32 bits
uint32_t timestamp = 1704067200U; // Non signé 32 bits
int64_t grand_negatif = -9000000000000000000LL;
uint64_t grand_positif = 18000000000000000000ULL;
Avantages :
#include <stdio.h>
#include <stdint.h>
#include <limits.h>
int main(void) {
// Types classiques
int negatif = -100;
unsigned int positif = 100;
// Underflow demonstration
unsigned int x = 5;
unsigned int y = 10;
unsigned int resultat = x - y;
// Types Ă largeur fixe
int32_t signe32 = -2000000000;
uint32_t non_signe32 = 4000000000U;
printf("=== Types classiques ===\n");
printf("Négatif (int) : %d\n", negatif);
printf("Positif (unsigned int) : %u\n", positif);
printf("\n=== Underflow ===\n");
printf("5 - 10 en unsigned : %u\n", resultat);
printf("(devrait ĂȘtre -5, mais unsigned boucle)\n");
printf("\n=== Types Ă largeur fixe ===\n");
printf("int32_t : %d\n", signe32);
printf("uint32_t : %u\n", non_signe32);
printf("\n=== Limites ===\n");
printf("INT_MAX : %d\n", INT_MAX);
printf("UINT_MAX : %u\n", UINT_MAX);
return 0;
}
Sortie :
=== Types classiques ===
Négatif (int) : -100
Positif (unsigned int) : 100
=== Underflow ===
5 - 10 en unsigned : 4294967291
(devrait ĂȘtre -5, mais unsigned boucle)
=== Types Ă largeur fixe ===
int32_t : -2000000000
uint32_t : 4000000000
=== Limites ===
INT_MAX : 2147483647
UINT_MAX : 4294967295
<limits.h>)Le header <limits.h> définit les valeurs minimales et maximales :
#include <limits.h>
#include <stdio.h>
int main(void) {
printf("CHAR_MIN : %d\n", CHAR_MIN);
printf("CHAR_MAX : %d\n", CHAR_MAX);
printf("UCHAR_MAX : %u\n", UCHAR_MAX);
printf("SHRT_MIN : %d\n", SHRT_MIN);
printf("SHRT_MAX : %d\n", SHRT_MAX);
printf("USHRT_MAX : %u\n", USHRT_MAX);
printf("INT_MIN : %d\n", INT_MIN);
printf("INT_MAX : %d\n", INT_MAX);
printf("UINT_MAX : %u\n", UINT_MAX);
return 0;
}
Ces macros sont utiles pour :
â
Utilisez int par défaut pour les entiers ordinaires
â
Ajoutez unsigned seulement si nécessaire (compteurs, bits, tailles)
â
Préférez les types à largeur fixe (uint32_t) pour la portabilité
â Ăvitez de mĂ©langer signed et unsigned dans les comparaisons
â
Attention aux soustractions avec unsigned (risque dâunderflow)
â
Utilisez les bons spécificateurs avec printf (%d vs %u)
â Nâutilisez jamais unsigned juste « pour ĂȘtre sĂ»r » sans raison valable
â Ne faites pas de boucles dĂ©croissantes avec unsigned naĂŻvement
đč Signed (signĂ©) : nombres positifs et nĂ©gatifs (bit de signe)
đč Unsigned (non signĂ©) : seulement positifs (double la plage positive)
đč Underflow : unsigned_var - x peut donner un trĂšs grand nombre si le rĂ©sultat serait nĂ©gatif
đč MĂ©langer signed/unsigned dans des comparaisons produit des rĂ©sultats inattendus
đč Types <stdint.h> : solution moderne et portable (int32_t, uint64_t)
đč char est ambigu : utilisez signed char ou unsigned char si le signe importe
Dans les prochaines sections, nous explorerons :
đŻ Vous savez maintenant choisir entre types signĂ©s et non signĂ©s ! Cette distinction est cruciale pour Ă©viter des bugs subtils liĂ©s aux dĂ©bordements et comparaisons.