Fen*_* Ma 9 c bit-manipulation bit-shift
在 C 语言中,许多运算都采用位移位,其中经常使用整数文字。例如,考虑以下代码片段:
#define test_bit(n, flag) (1UL << (n) & (flag))
Run Code Online (Sandbox Code Playgroud)
据我所知,整数文字后缀UL应该抑制移位中不需要的行为,例如对有符号整数进行符号扩展可能会导致设置多个位。但是,如果情况只是进行左移,如上所示,我们还需要整数文字后缀吗?
由于左移不会导致意外行为,因此我无法弄清楚其目的是什么。像上面这样的代码经常出现在Linux内核等项目中,这让我觉得一定是有需要的。UL有谁知道这种情况下后缀的用途吗?
Nat*_*dge 12
如果你int是 32 位的,并且你有
#define test_bit(n, flag) ((1 << (n)) & (flag))
Run Code Online (Sandbox Code Playgroud)
thentest_bit(31, flag)由于有符号整数溢出而具有未定义的行为。 为什么无符号整数溢出定义了行为,但有符号整数溢出却没有定义?
使1无符号类型避免了 UB。制作它unsigned long(这就是1UL实现的目标)允许将相同的宏用于更宽的掩模。例如,在int32 位和long64 位的系统上,通过使用1UL您可以安全地使用位到test_bit(63, flag).
符号扩展仅适用于右移,因此不适用。
\n<<定义如下:
\n\nC23 \xc2\xa76.5.7 \xc2\xb64 的结果为
\nE1 << E2左E1移位E2;空出的位用零填充。如果E1具有无符号类型,则结果的值为, 环绕。如果具有有符号类型和非负值,并且可以在结果类型中表示,则这就是结果值;否则,行为是未定义的。E1 \xc3\x97 2E2E1E1 \xc3\x97 2E2
左移值有两种方式导致基于以下的未定义行为E1:[1]
E1具有有符号类型和负值。E1具有有符号类型和非负值,并且是不可表示的。E1 \xc3\x97 2E2在我们的例子中,E1是一个正值,因此前者不适用。但是,后者可能适用,具体取决于 的类型E1。
让我们看看不同类型在两个系统上得到的结果。
\nint和64位long(例如x86-64上的Linux)。int和32位long(例如Windows on x86-64)。| 执行 | 用法 | “L”的结果 | “W”的结果 |
|---|---|---|---|
1 << (n) | test_bit( 31, flag ) | 未定义的行为 | 未定义的行为 |
1L << (n) | test_bit( 31, flag ) | 好的(因为 long 是 64 位) | 未定义的行为 |
1U << (n) | test_bit( 31, flag ) | 好的 | 好的 |
1U << (n) | test_bit( 63, flag ) | 结果不正确 | \xe2\x80\x94 |
1L << (n) | test_bit( 63, flag ) | 未定义的行为 | \xe2\x80\x94 |
1UL << (n) | test_bit( 63, flag ) | 好的 | \xe2\x80\x94 |
因此,假设您希望能够测试flag
1Uflag如果可以是 asigned int或 anunsigned int或更短,则需要。1ULflag如果也可以是 asigned long或 an ,则需要unsigned long。E2。E2如果为负数、等于 的宽度E1或大于 的宽度,则会发生这种情况E1。test_bit这对\ 的第一个参数的有效值施加了限制。整数文字“UL”
...不被称为“整数文字”。该术语根本没有在 C 语言规范中使用,并且它有与“整数常量”混淆的风险,而“整数常量”1UL就是一个例子。非正式地,您可以将(整个)整数常量称为“整数文字”,而不是单独的“UL”部分。“UL”本身可以称为“后缀”,如果我们从语言规范中的形式语法中提取名称,那么我们可以更具体地将其称为“整数后缀”。
恕我直言,整数文字“UL”应该抑制不需要的移位,例如对带符号整数进行符号扩展可能会导致设置多个位。
用后缀表示整型常量的主要目的是控制其数据类型。 1UL具有 type unsigned long int,而无后缀的1具有 type int。
但是,如果情况是,仅进行逻辑左移,如上所示,我们还需要整数文字吗?
在按位移位运算中,左操作数的类型就是结果的类型,并且它可以影响结果的值。它还会影响表达式的求值是否具有定义的行为。
对于您提供的特定宏,使用是否重要而1UL不仅仅1取决于宏的使用方式。但也有一些完全合理的用途,使用它们1UL可以产生预期的效果,但实际上却1没有。
由于左移不会导致意外行为,
您的意思是未定义的行为吗?就 C 而言,有符号类型的值(甚至是正值)的左移绝对可能会产生未定义的行为。在行为未定义的情况下,您完全没有理由假设结果将是您所期望或意图的。
我无法弄清楚它的目的是什么。
即使我们忽略有关符号性的问题,如果int和 的long int大小不同(通常情况下),那么1U << n可能会产生与实际情况不同的、明确定义的结果1UL。在这种情况下,尽管存在未定义的行为,但期望1 << n计算出相同的结果是不合理的1UL << n。