使用其他类型移动一种类型的值是否会违反严格的别名?

时间:2009-12-18 04:31:23

标签: c memcpy strict-aliasing

是否违反严格的别名规则,使用uint32_t移动任何类型的项目,然后再读回来?如果是这样,它是否也违反了严格的别名规则,从uint32_ts数组到任何类型的数组的memcpy,然后读回元素?

以下代码示例演示了两种情况:

#include <assert.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>

int main(void) {
    const char *strings[5] = {
        "zero", "one", "two", "three", "four"
    };
    uint32_t buffer[5];
    int i;

    assert(sizeof(const char*) == sizeof(uint32_t));

    memcpy(buffer, strings, sizeof(buffer));

    //twiddle with the buffer a bit
    buffer[0] = buffer[3];
    buffer[2] = buffer[4];
    buffer[3] = buffer[1];

    //Does this violate strict aliasing?
    const char **buffer_cc = (const char**)buffer;
    printf("Test 1:\n");
    for (i=0; i<5; i++)
        printf("\t%s ", buffer_cc[i]);
    printf("\n");

    //How about this?
    memcpy(strings, buffer, sizeof(strings));
    printf("Test 2:\n");
    for (i=0; i<5; i++)
        printf("\t%s ", strings[i]);
    printf("\n");

    return 0;
}

请忽略我对32位平台的假设。此外,如果元素与uint32_t的大小不同,我知道要填充它们并复制正确数量的uint32_t。我的问题集中在这样做是否违反了严格的别名。

2 个答案:

答案 0 :(得分:4)

第一个循环 在技术上违反严格别名 - 它通过uint32_t类型的左值访问char *个对象。但是,很难看出任何优化器在这种特定情况下会如何导致问题。如果你改变了一点,那么你就是这样做的:

printf("\t%s ", buffer_cc[0]);
buffer[0] = buffer[3];
printf("\t%s ", buffer_cc[0]);

可能看到两次打印的相同字符串 - 因为优化器只能将buffer_cc[0]加载到寄存器中一次,因为第二行只修改了一个对象输入uint32_t

第二个循环,memcpy他们回来了,很好。

答案 1 :(得分:1)

buffer_cc[0]strings[3](例如)是引用相同内存位置但属于同一类型的指针,因此不会违反严格别名。 buffer[0]不是指针,因此不会违反严格的别名。在解除引用指针时出现别名优化,所以我不希望这会导致问题。

正如您在代码和问题的最后一段中所提到的那样,当指针和uint32_t具有不同的大小时,示例代码中的实际问题就出现了。

此外,您始终可以为char*设置别名以指向其他类型而不违反严格别名,但反之亦然。