“std::string是否可以容纳'\0'字符”是按设计要求的吗?

8
< p > std::string 实际上可以容纳 '\0' 字符,这一点经常被提及。当然,这与 C 风格的字符串不一致。

所以我想知道,这是故意的设计,还是疏漏,或者只是标准没有禁止而编译器允许出现这种情况?


4
我认为C风格的字符串与“string”不一致。 '\0' 是一个有效的字符,将其用作终止符会导致元素数组出现奇怪的行为,其中一组256个值中的一个特定值被排除在外。 就我个人而言,我认为空终止符的使用对于99.9%的Unix“应用程序”使用缓慢的基于文本的解释Bash脚本负有责任 - 开发人员担心使用编译代码,以防数据中的null提前终止他们的字符串<g>。 - Martin James
1
@Martin:完全正确。不允许某些任意字符是怪癖,而不是反过来。 - Kerrek SB
5个回答

13

我想知道你们争论的是什么。 '\0' 只是另一个字符,没有有效的方式来禁止它出现在一般用途的 'char' 字符串中。 虽然相同的字符在 C 语言中有特殊含义,但只要你与它进行交互,就像是遇到遗留代码带来的每一个限制一样,必须处理它。

只要坚持使用仅使用 std::string 的代码,这不应该成为问题。

为了解决您的评论,我们需要查看使用 char* 的构造函数,这将在 n3242 中的 21.4.2 9/10 中表示为 basic_string(const charT* s, const Allocator& a = Allocator())。它指出内部字符串的大小是通过 traits::length(s) 确定的,在 std::string 的情况下,该函数是 strlen,它要求其参数是以 null 结尾的。因此,如果您尝试从 const char* 构造一个 std::string,则需要以 null 结尾。


我想我没有表达清楚。我的意思是,std::string 是否适用于终止字符串(不一定是以 NUL 结尾),或者根本不关心。 - Šimon Tóth
另外,函数c_str()生成一个以空字符结尾的字符串,其中空字符是字符类型的值初始化的结果。 - Potatoswatter
@Potatoswatter 这有点抽象,是整个NULL终止字符串的抽象。我从来没有这样想过。 - pmr

4

有一组函数接受'char *'参数并假定字符串以零结束。如果你小心使用它们,你可以确保字符串中有0。

相比之下,STL字符串有意允许零字节,因为它们不使用0作为终止符。所以简单地回答你的问题是,“是的,这是设计上的特点。”


3
但是...一个以空字符结尾的字符串 就是 C 风格的字符串,或者说是C字符串。当然,在 C 语言中,一个 char* 指针可以指向包括空字节的缓冲区,但这个缓冲区不能被称为 C 字符串。 - Magnus Hoff

1

按设计而来。

C语言也可以有非空终止字符串:

char sFoo[4];
strncpy(sFoo,"Test",sizeof(sFoo));

sFoo保存的是非NULL结尾的字符串。

它也可以有以Null结尾的字符串,这些字符串可能为0。

struct String {
  char *str;
  size_t length;
  size_t capacity;
};

字符串字面量以NUL结尾,但这并不总是指字符串。

因此,拥有以NUL结尾的字符串是一种惯例,但这并不意味着0是无效字符。


请查看C语言的Better String Library,其他人也考虑过它 :) - Matthieu M.
话虽如此,strncpystrncat等函数如果有足够的空间,仍然会添加一个空终止符。 - Kerrek SB
@KerrekSB 这些函数是不同的。请看我的回答。 - curiousguy

1

strncpy vs. strncat

话虽如此,如果有空间,strncpystrncat等函数会追加一个空终止符。

实际上,strncpystrncat是非常不同的:

strncpy将一个“NUL填充的n字节字符串”写入一个n字节缓冲区:一个长度为l的字符串,其中l最大为n,因此最后的n-l个字节都被填充为NUL。请注意复数形式:所有最后的字节都被清零,而不仅仅是一个。还要注意的是,l的最大允许值确实是n,因此可能没有NUL字节:缓冲区可能不包含以NUL结尾的字符串。(GCC有一个非便携式函数来测量这样的“NUL填充的n字节字符串”:strnlen。)

相反,strncat 将一个以 NUL 结尾的字符串输出到缓冲区中。在两种情况下,如果字符串过长,都会被截断,但是在 strncpy 的情况下,一个长度为 n 的字符串将适合于一个 n 字节的缓冲区,而在 strncat 的情况下,一个长度为 n 的结果只能适合于(n+1) 字节的缓冲区。
这种差异给 C 初学者甚至非初学者带来了很多困惑。我甚至看到过一些教授“安全的 C 编程”课程和书籍,它们对这些标准函数有混淆和矛盾的信息。
这些所谓的“安全”C字符串操作函数(“strn*”族)在 C “安全编程”界面临着很多批评,并已经发明了更好的设计(但非标准的)替代品(尤其是“strl*”族: strlcpy…)。
总结:
  • strncpy 只有在有空间的情况下才会附加一个空终止符;
  • strncat 总是会附加一个空终止符。

1
标准并未规定在 std::string 中 '\0' 是否为特殊字符。因此,任何符合标准的 std::string 实现都不应将 '\0' 视为特殊字符。当然,如果将 const char* 传递给字符串的成员函数,并且假定其以 null 结尾,则除外。

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