Reputation: 4063
I'm reading chapter 7 of C programming: a modern approach, and a bit unclear how printf
handles type conversion.
Consider the following code:
int main() {
int i = 47000; // This will overflow when squared
int i2 = square(i);
long l = square(i);
printf("i2 = %ld, l = %ld, i * i = %ld, square(i) = %ld",
i2, l, i * i, square(i));
return 0;
}
int square(int i) {
int j = i * i;
return j;
}
I expected the output to be (due to overflowing):
i2 = -2085967296, l = -2085967296, i * i = -2085967296, square(i) = -2085967296
But instead I got:
i2 = 2209000000, l = -2085967296, i * i = 2209000000, square(i) = 2209000000
Could you explain:
a) why some results didn't overflow?
b) why l
overflowed?
Upvotes: 0
Views: 87
Reputation: 5321
You get undefined behavior using %ld
to print int
s.
What you are actually seeing is an artifact of the X86-64 parameter passing.
When you explicitly convert the int
value returned by square
from 32 bit to 64 bit long
, you sign extend, preserving the overflow. So that one shows the expected overflow.
The first few int
s passed to a function in x86-64 are zero extended to 64-bits (later ones may be garbage extended). That should be an invisible effect, because only the low 32-bit should be used. But then you used %ld
causing all 64 bits to be used. The particular value had overflowed in a way that causes zero extension to 64-bit to accidentally get back to the non overflowed value.
So the undefined behavior of passing an int
that the receiving function aliases as long
accidentally undid the overflow.
Upvotes: 3
Reputation: 76438
printf
doesn't do type conversions. It takes its arguments (other than the format string) through an ellipsis, so it has to trust the format string to tell it what the types of the input values are.
"%ld"
tells printf
that the corresponding value has type long int
. The values of i2
, i * i
, and square(i)
all have type int
. So the format specifier is wrong, and the displayed values are nonsense. Use "%d"
for int
.
Upvotes: 0