C语言中的多维数组初始化

3

以下代码有一个指针我无法理解:在函数 set_array([][9]) 的部分,为什么编译器允许这样写而不是正常的完整表达式 set_array([4][9])。然而,在 main 部分中,int array1[4][9]array1[][9] 是不被允许的。

#include <stdio.h>
void set_array(int t_array[][9]);
int main(void) {
    int array1[4][9]; // array1[][9] doesn't allowed

    for(int i = 0; i < 4; i++) {
        for(int j = 0; j < 9; j++) {
            array1[i][j] = j + 1;
        }
    }
    set_array(array1);
    for(int i = 0; i < 4; i++) {
        for(int j = 0; j < 9; j++) {
            printf("%d ", *(*(array1 + i) + j));
            //printf("%d ", array1[i][j]);
        }
        puts("\n");
    }
    return 0;

}
void set_array(int t_array[][9]) {
    for(int i = 0; i < 4; i++) {
        for(int j = 0; j < 9; j++) {
            t_array[i][j] = 1;
        }
    }   
};

有关这个的任何解释吗?
6个回答

2
作为比较(C89):
当你编写代码时,
int a[] 

编译器不知道需要为'a'分配多少内存,在这种情况下,编译器需要使用初始化来确定数组的大小:

int a[] = {1,2,3}; // sizeof(a)==sizeof(int)*3

当你只写 < /p >
int a[][9]; 

编译器不知道数组'a'的大小,因此无法分配足够的内存。
如果将数组作为参数传递,则情况如下:
void foo(int a[][9]) 

该函数不需要知道两个维度的大小,因为它不会分配任何内容,它只是引用“a”。从参数中,函数知道在对其进行解除引用时应将“a”视为多维数组(其中第一个维度是任意数量)。


非常感谢您的清晰解释,直戳要点。顺便问一下,您能告诉我为什么我可以这样调用方法 set_array(&array1[0]) 吗? - mko

2
在函数调用中,数组实际上被视为指针,因此在函数定义中,t_array[][9] 实际上是 (*t_array)[9] //指向数组的指针,但是在声明静态数组时,必须明确给出大小,以便编译器为数组分配足够的内存。因此,array1[4][9] 是正确的,而 array1[][9] 是错误的。但是,如果你正在使用 array1[][9],那么你必须提供初始化列表。

你能告诉我为什么函数调用:set_array(&array1[0]);会导致数组部分更改,变成 `1 1 1 1 1 1 1 1 1 1 1 1 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9` 吗? - mko
这里的array1包含整个第一行(或列)数组的地址,具体取决于实现。(&array1[0])与类型为()[n]的(array1)相同。但是当你传递类型为()[m][n]的(&array)时,编译器会抛出错误。 - Omkant

1

您可以使用初始化列表声明和初始化,而无需指定第一个维度:

int array1[][9] = {
{1,2,3,4,5,6,7,8,9},
{1,2,3,4,5,6,7,8,9},
{1,2,3,4,5,6,7,8,9},
{1,2,3,4,5,6,7,8,9},
};

在C语言中,如果您在行内初始化数组,则只需要指定从第二个维度开始的尺寸大小。这些尺寸对于编译器生成索引代码是必要的,记住多维数组(在C语言中)是一个连续的缓冲区。

1
在 C 语言中,当你静态声明一个数组时,必须给它的大小赋值,否则编译器就不知道要为该变量分配多少内存。
当你将数组作为 array[][9] 传递时,它可以工作,因为编译器只需要知道数组元素的大小,而不需要知道它包含多少个元素。因此,将 array[][] 作为参数传递是行不通的,因为编译器不知道存储的元素大小。

2
这只是函数原型,告诉编译器期望将数组传递给该函数,这与实际声明变量是不同的。 - davepmiller
1
当您传递一个数组时,编译器只需要知道它的元素大小,而不需要知道它有多少个元素。 - davepmiller
1
C标准规定,在多维数组中,除第一个维度外,每个维度都必须指定大小。 - Omkant
1
这是因为所有的多维数组都映射到线性内存中,所以如果进一步维度的大小未知,编译器无法计算进一步的索引。 - fkl

1
这是因为你在做两件不同的事情:
首先,你声明了一个名为array1的变量。在C语言中,编译器需要知道你声明的空间大小,以便为你保留该内存量。因此,你必须指定数组的完整大小: int array1[4][9]; 这样,你的编译器就知道你需要分配4 * 9个整数。
现在,在你的函数中,编译器不需要知道你的数组的大小(在幕后,你的数组被转换为指针进行函数调用),它只需要知道其元素的大小。因此,t_array是一个指向大小为9 * sizeof(int)的元素的指针。正因为如此,你可以按照你声明的方式声明函数: void set_array(int t_array[][9]) 但也可以声明所有维度大小: void set_array(int t_array[4][9]) 但对于编译器来说并没有什么区别。

1
现在我能理解你的回答了,多维数组的布局是线性的,当将其传递给函数时,只传递第一个元素的指针,并且通过类型声明,函数知道如何对其进行指针算术运算。因此,第一部分可以省略。 - mko

0

你不能给数组赋值,只能初始化它们,因此你必须一次性完成:

int array1[4][9] = {
 {1,2,3,4,5,6,7,8,9},
 {1,2,3,4,5,6,7,8,9},
 {1,2,3,4,5,6,7,8,9},
 {1,2,3,4,5,6,7,8,9},
};

我清除了误导性的评论部分,感谢您的回答,但我已经知道这个。我不知道的是array[][9]是否被允许。 - mko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接