I am writing an application in c for an STM32F105, using gcc.
In the past (with simpler projects), I have always defined variables as char
, int
, unsigned int
, and so on.
I see that it is common to use the types defined in stdint.h, such as int8_t
, uint8_t
, uint32_t
, etc. This it true in multiple API's that I am using, and also in the ARM CMSIS library from ST.
I believe that I understand why we should do so; to allow the compiler to better optimize memory space. I expect there may be additional reasons.
However, because of c's integer promotion rules, I keep running up against conversion warnings any time I try to add two values, do a bitwise operation, etc. The warning reads something like conversion to 'uint16_t' from 'int' may alter its value [-Wconversion]
. The issue is discussed here and here.
It doesn't happen when using variables declared as int
or unsigned int
.
To give a couple of examples, given this:
uint16_t value16;
uint8_t value8;
I would have to change this:
value16 <<= 8;
value8 += 2;
to this:
value16 = (uint16_t)(value16 << 8);
value8 = (uint8_t)(value8 + 2);
It's ugly, but I can do it if necessary. Here are my questions:
-
Is there a case where the conversion from unsigned to signed and back to unsigned will make the result incorrect?
-
Are there any other big reasons for/against using the stdint.h integer types?
Based on the answers I'm receiving, it looks like the stdint.h types are generally preferred, even though c converts uint
to int
and back. This leads to a bigger question:
- I can prevent the compiler warnings by using typecasting (e.g.
value16 = (uint16_t)(value16 << 8);
). Am I just hiding the problem? Is there a better way to go about it?
Best Answer
A standards-conforming compiler where
int
was anywhere from 17 to 32 bits may legitimately do anything it wants with the following code:An implementation that wanted to do so could legitimately generate a program that would do nothing except output the string "Fred" repeatedly on every port pin using every imaginable protocol. The probability of a program getting ported to an implementation which would do such a thing is exceptionally low, but it is theoretically possible. If want wanted to write the above code so that it would be guaranteed not to engage in Undefined Behavior, it would be necessary write the latter expression as
(uint32_t)x*x
or1u*x*x
. On a compiler whereint
is between 17 and 31 bits, the latter expression would lop off the upper bits, but would not engage in Undefined Behavior.I think the gcc warnings are probably trying to suggest that the code as written is not completely 100% portable. There are times when code really should be written to avoid behaviors which would be Undefined on some implementations, but in many other cases one should simply figure that the code is unlikely to get used on implementations which would do overly annoying things.
Note that using types like
int
andshort
may eliminate some warnings and fix some problems, but would likely create others. The interaction between types likeuint16_t
and C's integer-promotion rules are icky, but such types are still probably better than any alternative.