问题
Consider the following piece of C code:
#include <stdint.h>
uint32_t inc(uint16_t x) {
return x+1;
}
When compiled with gcc-4.4.3 with flags -std=c99 -march=core2 -msse4.1 -O2 -pipe -Wall on a pure x86_64 system, it produces
movzwl %di,%eax
inc %eax
retq
Now, unsigned overflow is predicted in C. I do not know much about x86_64 assembly, but as far as I can see the 16bit argument register is being moved to a 32bit register, which is incremented and returned. My question is, what if x == UINT16_MAX. An overflow would occur and the standard dictates x+1==0, right? However, given %eax is a 32bit register, it now contains UINT16_MAX+1, which is not correct.
This lets me connect one question here: is there a portable way to disable unsigned overflow in C so that the compiler can assume the upper bits of a small variable stored in a large register will always be 0 (so it needs not clear them)? If not (or if the solution is syntactically nasty), is there a way to do it at least in GCC?
Thank you very much for your time.
回答1:
No, C types are subject to default promotions. Assuming uint16_t has lower conversion rank than int, it will be promoted to int and the addition will be carried out as an int, then converted to uint32_t when returned.
As for your related question at the end, I don't quite follow what you want.
回答2:
Use a coding style that does not use compiler intermediaries for calculations, note that (1) is going to have the data type int.
uint32_t inc(uint16_t x) {
uint16_t y = x + 1;
return y;
}
回答3:
A peculiarity of the way the standard describes integer overflow is that it allows compilers to assume that an overflow cannot occur. In the case you show there, the compiler is not expected to preserve the behavior of an overflow, since after all, the range of possible values that x+1 may take (assuming that overflow doesn't exist) fit in the return type.
回答4:
For your second question, in C there is no such thing as overflow for unsigned types, the applicable term is wrapping. By definition unsigned types are computed modulo 2^width. Whenever you cast a wider unsigned type to one that is narrower the upper bits will simply be thrown away. All C compilers should implement it like this, there is nothing you have to worry about.
In essence unsigned types are quite simple, the nasty things only come for signed types.
来源:https://stackoverflow.com/questions/3895741/unsigned-overflow-in-c