像一维数组一样初始化二维数组

时间:2017-06-16 19:10:35

标签: c arrays

任何人都可以解释这些值将如何存储在这个数组中:

int a[2][3] = {1,2,3,4,5}

使用2D阵列,但将其指定为1D阵列。

3 个答案:

答案 0 :(得分:4)

分配如下:

1 2 3
4 5 0

归零是因为您已经分配了一个大小为6的数组,但只指定了5个元素。

这称为"行主要顺序"。

您可能希望稍微正式化您的代码。您的代码目前是:

int a[2][3] = {1,2,3,4,5};

如果您使用gcc main.c -Wall -pedantic --std=c99进行编译,则会收到一些警告:

  

temp.c:2:17:警告:初始化程序周围缺少大括号[-Wmissing-braces]

使用

解决此问题
int a[2][3] = {{1,2,3,4,5}};

这会给你一个新警告:

  

temp.c:2:25:警告:数组初始值设定项中的多余元素

使用以下方法解决此问题:

int a[2][3] = {{1,2,3},{4,5,0}};

这明确地将数据表示为具有两行,每行三个元素。

关于内存布局的一些想法

int a[2][3]将生成"数组数组"。这与数组"指针数组相似,但相反。两者都具有类似的访问语法(例如a[1][2])。但仅适用于"阵列数组"你可以使用a+y*WIDTH+x可靠地访问元素。

有些代码可能会澄清:

#include <stdlib.h>
#include <stdio.h>

void PrintArray1D(int* a){
  for(int i=0;i<6;i++)
    printf("%d ",a[i]);
  printf("\n");
}

int main(){
  //Construct a two dimensional array
  int a[2][3] = {{1,2,3},{4,5,6}};

  //Construct an array of arrays
  int* b[2];
  b[0] = calloc(3,sizeof(int));
  b[1] = calloc(3,sizeof(int));

  //Initialize the array of arrays
  for(int y=0;y<2;y++)
  for(int x=0;x<3;x++)
    b[y][x] = a[y][x];

  PrintArray1D(a[0]);
  PrintArray1D(b[0]);
}

当你运行它时,你得到:

1 2 3 4 5 6 
1 2 3 0 0 0 

打印b会给出零(在我的机器上),因为它会进入未初始化的内存。结果是使用连续的内存可以让你做方便的事情,让我们设置所有的值而不需要双循环。

答案 1 :(得分:3)

在C中,1D数组存储在内存中的单个线性缓冲区中,称为&#34; row major&#34;订购。行主要意味着当您从元素到元素时,最后一个索引变化最快。专业列意味着第一个索引变化最快,例如在MATLAB中变化最快。

您声明的数组只是2D,因为编译器会通过为您计算元素的线性地址来帮助您。计算1D数组中元素的地址linear[x] = linear + x。同样,对于您的2D数组,a[y][x] = a + 3 * y + x。一般来说,a[y][x] = a + num_cols * y + x

您可以将数组初始化为单个元素向量,它将首先填充第一行,然后填充第二行,依此类推。由于您有两行,每行三个元素,第一行变为1, 2, 3,第二行变为4, 5, 0

在编译器至少关注的情况下,索引到行的末尾是完全有效的。在您给出的示例中,a[0][3]正在访问三个元素宽的数组中第一行的第四个元素。使用环绕,您可以看到这只是第二行的第一个元素,更明确地表示为a[1][0]

由于索引检查松散,只要提供初始化程序,就可以完全省略任何数组中的第一个索引。计算线性地址的公式不依赖于第一个索引(因为它是行主要),并且元素的总数由初始化程序本身指定。一维示例是int linear[] = {1, 2, 3};

请记住,数组的名称也指向指向其第一个元素的指针。这些是可以通过相同名称访问的两个不同的东西。

答案 2 :(得分:1)

从如何解释对像a[1][2]这样的2D数组的访问的定义,“由此得出数组以行主顺序存储”(例如,参见此{{3} })。 这意味着对于访问int a[ROWS][COLUMNS]的数组a[r][c],int值的偏移量计算为(r*COLUMNS + c)

因此,对于数组int a[2][3],访问a[0][1]的偏移量为0*3 + 1 = 1,访问权限a[1][0]的偏移量为1*3 + 0 = 3。也就是说,a[0][3]可能导致偏移3,而a[1][0]肯定会导致3。我写了“可能”,因为我认为使用int a[2][3]访问数组a[0][3]是未定义的行为,因为最后一个下标的范围是0..2。因此,根据6.5.6(8),表达式a[0][3]正在解决其范围内的子数组a[0],例如,online C standard comitee draft / array subscripting

现在解释int a[2][3] = {1,2,3,4,5}的解释方式。这个语句是here中定义的初始化,第(20)到(26)段描述了这里需要的东西:

  

(20)如果聚合或联合包含元素或成员   聚合或联合,这些规则递归地应用于   分包或包含工会。如果是初始化者   subaggregate或contains union以左括号开头   由该支撑包围的初始化器及其匹配的右支撑   初始化子聚合的元素或成员或   包含工会。否则,列表中只有足够的初始值设定项   考虑到子聚合的元素或成员或   所包含的联盟的第一个成员;任何剩余的初始化者   留下来初始化下一个元素或聚合的成员   当前的分包或包含联盟是其中的一部分。

     

(21)如果括号括起的列表中的初始值设定项少于那里   是聚合的元素或成员,或者是一个或多个字符   string literal用于初始化已知大小的数组   是数组中的元素,聚合的其余部分应为   隐式初始化与具有静态存储的对象相同   持续时间。

     

26示例

     

(3)声明

      int y[4][3] = {
            { 1, 3, 5 },
            { 2, 4, 6 },
            { 3, 5, 7 },
      };
     

是一个包含完全括号初始化的定义:135初始化y的第一行(数组对象y[0]) ,即   y[0][0]y[0][1]y[0][2]。同样,接下来的两行初始化   y[1]y[2]。初始化程序提前结束,因此初始化y[3]   零。

可以达到同样的效果
      int y[4][3] = {
            1, 3, 5, 2, 4, 6, 3, 5, 7
      };
     

y[0]的初始化程序不以左括号开头,因此使用列表中的三个项目。同样接下来的三个   是y[1]y[2]连续拍摄的。