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

时间:2012-08-25 17:44:29

标签: c++ string

C ++ Primer说

  

对于大多数应用程序,除了更安全之外,它还更多   高效使用库字符串而不是C风格的字符串

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

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

8 个答案:

答案 0 :(得分:26)

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

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

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

答案 1 :(得分:24)

  

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

因为如果不仔细编写,使用char*char[]的代码更有可能是无效的。例如,您是否看过 loop ,如下所示:

char *get_data();

char const *s = get_data(); 

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

效率这么高吗?不。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
}

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

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

答案 2 :(得分:7)

某些情况下std::string可能超过char[]。例如,C风格的字符串通常没有传递明确的长度 - 而是NUL终结符隐式定义长度。

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

答案 3 :(得分:7)

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

例如,给定:

const char* c1 = "Hello, world!";
const char* c2 = "Hello, world plus dog!";
std::string s1 = c1;
std::string s2 = c2;

strlen(c1)s1.length()慢。为了进行比较,strcmp(c1, c2)必须比较几个字符以确定字符串不相等,但s1 == s2可以判断长度不相同并立即返回false。

其他操作也可以通过提前知道长度来获益,例如: strcat(buf, c1)必须在buf中找到空终止符才能找到附加数据的位置,但s1 += s2已知道s1的长度,并且可以在正确的位置添加新字符立即

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

答案 4 :(得分:3)

字符串是包含字符数组及其大小和其他功能的对象。最好使用字符串库中的字符串,因为它们可以避免分配和释放内存,查找内存泄漏和其他指针危险。但由于字符串是对象,因此它们在内存中占用了额外的空间。

Cstrings只是字符数组。当你实时工作时,它们应该被使用;当你完全不知道你手上有多少内存空间时。如果您正在使用cstrings,则必须注意内存分配,然后通过strcpy或逐个字符将数据复制到其中,然后在使用后取消分配等。 因此,如果你想避免一堆头痛,最好使用字符串库中的字符串。

字符串提高了程序效率但降低了处理效率(尽管不一定)。反之亦然是cstrings

答案 5 :(得分:3)

嗯,一个明显而简单的事情是它们实际上更有效(关于运行时)是,它们将字符串的长度与数据一起存储(或者至少它们的size方法必须是O(1 ),几乎相同)。

因此,无论何时您需要在C字符串中找到NUL字符(并因此遍历整个字符串一次),您就可以在恒定时间内获得大小。这种情况发生了很多,例如复制或连接字符串时,事先分配一个新的字符串,你需要知道它的大小。

但我不知道这是作者的意思,还是它在实践中有很大的不同,但它仍然是一个有效的观点。

答案 6 :(得分:2)

这是一个简短的观点。

首先,C ++字符串是对象,因此在面向对象语言中使用它们更加一致。

然后,标准库为字符串,迭代器等提供了许多有用的功能。所有这些东西都是你不必再编码的东西,所以你得到时间并且你确定这段代码是(几乎没有问题。

最后,C字符串是指针,当你是新手时很难理解它们,并且它们带来了复杂性。由于引用比C ++中的指针更受欢迎,因此使用std :: string而不是C string会更加明显。

希望我帮助过。

答案 7 :(得分:1)

C风格字符串的难点在于,除非人们知道包含它们的数据结构,否则实际上不能对它们做很多事情。例如,当使用“strcpy”时,必须知道目标缓冲区是可写的,并且有足够的空间容纳源中第一个零字节的所有内容(当然,在很多情况下,实际上并非如此)知道这一点......)。很少有库例程为按需分配空间提供任何支持,我认为所有那些通过无条件地分配它来工作(所以如果有一个缓冲区的空间为1000字节,并且一个想要复制一个900字节的字符串,代码使用这些方法将不得不放弃1000字节的缓冲区,然后创建一个新的900字节缓冲区,即使重用1000字节缓冲区可能更好。)

在许多情况下,使用面向对象的字符串类型不如使用标准C字符串那样高效,而是找出分配和重用事物的最佳方法。另一方面,为最佳地分配和重用字符串而编写的代码可能非常脆弱,并且对需求的轻微更改可能需要对代码进行大量棘手的小调整 - 未能完美地调整代码可能会导致错误可能是明显的,严重的,或微妙的,但更严重的。避免使用标准C字符串的代码中的脆弱性的最实用的方法是非常保守地设计它。记录最大输入数据大小,截断任何太大的内容,并为所有内容使用大缓冲区。可行,但效率不高。

相比之下,如果使用面向对象的字符串类型,他们使用的分配模式可能不是最优的,但可能会比“分配一切大”的方法更好。因此,它们将手工优化代码方法的大部分运行时效率与“分配一切大”方法的安全性结合起来。