【问题标题】: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() 不计算终止 NUL 字符的字节?
【发布时间】:2020-02-16 05:11:15
【问题描述】:

我知道strlen() 不计算 NUL 终止字符。我真的知道这是事实。因此,这个问题不是要问为什么strlen() 可能“可能”不会返回正确的字符串长度,这已经在 StackOverflow 上被问到并得到了很好的回答,f.e.在这个thread,或this one

让我们继续我的问题:

在 ISO/IEC 9899:1990 (E) 中; 7.1.1.,声明:

字符串是由终止并包括第一个空字符的连续字符序列。

原因是什么,为什么strlen() 偏离了这个已形成的标准,并且不“想要”接受 其 NUL 终止字符的字符串?

为什么?

【问题讨论】:

  • 终止 NUL 字节是元数据(和实现细节),其他字符是数据。您可以轻松创建strlen_w_terminator 函数。
  • 因为更改它会破坏太多代码,以至于它可能会被标准采用。请记住,第一个 C 标准出现并基本上编纂了该语言的现有工作实践,
  • 典型的用例是获取字符串中实际字符的长度,不包括终止符。它还遵循从零开始的索引语义,因此some_string[strlen(some_string)] 将始终是终止符。
  • 将两个字符串连接在一起时,终止符不会保留为第一个字符串的一部分。因为它是元数据。它用于标记字符串的终止。它不是数据本身的一部分。 std::string 不将 '\0' 视为元数据,它是字符串本身的一部分。
  • 兼容其他语言。 IE。在 MS basic 中,字符串是由保存字符串长度的字段和保存字符串的数组组成的结构。使用Len 基本运算符,您期望检索字符串中的有效字符数或整个结构的大小?正如许多人已经说过的那样,终止 \0 空字符不是字符串的一部分,而是类型表示的功能部分。在C中添加不存在原生字符串类型,将其视为复合类型,该方法strlen返回有效的用户字符。

标签: c++ c string strlen nul


【解决方案1】:

因为你会期望这个伪代码的断言成立:

str1 = "foo"
str2 = "bar"
str3 = concatenate(str1, str2)

Assert strlen(str1) + strlen(s2) == strlen(str3)

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

【讨论】:

  • 也允许char* strcat( char* dest, const char* src) { strcpy(dest + strlen(dest), src); return dest; }...有些东西比较方便,有些东西要+1或者-1来适应物理表示。
  • @Eljay 我的意思并不是真正的方便,而是什么是合乎逻辑的。编辑问题以强调这一点。
【解决方案2】:

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

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

空终止字符纯粹是一种技术措施,用于由源自 C 的库函数确定字符串的结尾。不过,如果有人键入声明:

char * str = "some string";

从逻辑上讲,他们宁愿期望它的长度为11,这与他们在此声明中看到的一样多。因此,strlen() 值仅产生字符串的 1. 部分的长度。

【讨论】:

  • @Yunnosch 我不想判断是否没有奖励,但在工作周结束时听到一个好词是件好事。非常感谢:)
【解决方案3】:

不是您问题的真正答案,但请考虑以下示例:

char string[] = "string";
printf("sizeof: %zu\n", sizeof(string));
printf("strlen: %zu\n", strlen(string));

打印出来

sizeof: 7
strlen: 6

所以sizeof 计算\0,但strlen 不计算。

诸如此类的问题,即为什么某个古老的决定是以一种方式而不是另一种方式做出的,很难回答。我可以说,这对 me 来说是非常明显的,无论如何,strlen 应该只计算字符串 in 中真正的“有趣”字符,而忽略 @最后的 987654327@ 只是终止它。我习惯单独计算\0。我想如果strlen 以另一种方式定义,总体上会更令人讨厌。但我无法用令人信服的论据来证明这一点,而且我一直在使用strlen 及其当前定义很长时间,以至于我可能已经无可救药地有偏见了;即使strlen 的定义完全错误,我可能会说“这对我来说非常明显......”。

【讨论】:

    【解决方案4】:

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

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

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

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

    使用二进制零作为字符串终止符的另一个好品质是表示逻辑假的值,因此迭代字符串通常是增加数组索引或增加指针而逻辑为真的问题,因为除了所有字符之外字符串结束指示符有一个非零或逻辑真值。

    由于 C 编程语言与硬件非常接近,程序员需要关注两种表示,即分配内存以存储包含空字符的字符串时的物理表示和字符串的逻辑表示没有空字符。

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-11
      • 2014-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-04
      相关资源
      最近更新 更多