C-String与C++字符串的效率

Jam*_*ard 35 c++ string

C++ Primer说

对于大多数应用程序,除了更安全之外,使用库字符串而不是C样式字符串也更有效

安全是理解的.为什么C++字符串库更有效?毕竟,在它下面,是不是字符串仍然表示为字符数组?

为了澄清,作者是否谈论程序员效率(理解)或处理效率?

Leo*_*sky 26

C字符串通常更快,因为它们不调用malloc/new.但有些情况std::string会更快.函数strlen()是O(N),但是std::string::size()是O(1).

此外,当您搜索子字符串时,您需要'\0'在每个周期检查C字符串中std::string- 您没有.在一个天真的子串搜索算法中它并不重要,因为而不是检查'\0'你需要检查i<s.size().但现代高性能子串搜索算法以多字节步骤遍历字符串.并且需要'\0'检查每个字节会减慢它们的速度.这就是GLIBC memmem快了x2倍的原因strstr.我做了很多子串算法的基准测试.

这不仅适用于子串搜索算法.对于零终止字符串,许多其他字符串处理算法较慢.

  • C字符串以哪种方式不需要调用`malloc/new`?无论何时你想要一个动态大小的字符串,你需要动态分配的内存,这适用于C字符串以及`std :: string`s.除此之外,我认为搜索子字符串是一种算法,C字符串和`std :: string`s之间不应该有区别.再次选择了一个略微错误的50%,但是好吧,这是詹姆斯选择接受的答案. (19认同)
  • 好吧,当然指针不会自行分配.但是c字符串通常不会***"不调用malloc/new"*,因为使用简单的编译时数组不是*通常的方式来处理字符串,特别是考虑到任何字符串处理算法.当然你可以随时抛出一个`char [1024]`并称之为一天,但很好.但你是对的,在某些情况下,一个小的char数组是合适的,并且不需要任何动态分配,但我会把这些情况称为远离平常,特别是在进行精细的字符串处理时. (13认同)
  • 您可以将c-string(或几乎任何其他类型)放入动态分配的内存中,但您不必这样做.如果你动态分配C字符串 - 你将这样做,而不是C字符串的成员函数.C-string(作为int,double或任何POD类型)不会将自己称为malloc/new. (3认同)
  • @ChristianRau - 你需要重读我的帖子.你有没有机会对c-string和std :: string子串函数进行基准测试?我做到了. (2认同)
  • 不,我没有进行分析,只是在考虑搜索子字符串的时候我不一定看到每次计算长度的必要性,尽管我可能在那里错了.你知道我知道O(1)长度是主要的优点(这也是其他正确答案所写的).但即使在重读之后,我也看不出C字符串*"不要调用malloc/new"*,这只是垃圾,而我的主要批评点在你的答案中. (2认同)

Naw*_*waz 25

为什么C++字符串库更有效?毕竟,在它下面,是不是字符串仍然表示为字符数组?

因为如果不仔细写,使用char*char[]更可能无效的代码.例如,你看过这样的循环:

char *get_data();

char const *s = get_data(); 

for(size_t i = 0 ; i < strlen(s) ; ++i) //Is it efficent loop? No.
{
   //do something
}
Run Code Online (Sandbox Code Playgroud)

这有效吗?不是.时间复杂度strlen()O(N),并且在上面的代码中,在每次迭代中计算它.

现在你可以说"如果我strlen()只召唤一次,我可以提高效率." .当然可以.但是你必须自己和有意识地做所有那种优化.如果你遗漏了什么,你错过了CPU周期.但是std::string,许多这样的优化是由类本身完成的.所以你可以这样写:

std::string get_data();

std::string const & s = get_data(); //avoid copy if you don't need  it

for(size_t i = 0 ; i < s.size() ; ++i) //Is it efficent loop? Yes.
{
   //do something
}
Run Code Online (Sandbox Code Playgroud)

这有效吗?是.时间复杂度size()O(1).无需手动优化它,这通常会使代码看起来丑陋且难以阅读.与之std::string相比,生成的代码几乎总是整洁干净char*.

另请注意,std::string这不仅可以使您的代码在CPU周期方面有所作为,而且还可以提高程序员的工作效率!

  • 迭代C字符串的常用方法是:`for(int i = 0; s [i]; ++ i)`,这是有效的.参数应该是关于存储字符串大小的字符串类,使其在O(1)中可用. (3认同)
  • @DanielFleischman:你误解了我的回答.我给出了这个例子,首先询问*"你看过这样的循环吗?"*,然后添加"O(N)vs O(1)",解释其余答案中的其他指针.(所以我不认为downvote是合理的). (3认同)
  • @Nawaz:在gcc的C标准库的实现中,`strlen`被标记了一个属性,该属性指示编译器函数调用的结果仅取决于参数(除返回值之外没有副作用).这允许编译器从循环中分解出`strlen`.话虽如此,那就是*实现的质量*,并且在其他编译器/库实现上没有优化. (3认同)
  • @DavidRodríguez-dribeas这取决于编译器的质量,而不是依赖于这种优化的东西 (3认同)

Joh*_*eek 7

这里一些情况,其中一个std::string可能击败char[].例如,C风格的字符串通常没有传递明确的长度 - 而是NUL终结符隐式定义长度.

这意味着一个连续strcat进入a 的循环char[]实际上正在执行O(n²)工作,因为每个循环strcat都必须处理整个字符串以确定插入点.相比之下,std::string需要执行以连接到字符串末尾的唯一工作是复制新字符(并可能重新分配存储 - 但为了比较公平,您必须事先知道最大大小和reserve()它) .


Jon*_*ely 7

A std::string知道它的长度,这使得许多操作更快.

例如,给定:

const char* c1 = "Hello, world!";
const char* c2 = "Hello, world plus dog!";
std::string s1 = c1;
std::string s2 = c2;
Run Code Online (Sandbox Code Playgroud)

strlen(c1)比...慢s1.length().对于比较,strcmp(c1, c2)必须比较几个字符以确定字符串不相等,但s1 == s2可以告诉长度不一样并立即返回false.

其他操作也受益于提前知道长度,例如strcat(buf, c1)必须找到空终止符buf以查找附加数据的位置,但是s1 += s2知道s1已经的长度并且可以立即将新字符附加到正确的位置.

在内存管理方面,std::string每次增长时都会分配额外的空间,这意味着未来的附加操作不需要重新分配.