将const char *返回char *然后更改数据

时间:2014-04-04 08:56:21

标签: c++ pointers stdstring

我对以下代码感到困惑:

string _str = "SDFDFSD";
char* pStr = (char*)_str.data();
for (int i = 0; i < iSize; i++)
    pStr[i] = ::tolower(pStr[i]);

此处_str.data()返回const char*。但我们将其分配给char*。我的问题是,

_str.data()返回指向常量数据的指针。如何将它存储在指向数据的指针中?数据是不变的吗?如果我们将它分配给char指针而不是像我们在for语句中那样改变它,这对于常量数据是不可能的。

7 个答案:

答案 0 :(得分:5)

不要那样做。在这种情况下可能没问题,但正如data()的文档所说:

  

通过进一步调用其他指针,可能会使返回的指针无效   修改对象的成员函数。

     

程序不得改变此序列中的任何字符。

因此,如果你保持指针,你可能会非常意外地写入无效内存。或者,实际上,破坏了std :: string的实现。我几乎可以说不应该暴露这个功能。

std :: string为此目的提供了非常量operator[]

string _str = "SDFDFSD";
for (int i = 0; i < iSize; i++)
    _str[i] = ::tolower(_str[i]);

答案 1 :(得分:3)

您正在做的事情在标准库级别无效(您违反std::string contract)但在C ++核心语言级别有效。

char *返回的data不应该被写入,因为例如它可能在理论上(*)在具有相同值的不同字符串之间共享。

如果你想修改一个字符串,只需使用std::string::operator[]来告知对象意图,并在最初共享字符串的情况下为特定实例创建一个私有缓冲区。

从技术上讲,你可以从指针或引用中删除const-ness,但如果它是有效的操作,则取决于具体情况的语义。允许操作的原因是C ++的主要哲学是程序员不会犯错并知道他们在做什么。例如,从C ++语言的角度来看,在技术上是合法的,memcpy(&x, "hello", 5) x是一个类实例,但结果很可能是“未定义的行为”。

如果您认为您的代码“有效”,那是因为您对“工作”的真正含义有错误的理解(提示:“工作”并不意味着有人曾经观察过代码做了看似合理的事情,但是这将适用于所有情况)。如果你运行那个程序,一个有效的C ++实现可以随意做任何事情:你发现你觉得很好的东西并不意味着什么,可能你看起来不够近,或者你可能只是幸运(不幸的是,实际上并没有立即发生崩溃。

(*)在现代,std :: string的COW(写时复制)实现很少受欢迎,因为它们会带来很多问题(例如使用多线程)并且内存现在要便宜很多。仍std::string合同表示您不允许更改data()返回值指向的内存;如果你做了什么可能会发生。

答案 2 :(得分:2)

您永远不必直接更改从std::string::data()std::string::c_str()返回的数据。

创建std::string的副本:

std::string str1 = "test";
std::string str2 = str1; // copy.

更改字符串中的字符:

std::string str1 = "test"
str1[0] = 'T';

答案 3 :(得分:1)

&#34;正确&#34;方法是使用std::transform代替:

std::transform(_str.begin(), _str.end(), _str.begin(), ::tolower);

答案 4 :(得分:1)

你的问题的简单答案是,在C ++中,你可以抛弃&#39; const&#39;一个变量。

你可能不应该这样。

See this for const correctness in C++

答案 5 :(得分:0)

字符串总是在堆上分配内存,所以这实际上不是const数据,只是标记为(在方法data()签名中)以防止修改。

但是在C ++中没有什么是不可能的,所以使用简单的转换,虽然不安全,但现在可以将相同的内存空间视为可修改。

答案 6 :(得分:-2)

C / C ++程序中的所有常量(如下面的"SDFDFSD")将存储在单独的.rodata部分中。在执行期间将二进制文件加载到内存中时,此部分将映射为只读。

int main()
{
  char* ptr = "SDFDFSD";
  ptr[0]='x'; //segmentation fault!!
  return 0;
}

因此,任何修改该位置数据的尝试都会导致运行时错误,即分段错误


回到上面的问题,在创建字符串并为其分配字符串时,new copy in memory now exists(用于保存字符串对象_str的属性的内存)。这是在堆上,而不是映射到只读部分。成员函数_str.data()指向内存中映射读/写的位置。

  

返回类型的 const 限定符确保此函数不会意外地传递给期望非const char* 指针。

在当前的迭代中,对保存字符串对象数据的内存位置本身没有限制;即它使用读/写权限进行映射。因此,使用另一个非常量指针来修改位置,即分配左侧的pStr[i]不会导致运行时错误,因为内存位置本身没有固有的限制。

这又是 NOT 保证可以工作,只是你观察到的一个特定于实现的行为(即它恰好适合你)并且不能总是依赖于此。