Itâs easy to accidentally trigger these UB conditions, not easy to trigger them deliberately or detect them at run time, and hard to identify their root causes. For example, performing arithmetic on two operands, where at least one operand has an unsigned type, will convert both of them to either a signed or unsigned type depending on the implementation.Īrithmetic operations abound with undefined behaviors: signed overflow in add/ sub/ mul/ div, division by zero, bit shifts. When operands have different signedness and ranks, they get converted to a common type in a way that depends on the implementation-defined bit widths. Types with a lower rank than int are automatically promoted, causing unintuitive behavior with ranges and overflow. Unsigned types are straightforward, but signed types have so many possible legal implementations (e.g. Making matters worse, implicit conversions are allowed almost everywhere, which makes human auditing much harder. The conversion rules are completely insane. To make matters worse, standard library types like size_t bear no relationship with other types like unsigned int or uint32_t the standard allows them to be wider or narrower. unusual 128-bit int) could cause headaches or introduce vulnerabilities too. But sometimes, having too much range (e.g. One always needs to choose a type with enough range for the application. For example, int might be 16 bits wide, 32, 64, or some other number. The difficulties begin with the implementation-defined bit widths for basic integer types. Failing to adhere to the rules results in a risk of writing unportable or incorrect code, and such errors might surface immediately or lie latent for days or years. In short, itâs a heavy mental burden to know and use all these rules all the time. (This is false because promotions favor signed types.) For example: Suppose uint8_t x uint8_t y uint32_t z, then x + y should have a type like uint8_t or unsigned int or something reasonable, and +z would still be uint32_t. Whenever all the operand(s) of an arithmetic operator (unary or binary) have unsigned types, the arithmetic operation will be performed in unsigned mode (thus never triggering undefined behavior) and the result type will be unsigned. ) (See type punning and strict aliasing.) Data types Basic integer typesĪ basic integer type is specified using some legal sequence of keywords drawn from the set is safe. By contrast, integer math is a foundation of programming and computer science, and all calculations are always exact in theory (ignoring implementations issues like overflow). Note that floating-point number types will not be discussed at all, because that mostly deals with how to analyze and handle approximation errors that stem from rounding. Whenever I read or write C/C++ code, I need to recall and apply these rules in many situations, such as: Choosing an appropriate type for a local variable / array element / structure field, converting between types, and doing any kind of arithmetic or comparison. In this article, I try to strike a balance between brevity (easing the reader) and completeness (providing absolute correctness and extensive detail). This is my own collection of hard-earned knowledge about how integers work in C/C++, and how to use them carefully and correctly.
0 Comments
Leave a Reply. |