我已经像初始化一维数组一样初始化了一个二维数组:
int a[2][3] = {1,2,3,4,5}
这个数组中的值是如何存储的?
它们的分配如下:
1 2 3
4 5 0
出现 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
会得到零(在我的机器上),因为它遇到了未初始化的内存。重点是使用连续的内存可以让您做一些方便的事情,例如设置所有值而不需要双重循环。
int a[2][3] = {{1,2},{3,4},{5}}
会触发什么警告? - Mad Physicistint a [2] [3]
是一个数组,保证在内存中是连续的!它不能是其他任何东西。如果您指的是像int *a [3]
这样的内容:那是完全不同的数据类型,不是数组的数组! - too honest for this siteint a[2][3]
在内存中是连续的,所有访问都是有效的。然而,在分别分配行的情况下,对数组的数组进行 a[2][3]
访问模式不能保证是有效的。 - Richardlinear[x] = linear + x
计算的。同样,在您的二维数组中,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};
。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
。我写了“可能”,因为我认为使用a[0][3]
访问数组int a[2][3]
是未定义行为,因为最后一个下标的范围是0..2
。因此,根据6.5.6(8)条款,表达式a[0][3]
超出了其边界,如此处所述。int a[2][3] = {1,2,3,4,5}
。这个语句是初始化,如这份在线C标准委员会草案的第6.7.9节所定义,第20到26段描述了这里需要的内容:
(20) If the aggregate or union contains elements or members that are aggregates or unions, these rules apply recursively to the subaggregates or contained unions. If the initializer of a subaggregate or contained union begins with a left brace, the initializers enclosed by that brace and its matching right brace initialize the elements or members of the subaggregate or the contained union. Otherwise, only enough initializers from the list are taken to account for the elements or members of the subaggregate or the first member of the contained union; any remaining initializers are left to initialize the next element or member of the aggregate of which the current subaggregate or contained union is a part.
(21) If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, or fewer characters in a string literal used to initialize an array of known size than there are elements in the array, the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration.
26 EXAMPLE
(3) The declaration
int y[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 }, };
is a definition with a fully bracketed initialization:
1
,3
, and5
initialize the first row ofy
(the array objecty[0]
), namelyy[0][0]
,y[0][1]
, andy[0][2]
. Likewise the next two lines initializey[1]
andy[2]
. The initializer ends early, soy[3]
is initialized with zeros. Precisely the same effect could have been achieved by
int y[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 };
The initializer for
y[0]
does not begin with a left brace, so three items from the list are used. Likewise the next three are taken successively fory[1]
andy[2]
.
int a[2][3]
的二维数组是一个数组的数组。请注意,在大多数表达式中,a
将会衰减为指向其第一个元素的指针,即一个包含 3 个int
的数组。 - ad absurdum