C99 对多维数组的一维访问:它是定义良好的行为吗?

C99 对多维数组的一维访问:它是定义良好的行为吗?,c99,c,pointers,multidimensional-array,standards,language-lawyer,C99,C,Pointers,Multidimensional Array,Standards,Language Lawyer,我想我们都同意,通过以一维方式解引用指向其第一个元素的指针(可能是偏移量),访问真正的多维数组被视为惯用C,例如: void clearBottomRightElement(int *array, int M, int N) { array[M*N-1] = 0; // Pretend the array is one-dimensional } int mtx[5][3]; ... clearBottomRightElement(&mtx[0][0], 5, 3); 然

我想我们都同意,通过以一维方式解引用指向其第一个元素的指针(可能是偏移量),访问真正的多维数组被视为惯用C,例如:

void clearBottomRightElement(int *array, int M, int N)
{
    array[M*N-1] = 0;  // Pretend the array is one-dimensional
}


int mtx[5][3];
...
clearBottomRightElement(&mtx[0][0], 5, 3);
然而,我身上的语言律师需要让我相信这实际上是定义良好的C!特别是:

  • 标准是否保证编译器不会在mtx[0][2]和mtx[1][0]之间添加填充

  • 通常情况下,未定义从数组末尾(而不是从末尾过一个)进行索引(C99,6.5.6/8)。因此,以下内容显然没有定义:

    struct {
        int row[3];           // The object in question is an int[3]
        int other[10];
    } foo;
    int *p = &foo.row[7];     // ERROR: A crude attempt to get &foo.other[4];
    
    int mtx[5][3];
    int (*row)[3] = &mtx[0];  // The object in question is still an int[3]
    int *p = &(*row)[7];      // Why is this any better?
    
    因此,根据同样的规则,人们会认为以下内容是未定义的:

    struct {
        int row[3];           // The object in question is an int[3]
        int other[10];
    } foo;
    int *p = &foo.row[7];     // ERROR: A crude attempt to get &foo.other[4];
    
    int mtx[5][3];
    int (*row)[3] = &mtx[0];  // The object in question is still an int[3]
    int *p = &(*row)[7];      // Why is this any better?
    
    那么,为什么要定义这一点呢

    int mtx[5][3];
    int *p = &(&mtx[0][0])[7];
    
  • 那么C标准的哪一部分明确允许这样做呢?(为了讨论,让我们假设一下。)

    编辑

    请注意,我毫不怀疑这在所有编译器中都能正常工作。我所质疑的是,这是否是标准明确允许的

  • 可以确定数组的元素之间没有填充

  • 提供了比完整地址空间更小的地址计算。例如,可以在8086的大模式下使用此选项,这样,如果编译器知道您无法跨越段边界,则不会始终更新段部分。(对于我来说,提醒我使用的编译器是否从中受益已经太久了)

  • 对于我的内部模型--我不确定它是否与标准模型完全相同,而且检查起来太痛苦,信息到处都是--

    • 您在
      clearBottomRightElement
      中所做的操作是有效的

    • int*p=&foo.row[7]未定义

    • inti=mtx[0][5]未定义

    • int*p=&row[7]未编译(gcc同意我的意见)

    • int*p=&mtx[0][0])[7]处于灰色区域(上次我查看类似这样的详细信息时,我考虑了无效的C90和有效的C99,可能是这里的情况,也可能是我遗漏了一些内容)

  • 可以确定数组的元素之间没有填充

  • 提供了比完整地址空间更小的地址计算。例如,可以在8086的大模式下使用此选项,这样,如果编译器知道您无法跨越段边界,则不会始终更新段部分。(对于我来说,提醒我使用的编译器是否从中受益已经太久了)

  • 对于我的内部模型--我不确定它是否与标准模型完全相同,而且检查起来太痛苦,信息到处都是--

    • 您在
      clearBottomRightElement
      中所做的操作是有效的

    • int*p=&foo.row[7]未定义

    • inti=mtx[0][5]未定义

    • int*p=&row[7]未编译(gcc同意我的意见)

    • int*p=&mtx[0][0])[7]处于灰色区域(上次我查看类似这样的详细信息时,我考虑了无效的C90和有效的C99,可能是这里的情况,也可能是我遗漏了一些内容)

      • 我对的理解是,不要求多维数组必须在内存中按连续顺序排列。遵循我在标准中找到的唯一相关信息(每个维度保证是连续的)

        如果您想使用x[COLS*r+c]访问,我建议您坚持使用一维数组

        数组下标 连续的下标运算符指定多维数组对象的元素。 如果E是n维数组(n≥ 2) 尺寸为i×j×。x k,然后E(用作 而不是左值)转换为指向(n)的指针− 1) 二维数组 尺寸j×。×k。如果一元*运算符显式应用于此指针,或 作为订阅的隐式结果,结果是指向(n)的− 1) -维数组, 如果用作左值以外的值,则其本身将转换为指针。由此而来 数组按行主顺序存储(最后一个下标变化最快)

        数组类型 -数组类型描述了一组连续分配的非空对象,其中 特定的成员对象类型,称为元素类型。 36) 数组类型为 以其元素类型和数组中元素的数量为特征。一 数组类型称为从其元素类型派生,如果其元素类型为T,则 数组类型有时称为“T的数组”。从中构造数组类型 元素类型称为“数组类型派生”。

        我的理解是,不要求多维数组必须在内存中按连续顺序排列。遵循我在标准中找到的唯一相关信息(每个维度保证是连续的)

        如果您想使用x[COLS*r+c]访问,我建议您坚持使用一维数组

        数组下标 连续的下标运算符指定多维数组对象的元素。 如果E是n维数组(n≥ 2) 尺寸为i×j×。x k,然后E(用作 而不是左值)转换为指向(n)的指针− 1) 二维数组 尺寸j×。×k。如果一元*运算符显式应用于此指针,或 作为订阅的隐式结果,结果是指向(n)的− 1) -维数组, 如果用作左值以外的值,则其本身将转换为指针。由此而来 数组按行主顺序存储(最后一个下标变化快)
        (mtx[0] + 3) + 2