C ++中字符串和char []类型之间的区别


126

我了解一点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
Wael Dalloul

Answers:


187

一个char数组就是这样-一个字符数组:

  • 如果分配在堆栈上(例如您的示例),它将始终占据例如。256个字节,无论文本包含多长时间
  • 如果在堆上分配(使用malloc()或new char []),则您有责任在以后释放内存,并且总会有堆分配的开销。
  • 如果将超过256个字符的文本复制到数组中,则可能会崩溃,产生难看的声明消息或在程序中的其他地方导致无法解释的(错误)行为。
  • 要确定文本的长度,必须逐字符扫描数组以查找\ 0字符。

字符串是一个包含char数组的类,但会自动为您管理。大多数字符串实现都有16个字符的内置数组(因此,短字符串不会使堆碎片化),而将堆用于更长的字符串。

您可以像这样访问字符串的char数组:

std::string myString = "Hello World";
const char *myStringChars = myString.c_str();

C ++字符串可以包含嵌入的\ 0字符,无需计数即可知道它们的长度,比短堆的char数组(用于短文本)要快,并且可以防止缓冲区溢出。另外,它们更具可读性,更易于使用。


但是,C ++字符串不(非常)适合跨DLL边界使用,因为这将要求DLL函数的任何用户确保他使用的是完全相同的编译器和C ++运行时实现,以免冒冒其字符串类的行为不同的风险。

通常,字符串类还会在调用堆上释放其堆内存,因此,只有在使用共享(.dll或.so)运行时版本时,它才能再次释放内存。

简而言之:在所有内部函数和方法中使用C ++字符串。如果曾经编写.dll或.so,请在公共(dll / so-暴露)函数中使用C字符串。


4
此外,字符串还具有许多辅助函数,这些函数确实很整洁。
哈康

1
我不相信DLL边界。在非常特殊的情况下,它可能会崩溃((一个DLL静态链接到与其他DLL使用的版本不同的运行时版本),在这些情况下可能会首先发生更糟的事情),但通常情况下每个人都使用默认值标准运行时的共享版本(默认)将不会发生。
马丁·约克

2
示例:您分发了名为libfoo的公共库的VC2008SP1编译的二进制文件,该库在其公共API中具有std :: string&。现在,有人会下载您的libfoo.dll并进行调试。他的std :: string很可能在其中包含一些其他调试字段,从而导致动态字符串的指针偏移发生移动。
Cygon

2
示例2:在2010年,有人下载了您的libfoo.dll,并将其用于他的VC2010构建的应用程序中。他的代码加载了MSVCP100.dll,而您的libfoo.dll仍然加载了MSVCP90.dll->您将获得两个堆->无法释放内存,如果libfoo修改了字符串引用并使用新的std :: string,则在调试模式下会出现断言错误指针向后。
Cygon

1
我只是坚持“总之:在所有内部函数和方法中使用C ++字符串”。试图理解你的例子使我大吃一惊。
斯蒂芬,2010年

12

Arkaitz是正确的,它string是托管类型。这对意味着什么,您不必担心字符串的长度,也不必担心释放或重新分配字符串的内存。

另一方面,上述char[]情况下的符号将字符缓冲区限制为正好为256个字符。如果试图在该缓冲区中写入256个以上的字符,则充其量只能覆盖程序“拥有”的其他内存。最坏的情况是,您将尝试覆盖您不拥有的内存,并且操作系统将当场杀死您的程序。

底线?字符串对程序员更友好,而char []对于计算机则效率更高。


4
最糟糕的是,其他人会覆盖内存并在您的计算机上运行恶意代码。另请参见缓冲区溢出
大卫·约翰斯通

6

好吧,字符串类型是字符串的完全托管类,而char []仍然是C语言中的样子,C语言是一个为您表示字符串的字节数组。

在API和标准库方面,所有内容都是以字符串而不是char []的形式实现的,但是libc中仍然有很多函数可以接收char [],因此您可能需要对这些函数使用它,除此之外,始终使用std :: string。

当然,在效率方面,非托管内存的原始缓冲区在很多情况下几乎总是更快,但是考虑到比较字符串,例如std :: string始终具有首先检查它的大小,而使用char []时需要逐个字符地比较。


5

我个人没有看到任何理由要使用char *或char [],除了与旧代码兼容。std :: string不比使用c-string慢,除了它将为您处理重新分配。您可以在创建时设置其大小,从而避免重新分配。它的索引运算符([])提供恒定的时间访问(并且在每个词义上都与使用c字符串索引器完全相同)。使用at方法还可以为您提供边界检查的安全性,除非您编写了c字符串,否则您将无法使用它。您的编译器通常会在释放模式下优化索引器的使用。C弦很容易弄乱。诸如delete vs delete []之类的东西,异常安全性,甚至如何重新分配c字符串。

而且,当您需要处理高级概念(例如具有COW字符串和MT的非COW等)时,将需要std :: string。

如果您担心副本,则只要可以使用引用和const引用,就不会因副本而产生任何开销,这与处理c字符串一样。


+1尽管您没有考虑DLL兼容性之类的实现问题,但您得到了COW。

我知道12字节的char数组怎么办?如果我为此实例化一个字符串,那可能不是很有效,对吗?
大卫·天宇黄

@David:如果您对性能非常敏感,那么可以。除了初始化std :: string成员之外,您还可以将std :: string ctor调用视为开销。但是请记住,过早的优化使很多代码库不必要地采用了C样式,因此请当心。
阿布耶

1

字符串具有辅助功能,并自动管理char数组。您可以连接字符串,对于char数组,您需要将其复制到新数组,字符串可以在运行时更改其长度。char数组比字符串更难管理,某些函数可能只接受字符串作为输入,因此需要将数组转换为字符串。最好使用字符串,因为它们是由字符串制成的,因此您不必使用数组。如果数组在客观上更好,我们将没有字符串。


0

将(char *)视为string.begin()。本质上的区别是(char *)是一个迭代器,而std :: string是一个容器。如果您坚持使用基本字符串,则(char *)将为您提供std :: string :: iterator的功能。当需要迭代器的好处以及与C的兼容性时,可以使用(char *),但这是例外,而不是规则。与往常一样,请注意迭代器无效。当人们说(char *)不安全时,这就是他们的意思。它与任何其他C ++迭代器一样安全。


0

区别之一是Null终止(\ 0)。

在C和C ++中,char *或char []将使用指向单个char的指针作为参数,并将沿着内存进行跟踪,直到达到0的内存值(通常称为null终止符)为止。

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 *知道结尾的唯一方法),字符串类本身知道完整大小,因此只使用它。如果您知道char *的大小,也可以自己打印/使用所有chars。
水坑
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.