我的理解是,常规ol'short/int/long类型上的某些位操作要么依赖于实现(|&~>>(,要么未定义(<<(
然而,C99引入了固定宽度整数类型,明确定义它们为不带填充位的二补精确位。
这是否意味着所有的位操作都是定义良好的,并且可以在提供这些操作的平台之间移植?
例如,这在我的机器上工作™,但它能保证工作吗?
#include <inttypes.h>
#include <stdio.h>
int main() {
uint16_t a = 0xffff;
int16_t b = *(int16_t*)(&a);
printf("%" PRId16 "n", b);
// Prints '-1'
b <<= 4;
printf("%" PRId16 "n", b);
// Prints '-16'
return 0;
}
使用固定宽度类型并不能保证保护不受与位偏移相关的未定义行为的影响。关于精确宽度整数类型的C标准第7.20.1.1节规定:
1typedef名称
intN_t
指定一个宽度为N、没有填充位和2的补码的有符号整数类型代表。因此,int8_t
表示这样的有符号整数类型宽度恰好为8位。2typedef名称
uintN_t
指定一个宽度为N且没有填充位的无符号整数类型。因此,uint24_t
表示这样一个宽度恰好为24位的无符号整数类型。3这些类型是可选的。然而,如果实现提供宽度为8、16、32或64位的整数类型,没有填充位,并且(对于签名类型(具有二的补码表示,它应该定义相应的typedef名称。
这里没有提到关于这些类型上的位移位操作的行为的特殊处理。
这里的一个重要方面是整数促销。对于小于int
的固定宽度类型,在应用于大多数操作数之前,它们将首先升级为int
(而不是int16_t
或int32_t
(。然后你要处理潜在的未定义行为。
例如,假设一个32位的int
,这个代码显示出未定义的行为:
uint24_t x = 0xffffff;
uint24_t y = x << 8;
因为在表达式x << 8
中,x
的值被提升为int
,所以该移位导致一个比特被移位到该值的符号比特中。