【问题标题】:Why are strings in C++ usually terminated with '\0'?为什么 C++ 中的字符串通常以 '\0' 结尾?
【发布时间】:2012-06-12 04:04:36
【问题描述】:

在许多代码示例中,人们通常在创建一个新的字符数组后使用'\0',如下所示:

string s = "JustAString";
char* array = new char[s.size() + 1];
strncpy(array, s.c_str(), s.size());
array[s.size()] = '\0';

我们为什么要在这里使用'\0'

【问题讨论】:

  • cstrings 通常不是 C++ 代码中最好的主意
  • C 字符串,本质上是一个 char 数组,必须以 NUL 结尾。否则,string.h 中的函数将无法按预期运行。
  • 在 C 中,你会看到很多。在 C++ 中,可能有更好的方法来完成同样的事情。
  • 这样编译器就知道字符串结束了。
  • 它不适用于编译器,它适用于库,可能还有您的代码。 C 不能正确支持数组。您可以拥有本地数组,但无法传递它们。如果您尝试,您只需传递起始地址(第一个元素的地址)。所以你可以让最后一个元素变得特别,例如'\0' 或总是传递大小,小心不要搞砸。我使用一组宏来传递起始地址、长度双元组。结构是另一种方式。上课是最好的方式。但是 C 没有类。

标签: c++ c string


【解决方案1】:

您的问题标题引用了 C 字符串。 C++ std::string 对象的处理方式与 标准 C 字符串不同。 \0 在使用 C 字符串时很重要,当我在这里使用术语 string 时,我指的是标准 C 字符串

\0 在 C 中充当字符串终止符。它被称为 null 字符,或 NUL。它表示处理字符串的代码 - 标准库以及您自己的代码 - 字符串的结尾在哪里。一个很好的例子是strlen,它返回一个字符串的长度。

当你声明一个常量字符串时:

const char *str = "JustAString";

然后\0 会自动为您附加。在其他情况下,您将像数组示例一样管理非常量字符串,有时您需要自己处理它。在您的示例中使用的docs for strncpy 是一个很好的说明:strncpy 在复制整个字符串之前达到指定长度的情况下复制空终止字符 except。因此,您会经常看到strncpy 与空终止符的可能冗余 分配结合使用。 strlcpystrcpy_s 旨在解决因疏忽处理此案而产生的潜在问题。

在您的特定示例中,array[s.size()] = '\0'; 就是这样一种冗余:由于array 的大小为s.size() + 1,并且strncpy 正在复制s.size() 字符,因此该函数将附加\0

标准 C 字符串实用程序的文档将指出何时需要小心包含此类空终止符。但请仔细阅读文档:与 strncpy 一样,这些细节很容易被忽略,从而导致潜在的缓冲区溢出。

【讨论】:

  • 那么,C++ 中的字符串是如何终止的呢?我发现它们不是 NULL 终止的,因为在任意索引处添加 '\0' 不会像在 C 中那样修剪字符串,但它只会用空字符替换字符串中的索引。
  • @CaptainDaVinci 它们不一定会终止,因为长度是在内部存储的。如果你打电话给c_str(),那么你会得到一个正确终止的缓冲区,但这只是因为你问得很好。
【解决方案2】:

为什么 C++ 中的字符串通常以'\0' 结尾?

请注意,C++ 字符串和 C 字符串并不相同。
在 C++ 中字符串指的是 std::string,它是一个模板类,提供了很多直观的函数来处理字符串。
请注意,C++ std::string 不是 \0 终止的,但该类提供了将底层字符串数据作为 \0 终止的 c 样式字符串获取的函数。

在 C 中,字符串是字符的集合。此集合通常以 \0 结尾。
除非使用像 \0 这样的特殊字符,否则无法知道字符串何时结束。
它也被恰当地称为字符串空终止符。

当然,还有其他记账方法来跟踪字符串的长度,但是使用特殊字符有两个直接的好处:

  • 更直观,更
  • 没有额外的开销

请注意,\0 是必需的,因为大多数标准 C 库函数都对字符串进行操作,假设它们是 \0 终止的。
例如:
在使用printf() 时,如果您有一个不是\0terminated 的字符串,那么printf() 会一直向stdout 写入字符,直到遇到\0,简而言之,它甚至可能打印垃圾。

我们为什么要在这里使用'\0'

有两种情况不需要\0终止一个字符串:

  • 在任何用途中,如果您明确记账字符串的长度并且
  • 如果您使用某些标准库 api 将隐式添加 \0 到字符串。

在您的情况下,您已经有第二种情况适合您。

array[s.size()] = '\0';

上面的代码语句在您的示例中是多余的。

对于您的示例,使用 strncpy() 使其无用。 strncpy()s.size() 字符复制到您的 array,请注意,如果在复制字符串后还有剩余空间,它会附加一个空终止符。因为array的大小是s.size() + 1,所以会自动添加\0

【讨论】:

  • 不一定。您还可以通过将长度保持在某处来存储任意长度的数组(就像 Java 的工作原理一样。我假设)。
  • @BrendanLong:希望得到答案。
  • @BrendanLong 我假设在该评论之后进行了编辑,但正如指出的那样,它消除了额外的开销。要按照您建议的方式进行操作,您需要使用 int 和数组创建一个结构,这会提供更差的性能并消耗更多的内存。
  • @evanmcdonnal 更多开销,是的,但是空指针“没有开销”的想法是不正确的——它是一个额外的字符(1-4 个字节)。如果您使用的是 UTF32(出于某种原因),那么它们的大小将完全相同。在您需要查找长度的任何情况下,存储长度也快得多(因为使用空终止符,您需要一直遍历字符串以找出它的长度)。我只是想指出这不是“一种方法显然更好”。值得注意的是,C++ 存储字符串和向量的长度。
  • 我也不同意“更直观”的观点,因为在我看来,存储数据的长度与使用标记值一样直观。
【解决方案3】:

'\0' 是空终止字符。如果您的字符数组没有它并且您尝试执行 strcpy 您将有缓冲区溢出。许多函数依靠它来知道何时需要停止读取或写入内存。

【讨论】:

    【解决方案4】:
    strncpy(array, s.c_str(), s.size());
    array[s.size()] = '\0';
    

    我们为什么要在这里使用'\0'?

    你不应该,第二行是浪费空间。如果您知道如何使用它,strncpy 已经添加了一个空终止。代码可以改写为:

    strncpy(array, s.c_str(), s.size()+1);
    

    strncpy 是一个奇怪的函数,它假设第一个参数是第三个参数大小的数组。因此,如果在复制字符串后还有剩余空间,它只会复制空终止。

    在这种情况下,您也可以使用 memcpy(),它的效率会稍高一些,但可能会使代码阅读起来不那么直观。

    【讨论】:

    • 或相反,strncpy 如此奇怪可能使代码不如直接的 memcpy 直观。但是当我看到如上所示的代码时,我的第一个反应通常是检查是否无法通过直接使用 c_str() 内容来完全避免将数据复制到数组,因为最后的零通常会添加到以后不会修改的字符串中(输出字符串)。
    • 如果要复制到第一个 \0,请使用 strcpy(array, &s[0]);。 (这是 std::strlen(&s[0])+1 many chars)如果你想复制到第一个 \0 并用 \0 填充其余部分,请使用 strncpy(array, &s[0], s.size()+1); ...如果你想使用 memcpy(array, &s[0], s.size()+1);从 &s[0] 复制给定的大小。 (所以嵌入 \0 不会清除字符串的其余部分)
    【解决方案5】:

    在 C 中,我们用一个 char(或 w_char)数组来表示字符串,并使用特殊字符来表示字符串的结束。与 Pascal 不同,它将字符串的长度存储在数组的索引 0 中(因此字符串对字符数有硬性限制),理论上对字符数没有限制字符串(表示为字符数组)在 C 中可以具有的字符。

    在 C 中的默认库以及其他库中的所有函数中,特殊字符都应为 NUL。如果要使用依赖于字符串确切长度的库函数,则必须使用 NUL 终止字符串。您完全可以定义自己的终止字符,但您必须了解涉及字符串(作为字符数组)的库函数可能无法按您预期的那样工作,并且会导致各种错误。

    在给定代码的 sn-p 中,需要将终止字符显式设置为 NUL,因为您不知道分配的数组中是否有垃圾数据。这也是一个很好的做法,因为在大型代码中,您可能看不到字符数组的初始化。

    【讨论】:

      猜你喜欢
      • 2019-02-22
      • 2015-06-10
      • 2011-11-05
      • 2021-10-25
      • 1970-01-01
      • 2011-09-11
      • 1970-01-01
      • 2011-06-06
      • 1970-01-01
      相关资源
      最近更新 更多