为什么我们需要
的列大小呢?int arr[][] = { {1,2,3},{1,3,5} };//int arr[][3]
在这个特殊的例子中,为什么编译器不能从数据中推断出它必须在每行中只打包3个元素?为什么在编译器上有限制?
如果是
我能理解int arr[][] = { 1,2,3,1,3,5 };
则编译器不知道每行需要打包多少数据。
我读了一个类似的问题,为什么我们需要指定列的大小时,传递一个2D数组作为参数?但是它不包含答案。
编辑:为了避免混淆,我说的是上面提到的数据的确切格式。
是什么阻止编译器推断出列的大小?
C标准。
来自标准:
<<p>6.7.9初始化/em>要初始化的实体类型应为未知大小的数组或非可变长度数组类型的完整对象类型。
你可以初始化一个未知大小的数组但是你不能初始化一个未知大小的数组
-
因为您可能希望数组的大小比推导出来的大。像
int arr[5] = {1, 2, 3}; // Last two elements are zero.
-
如果像
这样声明数组int arr[][] = { {1, 2, 3}, {1, 3, 5, 7} };
编译器应该怎么做,报告错误,因为它期望数组
int array[][3]
,还是使数组int array[][4]
?这个决定应该由人来做。 -
二维阵列在内存中是平坦的。例如,
int arr[3][3]
和int arr[9]
具有相同的存储空间。因此,允许通过一个初始化列表来初始化两个向量,可以认为这是将初始化列表的值直接放入平面内存:#include <stdio.h> #include <memory.h> int main(void) { int arr2d[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; int arr1d[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; printf("size: %dn", sizeof(arr2d) == sizeof(arr1d)); printf("memcmp: %dn", memcmp(arr2d, arr1d, sizeof(arr2d))); return 0; } // size: 1 // memcmp: 0
-
扩展上述,下面所有4个函数都以不同的方式声明和初始化同一个数组
int arr[3][3]
:#include <stdio.h> void arr2_list2(void) { int arr[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}; printf("int arr[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}:n"); for (int i = 0; i < 3; ++i) printf("%d %d %dn", arr[i][0], arr[i][1], arr[i][2]); return 0; } void arr2_list1(void) { int arr[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; printf("int arr[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8}:n"); for (int i = 0; i < 3; ++i) printf("%d %d %dn", arr[i][0], arr[i][1], arr[i][2]); return 0; } void arr2open_list2(void) { int arr[][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}; printf("int arr[][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}:n"); for (int i = 0; i < 3; ++i) printf("%d %d %dn", arr[i][0], arr[i][1], arr[i][2]); return 0; } void arr2open_list1(void) { int arr[][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; printf("int arr[][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8}:n"); for (int i = 0; i < 3; ++i) printf("%d %d %dn", arr[i][0], arr[i][1], arr[i][2]); return 0; } int main(void) { arr2_list2(); arr2_list1(); arr2open_list2(); arr2open_list1(); return 0; } // int arr[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}: // 0 1 2 // 3 4 5 // 6 7 8 // int arr[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8}: // 0 1 2 // 3 4 5 // 6 7 8 // int arr[][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}: // 0 1 2 // 3 4 5 // 6 7 8 // int arr[][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8}: // 0 1 2 // 3 4 5 // 6 7 8
想象
中的意思?int arr[][] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
是允许的,为什么不允许int arr[][] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
,但是编译器如何决定一个人在语句int arr[][] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
int arr[1][9],
或int arr[3][3]
,或int arr[9][1]
?
简单的答案-在像
这样的声明中int arr[][] = { ... };
元素类型是int []
,它是不完整类型,不能声明元素类型不完整的数组。存在或不存在初始化式都不会改变这一点。初始化式所能告诉你的只是给定元素类型有多少个元素;它不能告诉你元素类型是什么
相反,在声明
中int arr[] = { ... };
元素类型是int
,这是一个完整类型。您仍然需要在初始化式中确定元素的数量,但它没有告诉您每个元素需要有多大。
这是因为尽管[][]
语法看起来像数组的数组,但在内存中它像一维数组一样放置。如果我们想象一个3 × 3的棋盘它的布局是{1A, 2A, 3A, 1B, 2B, 3B, 1C, 2C, 3C}。作为2-D访问它的唯一方法是将其命名为[row + column * columnsize]
。
现在很明显,为了开始考虑访问数组的元素,你必须知道columnsize。这个columnsize参数存储在哪里?数组类型为int (*)[3]
。Columnsize是类型的一个组成部分。与"t"是int
的一部分。
您要求的是auto
类型。编译器必须确定整个类型它自己。这相当于想要auto arr = 1;
或auto arr = "foo";
。C编译器不会处理这样的技巧。
请注意,columnsize与数组的长度完全不同。int arr[] = {1}
与int arr[] = {1,2,3}
的类型完全相同,编译器不关心数组的长度(内存中的实际大小),不出界完全是你的责任。这就是为什么一个元素可以不指定的原因。不是因为编译器可以发现它,而是因为编译器忽略了它。
int arr[][2] = {1,2,3,4,5,6}
与int arr[][3] = {1,2,3,4,5,6}
的类型完全不同,尽管它们在内存中的大小相同。对于编译器来说,它们就像int[1]
和char[4]
。
{{},{}}
只是一个语法糖来帮助你,而不是编译器。