尽管我很喜欢C和c++,但我还是忍不住对空结尾字符串的选择抓耳挠脑:
- 长度前缀(即Pascal)字符串在C
- 长度前缀字符串允许常量时间长度查找,从而使几种算法更快。
- 带长度前缀的字符串更难以引起缓冲区溢出错误。
- 即使在32位机器上,如果允许字符串的大小与可用内存相同,那么带前缀的长度字符串只比以空结尾的字符串宽3个字节。在16位机器上,这是一个单字节。在64位计算机上,4GB是一个合理的字符串长度限制,但即使您想将其扩展到机器字的大小,64位计算机通常有足够的内存,因此额外的7个字节有点像空参数。我知道最初的C标准是为极其糟糕的机器(就内存而言)编写的,但这里的效率论点并不能说服我。
- 几乎所有其他语言(即Perl, Pascal, Python, Java, c#等)都使用长度前缀字符串。这些语言通常在字符串操作基准测试中胜过C语言,因为它们处理字符串的效率更高。
- c++用
std::basic_string
模板纠正了这一点,但期望空结束字符串的普通字符数组仍然普遍存在。这也是不完美的,因为它需要堆分配。
- 以空结尾的字符串必须保留一个字符(即Null),该字符不能存在于字符串中,而以长度为前缀的字符串可以包含嵌入的Null。
其中一些东西比C语言出现得更晚,所以C语言不知道它们是有道理的。然而,在C语言出现之前,有些语言就已经很简单了。为什么会选择空终止字符串,而不是明显更好的长度前缀?
编辑:由于有些人在我上面的效率点上要求事实(并且不喜欢我已经提供的那些),它们源于以下几件事:
- 使用空结尾字符串的Concat需要O(n + m)时间复杂度。长度前缀通常只需要O(m)。
- 使用空结尾字符串的长度需要O(n)时间复杂度。长度前缀为O(1)。
- Length和concat是迄今为止最常见的字符串操作。在一些情况下,以空结尾的字符串可能更有效,但这种情况发生的频率要低得多。
从下面的答案,这些是一些情况下,空终止字符串更有效:
- 当你需要切断字符串的开头并需要将它传递给某个方法时。你不能用长度前缀在常数时间内做到这一点,即使你被允许销毁原始字符串,因为长度前缀可能需要遵循对齐规则。
- 在某些情况下,你只是一个字符一个字符地循环字符串,你可能能够保存一个CPU寄存器。注意,这只在没有动态分配字符串的情况下才有效(因为这样你就必须释放它,必须使用你保存的CPU寄存器来保存你最初从malloc和朋友那里得到的指针)。
上面这些词都没有length和concat常见。
下面的答案中还有一个断言:
但这个是不正确的——对于以null结尾的字符串和有长度前缀的字符串,它的时间是相同的。(以Null结尾的字符串只是在你想要的新结尾的地方插入一个Null,长度前缀只是从前缀中减去。)