进行对齐违规转换后,指针算术是否仍然定义正确?

问题描述

我知道,具有对齐冲突的指针强制转换的结果一旦被取消引用就会调用未定义的行为。

但是仅用于地址计算(不进行解引用)的指针转换呢?

void *addr_calc(single_byte_aligned_struct_t *ptr,uint32_t dword_offset)
{
    uint32_t *dw_ptr = (uint32_t *)ptr;

    return dw_ptr + dword_offset;
}

我们假设ptr的值为 X 。是否保证addr_calc()将返回X + sizeof(uint32_t) * dword_offset

我的假设是这样,但是最近我在C11标准的 J.2未定义行为

部分中看到了以下内容

-两种指针类型之间的转换产生的结果未正确对齐(6.3.2.3)。

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

解决方法

如果ptruint32_t的对齐方式不正确,则实际上可能导致未定义的行为。某些系统可能允许这样做,但其他系统可能会触发故障。

安全的转换将是转换为char *,然后对该指针进行算术运算。

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

是的,您理解正确。例如,在某些使用单词寻址但仍具有小于单词的char类型的计算机上,int *指针的大小可能更小 没有说明将未对齐的char *强制转换为int *会做什么-但是陷阱将是最佳情况方案。

如果需要按字节指针算术,请使用指向字符类型的指针。 所有其他对象指针类型永远只能用于引用指向类型的真实对象数组

,

在处理以下情况时,可能会出现一个明显的例子:施放未对准的指针可能会导致故障:

void test(void *dest,void *src)
{
    uint32_t *d = dest;
    uint32_t *s = src;
    memcpy(d,s,4);
}

在不支持未对齐单词访问的平台上使用clang。如果源和目标不重叠,则memcpy(d,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_t*永远不会保存未对齐的地址,从而生成使用单个32位加载和存储的代码,因此仅在指针对齐。尽管clang生成的代码用于执行对uint32_t*的赋值并不关心指针是否对齐,并且尽管在将指针传递给void*时将其强制转换为memcpy,在该事件序列中将指针转换为uint32_t*会导致clang生成确实在乎对齐的代码。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...