lake@alberta.UUCP (Robert Lake) (12/20/88)
Now that I have your attention with the title of the article (which is what
this article is about), let us consider the following program:
main() {
int i;
short j = 1;
i = -(unsigned short)j;
printf("i=%d\n", i);
}
If I run this program on a VAX 11/780 using 4.3 BSD, I obtain -1 as the
answer. However, if I run this on a SUN using SUN OS 3.5, I obtain 65535
as the answer. Who is right? I personally think 65535 should be the
answer, and the 4th line of the program should read:
i = -(int)((unsigned short)j);
if it is to perform the same function with all C compilers.
In case anyone is wondering why I would want to do anything like this,
you will find the following statement in the file netinet/ip_input.c in
the 4.3 kernel:
i = -(u_short)ip->ip_len;
which, I think, should really read:
i = -(int)((u_short)ip->ip_len);
Opinions anyone?
Rob Lake (alberta!lake)
University of Alberta
guy@auspex.UUCP (Guy Harris) (12/21/88)
>If I run this program on a VAX 11/780 using 4.3 BSD, I obtain -1 as the >answer. However, if I run this on a SUN using SUN OS 3.5, I obtain 65535 >as the answer. If I run this on a Sun-3 (and probably on other Suns - it's not an acronym any more, BTW), I get -1. >Who is right? Well, let's see: The expression, when fully parenthesized, is -((unsigned short)j) i.e., the cast is done first, and then the negation. K&R First Edition says, under 7.2 Unary operators: The result of the unary - operator is the negative of its operand. The usual arithmetic conversions are performed. The negative of an unsigned quantity is computed by subtracting its n value from 2 , where "n" is the number of bits in an "int". Under 6.6 Arithmetic conversions, it says: First, any operands of type "char" or "short" are converted to "int", and any of type "float" are converted to "double". (...stuff about "double" and "long", which doesn't matter here...) Otherwise, if either operand is "unsigned", the other is converted to "unsigned" and that is the type of the result. Unfortunately, "unsigned short" didn't exist in K&R First Edition C, so the question is "what do the 'usual arithmetic conversions' do to an 'unsigned short' in more modern C?" If it gets widened to "int", the right answer is -1; if it gets widened to "unsigned int", the right answer is 2^32 - 1 on a machine with 32-bit "int"s. K&R Second Edition, based on some ANSI C draft, says: A7.4.5 Unary Minus Operator ...The integral promotions are performed.... and A6.1 Integral Promotion A character, a short integer, or an integer bit-field, all either unsigned or not, may be used in an expression wherever an integer may be used. If an "int" can represent all the values of the original type, then the value is converted to "int"; otherwise the value is converted to "unsigned int". Since, if "int" has more bits than "unsigned short", any "unsigned short" value can fit into an "int" (that is the case on both the VAX and on all Suns), the "unsigned short" value of "(unsigned short)j" is converted to "int" before it is negated; thus, the right answer in dpANS C is -1. In "modern but not dpANS" C, though, I'm not sure. At least one draft of the ANSI C Rationale I saw spoke of a change from "unsignedness-preserving" to "value-preserving" conversion rules, the latter being pretty much the rules described under "Integral Promotion". As such, the old rules may have been "unsignedness-preserving"; I presume this means that the "usual arithmetic conversions" cause the "unsigned short" to be promoted to "unsigned int". What this means is that an "unsigned short" value of "1" is promoted to an "unsigned int" value of "1"; this is negated, yielding 2^32-1. This is then assigned to an "int", yielding -1.... So it appears 65535 isn't right in dpANS C nor, probably, in "modern but not dpANS" C.