以长度为前缀的字符串克服的零终止字符串有什么问题?

37 c c++ string

以长度为前缀的字符串克服的零终止字符串有什么问题?

我正在读这本书写的Great Code vol.1我想到了这个问题.

Jon*_*ely 30

一个问题是,对于零终止字符串,您必须重复查找字符串的结尾.这种效率低下的经典示例是连接到缓冲区:

char buf[1024] = "first";
strcat(buf, "second");
strcat(buf, "third");
strcat(buf, "fourth");
Run Code Online (Sandbox Code Playgroud)

每次调用strcat程序都必须从字符串的开头开始,找到终止符以知道从哪里开始追加.这意味着当字符串变长时,函数会花费越来越多的时间找到要追加的位置.

使用长度为前缀的字符串,该strcat函数的等效函数将立即知道结尾的位置,并且只是在追加它之后更新长度.

每种表示字符串的方式都有利有弊,它们是否会导致问题取决于您使用字符串做什么,以及哪些操作需要高效.上述问题可以通过在字符串增长时手动跟踪字符串的结尾来克服,因此通过更改代码可以避免性能成本.

  • `strcat`返回"错误"值并没有帮助.它可以被定义为返回指向`destination`的nul终结符的指针而不是它的开头.但这仍然只会在连接出现在一起的情况下有所帮助:通常,您仍然需要存储字符串结尾的长度或地址,以避免测量它. (4认同)
  • @Superbest:不,不是.但是,C和C++设计原则不是"发布标准函数,让人们使用它们,意识到它们有时是瓶颈,然后返回并改变标准函数的设计并发布对标准的重大改变" .希望明显的原因;-) (2认同)

Gal*_*lik 28

一个问题是您不能在零终止字符串中存储空字符(值为零).这使得无法存储一些字符编码以及加密数据.

长度前缀的字符串不受此限制.

  • @JonathanWakely在这里评论"这是一个问题",当你的回答很可能没有解决这个问题时,具有讽刺意味.C字符串不能包含空字符(其他结尾)是Galik回答的功能限制 - 没有解决C字符串的问题.在你的答案中,效率低下的问题虽然是正确的,但却是运行时的弱点,而不是功能限制. (3认同)
  • @chux,我并没有试图解决所有问题,只是列出一个没有提到的问题(当时我写的).这个答案最初说"**问题是......",这就是我评论的内容.问题并不是说"有什么功能问题",它会问问题是什么.这个答案和我的清单列出了问题. (2认同)

Mar*_* A. 23

第一澄清:C++字符串(即std::string) 不要求具有0至结束,直到C++ 11.但它们始终提供对零终止C字符串的访问.

由于历史原因, C风格的字符串以0字符结尾.

您所指的问题主要与安全问题有关:零结束字符串需要有一个零终止符.如果他们缺少它(无论出于何种原因),字符串的长度变得不可靠并且它们可能导致缓冲区溢出问题(恶意攻击者可以通过在不应该的地方写入任意数据来利用这些问题.DEP有助于缓解这些问题但这是偏离主题的.

  • 长度前缀字符串可能比零终止字符串更能抵抗读取溢出,但在一般情况下,它们与零终止字符串一样容易受到写入溢出的影响. (2认同)

Chr*_*een 20

最好用Poul-Henning Kamp 的最昂贵的单字节错误进行总结.

  1. 性能成本:以块为单位操作内存会更便宜,如果您总是需要查找NULL字符,则无法完成.换句话说,如果你事先知道你有一个129个字符的字符串,那么在64,64和1个字节的部分中操作它可能会更有效,而不是逐个字符.
  2. 安全:Marco A.已经非常努力了.过度和不足的字符串缓冲区仍然是黑客攻击的主要途径.

  3. 编译器开发成本:大的成本与优化编译器的null终止字符串有关,而这些字符串使用地址和长度格式会更容易.

  4. 硬件开发成本:与空终止字符串相关联的字符串特定指令的硬件开发成本也很高.


sup*_*cat 5

一些可以使用长度前缀字符串实现的额外功能:

  1. 可以有多种样式的长度前缀,可以通过字符串指针/引用标识的第一个字节的一个或多个位来标识。作为确定字符串长度的一点额外时间的交换,例如可以对短字符串使用单字节前缀,对较长字符串使用更长的前缀。如果使用大量 1-3 字节的字符串,与使用固定的四字节前缀相比,这些字符串的整体内存消耗可以节省 50% 以上;这种格式还可以容纳长度超过 32 位整数范围的字符串。

  2. 可以将可变长度字符串存储在边界检查缓冲区中,代价是长度前缀中只有一位或两位。与其他位组合的数字 N 将表示以下三件事之一:

    1. 一个 N 字节的字符串

    2. (可选)一个 N 字节的缓冲区,其中包含一个零长度的字符串

    3. 一个 N 字节的缓冲区,如果它的最后一个字节 B 小于 248,则保存长度为 NB-1 的字符串;如果 248 或更多,前面的 B-247 字节将存储缓冲区大小和字符串长度之间的差异。请注意,如果字符串的长度正好是 N-1,则字符串后面将跟一个 NUL 字节,如果小于该字节,则字符串后面的字节将不被使用,可以设置为 NUL。

    使用这种方法,需要在使用前初始化强缓冲区(以指示它们的长度),但随后不再需要将字符串缓冲区的长度传递给将在那里存储数据的例程。

  3. 人们可以使用某些前缀值来表示各种特殊的东西。例如,一个前缀可能表明它后面没有一个字符串,而是一个字符串数据指针和两个给出缓冲区大小和当前长度的整数。如果对字符串进行操作的方法调用一个方法来获取数据指针、缓冲区大小和长度,那么只要字符串本身比方法调用更有效,就可以廉价地将对该字符串的一部分的引用传递给这样的方法。

  4. 可以用一个位扩展上述功能,以指示字符串数据位于由 生成的区域中,malloc并且可以根据需要调整大小;此外,人们可以安全地拥有有时返回分配在堆上的动态生成字符串的方法,有时返回不可变的静态字符串,并让接收者执行“如果它不是静态的,则释放此字符串”。

我不知道是否有任何前缀字符串实现实现了所有这些额外的功能,但它们都可以以非常小的存储空间成本、相对较少的代码成本和比使用 NUL 所需的时间成本更低的成本来容纳 -长度未知或短的终止字符串。