C字面量后缀U、UL问题

3

请问如果在 ANSI C 中忘记常量(字面值)后缀(后缀)会发生什么?

例如,我看到位移操作的定义如下:

#define AAR_INTENSET_NOTRESOLVED_Pos (2UL) /*!< Position of NOTRESOLVED field. */
#define AAR_INTENSET_NOTRESOLVED_Msk (0x1UL << AAR_INTENSET_NOTRESOLVED_Pos) /*!< Bit mask of NOTRESOLVED field. */
#define AAR_INTENSET_NOTRESOLVED_Disabled (0UL) /*!< Interrupt disabled. */
#define AAR_INTENSET_NOTRESOLVED_Enabled (1UL) /*!< Interrupt enabled. */
#define AAR_INTENSET_NOTRESOLVED_Set (1UL) /*!< Enable interrupt on write. */

它被用在32位架构中,但也可以移植到16位或8位架构。

如果不使用后缀UL,而是按照预期使用这些宏进行位移操作,会发生什么情况呢?

我只是假设在8位架构中,(1<<30)可能导致溢出。

编辑: 我找到了一个好网站: http://dystopiancode.blogspot.cz/2012/08/constant-suffixes-and-prefixes-in-ansi-c.html

但是如果代码要移植到各种不同的架构上,使用后缀是否安全呢?

例如,如果后缀U代表无符号整数,则对于8位架构来说,通常是16位,但对于32位来说,则是32位变量,因此0xFFFFAAAAU对于32位编译器来说是可以的,但对于8位编译器则不行,是吗?


3
"8位架构"并不等同于"只能计数到255"。编译器会将所有超出总线大小的计算拆分成更小的块,以便更容易处理。 - Jongware
2个回答

7

没有任何后缀的十进制数如 -1、1、2、12345678 等将从 intlonglong long 中选择最小的类型。

没有任何后缀的八进制或十六进制数如 0、0123、0x123、0X123 将从 intunsignedlongunsigned longlong longunsigned long long 中选择最小的类型。


如果 AAR_INTENSET_NOTRESOLVED_Pos 超过 31,将会出现以下潜在问题。注意:unsigned long 至少应为 32 位。如果 unsigned long 是 32 位,则结果为 0 **,但是如果更长则不为零。

(0x1UL << AAR_INTENSET_NOTRESOLVED_Pos)

以下是一个类似的潜在问题,如果 AAR_INTENSET_NOTRESOLVED_Pos 超过 15,就会出现问题。 0x1 是一个无符号整数,必须至少为16位。此外,如果 unsigned/int 是16位,那么最小值 0x1 将成为 int。因此,如果没有明确使用 U0x1AAR_INTENSET_NOTRESOLVED_Pos == 15 时可能会出现问题。[@Matt McNabb]
(0x1 << AAR_INTENSET_NOTRESOLVED_Pos)

位移运算符
“每个操作数进行整数提升。结果的类型为升级后的左操作数的类型。如果右操作数的值为负数或大于等于升级后的左操作数的宽度,则行为未定义。” C11dr §6.5.7 3


机器宽度并不是关键问题。8位或16位的机器可以使用16、32等位大小的int。再次强调,16位是符合C编译器的最小位数。


[编辑] ** 我应该说,“如果unsigned long为32位,则(将超过31位的)位移会导致未定义行为 UB。”


好的...如果int是16位,0x1 << 15也会导致UB(有符号整数溢出),因此“超过15”应该是“等于或超过15”或类似的。 - M.M
@Matt McNabb 感谢您指出该错误 - 已更正。 - chux - Reinstate Monica

1

它可能会出错。

最好将 cast 包含在代码本身中,即:

uint32_t something = (uint32_t) AAR_INTENSET_NOTRESOLVED_Set << 30;

这样即使常量的#define只是一个整数,它也能正常工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接