我们知道字符串以'\0'
结尾。
这是因为要知道编译器该字符串结束了,还是为了防止垃圾值。
但是数组如何终止?
如果使用'\0'
,它将把它当作0
的有效整数,
那么编译器如何知道数组结束了?
答案 0 :(得分:5)
C不会对数组执行边界检查。这就是使其快速运行的部分原因。但是,这也意味着您有责任确保您不会在数组末尾读或写。因此,该语言将允许您执行以下操作:
int arr[5];
arr[10] = 4;
但是,如果这样做,您将调用undefined behavior。因此,您需要跟踪自己的数组大小,并确保不走到尽头。
请注意,这也适用于字符数组,如果它包含以空字节结尾的字符序列,则可以将其视为字符串。这是一个字符串:
char str[10] = "hello";
这也是:
char str[5] = { 'h', 'i', 0, 0, 0 };
但这不是:
char str[5] = "hello"; // no space for the null terminator.
答案 1 :(得分:1)
C不会为您提供任何有关“知道阵列已结束”的保护或保证。程序员要牢记这一点,以避免访问数组外部的内存。
答案 2 :(得分:1)
C语言没有本地字符串类型。在C语言中,字符串实际上是一维字符数组,以空字符'\0'
结尾。
来自C标准#7.1.1p1 [强调我的]
字符串是一个连续的字符序列,以第一个空字符结尾并包含第一个空字符。有时使用多字节字符串一词来强调对字符串中包含的多字节字符进行的特殊处理,或避免与宽字符串混淆。指向字符串的指针是指向其初始(最低寻址)字符的指针。 字符串的长度是空字符之前的字节数,字符串的值是所包含字符的值的顺序。
String 是字符数组的特例,它以空字符'\0'
终止。所有与标准库字符串相关的函数均基于此规则读取输入字符串,即读取直到第一个空字符。
除了C中的字符数组,任何类型的数组中的空字符'\0'
都没有意义。
因此,除字符串之外,对于所有其他类型的数组,程序员都应明确跟踪数组中元素的数量。
另外,请注意,第一个空字符('\0'
)表示字符串终止,但并不能阻止您继续阅读。
考虑以下示例:
#include <stdio.h>
int main(void) {
char str[5] = {'H', 'i', '\0', 'z'};
printf ("%s\n", str);
printf ("%c\n", str[3]);
return 0;
}
打印字符串时
printf ("%s\n", str);
您将获得的输出是-Hi
由于使用%s
格式说明符,printf()
写入每个字节,直到不包括第一个空终止符 [注意字符串中使用空字符] ,但您还可以打印数组的第4个 字符,因为它位于char
数组str
的范围内,但超出了第一个'\0'
字符
printf ("%c\n", str[3]);
您将获得的输出是-z
其他:
尝试访问超出其大小的数组会导致undefined behavior,其中包括程序可能执行不正确(崩溃或无声地生成不正确的结果),或者可能偶然执行了程序员想要的操作。
答案 3 :(得分:0)
这只是一个约定问题。如果您愿意,您可以完全编写一些通过哨兵值处理数组终止(对于任何类型的数组)的代码。这是一个做到这一点的示例,随意使用-1作为标记:
int length(int arr[]) {
int i;
for (i = 0; arr[i] != -1; i++) {}
return i;
}
但是,这显然是完全不可行的:您不能再在数组中使用-1。
相比之下,对于C字符串,前哨值'\0'
的问题较少,因为可以预期正常测试将不包含该字符。此假设是种有效的。但是即使如此,显然还是有很多 do 包含'\0'
作为有效字符的字符串,因此null终止绝不是通用的。
一种非常常见的替代方法是将字符串存储在如下所示的结构中:
struct string {
unsigned int length;
char *buffer;
}
也就是说,我们在缓冲区旁边显式存储一个长度。该缓冲区不是以空值结尾的(尽管实际上,它通常具有额外的终端'\0'
字节,以与C函数兼容)。
无论如何,答案归结为:对于C字符串,空终止是一种方便的约定。但这只是一个惯例,由C字符串函数(和C字符串文字语法)强制执行。您可以对其他数组类型使用类似的约定,但是这是不切实际的。这就是为什么为数组开发了其他约定的原因。值得注意的是,处理数组的大多数函数都期望数组和 length 参数。该length参数确定数组在哪里终止。