【问题标题】:C: strange string phenomenonC:奇怪的串现象
【发布时间】:2011-04-19 15:18:16
【问题描述】:

谁能解释一下这个现象。

#include "stdio.h"
#include "stdlib.h"

int main()
{
    char foo[]="foo";
    char bar[3]="bar";
    printf("%s",foo);
    printf("\n");
    printf("%s",bar);
    return 0;
}

结果:

foo
barfoo

如果我在 foo 之前更改顺序并创建 bar,我会得到正确的输出。

#include "stdio.h"
#include "stdlib.h"

int main()
{
    char bar[3]="bar";
    char foo[]="foo";
    printf("%s",foo);
    printf("\n");
    printf("%s",bar);
    return 0;
}

结果:

foo
bar

还有一个。

#include "stdio.h"
#include "stdlib.h"

int main()
{

    char foobar[]="foobar";
    char FOO[3]={'F','O','O','\0'};
    char BAR[3]="BAR";
    printf("%s",foobar);
    printf("\n");
    printf("%s",FOO);
    printf("\n");
    printf("%s",BAR);
    return 0;
}

结果:

foobar
FOOfoobar
BARFOOfoobar

【问题讨论】:

  • 不应该有关于您尝试用 4 字节数据填充 3 字节缓冲区的警告吗?
  • 系统标头(不是必需的文件)由<stdio.h> 标识。头文件由"my_header.h" 标识(如果引号处理失败,则重新处理为<my_header.h>)。
  • @MarkusG-:您的第三个示例是不可编译的。 FOO 的声明是非法的,任何符合标准的 C 编译器都会立即拒绝。如果您的编译器在没有诊断消息的情况下接受它,则说明您的编译器已损坏。
  • @Timbo 我没有收到任何警告。 (使用 Code::Blocks 10.05)
  • 这不是“奇怪”,也不是“现象”。阅读 C 书籍中关于字符串的章节。

标签: c arrays string printf


【解决方案1】:

字符串"bar" 有四个字符长:{'b', 'a', 'r', '\0'}。如果显式指定数组长度,则需要分配至少四个字符:

char bar[4]="bar";

当你这样做时:

char bar[3]="bar";
printf("%s",bar);

您正在调用未定义的行为,因为 bar 变量没有空终止符。什么事情都可能发生。在这种情况下,编译器已经在内存中连续布置了两个数组:

'b' 'a' 'r' 'f' 'o' 'o' '\0'
 ^           ^
bar[3]      foo[4]

当您打印 bar 时,它会一直读取,直到找到一个空终止符,任何空终止符。由于bar 没有,它会一直运行,直到找到"foo\0" 末尾的那个。

【讨论】:

    【解决方案2】:

    如果您声明char bar[3]="bar";,那么您将声明一个char 数组,其中没有空终止符的空间。所以printf() 将继续从内存中读取chars,将它们打印到控制台,直到遇到'\0'

    【讨论】:

      【解决方案3】:

      其他发帖人已经给你解释过了,在你的

      char bar[3] = "bar";
      

      示例字符串终止符不适合数组,因此字符串最终未终止。从形式上讲,它甚至不是一个字符串(因为根据定义需要终止 stings)。您正在尝试将非字符串打印为字符串(使用 %s 格式说明符),这会导致未定义的行为。未定义的行为正是您所观察到的。

      在 C++ 语言中(例如)

      char bar[3] = "bar";
      

      声明是非法的,因为 C++ 不允许零终止符在这样的声明中“脱落”。 C 允许它,但仅适用于隐式零终止符。

      char bar[3] = "barr";
      

      声明在 C 和 C++ 中都是非法的。

      再一次,“丢失零”技巧在 C 中仅适用于隐式零终止符。它不适用于任何显式初始化程序:您不允许显式指定比数组中的元素更多的初始化程序。这将我们带到您的第三个示例。在您的第三个示例中,您有

      char FOO[3] = { 'F', 'O', 'O', '\0' };
      

      声明,明确为大小为 3 的数组指定了 4 个初始化程序。这在 C 中是非法的。您的第三个示例不可编译。如果您的编译器在没有诊断消息的情况下接受了它,则您的编译器必须被破坏。第三个程序的行为不能用 C 语言解释,因为它不是 C 程序。

      【讨论】:

        【解决方案4】:

        正如您在char FOO[3]={'F','O','O','\0'}; 行中暗示的那样,这是一个空终止问题。问题是空终止符是一个字符。如果为 3 个字符分配内存,则不能在该位置放置 4 个字符(它只占用前 3 个字符并截断其余字符)。

        【讨论】:

          【解决方案5】:

          您缺少字符串末尾的 \0.. 并且一个包含 4 个元素的数组被声明为 FOO[4] 而不是 FOO[3]..

          【讨论】:

            【解决方案6】:

            在第一个示例中,您没有以 null 结尾的字符串。碰巧它们在内存中是连续排列的,因此可以将行为解释为从一个字符串到另一个字符串的运行。

            在下一个示例中,FOO 的大小为 3,但您要给它四个元素。在同样的许多BAR 中不是空终止的。

            char FOO[3]={'F','O','O','\0'};
            char BAR[3]="BAR";
            

            【讨论】:

            • 严格来说FOO的例子是非法的,不会编译。显式指定比数组中的元素更多的初始化程序是非法的。仅字符串终端例外,其中允许尾随隐式零“脱落”。
            【解决方案7】:

            bar 不是以 null 结尾的,因此 printf 会一直跟随数组,直到它到达一个 '\0' 字符。堆栈的排列方式是 bar 和 foo 在内存中彼此相邻。 C 知道数组大小的唯一方法是找到一个空终端。所以如果你在内存中布置你的堆栈,它看起来像:

              0    1    2    3    4    5    6
             'b'  'a'  'r'  'f'  'o'  'o'  '\0'
              ^bar begins    ^foo begins
            

            通过说 foo[] 编译器根据它初始化的常量字符串设置 foo 的大小。它足够聪明,可以在 4 个字符中包含空终止符 '\0'。

            要解决这个问题,bar的大小实际上应该是4,即:

             char bar[4] = "bar"; // extra space for null terminal
            

            或者更好,让编译器像你对 foo 所做的那样计算它:

             char bar[] = "bar"; // compiler adds null term character('\0')
            

            【讨论】:

              【解决方案8】:

              char bar[3]="bar"; 没有留下足够的空间来添加终止 '\0' 字符。

              如果你做char bar[4]="bar";,你应该得到你期望的结果。

              【讨论】:

                【解决方案9】:

                请阅读this的详细答案,这将有助于深入了解这一点...

                你看到“有趣”的东西的原因是因为字符串不是 NUL 终止的......

                【讨论】:

                  【解决方案10】:

                  罪魁祸首是这一行:

                  char bar[3]="bar";
                  

                  这会导致只有 'b'、'a' 和 'r' 在您创建的长度为 3 的数组中。

                  现在恰好发生了,foo 中的字符串是 'f'、'o'、'o' 和 '\0',并且它被分配了与 bar 连续的位置。所以内存看起来像:

                  b | a | r | f | o | o | \0
                  

                  我希望这能说明问题。

                  【讨论】:

                    【解决方案11】:

                    这一行

                    char bar[3]="bar";
                    

                    导致未定义的行为,因为 "bar" 是负责处理 '\0' 的四个字符。所以你bar数组应该是四个字节。

                    未定义的行为意味着任何事情都可能发生 - 包括好事和坏事

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2015-07-13
                      • 2016-01-03
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2012-08-15
                      • 2011-12-20
                      相关资源
                      最近更新 更多