此代码是否违反了严格的别名规则?

时间:2016-07-13 09:41:12

标签: c language-lawyer strict-aliasing

问题:

  1. 以下此代码是否违反了严格的别名规则?也就是说,是否允许智能编译器打印00000(或其他一些令人讨厌的效果),因为然后通过int*访问首先作为其他类型访问的缓冲区?

  2. 如果没有,那么只会在大括号之前移动ptr2的定义和初始化(所以ptr2已定义,当ptr1到达范围时)会破坏它吗?

  3. 如果没有,会删除大括号(因此ptr1ptr2在同一范围内)会破坏它吗?

  4. 如果是,代码如何修复?

  5. 奖金问题:如果代码没问题,而且2.或3.也不要破坏它,如何更改它会破坏严格的别名规则(例如,将支撑循环转换为使用int16_t) ?

    int i;
    void *buf = calloc(5, sizeof(int)); // buf initialized to 0
    
    {
        char *ptr1 = buf;    
        for(i = 0; i < 5*sizeof(int); ++i)
            ptr1[i] = i;
    }
    
    int *ptr2 = buf;
    for(i = 0; i < 5; ++i)
        printf("%d", ptr2[i]);
    

    寻找确认,如此简短(ish),关于这个特定代码的专家答案,理想情况下使用最低标准报价,就是我所追求的。我不是经过长时间的严格别名规则解释,只是与此代码相关的部分。如果答案明确列举上述编号问题,那就太好了。

    还假设没有整数陷阱值的通用CPU,并且还要说int是32位和2的补码。

2 个答案:

答案 0 :(得分:13)

不,它不是,但这只是因为内存已分配,并使用字符类型写入。

使用malloc分配内存。该对象没有声明 1 类型,因为它是使用malloc分配的。因此,该对象没有任何有效的类型。

然后代码使用类型char访问和修改对象。由于类型为 2 char并且没有复制具有有效类型的对象 5 ,因此复制不会将有效类型设置为char此次访问和后续访问,但仅将有效类型设置为char,仅用于访问 3 的持续时间。访问后,对象不再具有有效类型。

然后使用类型int来访问并仅读取该对象。由于对象没有有效类型,因此在读取期间它变为 3 int。访问后,对象不再具有有效类型。由于int显然与有效类型int兼容,因此定义了行为。

(假设读取的值不是int的陷阱表示。)

如果使用与int不兼容的非字符类型访问和修改对象,则行为将是未定义的。

假设你的例子是(假设sizeof(float)==sizeof(int)):

int i;
void *buf = calloc(5, sizeof(float)); // buf initialized to 0

{
    float *ptr1 = buf;    
    for(i = 0; i < 5*sizeof(float); ++i)
        ptr1[i] = (float)i;
}

int *ptr2 = buf;
for(i = 0; i < 5; ++i)
    printf("%d", ptr2[i]);

当写入float时,对象的有效类型变为类型float,在写入期间以及对不修改它的对象的所有后续访问 2 。当int访问这些对象时,有效类型仍为float,因为只读取值而不进行修改。使用float的先前写入将有效类型永久设置为float,直到下一次写入此对象(在这种情况下不会发生)。类型intfloat不兼容 4 ,因此行为未定义。

(以下所有文字引自:ISO:IEC 9899:201x)

1 (6.5表达式6)
用于访问其存储值的对象的有效类型是对象的声明类型(如果有)。 87)已分配的对象没有声明的类型。

2 (6.5表达式6)
如果通过类型不是字符类型的左值将值存储到没有声明类型的对象中,则左值的类型将成为该访问的对象的有效类型,并且后续访问不会修改存储的值。

3 (6.5表达式6)
对于没有声明类型的对象的所有其他访问,对象的有效类型只是用于访问的左值的类型。

4 (6.5表达式8)
对象的存储值只能由具有其中一个的左值表达式访问 以下类型:88) - 与对象的有效类型兼容的类型, - 与对象的有效类型兼容的类型的限定版本, - 对应于有效类型的有符号或无符号类型的类型 宾语, - 对应于合格版本的有符号或无符号类型的类型 有效的物体类型, - 聚合或联合类型,其中包括上述类型之一 成员(包括,递归地,子集合或包含的联合的成员),或 - 字符类型。

5 (6.5表达式6)
如果使用memcpy或memmove将值复制到没有声明类型的对象中,或者将其复制为字符类型数组,则该访问的修改对象的有效类型以及不修改值是从中复制值的对象的有效类型(如果有的话)。

答案 1 :(得分:2)

没有。这不违反严格的别名。

来自the C Standard 6.2.5类型,第28段:

  

指向void的指针应具有相同的表示形式   对齐要求作为指向字符类型的指针。 48

请注意48.这是脚注48:

  

48)相同的表示和对齐要求是   意味着可互换性作为函数的参数,   从函数和工会成员返回值。

因此,您可以通过calloc()指针访问char *内存(假设您的ptr意图为ptr1),没有问题。< / p>

虽然这非常有用,但由于 7.22.3内存管理功能,第1段指出:

  

如果分配成功,则返回指针   它可以被分配给指向任何类型的对象的指针   基本对齐要求,然后用于访问这样的   对象或分配的空间中的此类对象的数组

因此,您也可以通过calloc()指针以及int指针安全地访问char内存。还有一个double指针启动(假设你保持在已分配内存的范围内)。

相关问题