字符串常量中的符号\0是什么意思?

60

考虑以下代码:

char str[] = "Hello\0";

str数组的长度是多少,并以多少个0结尾?


@Ashot Martirosyan:您是否需要了解C语言,还是只对C++的答案感兴趣? - CB Bailey
2
@Charles Bailey 我主要使用C++,但我想知道C和C++之间是否有任何区别。这就是为什么我添加了C++标签的原因。 - UmmaGumma
@UmmaGumma如果你只标记为C语言,那么只了解C语言的人才能回答。如果你同时标记为C和C++,那么回答问题的人就被限制在那些了解两种语言之间细微差别的人中。只有当问题确实需要这种水平的知识和专业技能——了解两种语言之间的细微差别时,才应该使用两种语言一起标记。 - David Schwartz
7个回答

106

sizeof str 是 7,"Hello" 文本占了五个字节,加上显式的 NUL 结束符和隐含的 NUL 结束符。

strlen(str) 是 5,只计算了 "Hello" 的五个字节。

关键在于隐含的 NUL 结束符总是被添加——即使字符串字面值恰好以 \0 结尾也是如此。当然,strlen 在第一个 \0 处停止——它无法区分两者。

有一个例外是,如果你明确指定了数组大小,字符串将会被截断以适应指定大小:

char str[6] = "Hello\0"; // strlen(str) = 5, sizeof(str) = 6 (with one NUL)
char str[7] = "Hello\0"; // strlen(str) = 5, sizeof(str) = 7 (with two NULs)
char str[8] = "Hello\0"; // strlen(str) = 5, sizeof(str) = 8 (with three NULs per C99 6.7.8.21)

然而,这很少有用,并且容易计算字符串长度错误,导致未终止的字符串。在C++中也被禁止使用。


6
请添加说明,这种截断方式仅在C语言中有效,而不适用于C++。 - fredoverflow
3
你的 char [8] 示例看起来有误。如果原作者使用 char str[8] = { 'H', 'e', 'l', 'l', 'o', '\0', '\0' },那么剩余字符的值将 不会 是未定义的,而是零(这样你就可以合理地初始化例如 int arr[100] = { 0 } 以使其全部为零)。我不明白为什么对于字符串 "Hello\0" 而言,它与长形式是不同的,除非标准在这种情况下有明确的例外规定(这对我来说似乎非常奇怪)。 - Chris Lutz
@Chris,是的,我更新了它,可能是在你回复的时候 :) - bdonlan
1
顺带一提,有关的段落是:如果在花括号括起来的列表中的初始化项比聚合体中的元素或成员少,或者用于初始化已知大小的数组的字符串文字中的字符少于数组中的元素数,则聚合体的其余部分应隐式地初始化为具有静态存储期的对象相同。 - bdonlan
1
FYI,空字符(也称为空终止符),缩写为NUL,因此术语“NUL终止符”对我来说有点令人困惑。 - V-SHY
@V-SHY 那么我怀疑你不理解什么是词语和定义。一个词语并不是其定义的字面替代品。一个词语是一个概念单元,其定义可以帮助指向该词所代表的单一概念。用“终止符”这个词来定义“NUL”可能很方便,但这并不意味着“NUL终止符”是多余的。缩写也是如此,这就是为什么说“PIN号码”或“水肺装备”没有问题的原因。 - David Schwartz

12

这个数组的长度为7,NUL字符\0仍然计算在字符中,并且字符串以隐式的\0结尾。

请查看此链接以查看一个工作示例。

请注意,如果您将str声明为char str[6] = "Hello\0";,则长度将为6,因为只有在可以适合时才会添加隐式NUL(在这个例子中不能)。

  

§ 6.7.8/p14
字符类型的数组可以通过用括号括起来的字符字符串字面值进行初始化。 字符串字面值的连续字符(包括终止零字符如果有空间或如果数组大小未知)初始化数组的元素。

示例

char str[] = "Hello\0"; /* sizeof == 7, Explicit + Implicit NUL */
char str[5]= "Hello\0"; /* sizeof == 5, str is "Hello" with no NUL (no longer a C-string, just an array of char). This may trigger compiler warning */
char str[6]= "Hello\0"; /* sizeof == 6, Explicit NUL only */
char str[7]= "Hello\0"; /* sizeof == 7, Explicit + Implicit NUL */
char str[8]= "Hello\0"; /* sizeof == 8, Explicit + two Implicit NUL */

“字符串”的长度(如C函数视图字符串)为5。sizeof运算符返回7。 - Chris Lutz
2
@ChrisLutz:所问的问题是“str_array的长度是多少”(我强调了一下),所以这个答案是正确的。 - CB Bailey
1
@Chris 好的,我承认在 C 语言中,“长度”和“大小”这两个词在字符串方面有不同的含义。在我的回答中,我指的是后者。 - SiegeX

7
具体来说,我想提及一个可能会让您困惑的情况。
“\0”和“”之间有什么区别?
答案是,“\0”在数组中表示为“{0 0}”,而“”表示为“{0}”。
因为“\0”仍然是一个字符串文字,它也会在其末尾添加“\0”。而“”为空,但也会添加“\0”。
理解这一点将有助于您更深入地了解“\0”。

2
换句话说,在C语言中,空字符串仍然是以null结尾的 - David C. Rankin

5
打我的常规独奏“试试看”,以下是您在未来如何回答这类问题的方法:

$ cat junk.c
#include <stdio.h>

char* string = "Hello\0";

int main(int argv, char** argc)
{
    printf("-->%s<--\n", string);
}
$ gcc -S junk.c
$ cat junk.s

抱歉,我不能识别要翻译的内容。请提供具体的文本以便我能够为您提供准确的翻译。
.LC0:
    .string "Hello"
    .string ""

...

.LC1:
    .string "-->%s<--\n"

注意我在使用printf时的字符串只是"-->%s<---\n",而全局字符串由两部分组成:"Hello"""。GNU汇编器也会用隐含的NUL字符来终止字符串,因此第一个字符串(.LC0)被分为两个部分意味着有两个NUL。因此该字符串长度为7个字节。如果你真的想知道编译器对某个代码块的处理方式,可以将其隔离到像这样的虚拟示例中,并使用-S查看它的操作方式(GNU为此提供了一个选项,MSVC也有一个汇编输出标志,但我不记得了)。你将学习到有关如何使代码工作(或者无法正常工作)的很多知识,并且你将很快得到一个100%与你所使用的工具和环境相匹配的答案。


3
除非我们测试的东西恰好是未定义行为,否则答案可能只能保证在测试时与工具和环境完全匹配。此外,如果我们测试的东西是实现定义的,那么要真正得到答案,我们必须在所有可能的实现上进行测试。(而且我们还必须知道它是实现定义的,但如果我们已经知道了,我们就不必问了。)此外,为了以这种方式进行测试,我们需要了解GNU汇编器的规则以及我们实际尝试使用的语言。 - Rob Kennedy

3
这个问题是关于str数组的长度以及末尾有多少个0的问题。我们来看一下:代码示例
int main() {
  char str[] = "Hello\0";
  int length = sizeof str / sizeof str[0];
  // "sizeof array" is the bytes for the whole array (must use a real array, not
  // a pointer), divide by "sizeof array[0]" (sometimes sizeof *array is used)
  // to get the number of items in the array
  printf("array length: %d\n", length);
  printf("last 3 bytes: %02x %02x %02x\n",
         str[length - 3], str[length - 2], str[length - 1]);
  return 0;
}

0

'\0' 被称为 NULL 字符或 NULL 终止符 它是整数 0(零)的字符等效物,因为它指代了空值

在 C 语言中,它通常用于标记字符串的结尾。 例如字符串 a="Arsenic"; 每个字符都存储在一个数组中。

a[0]=A
a[1]=r
a[2]=s
a[3]=e
a[4]=n
a[5]=i
a[6]=c

数组的末尾包含''\0',以停止字符串'a'的数组内存分配。


0
char str[]= "Hello\0";

这将是7个字节。

在内存中,它将是:

48 65 6C 6C 6F 00 00
H  e  l  l  o  \0 \0

编辑:

  • C字符串中的\0符号代表什么意思?
    它是字符串的“结尾”。一个空字符。在内存中,实际上是零。通常处理字符数组的函数会寻找这个字符,因为这是消息的末尾。我会在最后放一个例子。

  • str数组的长度是多少?(在编辑部分之前已回答)
    7

  • 以及以多少个0结尾?
    你的数组有两个带零的“空位”;str[5]=str[6]='\0'=0

额外的例子:
假设你有一个打印文本数组内容的函数。 你可以将其定义为:

char str[40];

现在,您可以更改该数组的内容(我不会详细介绍如何操作),以便其中包含消息:“这只是一个打印测试” 在内存中,您应该有类似以下的东西:

54 68 69 73 20 69 73 20 6a 75 73 74 20 61 20 70 72 69 6e 74
69 6e 67 20 74 65 73 74 00 00 00 00 00 00 00 00 00 00 00 00

所以你打印那个字符数组。然后你想要一个新的消息。比如说“你好”

48 65 6c 6c 6f 00 73 20 6a 75 73 74 20 61 20 70 72 69 6e 74
69 6e 67 20 74 65 73 74 00 00 00 00 00 00 00 00 00 00 00 00

注意 str[5] 上的 00。这就是打印函数知道它实际需要发送的内容量,尽管向量的实际长度和整个内容可能不同的方法。

你没有回答原始问题“这个符号代表什么”。请扩展你的回答以回应原始问题。 - Michal
其他答案已经提到str是一个大小为7的数组,包括七年前被接受的答案。为什么要再次重复它(而不添加任何新内容)? - melpomene
@Michal,你知道原帖有3个问题,对吧? - L. Lopez
@melpomene。非常抱歉。我扩展了答案,希望能进一步澄清并添加更多内容,因为您似乎需要这样做。 - L. Lopez

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