使用C风格的字符串有哪些缺点?

我知道缓冲区溢出是使用C风格字符串(char数组)的一个潜在危险。 如果我知道我的数据适合我的缓冲区,是否可以使用它们? 我还需要注意C风格字符串固有的其他缺点吗?

编辑:这是一个接近我正在做的事情的例子:

char buffer[1024]; char * line = NULL; while ((line = fgets(fp)) != NULL) { // this won't compile, but that's not the issue // parse one line of command output here. } 

此代码从使用popen("df")命令创建的FILE指针获取数据。 我正在尝试运行Linux命令并解析其输出以获取有关操作系统的信息。 以这种方式将缓冲区设置为任意大小是否有任何错误(或危险)?

C字符串缺少C ++对应的以下方面:

  • 自动内存管理:您必须手动分配和释放内存。
  • 连接效率的额外容量:C ++字符串的容量通常大于其大小。 这允许在没有很多重新分配的情况下增加大小。
  • 没有嵌入的NUL:根据定义,NUL字符结束C字符串; C ++字符串保留一个内部大小计数器,因此它们不需要特殊值来标记它们的结尾。
  • 明智的比较和赋值运算符:即使允许比较C字符串指针,它几乎总是不是预期的。 类似地,分配C字符串指针(或将它们传递给函数)会产生所有权歧义。

C字符串有一些缺点:

  1. 获得长度是一项相对昂贵的操作。
  2. 不允许嵌入的nul字符。
  3. 字符的签名是实现定义的。
  4. 字符集是实现定义的。
  5. char类型的大小是实现定义的。
  6. 必须分别跟踪每个字符串的分配方式,以及它必须如何被释放,或者即使它根本需要被释放。
  7. 无法将字符串切片称为另一个字符串。
  8. 字符串不是不可变的,这意味着它们必须单独同步。
  9. 字符串无法在编译时进行操作。
  10. 切换案例不能是字符串。
  11. C预处理器无法识别表达式中的字符串。
  12. 无法将字符串作为模板参数传递(C ++)。

在许多应用中,不能在恒定时间内访问长度是一个严重的开销。

您可能知道今天1024字节足以包含任何输入,但您不知道明天或明年的情况会如何变化。

如果过早优化是所有邪恶的根源,魔术数字就是干。

如果需要,内存管理等需要增长字符串(字符数组),有点无聊重新发明。

没有办法将NUL字符(如果你需要它们)嵌入到C样式字符串中。

好吧,要评论您的具体示例,您不知道调用df返回的数据是否适合您的缓冲区。 永远不要相信未经过传真的输入到您的应用程序中,即使它应该来自像df这样的已知来源。

例如,如果名为“df”的程序放置在搜索路径中的某个位置,以便执行它而不是系统df,则可以使用它来利用缓冲区限制。 或者如果df被恶意程序替换。

从文件读取输入时,请使用允许您指定要读取的最大字节数的函数。 在OSX和Linux下,fgets()实际上定义为char *fgets(char *s, int size, FILE *stream); 所以在这些系统上使用是安全的。

当你有一个字节数组而不是一串字符时,字符编码问题往往浮出水面。

在您的特定情况下,它不是危险的c字符串,而是将不确定数量的数据读入固定大小的缓冲区。 例如,不要使用gets(char *)。

看看你的例子,它似乎没有正确 – 试试这个:

 char buffer[1024]; char * line = NULL; while ((line = fgets(buffer, sizeof(buffer), fp)) != NULL) { // parse one line of command output here. } 

这是对c字符串的完全安全使用,尽管你必须处理line不包含整行的可能性,但是被截断为1023个字符(加上一个空终止符)。

我认为使用它们是可以的,人们已经使用它们多年了。 但是如果可能的话我宁愿使用std :: string因为1)你不必每次都这么谨慎并且可以考虑你的域的问题,而不是认为你每次都需要添加另一个参数…管理和那些东西…在更高层次上编码更安全…… 2)可能还有一些其他小问题并不重要但仍然……就像人们已经提到的那样…编码,unicode …所有那些“相关的”有点人们创建std :: string的想法…… 🙂

更新

我在一个项目上工作了半年。 不知怎的,我愚蠢到永远不会在发货前在发布模式下编译…. :)嗯…幸运的是,我发现3小时后我发现了一个错误。 这是一个非常简单的字符串缓冲区溢出。

如今,没有Unicode支持是足够的理由……

c字符串有滥用的机会,因为必须扫描字符串以确定它的结束位置。

strlen – 查找长度,扫描字符串,直到您点击NUL,或访问受保护的内存

strcat – 必须扫描才能找到NUL,以确定从哪里开始连接。 ac字符串中没有任何知识,无法判断是否存在缓冲区溢出。

c字符串有风险,但通常比字符串对象快。

Imho,cstrings最难点的是内存管理,因为如果你需要传递一个cstring的副本或者你可以将一个文字传递给一个函数,你需要小心。 函数是否会释放传递的字符串,或者它会为函数调用保留更长的引用。 这同样适用于cstring返回值。

因此,如果没有大的努力,就不可能共享cstring copys。 这在许多情况下以内存中相同cstring的不必要的copiess结束。

这个问题真的没有答案。
如果你用C语写你有什么选择吗?
如果你用C ++写作,为什么要问? 不使用C ++原语的原因是什么?
我能想到的唯一原因是:链接C和C ++代码并在接口中的某处使用char *。 它有时候很容易使用char *而不是一直进行转换(特别是如果它真的’好’的C ++代码有3种不同的C ++字符串对象类型)。

与C的许多其他方面一样,C字符串为您提供了充足的空间。 它们简单快速,但在可能违反空终结符等假设或输入可能超出缓冲区的情况下不安全。 为了可靠地做到这一点,你必须观察相当的卫生编码实践。

曾经有一种说法,高级语言的规范定义是“比C更好的字符串处理”。

另一个考虑因素是谁将维护您的代码? 两年怎么样? 那个人会像你一样对C-stlye琴弦感到舒服吗? 随着STL越来越成熟,似乎人们对STL字符串的使用会比使用C风格的字符串更加舒适。