Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defending "U" suffix after Hex literals

There is some debate between my colleague and I about the U suffix after hexadecimally represented literals. Note, this is not a question about the meaning of this suffix or about what it does. I have found several of those topics here, but I have not found an answer to my question.

Some background information:

We're trying to come to a set of rules that we both agree on, to use that as our style from that point on. We have a copy of the 2004 Misra C rules and decided to use that as a starting point. We're not interested in being fully Misra C compliant; we're cherry picking the rules that we think will most increase efficiency and robustness.

Rule 10.6 from the aforementioned guidelines states:

A “U” suffix shall be applied to all constants of unsigned type.

I personally think this is a good rule. It takes little effort, looks better than explicit casts and more explicitly shows the intention of a constant. To me it makes sense to use it for all unsigned contants, not just numerics, since enforcing a rule doesn't happen by allowing exceptions, especially for a commonly used representation of constants.

My colleague, however, feels that the hexadecimal representation doesn't need the suffix. Mostly because we almost exclusively use it to set micro-controller registers, and signedness doesn't matter when setting registers to hex constants.

My Question

My question is not one about who is right or wrong. It is about finding out whether there are cases where the absence or presence of the suffix changes the outcome of an operation. Are there any such cases, or is it a matter of consistency?

Edit: for clarification; Specifically about setting micro-controller registers by assigning hexadecimal values to them. Would there be a case where the suffix could make a difference there? I feel like it wouldn't. As an example, the Freescale Processor Expert generates all register assignments as unsigned.

like image 411
Joey van Hummel Avatar asked Aug 10 '17 18:08

Joey van Hummel


2 Answers

Appending a U suffix to all hexadecimal constants makes them unsigned as you already mentioned. This may have undesirable side-effects when these constants are used in operations along with signed values, especially comparisons.

Here is a pathological example:

#define MY_INT_MAX  0x7FFFFFFFU   // blindly applying the rule

if (-1 < MY_INT_MAX) {
    printf("OK\n");
} else {
    printf("OOPS!\n");
}

The C rules for signed/unsigned conversions are precisely specified, but somewhat counter-intuitive so the above code will indeed print OOPS.

The MISRA-C rule is precise as it states A “U” suffix shall be applied to all constants of unsigned type. The word unsigned has far reaching consequences and indeed most constants should not really be considered unsigned.

Furthermore, the C Standard makes a subtile difference between decimal and hexadecimal constants:

  • A hexadecimal constant is considered unsigned if its value can be represented by the unsigned integer type and not the signed integer type of the same size for types int and larger.

This means that on 32-bit 2's complement systems, 2147483648 is a long or a long long whereas 0x80000000 is an unsigned int. Appending a U suffix may make this more explicit in this case but the real precaution to avoid potential problems is to mandate the compiler to reject signed/unsigned comparisons altogether: gcc -Wall -Wextra -Werror or clang -Weverything -Werror are life savers.

Here is how bad it can get:

if (-1 < 0x8000) {
    printf("OK\n");
} else {
    printf("OOPS!\n");
}

The above code should print OK on 32-bit systems and OOPS on 16-bit systems. To make things even worse, it is still quite common to see embedded projects use obsolete compilers which do not even implement the Standard semantics for this issue.

For your specific question, the defined values for micro-processor registers used specifically to set them via assignment (assuming these registers are memory-mapped), need not have the U suffix at all. The register lvalue should have an unsigned type and the hex value will be signed or unsigned depending on its value, but the operation will proceed the same. The opcode for setting a signed number or an unsigned number is the same on your target architecture and on any architectures I have ever seen.

like image 105
chqrlie Avatar answered Sep 21 '22 03:09

chqrlie


With all integer-constants

Appending u/U insures the integer-constant will be some unsigned type.


Without a u/U

  1. For a decimal-constant, the integer-constant will be some signed type.

  2. For a hexadecimal/octal-constant, the integer-constant will be signed or unsigned type, depending of value and integer type ranges.


Note: All integer-constants have positive values.

//      +-------- unary operator
//      |+-+----- integer-constant
int x = -123;

absence or presence of the suffix changes the outcome of an operation?

When is this important?

With various expressions, the sign-ness and width of the math needs to be controlled and preferable not surprising.

// Examples: assume 32-bit `unsigned`, `long`, 64-bit `long long`

// Bad       signed int overflow (UB)
unsigned a = 4000 * 1000 * 1000;  
// OK
unsigned b = 4000u * 1000 * 1000;  

// undefined behavior
unsigned c = 1 << 31
// OK
unsigned d = 1u << 31

printf("Size %zu\n", sizeof(0xFFFFFFFF));  // 8  type is `long long`
printf("Size %zu\n", sizeof(0xFFFFFFFFu)); // 4  type is `unsigned`

//              2 ** 63
long long e = -9223372036854775808;     // C99: bad "9223372036854775808" not representable
long long f = -9223372036854775807 - 1; // ok 
long long g = -9223372036854775808u;    // implementation defined behavior **

some_unsigned_type h_max = -1;  OK, max value for the target type.
some_unsigned_type i_max = -1u; OK, but not max value for wide unsigned types

// when negating a negative `int`
unsigned j = 0  - INT_MIN;  // typically int overflow or UB
unsigned k = 0u - INT_MIN;  // Never UB

** or an implementation-defined signal is raised.

like image 42
chux - Reinstate Monica Avatar answered Sep 20 '22 03:09

chux - Reinstate Monica