多维数组的别名

Ser*_*sta 5 c arrays strict-aliasing multidimensional-array language-lawyer

众所周知,二维数组是数组的数组,并且标准要求它是连续分配的非空对象集(6.2.5 类型 §20)——这里的对象是一维数组。

众所周知,对于所有常见的实现,以下等式对于T arr2d[X][Y]其中 T 是一个类型和 X 和 Y 整数常量的情况是正确的:

(char *) &arr2d[i][j] == (char *) &arr2d[0][0] + i * Y * sizeof(T) + j * sizeof(T)
Run Code Online (Sandbox Code Playgroud)

上面让我们认为可以允许为 2D 数组和相同大小的 1D 数组甚至另一个具有相同总大小的 2D 数组取别名:

例如,以下程序编译并运行时没有警告,并提供预期的输出:

#include <stdio.h>

int main() {
    int i, j, k=0;
    int arr2d[3][4];   // array of 3 array of 4 ints
    int *arr1 = &arr2d[0][0];  // pointer to first element of an array of 12 ints (1)
    int (*arrx)[3] = (int(*)[3]) arr1; //pointer to first row of an array of arrays of 3 ints
                                       //(2)

    for (i=0; i<12; i++) arr1[i] = k++; // (3)

    for (i=0; i<3; i++) {
        for (j=0; j<4; j++) {
            printf("%3d", arr2d[i][j]);
        }
        putc('\n', stdout);
    }
    for (i=0; i<4; i++) {
        for (j=0; j<3; j++) {
            printf("%3d", arrx[i][j]); 
        }
        putc('\n', stdout);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但:

  • 第 (1) 和 (3) 行将2D 数组 3x4别名为 1D 数组 12
  • 第 (2) 行将2D 数组 3x4别名为 2D 数组 4x3(通过指向 int 的指针)

我的问题是:

  • 根据 C 标准,(1) 和 (3) 是否有效?
  • 如果是,(2)是否有效?

Ser*_*sta -1

即使严格的别名规则允许从包含一种类型的对象访问该对象,标准中也有两个元素表明不允许将 2D 数组别名为相同大小的 1D 数组:

\n\n

兼容型

\n\n
\n

6.2.7 兼容型和复合型

\n\n

1 如果两个类型的类型相同,则它们具有兼容类型。
...
\n 3 复合类型可以由两种兼容的类型构造而成;它是与这两种类型兼容并满足以下条件的类型:

\n\n
    \n
  • 如果一种类型是已知常量大小的数组,则复合类型是该大小的数组;否则,如果一种类型是可变长度数组,则复合类型就是该类型。
    ...
  • \n
\n\n

这些规则递归地应用于派生这两种类型的类型

\n
\n\n

一致性:

\n\n
\n

4 一致性

\n\n

...

\n\n
    \n
  1. ...在本国际\n标准中,未定义的行为通过文字\xe2\x80\x98\xe2\x80\x98未定义的行为\xe2\x80\x99\xe2\x80\x99或通过省略任何显式定义来表示\n 的行为。这三者的侧重点没有区别;它们都描述\n \xe2\x80\x98\xe2\x80\x98未定义的行为\xe2\x80\x99\xe2\x80\x99
  2. \n
  3. 在所有其他方面都正确、对正确数据进行操作、包含未指定行为的程序应是正确的程序,并且按照 5.1.2.3 进行操作。
    \n ...
  4. \n
  5. 严格遵守的程序应仅使用本国际标准中指定的语言和库的功能。2)它不应产生依赖于任何未指定、未定义或实现定义的行为的输出,并且不应超过任何\n n 最低实施限制。
  6. \n
\n
\n\n

我的理解是,标准未指定二维数组与一维数组的别名,因此会导致未定义的行为。使用它的程序仍然是应成功编译的正确程序,但其输出未指定

\n\n

严格遵守的程序不应将 2D 数组别名为 1D 数组。

\n\n

话虽这么说,通用实现允许它并按预期处理它(每个未定义的行为都允许......),以便不破坏严重依赖它的遗留代码。

\n