C 在使用对齐冲突强制转换后,指针算法是否仍然定义良好?

C 在使用对齐冲突强制转换后,指针算法是否仍然定义良好?,c,undefined-behavior,memory-alignment,c11,pointer-arithmetic,C,Undefined Behavior,Memory Alignment,C11,Pointer Arithmetic,我知道,一旦取消引用,带对齐冲突的指针转换的结果将调用未定义的行为 但是,指针强制转换只用于地址计算(不取消引用)呢 假设ptr的值为X。是否保证addr\u calc()将返回X+sizeof(uint32\u t)*dword\u offset 我的假设是,但最近我在C11标准第J.2节“未定义行为”中看到了以下内容 -两种指针类型之间的转换会产生错误对齐的结果(6.3.2.3) 如果我理解正确,则强制转换本身调用未定义的行为,而不仅仅是取消引用,这意味着在这种情况下,即使是指针算法也可能表

我知道,一旦取消引用,带对齐冲突的指针转换的结果将调用未定义的行为

但是,指针强制转换只用于地址计算(不取消引用)呢

假设
ptr
的值为X。是否保证
addr\u calc()
将返回
X+sizeof(uint32\u t)*dword\u offset

我的假设是,但最近我在C11标准第J.2节“未定义行为”中看到了以下内容

-两种指针类型之间的转换会产生错误对齐的结果(6.3.2.3)


如果我理解正确,则强制转换本身调用未定义的行为,而不仅仅是取消引用,这意味着在这种情况下,即使是指针算法也可能表现出不可预测的行为。我说的对吗?

如果
ptr
未正确对齐
uint32\t
,这实际上可能导致未定义的行为。一些系统可能会允许,但其他系统可能会触发故障

一种安全的转换方法是将字符转换为
char*
,然后在此基础上进行指针运算

return (char *)ptr + dword_offset * sizeof(uint32_t);

是的,你理解对了。例如,在一些以单词为地址但字符类型小于单词的计算机上,
int*
指针的大小可能更小,因此不知道将未对齐的
char*
强制转换为
int*
会有什么效果-但陷阱是最好的情况


如果需要字节指针算法,请使用指向字符类型的指针。所有其他对象指针类型应仅用于引用指向类型的真实对象或数组。

一个显著的例子是,当处理时,投射未对齐的指针可能导致故障:

void test(void *dest, void *src)
{
    uint32_t *d = dest;
    uint32_t *s = src;
    memcpy(d, s, 4);
}
在不支持未对齐单词访问的平台上使用clang。在源和目标不重叠的情况下,
memcpy的行为(d,s,4)被指定为等同于:

((unsigned char*)d)[0] = ((unsigned char*)s)[0];
((unsigned char*)d)[1] = ((unsigned char*)s)[1];
((unsigned char*)d)[2] = ((unsigned char*)s)[2];
((unsigned char*)d)[3] = ((unsigned char*)s)[3];

然而,Clang将利用
uint32\u t*
可以被假定为永远不会持有未对齐的地址这一事实,从而生成使用单个32位加载和存储的代码,因此只有在指针对齐时才会工作。虽然生成代码铿锵以执行对
uint32\u t*
的赋值,但它并不关心指针是否对齐,而且当指针被传递到
memcpy
时,指针被强制为
void*
,在该事件序列中将指针转换为
uint32\u t*
将导致clang生成与对齐相关的代码。

可能转换为
uintptr\u t
并执行地址计算也可以,对吗?@AlexLop。这也可以,但是您需要对指针类型进行另一次显式转换。将任何指针类型转换为
void*
不需要强制转换。这是真的,可以说这是因为指针取消引用。实际上,您从
s
获取数据并将其复制(访问)到
d
。我的问题是关于地址计算的强制转换,而不需要进一步访问计算的地址。@AlexLop.:指定
memcpy
函数的行为就像它复制单个字节一样,不要求源地址和/或目标地址对齐。从语义上讲,该代码相当于将传递的地址转换为
uint32\u t*
,然后让
memcpy
将其转换为
uint8\u t*
,并使用它来执行访问。这里的主要问题,目前的任何答案都没有解决,如果指针操作数不指向同一数组(普通变量被视为大小为1的数组),则+运算符没有定义良好的行为。看,这是同一个问题。@Lundin我不确定这是同一件事。我的问题更多的是关于行“uint32\u t dw\u ptr=(uint32\u t*)ptr;”。让我们假设“+dw_offset”不会导致超出数组边界的结果。
((unsigned char*)d)[0] = ((unsigned char*)s)[0];
((unsigned char*)d)[1] = ((unsigned char*)s)[1];
((unsigned char*)d)[2] = ((unsigned char*)s)[2];
((unsigned char*)d)[3] = ((unsigned char*)s)[3];