【问题标题】:Is pointer arithmetic still well defined after casting with alignment violation?在使用对齐违规进行强制转换后,指针算术是否仍然定义良好?
【发布时间】:2020-09-09 17:35:02
【问题描述】:

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

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

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)。

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

【问题讨论】:

标签: c undefined-behavior memory-alignment c11 pointer-arithmetic


【解决方案1】:

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

安全的转换是到char *,然后对其进行指针运算。

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

【讨论】:

  • 大概转换成uintptr_t并进行地址计算也可以吧?
  • @AlexLop。这也可以,但是您需要另一个显式转换回指针类型。将任何指针类型转换为 void * 不需要强制转换。
【解决方案2】:

是的,你理解正确。例如,在一些使用字寻址但char 类型小于字的计算机上,int * 指针的大小可能更小,因此无法说明将未对齐的 char * 转换为 int * 会做什么 - 但陷阱将是 最佳情况 方案。

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

【讨论】:

    【解决方案3】:

    处理未对齐指针可能导致故障的一个显着示例是在处理时:

    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_t* 可以假定永远不会保存未对齐地址的事实,从而生成使用单个 32 位加载和存储的代码,因此只有在指针是对齐。虽然 clang 生成的代码用于执行对 uint32_t* 的赋值不会关心指针是否对齐,并且虽然指针在传递给 memcpy 时被强制转换为 void*,但指针转换为在该事件序列中的uint32_t* 将导致clang 生成确实关心对齐的代码。

    【讨论】:

    • 这是真的,可以说是由于指针取消引用而发生的。您实际上(访问)从s 获取数据并将其复制(访问)到d。我的问题是专门针对地址计算的转换,无需进一步访问计算的地址。
    • @AlexLop.:memcpy 函数被指定为就像复制单个字节一样,不需要对齐源地址和/或目标地址。从语义上讲,代码相当于将传递的地址转换为uint32_t*,然后让memcpy 将其转换为uint8_t* 并使用它来执行访问。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多