C++中的char*与std::string区别

88

在C++中管理字符数组时,何时应使用std::string,何时应使用char*

如果性能(速度)至关重要且你愿意因内存管理而承担一定的风险,那么似乎应该使用char*

还有其他需要考虑的情况吗?

12个回答

61

如果 std::string 很大,为避免复制,可以通过引用或实例的指针传递,因此我认为使用 char 指针没有什么优势。

对于任何实际文本,我都使用 std::string / wstring。但是 char * 对于其他类型的数据很有用,并且您可以确保它像应该一样被释放。否则,std::vector<char> 是最好的选择。

所有这些可能都有例外情况。


8
这两个之间有性能差异吗? - vtd-xml-author
3
@vtd-xml-author:有些可能是这样。直接使用char *几乎没有额外的开销。std::string具体有多大的开销我不确定,这可能取决于具体实现。我很难想象其开销会比裸指针更大。由于我没有标准库的副本,因此无法详细说明标准所做的任何保证。性能差异可能会因要执行的操作而异。 std::string::size 可以将大小存储在字符数据旁边,因此比 strlen 更快。 - Skurmedel
2
为什么不能在非文本数据中使用std::string?它们没有以null结尾,因此您应该能够在其中存储任何想要的内容。 - Casey Rodarmor
1
@rodarmor 你确实可以存储任何你想要的东西,不过这有点危险,因为字符串是为以 null 结尾的字符串设计的。 你必须小心使用只有二进制安全的操作,例如使用 append(const string&)append(const char*, size_t) 而不是 operator+=() - boycy
6
确定吗?我知道许多操作会假定char*是以空字符结尾的字符串,但我想不出任何一个假定std::string不包含null字符的操作。 - Casey Rodarmor
我们真的应该将(char *)与std::string::iterator进行比较吗?(char *) vs. std::string就像ranges vs. containers的论点一样,但有所不同。 - Samuel Danielson

61

我的观点是:

  • 如果你不调用"C"代码,永远不要使用char *。
  • 始终使用std::string:它更容易,更友好,更优化,更标准,可以防止出现错误,已经得到检查并被证明有效。

14

原始字符串的使用

是的,有时确实可以这样做。当使用const char *、在栈上分配的char数组和字符串字面量时,你可以以一种不需要任何内存分配的方式来完成。

编写这样的代码通常需要比使用string或vector更多的思考和关注,但是通过适当的技术,它是可以完成的。使用适当的技术,代码可以很安全,但你总是需要确保在将字符复制到char []中时,你要么对要复制的字符串长度有一些保证,要么检查并优雅地处理过长的字符串。不这样做就是给strcpy函数族带来不安全声誉的原因。

如何使用模板来帮助编写安全的char缓冲区

至于char []缓冲区的安全性,模板可以帮助解决问题,因为它们可以为你创建一个封装来处理缓冲区大小。例如,微软已经实现了此类模板,以提供strcpy的安全替代品。以下示例摘自我的代码,实际代码具有更多方法,但这应该足以传达基本思想:

template <int Size>
class BString
{
  char _data[Size];

  public:
  BString()
  {
    _data[0]=0;
    // note: last character will always stay zero
    // if not, overflow occurred
    // all constructors should contain last element initialization
    // so that it can be verified during destruction
    _data[Size-1]=0;
  }
  const BString &operator = (const char *src)
  {
    strncpy(_data,src,Size-1);
    return *this;
  }

  operator const char *() const {return _data;}
};

//! overloads that make conversion of C code easier 
template <int Size>
inline const BString<Size> & strcpy(BString<Size> &dst, const char *src)
{
  return dst = src;
}

1
使用const char*、在栈上分配的char数组和字符串字面量时,您可以以这种方式完成它,从而完全不需要内存分配。人们常常忘记,栈上的“分配”比堆快得多。+1 - NoSenseEtAl
char*类型的字符串并不总是在堆栈中。 char *str = (char*)malloc(1024); str[1024] = 0; - Cole Tobin
@ColeJohnson 我并不是在声称这一点,我只是想说如果你想让你的字符串被堆栈分配,你需要使用const char *与字符串字面值一起使用,而不是std::string。 - Suma

9
你必须使用char*而不是std::string的一个场景是需要使用静态字符串常量。原因是您无法控制模块初始化其静态变量的顺序,而来自另一个模块的另一个全局对象可能会在初始化之前引用您的字符串。http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Static_and_Global_Variables std::string优点:
  • 为您管理内存(字符串可以增长,实现将分配更大的缓冲区)
  • 更高级别的编程接口,与STL的其余部分很好地配合使用。
std::string缺点:
  • 两个不同的STL字符串实例不能共享相同的底层缓冲区。因此,如果按值传递,您始终会获得一个新副本。
  • 有一些性能损失,但我认为除非您的要求很特殊,否则可以忽略不计。

实际上,STL实现通常为std::string实现写时复制语义,因此按值传递它们并不会花费太多。尽管如此,最好不要依赖于它,并且通常最好仍然传递一个const引用。 - unwesen
1
一些std::string的实现放弃了COW(写时复制)的实现。此外,提供一个与标准兼容的(POSIX)线程安全类并不像看起来那么简单。请参见http://groups.google.fr/group/ifi.test.boa/browse_frm/thread/cb16ed54c3e78a78/215edbc9c7686fdd或http://groups.google.fr/group/comp.programming.threads/browse_frm/thread/dbdf76a8844bde5c/d8651dd45d13b862。 - Luc Hermitte

8

在以下情况下,您应该考虑使用 char*

  • 这个数组将作为参数传递。
  • 您预先知道数组的最大大小(您了解它或者您强制规定它)。
  • 您不会对此数组进行任何转换。

实际上,在C++中,char* 经常用于固定长度的小单词,如选项、文件名等。


3
不是传递数组本身,而是传递指向该数组的指针。这就是指针的作用 — 指向一个对象的指针。 - Cole Tobin

5

何时使用C++的std::string:

  • 总体而言,stringchar*更安全。通常情况下,当您使用char*时,您需要检查各种事情以确保正确性,但在string类中,所有这些都已为您完成。
  • 通常情况下,使用char*时,您需要释放您分配的内存,但在string中,它会在析构时自动释放其内部缓冲区。
  • string与C++的stringstream结合使用非常好,格式化IO非常容易。

何时使用char*:

  • 使用char*可以让您对“幕后”发生的事情更加掌控,这意味着如果需要,您可以调整性能。

5

如果你在编写一个库,使用(const) char*作为参数。因为std::string实现在不同的编译器之间是有差异的。


如果你正在用C++编写一个库,那么std::string的布局并不是你唯一需要关心的事情。两个实现之间有许多潜在的不兼容性问题。只有当源代码可用或已经为你使用的确切编译器编译过的情况下,才应该在C++中使用库。C库通常更具可移植性,但在这种情况下,你也无法使用std::string。 - David Thornley
确实,std::string并不是唯一的问题,但得出“仅在源代码可用或编译为您正在使用的确切编译器时才使用C++库”这样的结论有点过了。有些组件系统可以与不同的编译器很好地配合(例如COM),并且可以将C接口公开到内部使用C++编写的库中(例如Win32 API)。 - Nemanja Trifunovic

3

如果您想使用C库,您需要处理C字符串。同样的情况也适用于您想将API公开给C。


2
您可以期望大多数 std::string 操作(例如 find)都被优化得尽可能高效,因此它们的性能可能至少与纯 C 对应物一样好。值得注意的是,std::string 迭代器经常映射到基础 char 数组中的指针。因此,在迭代器之上构建的任何算法在性能方面本质上与在 char * 上构建相同算法相同。
需要注意的事项包括 operator[] - 大多数 STL 实现不执行边界检查,并且应将其转换为基础字符数组上的相同操作。据我所知,STLPort 可以选择执行边界检查,在这种情况下,此运算符会稍微慢一些。
那么使用 std::string 有什么好处呢?它使您免于手动内存管理; 调整数组大小变得更容易,而且您通常无需考虑释放内存。
如果您担心调整字符串大小时的性能问题,则可能会发现 reserve 函数很有用。

1

如果您正在使用类似文本等的字符数组,请使用std :: string更灵活,更易于使用。 如果您将其用于其他内容(如数据存储)? 请使用数组(首选向量)


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