À partir d'un exemple
unsigned long x = 12345678UL
Nous avons toujours appris que le compilateur n'a besoin que de voir "long" dans l'exemple ci-dessus pour définir 4 octets (en 32 bits) de mémoire. La question qui se pose est de savoir pourquoi devrions-nous utiliser L/UL dans les constantes longues même après avoir déclaré que c’était une longue.
Lorsqu'un suffixe L
ou UL
n'est pas utilisé, le compilateur utilise le premier type pouvant contenir la constante d'une liste (voir les détails dans la norme C99, clause 6.4.4: 5. Pour une constante décimale, la liste est int
, long int
, long long int
).
Par conséquent, la plupart du temps, il n'est pas nécessaire d'utiliser le suffixe. Cela ne change pas le sens du programme. Cela ne change pas le sens de votre exemple d'initialisation de x
pour la plupart des architectures, bien que ce serait le cas si vous aviez choisi un nombre qui ne pourrait pas être représenté par un long long
. Voir aussi la réponse de codebauer pour un exemple où la partie U
du suffixe est nécessaire.
Dans certaines circonstances, le programmeur peut vouloir définir explicitement le type de la constante. Un exemple est lorsqu’on utilise une fonction variadique:
printf("%lld", 1LL); // correct, because 1LL has type long long
printf("%lld", 1); // undefined behavior, because 1 has type int
Une raison courante d'utiliser un suffixe est de s'assurer que le résultat d'un calcul ne déborde pas. Deux exemples sont:
long x = 10000L * 4096L;
unsigned long long y = 1ULL << 36;
Dans les deux exemples, sans suffixe, les constantes auraient le type int
et le calcul serait fait sous la forme int
. Dans chaque exemple, cela comporte un risque de débordement. L'utilisation des suffixes signifie que le calcul sera effectué dans un type plus grand, avec une plage suffisante pour le résultat.
Comme le dit Lightness Races in Orbit, le suffixe littéral vient avant la tâche. Dans les deux exemples ci-dessus, déclarer simplement x
en tant que long
et y
en tant que unsigned long long
ne suffit pas pour empêcher le débordement dans le calcul des expressions qui leur sont affectées.
Un autre exemple est la comparaison x < 12U
où variable x
a pour type int
. Sans le suffixe U
, le compilateur tape la constante 12
sous la forme int
et la comparaison est donc une comparaison des entiers signés.
int x = -3;
printf("%d\n", x < 12); // prints 1 because it's true that -3 < 12
Avec le suffixe U
, la comparaison devient une comparaison d'ints non signés. "Conversions arithmétiques habituelles" signifie que -3 est converti en un grand entier non signé:
printf("%d\n", x < 12U); // prints 0 because (unsigned int)-3 is large
En fait, le type d'une constante peut même changer le résultat d'un calcul arithmétique, encore une fois en raison du fonctionnement des «conversions arithmétiques usuelles».
Notez que, pour les constantes décimales, la liste des types suggérés par C99 ne contient pas unsigned long long
. En C90, la liste se terminait par le type d’entier normalisé non signé le plus large du moment (qui était unsigned long
). Une conséquence en a été que la signification de certains programmes a été modifiée en ajoutant le type standard long long
à C99: la même constante tapée en tant que unsigned long
en C90 pouvait désormais être saisie en tant que long long
signé. Je crois que c’est la raison pour laquelle, en C99, il a été décidé de ne pas avoir unsigned long long
dans la liste des types pour les constantes décimales . Voir this et this blog posts pour un exemple.
Parce que les littéraux numériques sont typiques du type int. L'UL/L indique au compilateur qu'ils ne sont pas de type int, par ex. en supposant que 32bit int et 64bit long
long i = 0xffff;
long j = 0xffffUL;
Ici, les valeurs de droite doivent être converties en longs signés (32 bits -> 64 bits)
La question qui se pose est de savoir pourquoi devrions-nous utiliser L/UL dans les constantes longues même après avoir déclaré que c’était une longue.
Parce que ce n'est pas "après"; c'est "avant".
Vous avez d’abord le littéral, puis il est converti en un type quelconque de la variable dans laquelle vous essayez de le compresser.
Liée à ce post est pourquoi un u
.
Une raison pour u
est d'autoriser un nombre entier constant supérieur à LLONG_MAX
sous forme décimale.
// Likely to generate a warning.
unsigned long long limit63bit = 18446744073709551615`; // 2^64 - 1
// OK
unsigned long long limit63bit = 18446744073709551615u`;