当 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?
我知道 strlen()
不计算以 NUL 结尾的字符。我真的知道这是事实。因此,这个问题并不是要问为什么 strlen()
可能 "presumably" 不是 return 正确的字符串长度,这已经在 Whosebug f.e 上被很好地询问和回答了。在此 thread, or this one。
那么让我们继续我的问题:
在ISO/IEC9899:1990(E); 7.1.1., 声明:
A string is a contiguous sequence of characters terminated by and including the first null character.
什么原因,为什么strlen()
偏离了这个形成的标准,不"want"接受一个字符串with它的NUL-terminating character ?
为什么?
把你的疑问当成一个合理的观点,我们可以声明: C 字符串由两部分组成:
- 字符串的有用内容("the text");
- 空终止符;
空终止符纯粹是C源库函数判断字符串结尾的技术措施。尽管如此,如果输入一个声明:
char * str = "some string";
从逻辑上讲,他们宁愿期望其长度为 11
,这是他们在此声明中可以看到的长度。因此 strlen()
值仅产生字符串 1.
部分的长度。
不是你问题的真正答案,但考虑这个例子:
char string[] = "string";
printf("sizeof: %zu\n", sizeof(string));
printf("strlen: %zu\n", strlen(string));
这会打印
sizeof: 7
strlen: 6
因此 sizeof
计算 [=13=]
,但 strlen
不计算。
像这样的问题,即为什么某个古老的决定是以一种方式而不是另一种方式做出的,很难回答。我可以说这对 我 来说是非常明显的,无论如何,strlen
应该只计算真实的 "interesting" 个 在 字符串,并忽略仅终止它的末尾的 [=13=]
。我习惯于单独计算 [=13=]
。我想如果 strlen
以另一种方式定义的话,总体上会更麻烦。但是我无法用令人信服的论据来证明这一点,而且我一直在使用 strlen
及其当前定义这么久,以至于我可能无可救药地有偏见;我可能会说 "it's perfectly obvious to me that...",即使 strlen
的定义完全错误。
C 风格字符串的物理存储表示与 C 风格字符串的逻辑表示之间存在差异。
物理表示,字符串实际存储在内存或其他媒体中的方式,包括空字符。讨论物理表示时包含空字符,因为它占用额外的存储空间。为了成为 C 风格的字符串,必须存储空字符。
然而,字符串的逻辑表示不包括空字符。字符串的逻辑表示仅包括程序员想要操作的文本字符。
我怀疑之所以选择空字符,即二进制零值,是因为原始 ASCII 字符集将字符值零定义为 NULL 字符。各种电传控制代码中较低值的一部分,它似乎是最不可能出现在文本中的 ASCII 字符。参见 ASCII Character Codes。
使用二进制零作为字符串终止符的另一个好处是,该值表示逻辑假,因此遍历字符串通常是递增数组索引或递增指针的问题,因为所有字符都是逻辑真除了字符串结尾指示符之外,还有一个非零值或逻辑真值。
由于C编程语言与硬件的距离有多近,程序员需要关注两种表示,分配内存以存储包含空字符的字符串时的物理表示和逻辑表示没有空字符的字符串。
标准库中的各种C 风格字符串操作函数(strlen()
、strcpy()
等)都是围绕C 风格字符串的逻辑表示而设计的。他们通过使用空字符来执行他们的操作,因为空字符不是文本的一部分,而是作为指示字符串结尾的特殊指示符。但是,作为其操作的一部分,他们需要了解空字符及其作为特殊符号的用途。例如,当 strcpy()
或 strcat()
用于复制字符串时,它们还必须复制指示字符串结尾的空字符,即使它不是逻辑表示的实际文本的一部分。
此选项允许将文本字符串存储为字符数组,这符合 C 的硬件方向和效率特征。无需为文本字符串创建额外的内置类型,它非常适合精益C 编程语言的字符。
C++ 之所以能够提供 std::string
,是因为它是面向对象的,并且具有允许创建和管理对象的语言的附加功能。 C 编程语言由于其简单的语法和缺乏面向对象的设施而没有这种便利。
这种方法的问题在于,程序员需要了解文本字符串的物理表示和逻辑表示,并在编写程序时能够兼顾两者的需要。
因为您希望此伪代码的断言成立:
str1 = "foo"
str2 = "bar"
str3 = concatenate(str1, str2)
Assert strlen(str1) + strlen(s2) == strlen(str3)
如果终止 '[=11=]'
被 strlen
计算在内,以上断言将不成立,这比当前的 C 字符串行为更令人头疼。更重要的是,在我看来,这将是非常不直观和不合逻辑的。
我知道 strlen()
不计算以 NUL 结尾的字符。我真的知道这是事实。因此,这个问题并不是要问为什么 strlen()
可能 "presumably" 不是 return 正确的字符串长度,这已经在 Whosebug f.e 上被很好地询问和回答了。在此 thread, or this one。
那么让我们继续我的问题:
在ISO/IEC9899:1990(E); 7.1.1., 声明:
A string is a contiguous sequence of characters terminated by and including the first null character.
什么原因,为什么strlen()
偏离了这个形成的标准,不"want"接受一个字符串with它的NUL-terminating character ?
为什么?
把你的疑问当成一个合理的观点,我们可以声明: C 字符串由两部分组成:
- 字符串的有用内容("the text");
- 空终止符;
空终止符纯粹是C源库函数判断字符串结尾的技术措施。尽管如此,如果输入一个声明:
char * str = "some string";
从逻辑上讲,他们宁愿期望其长度为 11
,这是他们在此声明中可以看到的长度。因此 strlen()
值仅产生字符串 1.
部分的长度。
不是你问题的真正答案,但考虑这个例子:
char string[] = "string";
printf("sizeof: %zu\n", sizeof(string));
printf("strlen: %zu\n", strlen(string));
这会打印
sizeof: 7
strlen: 6
因此 sizeof
计算 [=13=]
,但 strlen
不计算。
像这样的问题,即为什么某个古老的决定是以一种方式而不是另一种方式做出的,很难回答。我可以说这对 我 来说是非常明显的,无论如何,strlen
应该只计算真实的 "interesting" 个 在 字符串,并忽略仅终止它的末尾的 [=13=]
。我习惯于单独计算 [=13=]
。我想如果 strlen
以另一种方式定义的话,总体上会更麻烦。但是我无法用令人信服的论据来证明这一点,而且我一直在使用 strlen
及其当前定义这么久,以至于我可能无可救药地有偏见;我可能会说 "it's perfectly obvious to me that...",即使 strlen
的定义完全错误。
C 风格字符串的物理存储表示与 C 风格字符串的逻辑表示之间存在差异。
物理表示,字符串实际存储在内存或其他媒体中的方式,包括空字符。讨论物理表示时包含空字符,因为它占用额外的存储空间。为了成为 C 风格的字符串,必须存储空字符。
然而,字符串的逻辑表示不包括空字符。字符串的逻辑表示仅包括程序员想要操作的文本字符。
我怀疑之所以选择空字符,即二进制零值,是因为原始 ASCII 字符集将字符值零定义为 NULL 字符。各种电传控制代码中较低值的一部分,它似乎是最不可能出现在文本中的 ASCII 字符。参见 ASCII Character Codes。
使用二进制零作为字符串终止符的另一个好处是,该值表示逻辑假,因此遍历字符串通常是递增数组索引或递增指针的问题,因为所有字符都是逻辑真除了字符串结尾指示符之外,还有一个非零值或逻辑真值。
由于C编程语言与硬件的距离有多近,程序员需要关注两种表示,分配内存以存储包含空字符的字符串时的物理表示和逻辑表示没有空字符的字符串。
标准库中的各种C 风格字符串操作函数(strlen()
、strcpy()
等)都是围绕C 风格字符串的逻辑表示而设计的。他们通过使用空字符来执行他们的操作,因为空字符不是文本的一部分,而是作为指示字符串结尾的特殊指示符。但是,作为其操作的一部分,他们需要了解空字符及其作为特殊符号的用途。例如,当 strcpy()
或 strcat()
用于复制字符串时,它们还必须复制指示字符串结尾的空字符,即使它不是逻辑表示的实际文本的一部分。
此选项允许将文本字符串存储为字符数组,这符合 C 的硬件方向和效率特征。无需为文本字符串创建额外的内置类型,它非常适合精益C 编程语言的字符。
C++ 之所以能够提供 std::string
,是因为它是面向对象的,并且具有允许创建和管理对象的语言的附加功能。 C 编程语言由于其简单的语法和缺乏面向对象的设施而没有这种便利。
这种方法的问题在于,程序员需要了解文本字符串的物理表示和逻辑表示,并在编写程序时能够兼顾两者的需要。
因为您希望此伪代码的断言成立:
str1 = "foo"
str2 = "bar"
str3 = concatenate(str1, str2)
Assert strlen(str1) + strlen(s2) == strlen(str3)
如果终止 '[=11=]'
被 strlen
计算在内,以上断言将不成立,这比当前的 C 字符串行为更令人头疼。更重要的是,在我看来,这将是非常不直观和不合逻辑的。