C ++ Primer说
对于大多数应用程序,除了更安全之外,它还更多 高效使用库字符串而不是C风格的字符串
了解安全。为什么C ++字符串库更有效?毕竟,在它之下,是不是字符串仍然表示为字符数组?
为了澄清,作者是否谈论程序员效率(理解)或处理效率?
答案 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 memmem
比strstr
快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终结符隐式定义长度。
这意味着连续strcat
到char[]
的循环实际上正在执行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字符串的代码中的脆弱性的最实用的方法是非常保守地设计它。记录最大输入数据大小,截断任何太大的内容,并为所有内容使用大缓冲区。可行,但效率不高。
相比之下,如果使用面向对象的字符串类型,他们使用的分配模式可能不是最优的,但可能会比“分配一切大”的方法更好。因此,它们将手工优化代码方法的大部分运行时效率与“分配一切大”方法的安全性结合起来。