用表达式初始化char *无法工作

6
以下代码输出结果不正确:
string my_string="My_First_Text";
char * my_pointer=(char *)(my_string+"My_Second_Text").c_str();

为什么?当我初始化my_pointer时,我认为不需要my_pointer=new char[100]。如果这个假设不成立,那么为什么?


1
你应该注意警告。哦,我明白了,你忽略了这个警告:现在你就只能自己解决问题了,这似乎不是一件好事。 - Peter - Reinstate Monica
附带说明,该代码不会产生任何输出,更不会产生任何错误。 - Peter - Reinstate Monica
3
这是编译器告诉你的内容(http://ideone.com/hWe8mu)。但由于某种原因,你认为自己比编译器更懂,并消除了错误。 - PaulMcKenzie
1
@PeterA.Schneider,@Paul:禁用的警告与问题无关。在第二行添加“const”即可消除警告,但问题仍然存在。 - Ben Voigt
@Ben 你是对的。我认为所有的答案都指出了导致问题的临时性问题。 - Peter - Reinstate Monica
5个回答

6
请注意,my_string+"My_Second_Text"是一个临时的std::string,在表达式执行完之后将被销毁。这意味着my_pointer会立即变为悬空指针,因为它应该指向的字符数组已经随着临时std::string的销毁而被销毁;请注意,返回的字符数组属于std::string,它不是独立的。然后,在悬挂的指针上解除引用将导致未定义行为。
string my_string="My_First_Text";
char * my_pointer=(char *)(my_string+"My_Second_Text").c_str();
// the temporary constructed from my_string+"My_Second_Text" has been destroyed
// my_pointer is dangled now; deference on it is UB

使用命名变量代替临时变量会更好。例如:
string my_string = "My_First_Text";
my_string += "My_Second_Text";
const char * my_pointer = my_string.c_str();

顺便提一下:std::basic_string::c_str的返回类型是const char*,对其进行任何修改都是未定义行为。因此,试图显式将其转换为char*是危险的。

尝试写入通过c_str()访问的字符数组是未定义行为。


如果对象确实不是const(例如字符串的char数组,因为它是动态分配的),则修改指向const的指针所指向的存储只有在UB(未定义行为)。C++ 2012 std, 7.1.6.1: int i=2; const int *cip; cip = &i; int* ip; ip = const_cast<int*>(cip); *ip = 4; 是可以的。 - Peter - Reinstate Monica
1
@PeterA.Schneider 这里情况更加复杂,涉及到 std::string。无论如何,std::basic_string::c_str 表示返回的数组不应被修改。 "通过 c_str() 访问的字符数组的写入是未定义行为。" - songyuanyao
返回已翻译的文本:True(std 21.4.7.1)。但原因并不是constness ;-). - Peter - Reinstate Monica
@songyuanyao 我完全理解了我的错误,现在我知道如何以正确的方式解决它。但是,我是否也可以使用const char * my_pointer来解决这个问题,而不是使用char* my_pointer?换句话说,通过使用const char * my_pointer,我是否可以强制编译器将my_string +“My_Second_Text”的结果存储在某个地方,并在;结束时不销毁它? - Admia
1
@Admia 不行,因为my_pointer指针随着临时std::string的销毁而变得无效,这个事实并没有改变,所以仍然是未定义行为。 (UB意味着,即使在某些情况下似乎工作正常,但你根本不能依赖它。这只是C++世界中的怪物。) - songyuanyao

3
除了将 c_str ( const char * )转换为 char * ,这不是一个好主意,“my_pointer”使用临时变量进行初始化,并在表达式评估后被销毁。这意味着,在您的代码中的最后一个“;”之后, my_pointer 指向的内存已不再有效,并且将产生意想不到的结果。

3

(my_string+"My_Second_Text").c_str() 是一个临时值,在程序运行时会动态创建和销毁,不会被保存在内存中。

指向它会导致未定义的行为,因为所指向的内存未定义。对于字符串赋值到 char * 中,请使用 strcpy 或变量赋值,而不是临时值。


3
你的代码存在未定义行为。 +运算符返回一个新的临时字符串对象,该对象将在(my_string+"My_Second_Text").c_str()表达式之外被销毁,因此对该字符串的任何引用都将悬空,并且通过它们访问的行为是未定义的。 c_str() 返回一个指向string内部char数组的const指针。你不能且不应该通过该指针来操作string
(my_string + "My_Second_Text")的结果存储在一个新变量中,或使用append函数将新字符串附加到现有字符串对象中。
std::string new_string = my_string + "My_Second_Text";
const char* char_pointer = new_string.c_str();

my_string.append("My_Second_Text");
const char* char_pointer = my_string.c_str();

3

临时对象可能会引起晦涩的问题。

Bjarne C++ 书中相关摘录:

void f(string& s1, string& s2, string& s3)
{
  const char∗ cs = (s1+s2).c_str();  // << Code similar to OP's example
  cout << cs;
  if (strlen(cs=(s2+s3).c_str())<8 && cs[0]=='a') {
  // cs used here
  }
}

Probably, your first reaction is ‘‘But don’t do that!’’ and I agree. However, such code does get written, so it is worth knowing how it is interpreted.

A temporary string object is created to hold s1 + s2. Next, a pointer to a C-style string is extracted from that object. Then – at the end of the expression – the temporary object is deleted. However, the C-style string returned by c_str() was allocated as part of the temporary object holding s1 + s2, and that storage is not guaranteed to exist after that temporary is destroyed. Consequently, cs points to deallocated storage.

The output operation cout << cs might work as expected, but that would be sheer luck. A compiler can detect and warn against many variants of this problem.

作为一个副注,C++中应该使用适当的类型转换而非C风格的类型转换。阅读以下链接:何时应该使用static_cast、dynamic_cast、const_cast和reinterpret_cast?

你应该以粗体、大字和斜体渲染“但别这么做!” - Peter - Reinstate Monica
1
这里不需要一个 const 强制类型转换吗? - Peter - Reinstate Monica
是的@Peter,需要使用const_cast。我对转换进行了概括性回答。 - Saurav Sahu
@SauravSahu 我为我的GNU g++编译器设置了“-W -Wall”选项。但是编译器没有警告我这个错误。 - Admia
尝试使用“-Wcast-qual”。请查看列表https://gcc.gnu.org/onlinedocs/gcc-4.6.3/gcc/Warning-Options.html。 - Saurav Sahu

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