【问题标题】:Confusing output of two different Char Array两个不同字符数组的混淆输出
【发布时间】:2019-06-28 02:19:08
【问题描述】:

问题:虽然我声明了两个char字符串,它们的内容相同,但输出不同。

#include <stdio.h>

int main(void)
{
    /* Initialization of two different array that We deal with */

    char arr1[10]={'0','1','2','3','4','5','6','7','8','9'};
    char arr2[10]="0123456789";

    /* Initialization End */

    for(int i = 0 ; i < 11 ; ++i)
    {
        printf("arr1[%d] is %c \t\t",i,arr1[i]);
        printf("arr2[%d] is %c\n",i,arr2[i]);

        if(arr1[i]=='\0')
            printf("%d . character is \\0 of arr1 \n",i);

        if(arr2[i]=='\0')
            printf("%d . character is \\0 of arr2 \n",i);
    }

    return 0;
}

期望:我期望两个 if 语句对于任何类型的 'i' 值都是正确的。

输出: 这是我得到的输出。

arr1[0] is 0        arr2[0] is 0
arr1[1] is 1        arr2[1] is 1
arr1[2] is 2        arr2[2] is 2
arr1[3] is 3        arr2[3] is 3
arr1[4] is 4        arr2[4] is 4
arr1[5] is 5        arr2[5] is 5
arr1[6] is 6        arr2[6] is 6
arr1[7] is 7        arr2[7] is 7
arr1[8] is 8        arr2[8] is 8
arr1[9] is 9        arr2[9] is 9
arr1[10] is 0       arr2[10] is 
10 . character is \0 of arr2 

【问题讨论】:

  • 它们不一样。 "0123456789" 自动包含空终止符,但您手动构造的字符串不包含。
  • array[10] 是数组中的第 11 个元素。
  • @SeptemberSKY 内容不一样 :) 听@Blaze - 字符串文字有一个自动附加的 0 终止符。例如。 arr2 在 idx 10 处的第 11 个元素是 0x00, 0, '\0'。您错误地声明 arr2 包含 10 个字符,但您的初始化字符串包含 11(包括隐式零终止符)
  • arr1[10] 中,您正在调用未定义的行为(读取超出数组大小)。可能您正在阅读的'0' 是第二个数组arr2 中的第一个元素,它很可能存储在arr1 之后
  • @MortenJensen 是的,string literal 包含一个空终止符,但 array char[10] 没有空间来存储它。使示例等效。桑德是正确的。

标签: c arrays string char


【解决方案1】:

这两种情况都通过越界访问数组来调用未定义的行为。您无法访问从索引 0 到 9 分配的项目的数组的索引 10。因此,您需要将循环更改为 i&lt;10 否则可能会发生任何事情。只是碰巧打印了不同的值 - 因为您不保证将为索引 10 处的字节打印什么。

在这两个示例中,都没有空终止符,因此它们是等价的。由于 C 语言中有一个微妙而奇怪的规则(C17 6.7.9/14 强调我的):

字符类型的数组可以由字符串字面量或 UTF-8 字符串初始化 字面量,可选地用大括号括起来。字符串文字的连续字节(包括 如果有空间或数组大小未知,则终止空字符)初始化 数组的元素。

通常,当尝试在数组中存储太多初始化时,我们会收到编译器错误。但在这种非常特殊的情况下,字符串字面量初始值设定项不是,这是一种“语言错误”。更改为char arr2[9]="0123456789"; 将无法编译。更改为 char arr2[11]="0123456789"; 即可正常工作,即使迭代 11 个元素也是如此。

【讨论】:

  • @Victor 这实际上几乎是一个常见问题解答。就在 4 年前,我也从 SO 那里学到了这一点:stackoverflow.com/questions/31296727/…
  • Re “但在这种非常特殊的情况下,使用字符串文字初始化器,……”:禁止太多初始化器的规则确实适用于字符串文字。答案中引用的 6.7.9 14 文本并未告诉我们允许字符串文字违反此规则或该规则不适用于它们。相反,它告诉我们字符串文字提供其显式字符作为初始值设定项,并在合适的情况下提供其终止空字符。如果不适合,则不认为是初始化器,并且满足 6.7.9 2 中的规则(如果显式字符适合)。
【解决方案2】:

您的代码和您对它的假设似乎存在一些小问题。

1.这两个声明不一样

char arr1[10]={'0','1','2','3','4','5','6','7','8','9'};
char arr2[10]="0123456789";

第二行等于这个:

char arr2[10]={'0','1','2','3','4','5','6','7','8','9', 0x00};

... 它定义了一个包含 11 个元素的数组。查看字符串文字的隐式零终止。

编辑: 我在这一点上得到了很多反对票。请参阅下面Lundin 的评论,它澄清了这个问题。

2。你的 for 循环迭代了 11 个元素

for(i=0 ; i&lt;11 ;++i)

上面的循环经过 i = 0..10,即 11 个元素....但您只想比较前 10 个元素对吗?

您可以将循环更改为仅比较前十个元素 [for(i = 0; i &lt; 10; ++i)],这将使您的程序按预期工作。

鉴于您的假设,我建议您阅读 C 中的字符串、数组索引和未定义的行为。

【讨论】:

  • 你说得对,字符串文字需要 11 个字符,但它用于初始化一个仅包含 10 个字节的数组。终结者在这里丢失了。
  • 第一点不是问题。 arr1arr2 的内容将相同(arr2 仅包含字符串文字的前 10 个字符,而不包含空终止符)。真正的问题是第 2 点的未定义行为。
  • 但确实如此:OP假设两个数组的内容相同,并且该假设是正确的。
  • 两者都没有明确定义,您的观点 1) 是错误的。 char arr2[10]="0123456789"; 等于 char arr2[10]={'0','1','2','3','4','5','6','7','8','9'} 没有空终止符。这是因为 C 中的“语言错误”允许大小与指定字符串完全匹配的字符串文字删除空终止。
  • 您认为这种初始化是未定义的行为是错误的:“字符类型的数组可以由字符串文字初始化,可选地括在大括号中。字符串文字的连续字符(包括终止如果有空间或数组大小未知,则为空字符)初始化数组的元素。"
【解决方案3】:

当使用双引号字符串初始化字符数组并且未指定数组大小时,编译器会自动为字符串终止符'\0'分配一个额外的空间

Ref

【讨论】:

    猜你喜欢
    • 2021-07-27
    • 2019-03-29
    • 2011-11-09
    • 1970-01-01
    • 2017-03-11
    • 1970-01-01
    • 2021-10-26
    • 2021-11-21
    • 1970-01-01
    相关资源
    最近更新 更多