为什么在C++中你更喜欢使用char*而不是string?

18

我是一名C程序员,尝试编写C++代码。听说C++中的string相较于char*在安全性、性能等方面更优秀,但有时候似乎char*是一个更好的选择。有人建议程序员在C++中不应使用char*,因为我们可以使用string完成char*的所有功能,并且更加安全和快速。

您曾经在C++中使用过char*吗?在具体的什么情况下使用呢?

12个回答

35

使用 std::string 更加安全,因为您不需要担心为字符串分配/释放内存。C++ 的 std::string 类可能在内部使用 char* 数组。然而,该类将为您管理内部数组的分配、重新分配和释放。这消除了使用原始指针所伴随的所有常见风险,如内存泄漏、缓冲区溢出等。

此外,它也非常方便。您可以复制字符串、追加字符串等,而无需手动提供缓冲区空间或使用如strcpy/strcat之类的函数。使用 std::string 就像使用=+运算符一样简单。

基本上,就是:

 std::string s1 = "Hello ";
 std::string s2 = s1 + "World";

对比...

 const char* s1 = "Hello";
 char s2[1024]; // How much should I really even allocate here?
 strcpy(s2, s1);
 strcat(s2, " World ");

编辑:

针对您关于在C++中使用char*的编辑:许多C++程序员会认为,除非您正在使用某些需要它的API /遗留函数,否则您永远不应该使用char*,在这种情况下,您可以使用std::string :: c_str()函数将std::string转换为const char *

但是,我认为在C ++中有一些合法使用C数组的方法。例如,如果性能绝对关键,则堆栈上的小型C数组可能比std::string更好。您还可能编写需要绝对控制内存分配/释放的程序,在这种情况下,您将使用char*。 此外,正如评论部分所指出的那样,std::string不能保证为您提供可写连续的缓冲区*,因此,如果您的程序需要完全可移植,您无法直接从文件中写入std::string。但是,如果您需要这样做,std::vector仍然可能比使用原始C数组更可取。


* 虽然在C++11中已更改了这一点,以便std::string提供连续的缓冲区


17
我认为调用函数(std::string::c_str())是相当简单的事情。 - sbi
2
std::string 使得获取可写缓冲区变得困难甚至不可能,这是一个问题——你必须依赖于实现细节(对于标准是否实际要求 &s[0] 像 vector 那样工作,这个问题有两种解释),或者将另一个容器(vector 或 char 数组)与字符串混合使用。 - Roger Pate
3
使用char*代替string的一个简单原因是,如果你需要避免动态内存分配(例如在中断处理程序或嵌入式系统中),这样做就比较方便。 - Adisak
3
我认为所有的例外情况都可以总结为,如果你知道必须使用char*,那就使用它。如果没有充分的理由(而大多数时候你并没有),则使用std::string。 - KeithB
2
另一个偏好使用char而不是std::string的具体情况是需要静态字符串常量的时候。如果有静态的std::string实例(在cpp文件中或作为静态类成员),那么可能会遇到初始化顺序问题。使用原始的char可以避免这个问题。 - the_mandrill
显示剩余11条评论

6

好的,自我回答以来问题已经有了很大变化。

与std::string相比,本地字符数组在内存管理和缓冲区溢出方面是一场噩梦。 我总是更喜欢使用std::string。

尽管在某些情况下由于性能限制(尽管std::string在某些情况下可能实际上更快 - 先测量!)或在嵌入式环境中禁止使用动态内存等原因,字符数组可能是更好的选择。


2
总的来说,std::string是一种更清晰、更安全的选择,因为它将内存管理的负担从程序员身上卸下。它比char *更快的主要原因在于,std::string存储了字符串的长度。因此,每次进行复制、追加等操作时,你不必通过遍历整个字符数组寻找终止的NULL字符来完成工作。
尽管如此,你仍然会发现很多c++程序同时使用std::string和char *,或者甚至从头开始编写自己的字符串类。在旧的编译器中,std::string会占用大量内存,并且不一定比其他方法更快。随着时间的推移,这种情况已经有所改善,但是一些高性能应用程序(例如游戏和服务器)仍然可以从手动调整的字符串操作和内存管理中受益。
我建议首先使用std::string,或者可能创建一个具有更多实用函数(例如starts_with()、split()、format()等)的包装器。如果在对代码进行基准测试时发现字符串操作是瓶颈,或者使用了太多内存,那么你可以决定是否愿意接受自定义字符串库所带来的额外风险和测试。
提示:解决内存问题且仍使用std::string的一种方法是使用嵌入式数据库,例如SQLite。这在生成和操作极大的字符串列表时特别有用,并且性能比您预期的要好。

1

C语言的char *字符串不能包含'\0'字符。C++的string可以轻松处理空字符。如果用户输入包含\0的字符串,而你使用C字符串,你的代码可能会失败。这也存在安全问题。


3
在C和C++中,char*字符串指向普通的内存数组。如果你愿意,你可以在它们中间插入额外的'\0'字符。当解析文件系统路径等内容时,一个常见的性能技巧是通过用'\0'替换字符来临时“截断”字符串。另一个常见的用途是使用'\0'作为分隔符,在单个数组中表示字符串列表,并在末尾放置一个双'\0'。 - kgriffs
1
更准确地说,C字符串函数(如strcpy等)无法处理'\0',因为它们将其用作字符串结束标记。如果您愿意编写自己的函数,char*可以很好地包含'\0'。 - KeithB
我从未见过有人使用字符 '\0' - phuclv
C的char字符串只是指向原始内存的指针。至于字符'\0',我曾经看到它被用在一个内存区域中有多个字符串的情况下。你可以用两个'\0'来结束这些多个字符串。原因是与多个指针列表相比,它节省了内存。话虽如此,这种情况很少见。 - rxantos

1

std::string 的实现将内存使用信息隐藏起来,如果你在编写性能关键的代码,或者确实需要担心内存碎片问题,那么使用 char* 可以避免很多麻烦。

但对于其他情况来说,std::string 将所有这些信息都隐藏起来,使得它更加易用。


0

从性能角度来看,std::string实际上可能更好。此外,还有无数其他原因 - 安全性、内存管理、方便的字符串函数,使std::string成为一个无限好的选择。

编辑:要了解为什么std::string可能更有效率,请阅读Herb Sutter的书籍 - 他讨论了一种将惰性初始化与引用相结合来内部实现字符串的方法。


如果您能提供有关字符串更高效的详细信息,我会点赞。只需要摘要/结论,如果我想了解详情,我可以去查阅书籍。 - deft_code
我认为他指的是写时复制语义,但由于多线程,这实际上是一个不好的想法。 - Charles Salvia
如果你指的是写时复制(copy-on-write), 那么时间已经证明了它的价值,最终似乎copy-on-write并不会提高性能,超过当前早期的复制实现。写时复制在多线程方面存在许多问题,解决方案需要独占锁定,而这种代价比写时复制带来的好处更大。 - David Rodríguez - dribeas
1
一些更有效率的事情:在std::string中获取字符串大小是一个常数时间操作,但在const char*中,它是线性时间操作,因此任何需要计算字符串大小的操作都是如此。 - David Rodríguez - dribeas
@Charles、Dribeas、Caspin - 我所指的是写时复制语义,我理解你们的观点(尽管我需要权衡利弊)。谢谢。 - Fox
字符串在一个 size_t 元素中保存其大小,因此不需要遍历整个字符串来查找其长度。这样更有效率。然而它确实使用构造函数/析构函数并且总是使用堆。因此有时候谦逊的 C 字符串会更加高效。 - rxantos

0

使用 std::string,因为它具有令人难以置信的便利性——自动内存处理和方法/运算符。通过一些字符串操作,大多数实现都会进行优化(例如延迟评估几个后续操作——节省内存复制)。

如果您需要依赖于内存中特定的字符布局进行其他优化,请尝试使用 std::vector<char>。如果您有一个非空向量 vec,则可以使用 &vec[0] 获取 char* 指针(向量必须是非空的)。


在这种情况下,我还建议明确包装字符串函数以处理空值分隔符 - 只设置字符向量不会将其分隔。 - Fox

0
简短回答,我不使用C字符串。唯一的例外是当我使用需要它们的第三方库时。在这些情况下,我尽量坚持使用std::string::c_str()。

-1
在我的整个职业生涯中,我只有在两个项目中有机会使用std::string。其他所有项目都有自己的字符串类 :)
话虽如此,对于新代码,我通常在可以的情况下使用std::string,除了模块边界(由dlls /共享库导出的函数),在这些地方我倾向于暴露C接口并远离C ++类型和编译器之间的二进制不兼容性问题以及std库实现。

大多数库已经开发了很长时间,并提供了自己的字符串函数。其中大部分(至少是那些得到良好维护的)都提供了一种将其转换为std::string和从std::string转换的方法。即使它们没有,使用c_str()函数将std::string转换为c-string在接口点可能是一个好主意。我要冒险说,将每个供应商API封装在自己的API中-这样可以更容易地在库之间进行转换,并远离过时的库(即那些甚至没有时间编写支持std::string的代码)。 - Fox

-2

比较并对比以下C和C++示例:

strlen(infinitelengthstring)

对比

string.length()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接