任何人都可以解释这些值将如何存储在这个数组中:
int a[2][3] = {1,2,3,4,5}
使用2D阵列,但将其指定为1D阵列。
答案 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 }, };
是一个包含完全括号初始化的定义:
可以达到同样的效果1
,3
和5
初始化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]
连续拍摄的。