我的代码是这样的:
string s = "abc";
char* pc = const_cast<char*>( s.c_str() );
pc[ 1 ] = 'x';
cout << s << endl;
当我使用GCC编译上面的代码片段时,我得到了结果“axc”。我的问题是,以这种方式修改C ++字符串的基础char
数组是否安全且可移植?或者可能有其他方法直接操作字符串数据?
仅供参考,我的目的是编写一些可由C和C ++调用的纯C函数,因此,它们只能接受char*
作为参数。从char*
到字符串,我知道涉及复制,惩罚是不利的。所以,任何人都可以提出一些建议来处理这种情况。
答案 0 :(得分:5)
(a)这不一定是基础字符串。 std::string::c_str()
应该是底层字符串的副本(尽管C ++标准中的一个错误意味着,实际上,它通常不是......我相信这在C ++ 0x中得到修复)。
(b)const_cast
离开constness只会破坏变量类型:实际对象仍然是const
,你修改它是未定义的行为 - 非常糟糕。
简单地说,不这样做。
你可以使用&myString[0]
吗?它有一个非const版本;然后,它被声明与没有非const版本的data()[0]
相同。有一个像样的库参考的人可以清除它。
答案 1 :(得分:5)
对于第一部分,c_str()
返回const char*
,这意味着它所说的内容。在这种情况下,所有const_cast
都会实现未定义的行为编译。
对于第二部分,在C ++中,0x std::string
保证具有连续存储,就像C ++ 03中的std::vector
一样。因此,只要字符串不为空,您就可以使用&s[0]
将char*
传递给函数。在实践中,目前处于活动开发状态的所有string
实现已经具有连续存储:在标准委员会会议上进行了一次民意调查,没有人提供反例。因此,如果您愿意,可以立即使用此功能。
然而,std::string
使用与C风格字符串完全不同的字符串格式,即数据+长度而不是nul-terminated。如果从C函数修改字符串数据,则无法更改字符串的长度,并且无法确定在没有c_str()
的情况下末尾有一个空字节。并且std::string
可以包含作为数据一部分的嵌入式nuls,所以即使你确实找到了一个nul,在不知道长度的情况下你仍然不知道你已经找到了字符串的结尾。对于能够在不同类型的数据上正确运行的函数,您可以做的非常有限。
答案 2 :(得分:3)
正如其他人所说,它不便携。但是还有更多的危险。一些std :: string实现(我知道GCC会这样做)使用COW(写入时复制)。
#include <iostream>
#include <string>
int main()
{
std::string x("abc");
std::string y;
y = x; // x and y share the same buffer
std::cout << (void*)&x[0] << '\n';
std::cout << (void*)&y[0] << '\n';
x[0] = 'A'; // COW triggered
// x and y no longer share the same buffer
std::cout << (void*)&x[0] << '\n';
std::cout << (void*)&y[0] << '\n';
return 0;
}
答案 3 :(得分:2)
显而易见的答案是否定的,这是未定义的行为。在另一 亲手,如果你这样做:
char* pc = &s[0];
您可以在今天的实践中访问基础数据 在C ++ 11中得到保证。
答案 4 :(得分:1)
这依赖于未定义的行为,因此不可移植。
答案 5 :(得分:1)
这取决于您的操作系统。在GNU libc库中,std::string
使用copy-on-write (CoW) pattern实现。因此,如果多个std::string
对象最初包含相同的内容,则它们将在内部全部指向相同的数据。因此,如果您在问题中显示的方法中修改其中的任何一个,则所有(看似)不相关的std::string
对象的内容都将发生变化。
在Windows上,我认为实现不使用CoW,我不确定那里会发生什么。
无论如何,它是未定义的行为,所以我要保持清醒。有可能,即使你让它工作,你最终也会陷入非常难以追查的错误。
答案 6 :(得分:0)
你不应该搞乱底层字符串。在一天结束时,字符串是一个对象,你会以这种方式混淆任何其他对象吗?
您是否剖析了代码以查看是否存在惩罚。