以长度为前缀的字符串克服的以零结尾的字符串有什么问题?
我正在读“写大代码”一书。 1我想到了这个问题。
答案 0 :(得分:30)
一个问题是,对于零终止字符串,您必须不断重复查找字符串的结尾。这种效率低下的典型例子是连接到缓冲区:
char buf[1024] = "first";
strcat(buf, "second");
strcat(buf, "third");
strcat(buf, "fourth");
每次调用strcat
时,程序必须从字符串的开头开始,找到终止符以知道从哪里开始追加。这意味着当字符串变长时,函数会花费越来越多的时间找到要追加的位置。
使用长度为前缀的字符串,相当于strcat
函数会立即知道结尾的位置,并且只会在追加它后更新长度。
表示字符串的每种方式都有利有弊,它们是否会导致问题取决于您对字符串的操作以及哪些操作需要高效。上述问题可以通过在字符串增长时手动跟踪字符串的结尾来克服,因此通过更改代码可以避免性能成本。
答案 1 :(得分:28)
一个问题是您无法在零终止字符串中存储空字符(值为零)。这使得无法存储一些字符编码以及加密数据。
长度前缀的字符串不受此限制。
答案 2 :(得分:23)
首先澄清:C ++字符串(即std::string
)不是 weren't required to end with zero until C++11。它们总是提供对零终止C字符串的访问。
C风格的字符串以0字符for historical reasons结尾。
您所指的问题主要与安全问题有关:零结束字符串需要以使终止符为零。如果他们缺少它(无论出于何种原因),字符串的长度变得不可靠并且它们可能导致缓冲区溢出问题(恶意攻击者可以通过在不应该的地方写入任意数据来利用它。DEP帮助在减轻这些问题,但它在这里是偏离主题的。)
答案 3 :(得分:20)
最好由Poul-Henning Kamp在The Most Expensive One-byte Mistake中进行总结。
安全:Marco A.已经非常努力了。过度和不足的字符串缓冲区仍然是黑客攻击的主要途径。
编译器开发成本:与使用地址和长度格式更容易的空终止字符串优化编译器相关的成本很高。
硬件开发成本:与空终止字符串相关联的字符串特定指令的硬件开发成本也很高。
答案 4 :(得分:4)
可以使用长度前缀字符串实现的一些额外功能:
可以有多种样式的长度前缀,可通过字符串指针/引用标识的第一个字节的一个或多个位来识别。作为确定字符串长度的一点额外时间的交换,可以例如对于短字符串使用单字节前缀,对较长字符串使用较长的前缀。如果使用大量的1-3字节字符串,与使用固定的四字节前缀相比,这些字符串的整体内存消耗可节省50%以上;这种格式也可以容纳长度超过32位整数范围的字符串。
可以在 bounds-checked 缓冲区中存储可变长度字符串,其成本仅为长度前缀中的一位或两位。数字N与其他位组合将表示以下三种情况之一:
N字节字符串
(可选)保存零长度字符串的N字节缓冲区
一个N字节缓冲区,如果它的最后一个字节B小于248,则保存一个长度为N-B-1的字符串;如果是248或更多,则前面的B-247字节将存储缓冲区大小和字符串长度之间的差异。请注意,如果字符串的长度精确为N-1,则字符串后跟一个NUL字节,如果小于该字符串,则字符串后面的字节将被使用,并且可以设置为NUL。
< / LI> 醇>使用这种方法,需要在使用前初始化强缓冲区(以指示它们的长度),但是不再需要将字符串缓冲区的长度传递给将在那里存储数据的例程。 / p>
可以使用某些前缀值来表示各种特殊事物。例如,一个可能有一个前缀,表示它后面没有字符串,而是一个字符串数据指针和两个整数给出缓冲区大小和当前长度。如果对字符串进行操作的方法调用一个方法来获取数据指针,缓冲区大小和长度,那么可以通过这样的方法对字符串的一部分进行廉价提供,条件是字符串本身将比方法调用更长。
可以用一点来扩展上述特征,以指示字符串数据位于malloc
生成的区域中,并且可以根据需要调整大小;另外,可以安全地拥有有时返回在堆上分配的动态生成的字符串的方法,有时会返回不可变的静态字符串,并且如果收件人不是静态的,则让收件人执行“释放此字符串”。
我不知道是否有任何前缀字符串实现实现了所有这些奖励功能,但它们都可以在存储空间中花费很少的成本,代码成本相对较低,并且花费的成本比使用NUL终止的字符串,其长度既不知道也不短。
答案 5 :(得分:-16)
以长度为前缀的字符串克服的以零结尾的字符串有什么问题?
无论如何 这只是眼睛糖果。
长度前缀字符串作为其结构的一部分,具有字符串长度的信息。如果你想对零终止字符串做同样的事情,你可以使用辅助变量;
lpstring = "foobar"; // saves '6' somewhere "inside" lpstring
ztstring = "foobar";
ztlength = 6; // saves '6' in a helper variable
许多C库函数使用以零结尾的字符串,并且不能使用超过'\0'
字节的任何内容。这是函数本身的问题,而不是字符串结构。如果您需要处理带有嵌入零的零终止字符串的函数,请编写自己的函数。