uint64_t的安全低32位屏蔽

时间:2019-08-21 14:31:10

标签: c bit-manipulation 64-bit implicit-conversion bitmask

假设以下代码:

uint64_t g_global_var;

....
....

void foo(void)
{
    uint64_t local_32bit_low = g_global_var & 0xFFFFFFFF;
    ....
}

在当前工具链中,此代码可以按预期工作,local_32bit_low实际上包含g_global_var的低32位。

我想知道标准C是否保证此代码将始终按预期工作? 我担心的是,编译器可能会将0xFFFFFFFF视为-1的整数,当提升为uint64_t时,它将变为0xFFFFFFFFFFFFFFFF。

PS

我知道,为了安全起见,在这种情况下最好使用0xFFFFFFFFULL。关键是我在旧版代码中看到了它,我想知道是否值得修复。

4 个答案:

答案 0 :(得分:8)

没有问题。整数常量0xFFFFFFFF具有能够按原样存储值的类型。

根据C标准(6.4.4.1整数常量)

  

5整数常量的类型是对应的第一个   可以表示其值的列表

因此该值存储为正值。

如果类型unsigned int是32位整数类型,则常量将具有类型unsigned int

否则,它将具有可以存储值的一种类型。

long int
unsigned long int
long long int
unsigned long long int 

由于表达式中通常的算术转换

g_global_var & 0xFFFFFFFF;

它被提升为

0x00000000FFFFFFFF

请注意,在C中没有负整数常量。例如类似

的表达式
-10

包含两个子表达式:主表达式10和带有一元运算符- -19的子表达式,该子表达式与完整表达式一致。

答案 1 :(得分:3)

0xffffffff永远不是-1。如果将其强制转换(例如通过赋值)为带符号的32位类型,则可能会转换为-1,但是C中的整数文字始终具有其数学值,除非它们溢出。

对于十进制文字,该类型是可以表示该值的最窄带符号类型。对于十六进制文字,将使用无符号类型,然后再使用下一个较宽的带符号类型。因此,在int是32位的常见情况下,0xffffffff的类型为unsigned int。如果将其写为十进制,则其类型为long(如果long是64位)或long long(如果long只有32位)。

答案 2 :(得分:1)

无后缀十六进制或八进制常量的类型是可以表示其值的以下列表的第一个:

int
unsigned int
long int
unsigned long int
long long int
unsigned long long int

(对于不带后缀的十进制常量,请从上面的列表中删除unsigned类型。)

十六进制常量0xFFFFFFFF可以肯定地用unsigned long int表示,因此其类型将是intunsigned intlong int或{{ 1}}可以代表其价值。

请注意,尽管unsigned long int始终求值为1(真),但0xFFFFFFFF > 0在不同的实现上也可能求值为0(假)或1(真)。因此,在将整数常量彼此或与其他整数类型的对象进行比较时,需要小心。

答案 3 :(得分:0)

其他人回答了这个问题,只是一个建议,下次(如果您使用的是C11),您可以使用_Generic自己检查表达式的类型

#include <stdio.h>
#include <stdint.h>

#define print_type(x) _Generic((x), \
    int64_t:  puts("int64_t"),      \
    uint64_t: puts("uint64_t"),     \
    default:  puts("unknown")       \
)

uint64_t g_global_var;

int main(void)
{
    print_type(g_global_var & 0xFFFFFFFF);
    return 0;
}

输出是

uint64_t