为什么在C语言中需要在字符数组末尾添加'\0'(空字符)?

12
为什么在C语言中需要在字符数组末尾添加'\0' (null)? 我在K&R 2书中读过(第1.9章节"字符数组")。书中用于查找最长字符串的代码如下:
#include <stdio.h>
#define MAXLINE 1000
int readline(char line[], int maxline);
void copy(char to[], char from[]);

main() {
    int len;
    int max;
    char line[MAXLINE];
    char longest[MAXLINE];
    max = 0;
    while ((len = readline(line, MAXLINE)) > 0)
        if (len > max) {
            max = len;
            copy(longest, line);
        }
    if (max > 0)
        printf("%s", longest);
    return 0;
}

int readline(char s[],int lim) {
    int c, i;
    for (i=0; i < lim-1 && (c=getchar())!=EOF && c!='\n'; ++i)
        s[i] = c;
    if (c == '\n') {
        s[i] = c;
        ++i;
    }
    s[i] = '\0'; //WHY DO WE DO THIS???
    return i;
}

void copy(char to[], char from[]) {
    int i;
    i = 0;
    while ((to[i] = from[i]) != '\0')
        ++i;
}

我的问题是为什么我们要将字符数组的最后一个元素设置为'\0'?即便没有这个也能正常运行程序...请帮帮我...


5
\0表示字符串的结尾。 - jbowes
1
在C语言中,局部变量没有初始化。因此,局部变量line在你没有写入它的地方会有垃圾值。如果这个垃圾值恰好是0,那么你的程序将在不显式写入空值的情况下工作。然而,如果你对line变量进行另一个readline操作,并且这次读取的行比第一次短,你将会在第二行的末尾看到第一行的残留内容。在末尾写入空字符将可以防止这种情况的发生。 - Erik Eidt
9个回答

16

在C语言中,你需要用'\0'结束字符串,因为这是库知道字符串的结束位置(而在您的情况下,这也是copy()函数所期望的内容)。

没有它程序也能正常工作......

如果没有它,你的程序会出现未定义行为。如果程序恰好做了你想要的事情,那么你只是幸运的(或者说不幸的是,在现实世界中,未定义行为会选择在最不方便的时候表现出来)。


1
@SandyLee_user53167 这次你很幸运 :) - Maroun
1
@SandyLee_user53167,你代码中的copy函数会一直运行,直到遇到\0字符。 - rohit89
不行,绝对不行。这会导致不确定的行为。在这个简单的例子中可能看起来能工作,但那只是纯属运气。在不同的操作系统或编译器下,或者如果你正在做更复杂的事情,比如复制字符串,你将会导致内存溢出 - 写入内存的某些部分它不应该写入的地方。尝试添加一些像在定义 longest 之前和之后定义一个字符变量(例如 char x='Z';)的东西,并查看您的程序是否仍然符合您的预期.. - Steve Atkinson

1

特别是指向未知长度的字符数组的字符串指针是唯一的方法,NULL终止符将确定字符串的长度。

关于NULL终止符的精彩讨论,请参见link


1
在C语言中,“字符串”指的是以空字符结尾的字符数组。与此相比,Pascal字符串最多包含255个字符,并且需要一个字节来指示字符串的长度(但不需要终止符)。
每种方法都有其优缺点。

值得注意的是,流行(或多或少)的编程语言Delphi引入了各种类型的字符串,其中只有一种类型有255个字符的限制。其他所有类型(如ansi、unicode、wide等)都有(我想)4 GB的限制。 - alzaimar
@alzaimar 嗯,现在肯定比 per se 的 Pascal 更受欢迎。我自己不太了解。无论如何,程序员可以选择自己喜欢的编程语言,这总是很好的。 - dmckee --- ex-moderator kitten

0

我刚查了一下 如果您的数组被视为字符串 就像这样 char array[MAX]="string"; 或者像这样 scanf("%s",array); 或者 char* table; 那么空字符 '\0' 将自动附加在该表上的字符末尾 但是,如果您像这样初始化它 char array[MAX]={'n','o','t','s','t,'r'}; 或者使用字符格式 %c 逐个填充它

for(int i=0;i<MAX;i++)
   scanf("%c",&array[i]);

或者使用getchar()代替scanf("%c",...),然后你需要自己添加'\0'。因为现在它被视为任何其他数组类型(int、float等),所以我们认为是空的情况实际上被随机数字或字符填充,具体取决于类型。与此同时,在字符串类型的情况下,最后一个考虑的字符之后的下一个字符默认为'\0'。更多解释:这个char array[]="12345"的长度为6,array[5]=='\0'将返回1。换句话说,你不能像这样定义一个字符串数组:char array[3]="123",因为我们没有留下'\0'自动追加的空间。最后一个例子:char array[7]={'t','e','s','t','\0'};这里array[4]是空字符,array[5]和array[6]是随机值。但如果它是字符串,那么"test"的array[4]array[5]array[6]都被空字符填充(空字符可以指任何空格,如制表符'\t'和回车'\n'也是空字符,就像'\0'一样,可能指空格键)。注意:我们都知道不能分配array[7]或更多,但如果你尝试输出它,它会显示一个随机值作为空的情况。

0

'\0' 在数组中表示字符串的结束,这意味着在此字符之后的任何字符都不被视为字符串的一部分。但它们依然是字符数组的一部分,即我们仍然可以通过索引访问这些字符,但是当我们将此字符数组与字符串相关的函数联系起来时,它们就不是一部分了。

为了使字符串符合规范格式并能够正常使用字符串函数,它必须是一个以 null 结尾的字符数组。没有 NULL,当我们在字符数组上调用字符串函数时程序会显示未定义行为。尽管大多数情况下我们可能会得到所期望的结果,但它仍然是一种未定义行为。


0

实际上,你不需要通过\0来结束一个字符数组。需要以它结尾的是char*或者C语言中表示字符串的方式。

至于数组,如果你想将其转换为字符串(由char*表示),则必须在其末尾添加\0。

另一方面,如果你想将其作为char*使用并计划对其使用char*函数,则需要在数组末尾添加\0。


0

因为C语言将字符串定义为由包含第一个空字符的连续字符序列组成

基本上,C语言的作者可以选择将字符串定义为字符序列+字符串长度,或者使用魔术标记来界定字符串的结尾。

如需了解更多信息,请阅读以下文章:

《最昂贵的一字节错误》by Poul-Henning Kamp http://queue.acm.org/detail.cfm?id=2010365


0

实际上,你已经在这里自己写下了答案:

void copy(char to[], char from[]) {
    int i;
    i = 0;
    while ((to[i] = from[i]) != '\0')
        ++i;
}

该函数中的循环将一直持续到在数组from中遇到'\0'为止。如果没有终止零,则循环将继续未知数量的步骤,直到遇到零或无效的内存区域为止。

-1

这是一个字符串终止符号,当遇到它时,编译器就知道你的字符串已经结束了。


编译器与此无关,它仅在运行时进行评估。 - njzk2

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