当 NUL 字符被定义为字符串的一部分时,为什么 strlen() 不计算终止 NUL 字符的字节?

Why doesn't strlen() count the byte of the terminating NUL-character, when the NUL-character is defined to be part of a string?

本文关键字:字符 NUL 为什么 strlen 计算 字节 终止 定义 字符串 一部 分时      更新时间:2023-10-16

我知道strlen()不计算带有的NUL终止字符。我真的知道这是事实。因此,这个问题并不是问为什么strlen()可能"可能"没有返回正确的字符串长度,这在StackOverflow上已经被问到并得到了很好的回答,例如在这个线程中,或者这个线程中。

所以让我们继续我的问题:

在ISO/IEC 9899:1990(E)中;7.1.1.,规定:

字符串是以结尾的连续字符序列,包括第一个空字符。

strlen()偏离这种形成的标准,并且不"希望"接受具有NUL终止字符的字符串的原因是什么?

为什么?

因为您希望这个伪代码的断言为真:

str1 = "foo"
str2 = "bar"
str3 = concatenate(str1, str2)
Assert strlen(str1) + strlen(s2) == strlen(str3)

如果strlen计算终止'',那么上述断言将不成立,这将比当前的C字符串行为更令人头疼。更重要的是,在我看来,这将是非常不直观和不合逻辑的。

C风格字符串的物理存储表示与C风格字符串逻辑表示之间存在差异。

物理表示,字符串实际存储在内存或其他介质中的方式包括空字符。在讨论物理表示时会包含null字符,因为它会占用额外的存储空间。为了成为C风格的字符串,必须存储null字符。

但是,字符串的逻辑表示不包括null字符。字符串的逻辑表示只包括程序员想要操作的文本字符。

我怀疑之所以选择null字符(二进制值为零),是因为原始ASCII字符集将字符值为零定义为null字符。作为各种电传控制码中较低值的一部分,它似乎是最不可能出现在文本中的ASCII字符。请参见ASCII字符代码。

使用二进制零作为字符串终止符的另一个好处是,它是表示逻辑false的值,因此在字符串上迭代通常是增加数组索引或在逻辑true时增加指针的问题,因为除了字符串结尾指示符之外的所有字符都具有非零或逻辑true值。

由于C编程语言与硬件的接近程度,程序员需要关注这两种表示,即在分配内存以存储包含null字符的字符串时的物理表示,以及作为不包含null字符字符串的逻辑表示。

标准库中的各种C风格字符串操作函数(strlen()strcpy()等)都是围绕C风格字符串的逻辑表示设计的。它们通过使用null字符来执行操作,因为null字符不是文本的一部分,而是作为一个特殊的指示字符来指示字符串的末尾。然而,作为操作的一部分,他们需要注意空字符及其作为特殊符号的使用。例如,当strcpy()strcat()用于复制字符串时,它们还必须复制指示字符串结尾的null字符,即使它不是逻辑表示的实际文本的一部分。

这种选择允许将文本字符串存储为字符数组,这符合C的硬件方向和效率特性。无需为文本字符串创建额外的内置类型,它非常适合C编程语言的精简特性。

C++之所以能够提供std::string,是因为它是面向对象的,并且具有允许创建和管理对象的语言的附加功能。C编程语言由于其简单的语法和缺乏面向对象的功能而没有这种便利性。

这种方法的问题在于,程序员需要了解文本字符串的物理表示和逻辑表示,并且在编写程序时能够满足这两者的需求。

把你的怀疑作为一个合理的观点,我们可以声明:C字符串由两部分组成:

  1. 字符串的有用内容("文本")
  2. 空终止字符

空终止字符纯粹是由源自C的库函数确定字符串末尾的技术措施。尽管如此,如果键入声明:

char * str = "some string";

他们在逻辑上更希望它的长度是CCD_ 11,这是他们在这句话中看到的那么多。因此,strlen()的值仅产生字符串的部分1.的长度。

这并不是你问题的答案,但考虑一下这个例子:

char string[] = "string";
printf("sizeof: %zun", sizeof(string));
printf("strlen: %zun", strlen(string));

这将打印

sizeof: 7
strlen: 6

所以sizeof计数,而strlen不计数。

像这样的问题,问为什么某个古老的决定是以一种方式而不是另一种方式做出的,很难回答。我可以说,对me来说,很明显,无论如何,strlen应该只计算字符串中的真实"有趣"字符,而忽略末尾的,这只是终止它。我习惯于单独计算。我想,如果strlen以另一种方式定义的话,总体来说会更麻烦。但我无法用令人信服的论据来证明这一点,而且我使用strlen及其当前定义已经很长时间了,我可能有无可救药的偏见;我可能会说"这对我来说是非常明显的…"即使strlen的定义是完全错误的。