在Hex文字后保护“U”后缀

我和我的同事之间就hex表示的文字之后的U后缀存在争议。 请注意,这不是关于此后缀的含义或其作用的问题。 我在这里找到了几个主题,但我没有找到我的问题的答案。

一些背景资料:

我们试图达成一套我们都同意的规则,从那时起就将它作为我们的风格。 我们有一份2004年Misra C规则的副本,并决定将其作为起点。 我们对完全符合Misra C标准不感兴趣; 我们正在挑选我们认为最能提高效率和稳健性的规则。

上述准则第10.6条规定:

“U”后缀应适用于所有无符号类型的常量。

我个人认为这是一个很好的规则。 它只需要很少的努力,看起来比显式转换更好,并且更明确地显示了常量的意图。 对我来说,将它用于所有未签名的内容是有意义的,而不仅仅是数字,因为通过允许exception来执行规则不会发生,特别是对于常用的常量表示。

但是,我的同事认为hex表示不需要后缀。 主要是因为我们几乎只使用它来设置微控制器寄存器,并且在将寄存器设置为hex常量时,符号性无关紧要。

我的问题

我的问题不在于谁是对是错。 它是关于确定是否存在后缀的缺失或存在改变操作结果的情况。 是否存在任何此类情况,还是一致性问题?

编辑:澄清; 特别是关于通过为它们分配hex值来设置微控制器寄存器。 是否会出现后缀可能会产生影响的情况? 我觉得不会。 例如,飞思卡尔处理器专家将所有寄存器分配生成为无符号。

U后缀附加到所有hex常量会使它们无符号,如您所述。 当这些常数与符号值一起用于操作时,这可能具有不希望的副作用,尤其是比较。

这是一个病态的例子:

 #define MY_INT_MAX 0x7FFFFFFFU // blindly applying the rule if (-1 < MY_INT_MAX) { printf("OK\n"); } else { printf("OOPS!\n"); } 

签名/无符号转换的C规则是精确指定的,但有点违反直觉,因此上面的代码确实会打印OOPS

MISRA-C规则是精确的,因为它表示A“U”后缀应该应用于所有无符号类型的常量。 无符号的词具有深远的影响,实际上大多数常量不应该被认为是无符号的。

此外,C标准在十进制和hex常量之间产生了微妙的差异:

  • 如果hex常量的值可以用无符号整数类型表示,而不是int和更大类型的相同大小的有符号整数类型,则认为hex常量是无符号的。

这意味着在32位2的补码系统中, 2147483648longlong long0x80000000unsigned int 。 在这种情况下,附加U后缀可能会使这更明确,但避免潜在问题的真正预防措施是gcc -Wall -Wextra -Werror编译器完全拒绝签名/未签名的比较: gcc -Wall -Wextra -Werrorclang -Weverything -Werrorclang -Weverything -Werror

这有多糟糕:

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

上面的代码应该在32位系统上打印OK ,在16位系统上打印OOPS 。 更糟糕的是,看到嵌入式项目使用过时的编译器仍然很常见,这些编译器甚至没有为此问题实现标准语义。

对于您的具体问题,微处理器寄存器的定义值专门用于通过赋值设置它们(假设这些寄存器是存储器映射的),根本不需要U后缀。 寄存器左值应该是无符号类型,hex值将根据其值进行有符号或无符号,但操作将继续相同。 用于设置有符号数或无符号数的操作码在目标体系结构和我见过的任何体系结构上都是相同的。

使用所有整数常量

附加u/U确保整数常量将是一些无符号类型。


没有u/U

  1. 对于十进制常量整数常量将是某些有符号类型。

  2. 对于hex/八进制常量整数常量将是有符号无符号类型,具体取决于值和整数类型范围。


注意:所有整数常量都具有正值。

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

缺少或存在后缀会改变操作的结果吗?

什么时候这很重要?

通过各种表达式,数学的符号和宽度需要得到控制,并且最好不要惊讶。

 // 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 

**或实现定义的信号被引发。