字符串的c_str()和data()方法有什么区别?

144

我读过几篇文章,其中提到c_str()和data()(在STL和其他实现中)的区别在于c_str()始终以空字符结尾,而data()则不是。

就我在实际实现中看到的情况,它们要么相同,要么data()调用c_str()。

我错过了什么?在哪些场景中使用哪一个更正确?

6个回答

145
文档是正确的。如果您想要一个空终止字符串,请使用c_str()
如果实现者恰好是在 c_str() 的术语中实现了data(),则您不必担心,如果您不需要字符串以空终止,则仍然使用 data(),在某些实现中,它可能比c_str()执行得更好。
字符串不一定由字符数据组成,它们可以由任何类型的元素组成。在这些情况下,data()更有意义。在我的观点中,c_str()只在您的字符串元素基于字符时才真正有用。
额外信息:从C++11开始,这两个函数需要相同。即data现在需要以null结尾。根据cppreference的说法:“返回的数组以null结尾,即,data()和c_str()执行相同的功能。”

14
在C++17及以后的版本中,.data()方法现在也有一个非const的重载版本,因此对于非常量字符串来说,它们不再是等价的。 - Deduplicator
3
如果数据不是字符类型,为什么要使用字符串而不是向量或其他容器? - Fayeure
1
@Fayeure,我猜是因为std::basic_string<T>具有向量没有的功能,例如它很容易连接字符串。 - Bolpat

39
C++11/C++0x 中,data()c_str() 不再不同。因此,data() 也需要在末尾具有空值终止符。

21.4.7.1 basic_string 访问器 [string.accessors]

const charT* c_str() const noexcept;

const charT* data() const noexcept;

1 返回:一个指针 p,使得对于每个 i[0,size()] 中,p + i == &operator[](i)


21.4.5 basic_string元素访问 [string.access]

const_reference operator[](size_type pos) const noexcept;

1 要求:pos <= size()。 2 返回值:如果pos < size(),则返回*(begin() + pos),否则返回一个类型为T的对象的引用, 其值为charT();所引用的值不得被修改。


如果字符串由非字符数据组成,这在我所知的情况下对于字符串数据来说是合法的,包括空值,那该怎么办? - taz
4
即使存储二进制数据,C++11 要求 std::string 为尾部的 '\0' 分配一个额外的字符。当你执行 std::string s("\0"); 时,保证 s.data()[0]s.data()[1] 都等于0。 - bcrist
@bcrist 有任何参考资料吗? - John
在C++11/C++0x中,data()和c_str()不再有区别。但请不要忘记自C++17起,至少有一个区别:data()可以返回非const指针。 - kingsjester
你能确认一下,myStr.data() 和 &myStr[0] 是相同的吗? - kingsjester
显示剩余2条评论

20

即使您看到它们执行相同的操作,或者.data()调用.c_str(),也不应假定其他编译器也是如此。而且,您的编译器在未来的版本中可能会更改。

使用std::string的2个原因:

std::string可用于文本和任意二进制数据。

//Example 1
//Plain text:
std::string s1;
s1 = "abc";

//Example 2
//Arbitrary binary data:
std::string s2;
s2.append("a\0b\0b\0", 6);

当您将字符串用作示例1时,应使用 .c_str() 方法。

当您将字符串用作示例2时,应使用 .data() 方法。这并不是因为在这些情况下使用 .c_str() 是危险的,而是因为这更明确地表明您正在处理二进制数据,方便其他人审核您的代码。

使用 .data() 可能存在隐患

以下代码是错误的,可能会导致程序崩溃:

std::string s;
s = "abc";   
char sz[512]; 
strcpy(sz, s.data());//This could crash depending on the implementation of .data()

为什么实现者常常使.data()和.c_str()做相同的事情?

因为这样更加高效。如果想让.data()返回一个没有空终止符的内容,那么就需要让.c_str()或者.data()复制它们的内部缓冲区,或者使用两个缓冲区。而使用单个空终止符缓冲区意味着在实现std::string时只需使用一个内部缓冲区。


6
.data() 的关键是不应该复制内部缓存区。这意味着实现不必在需要之前浪费一个字符的 \0。您永远不希望有两个缓冲区:如果您调用 .c_str(),请向缓冲区附加一个 \0。.data() 仍然可以返回该缓冲区。 - MSalters
2
完全同意,使用2个缓冲区是荒谬的。但你怎么知道这就是.data的预期用途呢? - Brian R. Bondy
@BrianR.Bondy 我尝试了这段代码:.. auto str = string { "Test \0String!" }; cout << "DATA : " << str.data() << endl; 输出结果只有"Test"而不是整个字符串,请问我做错了什么? - programmer
最后一部分是错误的,data和c_str可以使用相同的缓冲区而不必是0终止的 - c_str只需在第一次调用时添加0即可。 - Remember Monica
注意,C++11将.data()作为.c_str()的别名。 - hanshenrik
如果我理解正确的话,@BrianR.Bondy所说的strcpy(sz, s.data())可能是错误的,因为在C++11之前,std::data()不能保证以空字符结尾。我的理解正确吗? - John

3
所有之前的评论都是一致的,但我想补充一点,在c++17中,str.data()返回的是char*而不是const char*。原始答案翻译成“最初的回答”。

3
自C++17以来,constnon-const重载都可用。 - TonySalimi

3
已经有人回答过了,以下是一些关于目的的注释: 实现自由。
例如字符串操作 - 如迭代、连接和元素变异 - 不需要零终止符。除非你把字符串传递给期望零终止字符串的函数,否则可以省略它。
这将允许实现共享子字符串的实际字符串数据:string::substr 可以在内部保存对共享字符串数据的引用以及开始/结束范围,避免实际字符串数据的复制(和额外的分配)。如果涉及的子字符串仅被读取,则实现将推迟复制,直到你调用 c_str 或修改任何字符串为止,永远不会进行复制。
(写时复制实现在多线程环境中并不好玩,而且通常的内存/分配节省并不值得今天更复杂的代码,因此很少这样做)。
同样,string::data 允许不同的内部表示,例如绳索(字符串段的链接列表)。这可以显著改善插入/替换操作。再次强调,当你调用 c_strdata 时,段列表必须被合并为单个段。

当调用string::c_str()时,实际上会执行什么操作?string::c_str()将分配内存,复制内部数据,并在新分配的内存末尾添加一个空字符。 - John
不,你确实是错了。详见 https://dev59.com/jMLra4cB1Zd3GeqPEjnx。 - John

2

引用自 ANSI ISO IEC 14882 2003(C++03标准):

    21.3.6 basic_string string operations [lib.string.ops]

    const charT* c_str() const;

    Returns: A pointer to the initial element of an array of length size() + 1 whose first size() elements
equal the corresponding elements of the string controlled by *this and whose last element is a
null character specified by charT().
    Requires: The program shall not alter any of the values stored in the array. Nor shall the program treat the
returned value as a valid pointer value after any subsequent call to a non-const member function of the
class basic_string that designates the same object as this.

    const charT* data() const;

    Returns: If size() is nonzero, the member returns a pointer to the initial element of an array whose first
size() elements equal the corresponding elements of the string controlled by *this. If size() is
zero, the member returns a non-null pointer that is copyable and can have zero added to it.
    Requires: The program shall not alter any of the values stored in the character array. Nor shall the program
treat the returned value as a valid pointer value after any subsequent call to a non- const member
function of basic_string that designates the same object as this.

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