没有 '\0' 字符的字符串是什么?

8
如果我错误地定义了一个没有\0作为最后一个字符的char数组,那么会发生什么?
我之所以问这个问题,是因为我注意到如果我尝试通过while(cnt!='\0')迭代数组,其中cnt是用作数组索引的int变量,并同时打印cnt值以监视发生了什么,迭代会在最后一个字符+2处停止。额外的字符当然是随机的,但我不明白为什么它必须在2之后停止。编译器是否自动插入\0字符?相关文档链接将不胜感激。
为了让它更清楚,我举个例子。假设数组str包含单词doh(没有'\0')。在每次循环中打印cnt变量将给我以下结果:doh+doh^等等。
6个回答

9

编辑(未定义的行为)

访问数组边界之外的元素是未定义的行为。
使用除C字符串以外的任何内容调用字符串函数都是未定义的行为。
不要这样做!

C字符串是以'\0'(NUL终止符)结尾并包含一系列字节。所有字节必须属于同一对象。


无论如何,你看到的只是巧合!

但它可能会像这样发生

                        ,------------------ 垃圾
                        | ,---------------- str[cnt](当cnt == 4时,没有边界检查)
memory ----> [...|d|o|h|*|0|0|0|4|...]
                  |   |   \_____/  -------- cnt(大端序,正确对齐为4个字节)
                  \___/  ------------------ str

5
如果你定义一个字符数组而没有以终止符\0(称为“null终止符”)结尾,那么你的字符串就不会有这个终止符。你可以这样做:
char strings[] = {'h', 'e', 'l', 'l', 'o'};

编译器在这种情况下不会自动插入空终止符。你的代码在“+2”后停止是巧合;它也可能在+50或任何其他位置停止,这取决于在你的字符串后面的内存中是否恰好有\0字符。
如果你定义一个字符串为:
char strings[] = "hello";

那确实会以空字符结尾。当你在C语言中使用引号时,即使在文本编辑器中看不到,字符串末尾也有一个空字符终止符。
有一些与C语言字符串相关的函数会自动添加空字符终止符。这不是编译器做的事情,而是函数规范的一部分。例如,strncat() 函数将一个字符串连接到另一个字符串上时,会在末尾添加空字符终止符。
但是,如果你使用的其中一个字符串没有该终止符,那么该函数将不知道字符串何时结束,最终导致垃圾值(或分段错误)。

5
在C语言中,“字符串”一词指的是以“零终止符”结尾的字符数组。因此,严格来说,“没有'\0'字符的字符串”这种说法是不存在的。如果没有以零终止,则不是字符串。
现在,仅有字符数组而没有任何零的情况并没有错,只要你明白它不是字符串即可。如果您试图将这样的字符数组作为字符串进行操作,则程序的行为是“未定义”的。任何事情都可能发生。它可能因为某些神奇的原因似乎“工作”。或者它可能一直崩溃。实际上这个程序会做什么并不重要,因为如果行为是未定义的,那么该程序是无用的。

3
如果恰巧发生*(str + 5)字节的值为0(作为数字,而不是ASCII码),则会发生这种情况。

@kaiseroskilo:是巧合,还是相邻变量的一个字节的实际值。未经明确初始化的内存可能包含任何值,而零是相当常见的。 - Clifford

3
就大多数字符串处理函数而言,字符串总是在遇到 '\0' 字符时停止。如果你在某个地方忘记了这个空终止符,通常会发生以下三种情况之一:
  • 程序将继续读取超出字符串结尾的内容,直到找到一个刚好存在的 '\0' 字符。有多种方式可以让这个字符存在,但通常在事先无法预测:它可能是另一个变量的一部分,也可能是可执行代码的一部分,甚至可能是之前存储在同一缓冲区中的更大的字符串的一部分。当然,在此发生之时,程序可能已经处理了大量的垃圾数据。如果你看到 printf() 产生了大量垃圾数据,那么未终止的字符串就是一个常见的原因。
  • 程序将继续读取超出字符串结尾的内容,直到尝试读取其地址空间之外的地址,导致内存错误(例如在 Linux 系统中恐怖的“段错误”)。
  • 复制字符串时程序将耗尽空间,再次导致内存错误。
而且,不,C 编译器通常不会做除了你在程序中指定的操作以外的任何事情 - 例如它不会自动终止字符串。这就是 C 如此强大也如此难以编写的原因。

0

我敢打赌,在你的字符串后面定义了一个int,并且这个int只取小值,以至于至少有一个字节为0。


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