char a[n][m] 和 char a[][m] 之间有区别吗?

时间:2021-05-18 12:56:54

标签: c++ arrays multidimensional-array char

当我提供 m > 我的列值时,为什么它不打印垃圾,就像在第二种情况下一样?有什么具体原因吗?

#include <iostream>
using namespace std;

int main()
{
    char a[2][5] = {{'a', 'b', 'c', 'd', 's'}, {'e', 'f', 'g', 'h', 'q'}};
    cout << a[0] << endl; // abcdsefghq!V
    cout << a[1] << endl; // efghq!V
    
    char a1[][6] = {{'a', 'b', 'c', 'd', 's'}, {'e', 'f', 'g', 'h', 'q'}};
    cout << a1[0] << endl; // abcds
    cout << a1[1] << endl; // efghq

    return 0;
}

5 个答案:

答案 0 :(得分:2)

<块引用>

char a[n][m] 和 char a[][m] 有区别吗?

取决于上下文。首先是一个由 m 个字符组成的 n 个数组的数组。第二个是 m 个字符数组的未知边界数组。所以,它们是不同的类型。

然而,给定一个带括号的初始化列表,例如在示例中,未知边界数组将调整为已知边界数组,其中边界是从初始化器的数量推导出来的。如果推导出的初始化器数量为 n,则调整后的类型将与 char[n][m] 完全相同。

简而言之,答案是:大体上的区别是前者是一个已知边界的数组,后者是一个未知边界的数组,但是在这个上下文中,后面没有区别未知边界数组调整为已知边界数组。


<块引用>

还有为什么不打印垃圾

当您使用数组调用流插入运算符时,操作数会衰减为指向第一个元素的指针。该流要求 char* 参数是一个指向空终止字符数组的指针。违反此前提条件会导致未定义的行为。

您在 a 中的数组不包含空终止符。您程序的行为未定义。


你应该避免未定义的行为。 切勿将非空终止数组传递给字符流

答案 1 :(得分:1)

char a[2][5] 明确指定行数。 char a1[][6] 告诉编译器暗示来自初始化程序的行数。

例如

char b[10][5] = {"a", "b", "c"};

创建 10 行,因为显式指定了数字,但是

char b1[][5] = {"a", "b", "c"};

只创建 3 行,因为初始化器中有 3 行。

第二种情况每行6个字符,每行指定5个字符。剩余的 1 个字符初始化为零,它用作 C 样式字符串的终止符。另一方面,第一种情况每行只有 5 个字符,并且这 5 个字符是完全指定的,因此数组中没有终止空字符。将其打印为 C 样式字符串将导致危险的超出范围的访问。

答案 2 :(得分:0)

a 的情况下,当打印 a[0]a[1] 时,它们具有类型 char [5],当传递给 {{1} 时衰减为 char * }} 操作员。所以它们被打印为 C 字符串。但是,您实际上并没有以空字符结尾的字符串,因此 << 读取超出了数组的边界,触发了 undefined behavior

operator <<的情况下,你没有指定第一个维度,所以它的大小取自有两个元素的初始化器,所以这个维度的大小是2。这个变量也有更大的大小对于第二个数组维度,初始值设定项不显式初始化所有元素,因此未显式初始化的元素设置为 0。因此,当您打印 a1a1[0] 时,您do 有空终止字符串,所以你得到预期的输出。

答案 3 :(得分:0)

为了标题

<块引用>

char a[n][m] 和 char a[][m] 有区别吗?

不需要,只要在第二种情况下您在初始化时提供 n 行。


不过,这不是您问题中的实际情况。在您的问题中,您有一个 char a[2][5] 并用字符填充它。因为这些字符都不是空终止符,所以将这些行传递给 cout 具有未定义的行为。 cout 要求 char 数组(c-string)有一个空终止符。

在您的第二种情况下,您有一个 char a[][6] 并为初始化提供 2 行,每行 5 个字符。这使得该类型成为 char a[2][6],但是因为您只为 6 个字符提供了 5 个初始值设定项,编译器会自动将丢失的第六个字符(即空终止符)初始化为零,因此将这些行传递给 {{1} } 正常工作。

答案 4 :(得分:0)

这里发生了一些不同的事情。

首先,如果你给出了一个明确的数组大小但没有在初始化器中指定所有元素,那么剩余的元素将被零初始化。例如:

int a[5] = {1, 2, 3};  // equivalent to {1, 2, 3, 0, 0}

这是两个示例之间的主要区别 - 一个明确指定大小,另一个根据初始化程序推断大小。

接下来,数组“衰减”为指针,因此 char[] 的作用类似于 char*,也就是 c 字符串。 c 字符串必须以空字符结尾。在第一个示例中打印垃圾是因为您缺少空终止符。你不要在第二个例子中打印垃圾,因为数组有一个(隐藏的)空终止符,因为最后一个元素是零初始化的。