首页 > 解决方案 > 将pointer-to-char转换为pointer-to-int后意外的字节顺序

问题描述

unsigned char tab[4] = 14;

如果我打印为单个字节...

printf("tab[1] : %u\n", tab[0]); // output: 0
printf("tab[2] : %u\n", tab[1]); // output: 0
printf("tab[3] : %u\n", tab[2]); // output: 0
printf("tab[4] : %u\n", tab[3]); // output: 14

如果我打印为整数...

unsigned int *fourbyte;
fourbyte = *((unsigned int *)tab);
printf("fourbyte : %u\n", fourbyte); // output: 234881024

我的二进制输出是:00001110 00000000 00000000 00000000,这是我想要的数据,但按此顺序 tab[3] tab[2] tab[1] tab[0]。对此有何解释,为什么 unsigned int 指针指向最后一个字节而不是第一个?

标签: cpointerscasting

解决方案


这里的正确答案是你不应该期望任何关系、顺序或其他。除了联合之外,C 标准没有定义不同类型的对象可以在其中重叠的线性地址空间。在许多架构/编译器工具链组合中,这些巧合可能会不时发生,但您永远不应该依赖它们。事实上,通过将指针转换为合适的标量类型会产生一个与其他相同类型的数字相当的数字,这绝不意味着该数字是任何特定的内存地址。

所以:

int* p;
int z = 3;
int* pz = &z;
size_t cookie = (size_t)pz;
p = (int*)cookie;
printf("%d", *p); // Prints 3.

之所以有效,是因为标准说它必须在cookie从要转换为的同一类型的指针派生时才能工作。转换为任何其他类型是未定义的行为。指针不代表内存,它们在抽象中引用“存储”。它们只是对对象或 NULL 的引用,标准定义了指向同一对象的指针必须如何表现以及如何将它们转换为标量值并再次返回。

鉴于:

char array[5] = "five";

该标准说 that&(array[0]) < &(array[1])和 that (&(array[0])) + 1) == &(array[1]),但它对元素array在内存中的排序方式保持沉默。编译器编写者可以自由使用他们认为适合目标架构的任何机器代码和内存布局。

在联合的情况下,它提供了存储中对象的一些重叠,标准只说它的每个字段都必须根据它们的类型适当地对齐,但是关于它们的几乎所有其他内容都是实现定义的。关键条款是6.2.6.1 p7

当一个值存储在联合类型对象的成员中时,对象表示中不对应于该成员但对应于其他成员的字节采用未指定的值。

所有这一切的要点是 C 标准定义了一个抽象机器。编译器会根据您的代码生成该机器的体系结构特定模拟。您无法通过简单的经验方法来理解 C 抽象机,因为实现细节会渗入您的数据集中。您必须将您的观察限制在与抽象相关的那些。因此,避免未定义的行为并非常注意实现定义的行为。


推荐阅读