如何解决 C 中的严格别名问题?

问题描述 投票:0回答:1

我的目标是制作一个通用的 arena 分配器,其缓冲区存储在可执行文件的

.bss
部分中,以避免实际程序中的任何分配,但这在 C 中存在严格别名的问题。如果我将缓冲区定义为
static char buffer[BUFFER_SIZE];
,那么通过严格别名,我无法使用指向它的其他类型的指针而不导致未定义的行为。因为我希望这个分配器是通用的,所以我需要能够分配其中的任何类型。

我不能使用

memcpy()
,因为分配器需要返回一个可以以任何方式使用的指针,而不是值的副本。我也不能使用基于联合的类型修剪,因为如果我没有记错的话,它不能通过指针工作(请参阅第二个代码块here)。

我知道在 C 中解决这个问题的唯一方法是使用

-fno-strict-aliasing
标志完全禁用严格别名。在这篇博文中,作者尝试做同样的事情,但他得出的解决方案是使用内联汇编来“清洗”指针,以便优化器无法应用严格的别名优化,但这看起来非常脆弱。

有没有更好的方法来做这样的事情?理想情况下,应该有一个内置函数,可以清楚地标记要通过严格别名忽略的强制转换或内存区域。

c strict-aliasing
1个回答
1
投票

将分配代码放在一个或多个与使用它的代码(客户端代码)分开的模块中。然后,只要您使用不执行跨模块优化的传统编译器和链接器进行构建,由于构建工具的独立模块性质,而不是由于 C 标准的要求,别名就一定会起作用。 (分配代码本身需要遵守别名规则。)

这样做的原因是,在编译分配例程的某些客户端代码时,编译器只能看到客户端代码,而看不到分配代码。它无法知道分配代码使用的声明类型。编译客户端代码后,您可以将其与分配例程链接,或者假设您可以将其与某些其他代码链接,其中分配代码返回的所有地址都已声明与其用途匹配的类型,或者与某些其他代码链接,其中分配代码返回的所有地址都声明了与其用途相匹配的类型。分配代码返回的所有地址都是由 C 标准定义的

malloc
提供的,而不是您自己的代码。由于客户端代码可以与其他代码链接,因此编译器必须生成一个按照 C 标准指定的与其一起工作的对象模块。但是,从它们之间的接口来看,分配代码的行为和其他代码的行为是相同的:客户端代码调用分配例程并接收指针,或者调用释放例程并提供指针,等等在。因此,当编译器生成与其他符合标准的代码一起使用的代码时,它也必须与您的实际分配代码一起使用。

© www.soinside.com 2019 - 2024. All rights reserved.