你好,我有这个例子:
int main(){
int x = 300;
char* p = (char*)&x;
printf("%sn", p);
printf("%dn", *p);
}
输出:
,
44
是否存在未定义行为,只要
x
具有溢出signed char
的正值,就将x
的地址转换为char*
?为什么我得到这个输出?
,
和44
?我试图理解,所以我认为300 % 256 = 44
被转换并分配给p
,所以它的字符ASCII值是,
在我的实现。
你认为和推荐什么?
这里没有UB。一般来说,这样做违反了严格的混叠(导致UB),但有一个例外允许您使用char
指针访问任何类型。
300
在内存中表示为4字节:44 1 0 0
(假设sizeof(int) == 4
,这是很常见的,并且假设是小端序)。
printf("%sn", p);
将这4个字节解释为C-string。输出
const char str[4] = {44, 1, 0, 0}; // Same as `{',', '1', ' ', ' '}`
理论上,如果300
的字节表示不包含空字节,您可以获得UB,因为它会导致printf
继续读取x
的边界之外的内存,寻找空终止符。但是没有真正的平台可以这样做,所以它应该是安全的。
并且*p
访问x
表示中的第一个字节,即44
。
如果您在大端平台上尝试这段代码,您将分别得到一个空字符串和一个0
,因为300
的字节表示将是0 0 1 44
。
x
有一个正值,表示signed char
溢出
这不是问题。
- 是未定义的行为cast
x
的地址到char*
只要x有一个正的值溢出一个有符号的字符?
不管x
的值是多少,这都不是未定义的行为;只要const
没有问题,你可以从任何类型的指针到char*
指针进行reinterpret_cast
操作。
将此值打印为字符串是但是,未定义的行为:您可能在int
只使用16位的系统上,因此通过char
指针可访问的int
的任何字节都不会为0,从而导致字节为0;因此,printf()
可以读取超过保证可访问的内存。
- 为什么我得到这个输出?
,
和44
?我试图理解,所以我认为300 % 256 = 44
被转换并分配给p
,所以它的字符ASCII值是,
在我的实现。
你使用低位优先的系统上使用32位int
年代。这意味着300 = 0x12C
被视为数组{ 0x2C, 0x01, 0x00, 0x00 }
(小端系统首先存储最低有效字节)。
现在,尝试将其作为字符串打印,您将获得值为0x2C
的第一个字符,即','
字符,然后是不可打印的字符0x01
。下面的0x00
字符终止了字符串,所以看到的输出只是,
。
当以十进制形式打印指针值时,只打印0x2C
,即44
。