gpt4 book ai didi

c - 指针对齐和别名

转载 作者:行者123 更新时间:2023-12-02 09:25:53 25 4
gpt4 key购买 nike

我已经看到了很多以下代码(抽象示例):

char* byteBlockPtr;

long* alignedPtr = NULL;

/* ... */

/* aligning pointer by long boundary */
while (!ALIGNED(byteBlockPtr))
{
byteBlockPtr++;
}

alignedPtr = (long*)byteBlockPtr;

/* ... */

/* do stuff with memory */
alignedPtr++; /* go to next block */

/* ... */

这是完全可以理解的,原因是从char指针强制转换为更严格的指针类型(在本例中为long指针)要求对齐方式是相同的。

是否同样适用于void指针?

例如,如果有人正在编写自己的内存集,那么是否必须遵循一些通用规则才能不破坏指针的对齐方式?

如果有关于char和void指针以及其他指针,则别名和对齐之间的联系是什么?例如,如果按照标准将void指针隐式转换为任何其他指针类型,这是否意味着可以保证也满足对齐要求?

附言预先对一个以上的问题感到抱歉,但显然我的知识尚有不足,我也不知道如何缩小范围。

最佳答案

如果使用除标准中列出的特定类型以外的任何类型的指针修改了任何类型的对象,则C标准允许钝化的实现执行其所需的任何操作,而不考虑编译器是否有理由期望该对象正在被使用。改性。指针是否正确对齐都没有关系。根据基本原理,该规则存在,因此给定的代码如下:

float f;
void hey(int *p)
{
f=1.0f;
*p=6;
f+=1.0f;
}

编译器不必过于悲观地认为 p可以保存 f的地址,因此可以在指针分配之前编写 f并在之后进行读取。在这种情况下,编译器将没有理由期望对 p的写入会影响 f,因此没有理由期望冗余存储和装载将有任何用途。

尽管没有证据表明该标准的作者打算让编译器编写者如此笨拙,以至于忽略明显出现混叠的情况,但某些编译器编写者(包括与gcc相关的人员)将缺少授权的情况解释为他们应该忽略的指示。这样做时,明显的别名将使代码更加“高效”,而无需考虑所讨论的代码是否真正有用。

在定义了检查指针是否适合给定类型的方式,将指针转换为char *,将其递增(除非或直到其适当地对齐),然后将其转换为其他类型的方式将产生指针的方法其他类型。不幸的是,尽管C11定义了一种确保一种类型的对象以符合另一种对齐要求的方式定位的标准方法,但是C11并 undefined 一种标准方法,使代码可以利用这种对齐方式而不会产生别名问题。

如果代码只需要在非钝编译器上运行,我建议将一个类型转换为另一类型并以后者类型进行访问应该是可靠的,前提是使用新类型的操作必须使用从旧类型转换的指针来完成在使用旧类型的最后一次访问之后,将其转换为新类型,并且使用类型转换指针的所有操作都在使用旧类型的下一次访问之前完成。大多数使用“分块优化”的代码都适合该模式,这是编译器支持的简单模式,而无需进行过于悲观的假设(如果代码将指针从T1 *类型转换为T2 *然后写入该假设,这种操作很可能影响类型T1的对象可能是悲观的,但在大多数情况下,这也是正确的。

不幸的是,因为即使在显而易见的情况下,该标准仍未要求编译器识别别名,并且gcc的作者对没有授权的这种识别不感兴趣,因此无法在不使用gcc的情况下安全地在gcc中使用分块优化标准的gcc特定扩展名,或者使用 -fno-strict-aliasing标志。使用该标志时要获得良好的性能,将需要学习使用 restrict限定符,但是与使用慢速非分块循环相比,使用分块来加快热循环并使用 restrict最小化 -fno-strict-aliasing对性能的影响似乎是一种更好的方法。还要注意,gcc经常会处理带有或不带有标志的,正确使用分块优化的代码,但是,当gcc的作者在编译时没有标志为“偶然”且对“修复”没有厌恶时,gcc的作者会考虑任何正确的行为。破坏]这样的代码而没有警告。

顺便说一句,如果要以完全一致的方式使用分块优化,唯一的实现方法是(1)使用面向字节的代码,并希望优化器以某种方式找出如何用分块版本替换它,或者(2)使用memcpy / memmove从其他存储中加载单词大小的变量,并希望优化程序设法将其替换为合理的代码。例如,如果一个具有指向一堆uint16_t值的64位对齐指针,并希望计算它们的补码,则可以使用:
void flip_quad16s(uint16_t *p, int num_quads)
{
uint64_t *pp = (uint64_t*)p;
union {
uint64_t dw;
uint16_t hw[4];
} u;
for (int i=0; i<num_quads; i++)
{
memcpy(u.hw, pp, 8);
u.dw = ~u.dw;
/* Note that if p actually identifies something which has no declared
type but will be used as uint16_t, we must make sure that memcpy
uses that as a source type */
memcpy(pp++, u.hw, 8);
}
}

当然,这将要求编译器假定p可能是别名
任何类型的任何东西,甚至都可能妨碍完美的优化编译器
取得与非钝编译器一样好的结果
通过使用uint16_t的代码实现,将其转换为uint64_t,然后
与之合作,例如
void flip_quad16s(uint16_t *p, int num_quads)
{
uint64_t *pp = (uint64_t*)p;
for (int i=0; i<num_quads; i++)
pp[i] = ~pp[i];
}

对于一个明智的编译器来说,将后一个函数转换成
最佳代码,它将比任何编译器都反转一堆uint16_t值
同样地使用前一个函数,尤其是在
使用其他类型的循环,因为使用memcpy会强制
编译器承认所有类型的潜在别名,而不仅仅是
uint16_t和uint64_t。

关于c - 指针对齐和别名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38160925/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com