C++中string和char[]类型的区别
Posted
技术标签:
【中文标题】C++中string和char[]类型的区别【英文标题】:Difference between string and char[] types in C++ 【发布时间】:2010-11-20 05:07:48 【问题描述】:我知道一点 C,现在我正在研究 C++。 我习惯于用 char 数组来处理 C 字符串,但是当我查看 C++ 代码时,我看到有使用字符串类型和 char 数组的示例:
#include <iostream>
#include <string>
using namespace std;
int main ()
string mystr;
cout << "What's your name? ";
getline (cin, mystr);
cout << "Hello " << mystr << ".\n";
cout << "What is your favorite team? ";
getline (cin, mystr);
cout << "I like " << mystr << " too!\n";
return 0;
和
#include <iostream>
using namespace std;
int main ()
char name[256], title[256];
cout << "Enter your name: ";
cin.getline (name,256);
cout << "Enter your favourite movie: ";
cin.getline (title,256);
cout << name << "'s favourite movie is " << title;
return 0;
(两个例子都来自http://www.cplusplus.com)
我想这是一个被广泛询问和回答(显而易见?)的问题,但如果有人能告诉我在 C++ 中处理字符串的两种方法之间的确切区别(性能、API 集成、方法每个都更好,...)。
谢谢。
【问题讨论】:
这可能会有所帮助:C++ char* vs std::string 【参考方案1】:好吧,字符串类型是一个完全托管的字符串类,而 char[] 仍然是 C 中的样子,一个字节数组,为你代表一个字符串。
就 API 和标准库而言,一切都是根据字符串而不是 char[] 实现的,但仍有许多来自 libc 的函数接收 char[],因此您可能需要将它用于这些函数,除了我会一直使用 std::string。
当然,就效率而言,非托管内存的原始缓冲区对于很多事情来说几乎总是更快,但考虑到比较字符串,例如,std::string 总是首先检查它的大小,而使用 char [] 你需要逐个字符比较。
【讨论】:
【参考方案2】:Arkaitz 是正确的,string
是托管类型。这对你意味着你永远不必担心字符串有多长,也不必担心释放或重新分配字符串的内存。
另一方面,上述案例中的char[]
表示法将字符缓冲区限制为正好256 个字符。如果您尝试将超过 256 个字符写入该缓冲区,那么您最多会覆盖您的程序“拥有”的其他内存。在最坏的情况下,您会尝试覆盖不属于您的内存,而您的操作系统会当场杀死您的程序。
底线?字符串对程序员更友好,char[] 对计算机来说效率更高。
【讨论】:
在最坏的情况下,其他人会覆盖内存并在您的计算机上运行恶意代码。另请参阅buffer overflow。【参考方案3】:我个人认为除了与旧代码兼容之外,没有任何理由愿意使用 char* 或 char[]。 std::string 不比使用 c-string 慢,除了它会为你处理重新分配。您可以在创建它时设置它的大小,从而避免在需要时重新分配。它的索引运算符 ([]) 提供了恒定的时间访问(并且在每个意义上都与使用 c 字符串索引器完全相同)。使用 at 方法还可以为您提供边界检查安全性,这是您使用 c-strings 无法获得的,除非您编写它。您的编译器通常会在发布模式下优化索引器的使用。很容易弄乱 c 字符串;诸如 delete vs delete[]、异常安全,甚至如何重新分配 c-string。
当您必须处理高级概念(例如拥有 COW 字符串和用于 MT 的非 COW 等)时,您将需要 std::string。
如果您担心副本,只要您尽可能使用引用和 const 引用,您就不会因副本而产生任何开销,这与您使用 c-string 所做的事情相同。
【讨论】:
+1 虽然你没有考虑 DLL 兼容性等实现问题,但你得到了 COW。 我知道我的 char 数组是 12 个字节怎么办?如果我为此实例化一个字符串,它可能效率不高,对吧? @David:如果您的代码对性能非常敏感,那么可以。除了初始化 std::string 成员之外,您可能会将 std::string ctor 调用视为开销。但请记住,过早的优化使许多代码库不必要地变成了 C 风格,所以要小心。【参考方案4】:char 数组就是这样 - 一个字符数组:
如果在堆栈上分配(如您的示例中),它将始终占用例如。 256 字节,无论它包含多长的文本 如果在堆上分配(使用 malloc() 或 new char[]),您将负责随后释放内存,并且您将始终承担堆分配的开销。 如果将超过 256 个字符的文本复制到数组中,它可能会崩溃、产生丑陋的断言消息或在程序的其他地方导致无法解释的(错误)行为。 要确定文本的长度,必须逐个字符扫描数组以查找 \0 字符。字符串是一个包含 char 数组的类,但会自动为您管理它。大多数字符串实现都有一个 16 个字符的内置数组(因此短字符串不会使堆碎片化),并将堆用于较长的字符串。
您可以像这样访问字符串的 char 数组:
std::string myString = "Hello World";
const char *myStringChars = myString.c_str();
C++ 字符串可以包含嵌入的 \0 字符,无需计数即可知道它们的长度,比用于短文本的堆分配字符数组更快,并保护您免受缓冲区溢出。此外,它们更具可读性和更易于使用。
但是,C++ 字符串并不(非常)适合跨 DLL 边界使用,因为这将要求此类 DLL 函数的任何用户确保他使用完全相同的编译器和 C++ 运行时实现,以免他的字符串类冒险行为不同。
通常,字符串类也会在调用堆上释放其堆内存,因此只有在您使用运行时的共享(.dll 或 .so)版本时才能再次释放内存。
简而言之:在所有内部函数和方法中使用 C++ 字符串。如果您曾经编写过 .dll 或 .so,请在您的公共(dll/so-exposed)函数中使用 C 字符串。
【讨论】:
此外,字符串还有一堆非常简洁的辅助函数。 我不相信关于 DLL 边界的说法。在非常特殊的情况下,它可能会中断((一个 DLL 静态链接到与其他 DLL 使用的不同版本的运行时),并且在这些情况下可能首先发生更糟糕的事情)但在一般情况下,每个人都使用默认值标准运行时的共享版本(默认)不会发生这种情况。 示例:您分发一个名为 libfoo 的公共库的 VC2008SP1 编译的二进制文件,该库在其公共 API 中有一个 std::string &。现在有人下载了您的 libfoo.dll 并进行了调试构建。他的 std::string 中很可能有一些额外的调试字段,导致动态字符串指针的偏移量移动。 示例 2:2010 年,有人下载了您的 libfoo.dll 并在他的 VC2010 构建的应用程序中使用它。他的代码加载 MSVCP100.dll 而你的 libfoo.dll 仍然加载 MSVCP90.dll -> 你得到两个堆 -> 内存无法释放,如果 libfoo 修改了字符串引用并将 std::string 传递给新的字符串引用,则在调试模式下断言错误指针返回。 我只会坚持“简而言之:在所有内部函数和方法中使用 C++ 字符串。”试图理解你的例子使我的大脑弹出。【参考方案5】:将 (char *) 视为 string.begin()。本质区别在于 (char *) 是一个迭代器,而 std::string 是一个容器。如果您坚持使用基本字符串,则 (char *) 将为您提供 std::string::iterator 的功能。当您想要迭代器的好处以及与 C 的兼容性时,您可以使用 (char *),但这是例外而不是规则。与往常一样,请注意迭代器失效。当人们说 (char *) 不安全时,这就是他们的意思。它与任何其他 C++ 迭代器一样安全。
【讨论】:
【参考方案6】:字符串具有辅助函数并自动管理字符数组。您可以连接字符串,对于 char 数组,您需要将其复制到新数组中,字符串可以在运行时更改其长度。 char 数组比字符串更难管理,并且某些函数可能只接受字符串作为输入,需要您将数组转换为字符串。最好使用字符串,它们是为了您不必使用数组而制作的。如果数组客观上更好,我们就不会有字符串了。
【讨论】:
【参考方案7】:其中一个区别是空终止 (\0)。
在 C 和 C++ 中,char* 或 char[] 将指向单个 char 的指针作为参数,并沿内存跟踪,直到达到 0 内存值(通常称为空终止符)。
C++ 字符串可以包含嵌入的 \0 字符,知道它们的长度而不用计算。
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
void NullTerminatedString(string str)
int NUll_term = 3;
str[NUll_term] = '\0'; // specific character is kept as NULL in string
cout << str << endl <<endl <<endl;
void NullTerminatedChar(char *str)
int NUll_term = 3;
str[NUll_term] = 0; // from specific, all the character are removed
cout << str << endl;
int main()
string str = "Feels Happy";
printf("string = %s\n", str.c_str());
printf("strlen = %d\n", strlen(str.c_str()));
printf("size = %d\n", str.size());
printf("sizeof = %d\n", sizeof(str)); // sizeof std::string class and compiler dependent
NullTerminatedString(str);
char str1[12] = "Feels Happy";
printf("char[] = %s\n", str1);
printf("strlen = %d\n", strlen(str1));
printf("sizeof = %d\n", sizeof(str1)); // sizeof char array
NullTerminatedChar(str1);
return 0;
输出:
strlen = 11
size = 11
sizeof = 32
Fee s Happy
strlen = 11
sizeof = 12
Fee
【讨论】:
"从具体来说,所有字符都被删除了"不,它们没有被"删除",打印一个字符指针只打印到空终止符。 (因为这是 char* 知道结尾的唯一方法)字符串类知道完整大小本身,所以它只是使用它。如果您知道 char* 的大小,您也可以自己打印/使用所有字符。以上是关于C++中string和char[]类型的区别的主要内容,如果未能解决你的问题,请参考以下文章