C语言 如何理解下面这段话

mccptt67  于 2023-02-11  发布在  其他
关注(0)|答案(1)|浏览(135)

我读了这个on a blog

违反类型规则:将int* 强制转换为float* 并解引用它(将“int”当作“float”访问)是未定义的行为。C要求通过memcpy进行以下类型转换:使用指针强制类型转换是不正确的,并且会导致未定义的行为。这方面的规则非常微妙,我不想在这里深入细节(char* 有一个例外,向量有特殊属性,联合会改变事情,等等)。这种行为启用了一种称为“基于类型的别名分析”(TBAA)的分析,编译器中的大量内存访问优化都使用这种分析。并且可以显著提高生成代码的性能。例如,此规则允许clang优化此函数:

如何使用memcpy函数进行类型强制?char* 的异常又如何呢?
我不明白如何使用memcpy函数进行类型强制?

vh0rcniy

vh0rcniy1#

假设float的值为1.25,并希望确认其实际的IEEE-754十六进制表示形式为3fa00000,则至少有四种不同的方法可以尝试执行此操作:
(1)取一个float指针并将其强制转换为整型指针,并间接作用于它:

float f = 1.25;
printf("%08x\n", *(uint32_t *)&f);

(This片段默认为32位int。为了获得更好的可移植性,您可以使用printf("%08" PRIx32 "\n", *(uint32_t *)&f);。)
(2)使用活接头:

union {float f; uint32_t i;} u;
u.f = f;
printf("%08x\n", u.i);

(3)使用char指针,并迭代/索引:

unsigned char *p = (unsigned char *)&f;
for(int i = 3; i >= 0; i--) printf("%02x", p[i]);

(Note此代码片段采用小端字节序。)
(4)使用memcpy

uint32_t x;
memcpy(&x, &f, 4);
printf("%08x\n", x);

现在,带回家的教训是,由于strict aliasing rule,* 并非所有这些方法都能可靠地工作 *。
特别是方法(1)是完全非法的,这是严格别名规则所不允许的教科书式的例子。
我 * 认为 * 你仍然可以像方法2那样使用联合,但是你可能必须戴上语言律师的帽子来说服自己。(也可以参见下面对这个答案的评论)
然而,方法(3)和(4)仍然有效,因为它们利用了严格别名规则的一个显式例外,也就是说,只要“错误类型”是一个字符指针,就允许你使用一个“错误”类型的双关指针来访问对象的位。
所以我想这是很清楚的,但是在回答你的具体问题时:
如何使用memcpy函数进行类型强制?
如方法(4)。
那么char *的异常又如何呢?
这是允许方法(3)工作的严格别名规则中的显式例外。
顺便说一句,C和C中的规则有很大的不同,严格地说,我相信在C中甚至连方法都没有(3)是法律的的,唯一允许你做这类事情的方法就是方法(4)和对memcpy的隐式调用。(然而,我听说优化编译器现在倾向于非常特别地处理对memcpy的调用,不仅用内联寄存器移动替换显式函数调用,但有时甚至会完全优化副本,并在内部执行类似方法1或2的操作,如果他们知道可以成功的话。)

相关问题