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

14

我正在阅读 Kochen 的 "Programming in C",但是在他解释多维数组的初始化时感到困惑。

here

具体来说,我不理解以下句子的含义请注意,在这种情况下,内部花括号是必需的,以强制进行正确的初始化。如果没有它们,将初始化前两行和第三行的前两个元素。我不确定这句话到底意味着什么。

5个回答

17
这是因为M[4][5]数组有20个元素(4行,5列),如果没有使用内部花括号显式指定行,则默认按行初始化顺序。这意味着,如果您将相同的12个值分配为简单线性列表而不使用内部花括号,则值将分配给前两行(2*5=10个元素)加上第三行的前2列。(您没有明确初始化的数组的其余8个元素将自动设置为0。) C编译器知道每行只有5列,并且每次达到5列边界时会自动将数字列表换行到下一行。因此,
int M[4][5] = {10, 5, -3, 9, 0, 0, 32, 20, 1, 0, 0, 8};

被理解为意味着

int M[4][5] =
{
  {10,  5, -3, 9, 0},
  { 0, 32, 20, 1, 0},
  { 0,  8,  0, 0, 0},
  { 0,  0,  0, 0, 0}
};

你可以使用内部括号将你的12个值分成自己喜欢的行数来覆盖默认顺序(但是按照数组 M 的定义,每行不能超过5列)。

例如,当您使用内部括号将相同的12个值分为四组3个值时,就像您从书中看到的页面一样,那些内部括号被解释为初始化多维数组的单独行。结果将初始化四行的数组,但仅将这四行的前3列设置为非零值,将其余列设置为零(每行末尾有两个空白零值)。

也就是说,C编译器知道数组 M 每行有5列,因此它会向每行添加缺失的列,使每行都有5列,因此该数组将具有20个值:

int M[4][5] =
{
  {10,  5, -3},
  { 9,  0,  0},
  {32, 20,  1},
  { 0,  0,  8}
};

被理解为意味着

int M[4][5] =
{
  {10,  5, -3, 0, 0},
  { 9,  0,  0, 0, 0},
  {32, 20,  1, 0, 0},
  { 0,  0,  8, 0, 0}
};

但是,当比较两个初始化时,您还要编写所有隐含的0时,表示未使用内部大括号的第一个(用内部大括号的第二个)应该如何初始化第三行的前两行和前两个元素?看起来所有数字仍然被初始化,但不在正确的位置。实际上,只有第一行在正确的位置。 - velocirabbit

9

使用内部大括号,数组看起来像这样:

10  5 -3  0  0
 9  0  0  0  0
32 20  1  0  0
 0  0  8  0  0

所以每一行的最后两个值都是零(因为您没有为它们设置值)。 如果没有内部括号,该数组将如下所示:

10  5 -3  9  0
 0 32 20  1  0
 0  8  0  0  0
 0  0  0  0  0

只有前12个元素将成为给定的值,其余的将变为0。


不使用内部大括号的示例如何等同于“第一行和第二行的前两个元素以及第三行的前两个元素将被初始化”? - velocirabbit

8

由于所有数组在内部都表现为1维数组,因此您必须使用方括号指定要初始化的确切行。

例如:

int a[4][5] = {
              { 1, 2, 3 },  // those are the elements of the first row.
                            // only the first 3 elements are initialized
              { 1, 2, 3, 4} // those are the elements of the 2nd row.
                            // only the first 4 element are initialized
              };
                            // everything else will be equal to 0

int a[4][5] = { 1, 2, 3, 1, 2, 3, 4}; // this will initialize first 5 elements 
                                      // of the first row and then continue 
                                      // with the 2nd one making the first 2 
                                      // elements to be 3 and 4 respectivly
                                      // everything else will be equal to 0

3
在C语言中,多维数组只是一维数组的“语法糖”。当你分配一个4x5的int数组时,你实际上是在内存中分配20个整数的空间,这些整数按照第一行所有元素、第二行所有元素等方式存储。如果没有内部括号,你的初始化程序也是一维的,并且表示你想要初始化这20个整数的前12个,即前两行和第三行的前两个元素。请保留HTML标记。

没有多维数组就是数组的数组。给定声明 int arr[3][3];,引用 arr[0][3] 具有未定义的行为,即使您可能期望它等同于 arr[1][0] - Keith Thompson
@KeithThompson 我所指的是数组的底层内存表示,这才是相关的,我并没有暗示访问数组的行为是等效的。不过你说得很好。 - 1''
@1 "C类型系统与底层表示是两个不同的概念。" - LearningMath
嗯,我猜你不能把它称作语法糖。C编译器是否需要将一个二维数组的一维初始化器解释为初始化前两行和前两列,就好像二维数组在内存中是连续存储的一样? - 1''
1
在这个上下文中,短语“语法糖”非常贴切。这让我感到很开心。谢谢。 - ryyker

2

能否提供具体示例会更有帮助。

多维数组是由数组组成的数组(它不仅仅是一个长的一维数组的语法糖)。

在初始化器中,可以省略尾随元素(它们会被隐式地初始化为零)和内部花括号。

例如:

int arr[2][2];

完整的初始化可能如下所示:
int arr[2][2] = { { 10, 20 }, { 30, 40 } };

您可以(但我认为不应该)省略内部括号:
int arr[2][2] = { 10, 20, 30, 40 };

编译器将把初始化程序的元素映射到arr的元素。

如果省略尾随元素:

int arr[2][2] = { { 10, 20 } };

那么第二行将被隐式初始化为{ 0, 0 }

或者你可以这样写:

int arr[2][2] = { { 10 }, { 20 } };

该语句会将值10和20赋给每行的第一个元素,而不是第一行。

再次说明,在没有示例的情况下很难确定作者所说的内容,但内部大括号告诉编译器开始新的一行,即使第一行不完整也是如此。

如果为所有4个元素(或更普遍地说,所有元素)提供初始值,则内部花括号并不是必需的;无论哪种方式,元素的顺序都是相同的。

个人认为最好包含所有内部大括号,因为它们反映了您正在初始化的实际结构。

(那么一维数组和二维数组之间有什么区别,除了语法糖?给定arr的上述声明,如果它与一维数组相同,则arr [0] [2]将与arr [1] [0]相同,第二个索引溢出到第二行。实际上,这可能是可行的,但是arr [0] [2]具有未定义的行为。这确实有实际后果;优化编译器可以假设所有边界都在范围内,并生成在违反该假设时会出现问题的代码。)

请参见此问题


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