【问题标题】:Is there a difference between char a[n][m] & char a[][m]?char a[n][m] 和 char a[][m] 之间有区别吗?
【发布时间】:2021-08-07 17:12:49
【问题描述】:

还有为什么当我提供 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;
}

【问题讨论】:

  • 如果你给出一个明确的数组大小但没有在初始化器中指定所有元素,剩余的元素将被初始化为零。 char[] 转换为 char*,因此零初始化元素的作用类似于 c 字符串中的空终止符。
  • 问题终止于零!
  • 当你的代码有 ub 时,危险不是得到“垃圾”,而是得到看起来合理的输出。我不知道这个“垃圾”故事从何而来,恕我直言,这只是误导,并没有帮助。 “我期待垃圾,但输出很好,为什么?”错过了未定义行为的重点。

标签: c++ arrays multidimensional-array char


【解决方案1】:

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

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

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

简而言之,答案是:一般的区别是前者是一个已知边界的数组,后者是一个未知边界的数组,但是在this context中,后面没有区别未知边界数组调整为已知边界数组。


还有为什么它不打印垃圾

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

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


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

【讨论】:

    【解决方案2】:

    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 风格的字符串会导致危险的超出范围的访问。

    【讨论】:

      【解决方案3】:

      a 的情况下,当打印a[0]a[1] 时,它们具有char [5] 类型,当传递给char * 运算符时,它会衰减为char *。所以它们被打印为 C 字符串。但是,您实际上并没有以 null 结尾的字符串,因此 operator &lt;&lt; 会读取超出数组的边界,从而触发 undefined behavior

      a1 的情况下,您没有指定第一个维度,因此它的大小取自具有两个元素的初始值设定项,因此该维度的大小为 2。对于第二个数组维度,并且初始化器不会显式初始化所有元素,因此那些未显式初始化的元素设置为 0。因此,当您打印 a1[0]a1[1] 时,您 do 有空终止字符串和所以你得到了预期的输出。

      【讨论】:

        【解决方案4】:

        标题

        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 个初始化程序,编译器会自动将丢失的第六个字符(即空终止符)初始化为零,因此将这些行传递给 cout 可以正常工作。

        【讨论】:

          【解决方案5】:

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

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

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

          这是两个示例之间的主要区别 - 一个显式指定大小,另一个根据初始值设定项推断大小。

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

          【讨论】:

            猜你喜欢
            • 2018-09-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-10-21
            • 1970-01-01
            • 2014-07-28
            • 2012-03-16
            相关资源
            最近更新 更多