TL;DR : Il me semble que la premiere expression est toujours vraie effectivement vraie ou fausse suivant le systeme, et que la deuxieme est toujours fausse. Ce passage du K&R est peut-etre un peu vieux, ou alors c'est une erreur (je ne l'ai pas sous la main) ?
Voila le point de vue theorique, ie. comment la sémantique du langage est definie par la norme (qui est plus actuelle).
-1U
est l'expression formee de l'operateur unaire -
applique à la constante 1U
(a priori, ce n'est pas pareil que la conversion de -1
en unsigned
, qu'on ecrirait (unsigned)-1
).
The operand of the unary + or - operator shall have arithmetic type; of the ~ operator, integer type; of the ! operator, scalar type.
Deja, c'est une expression valide, car 1U
a un type arithmetique (ie. entier ou flottant). De plus :
The result of the unary - operator is the negative of its (promoted) operand. The integer promotions are performed on the operand, and the result has the promoted type.
La promotion entiere concerne les types "plus petits" qu'un int
, ce qui n'est pas le cas ici. Le "type promu" reste donc unsigned int
.
Il faut donc chercher ailleurs pour savoir quel sens donner a la "negation de l'operande" quand celle-ci a un type non signe. Apres un peu de recherche, il s'agirait de :
A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.
Ainsi, -1U
vaut UINT_MAX - 1
(au sens de l'arithmetique usuelle).
Regardons maintenant l'expression (-1L < -1U)
. Les deux operandes -1L
et -1U
ont un type reel, donc c'est correct.
If both of the operands have arithmetic type, the usual arithmetic conversions are performed.
On applique donc les conversions arithmetiques usuelles aux deux operandes. Avant de les citer, voila une propriete du "rank of a type" qui apparait dans ce qui suit :
rank(signed char) = rank(unsigned char) < rank(short) = rank(unsigned sort) < rank(signed) = rank(unsigned) < rank(long) = rank(unsigned long) < rank(long long) = rank(unsigned long long)
First, if the corresponding real type of either operand is long double, the other operand is converted, without change of type domain, to a type whose corresponding real type is long double. (ne s'applique pas ici)
Otherwise […] (d'autres conditions qui ne s'appliquent pas ici)
Otherwise, the integer promotions are performed on both operands (sans effet ici). Then the following rules are applied to the promoted operands:
-
If both operands have the same type, then no further conversion is needed. (ce n'est pas le cas, l'une a le type unsigned int
, l'autre le type long int
)
-
Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank. (ce n'est pas le cas, l'une a un type signe, l'autre non signe)
-
Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type. (ce n'est pas le cas, long int
a un rang superieur a unsigned int
, qui est signe)
-
Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type. (si long int
peut representer toutes les valeurs de unsigned int
, cette regle s'applique)
-
Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type. (sinon, c'est celle-ci qui s'applique)
Dans l'exemple de systeme du K&R, un long
peut representer toutes les valeurs d'un unsigned
, donc -1U
est converti en long
. Or on a vu que -1U
valait UINT_MAX - 1
, donc cette derniere expression typee en long
est effectivement un "grand" nombre, tandis que -1L
est negatif. D'ou : (-1L < -1U)
est evalue a vrai.
Sur un systeme ou long
ne peut pas representer toutes les valeurs d'un unsigned
, les deux operandes seraient converties en unsigned long
, et dans ce cas, en vertu des conversions vers un type non signe, on aurait toujours dans l'arithmetique usuelle -1L = ULONG_MAX - 1
et -1U = UINT_MAX - 1
. Dans ce cas aussi, (-1L < -1U)
est donc aussi evaluee a vrai. Etant donne que UINT_MAX <= ULONG_MAX
, on aura dans ce cas -1U <= -1L
donc l'expression initiale est fausse.
En particulier, je t'invite a mediter sur le resultat suivant :
| // a.c
#include <stdio.h>
#include <limits.h>
int main(void)
{
printf("%ld %u\n", LONG_MAX, UINT_MAX);
printf("%d\n", -1U <= -1L);
return 0;
}
|
| $ gcc -m32 a.c && ./a.out
2147483647 4294967295
1
$ gcc a.c && ./a.out
9223372036854775807 4294967295
0
|
Exercice : expliquer avec les regles ci-dessus.
Si maintenant on prend l'expression (-1L > -1UL)
, cette fois c'est le troisieme point des conversions arithmetiques usuelles qui s'applique. Les deux operandes sont converties en unsigned long
. Sauf erreur de ma part, dans ce cas -1L
vaut ULONG_MAX - 1
et -1UL
vaut… ULONG_MAX - 1
. Donc je pense que l'expression -1L > -1UL
est au contraire toujours evaluee a faux.
Sinon, pour ta derniere question, l'ordre des suffixes n'a ici pas d'importance : 1UL
et 1LU
sont equivalents.