std::string::c_str() 覆盖函数返回的前一个

Posted

技术标签:

【中文标题】std::string::c_str() 覆盖函数返回的前一个【英文标题】:std::string::c_str() overwrittes the previous one returned by a function 【发布时间】:2017-03-26 00:36:14 【问题描述】:

我不明白当文本大小相等时指针怎么可能相同。似乎 firstStringObj::c_str() 覆盖了前一个指针。

#include <iostream>
#include <string>
#include <string>
#include <stdio.h>

std::string getConstCharAndModifyItWithANewString( const char* constchar )

    std::string stringAtStack( constchar );
    stringAtStack += "::isModified";
    return stringAtStack;


int main()

    const char* firstConstCharPointer = getConstCharAndModifyItWithANewString("Hi!").c_str();
    std::string firstStringObj = "Hi+";

    printf(" firstConstCharPointer(%s)(%p)\nfirstStringObj(%s)(%p)\n\n", firstConstCharPointer,firstConstCharPointer, firstStringObj.c_str(),     firstStringObj.c_str()  );

输出: firstConstCharPointer(Hi+)(0x4593eb8) firstStringObj(Hi+)(0x4593eb8)

【问题讨论】:

看起来像是编译器优化问题。在 gcc 4.9.2 上不会发生 您正在观察未定义的行为,因此您不能责怪优化。另外:阅读.c_str()的文档 不会发生在长字符串上。 是的,这不是优化问题。因为,codechef 通过但 cpp.sh 失败。 【参考方案1】:

您的函数返回一个临时 std::string 对象。在分配了firstConstCharPointer 变量并且表达式完成后,该临时对象被销毁,释放其分配的内存块并让变量指向已释放的内存。这称为悬空指针

firstStringObj 然后分配一个新的内存块,恰好重用 temp std::string 之前分配和释放的内存块。所以悬空指针发生现在再次指向有效内存。这就是为什么您的printf() 语句能够为两个字符串显示相同的内存地址和内容。

但这是未定义的行为。每次分配的内存块完全由字符串的Allocator 来决定。第二个std::string 可以很容易地分配一个完全不同的内存块,然后当它试图取消引用仍然指向无效内存的悬空指针时,代码更有可能崩溃,或者至少打印垃圾。

为了让您的代码正常工作,您需要将firstConstCharPointer 更改为std::string 对象,以便正确复制临时std::string,例如:

#include <iostream>
#include <string>
#include <cstdio>

std::string getConstCharAndModifyItWithANewString( const char* constchar )

    std::string stringAtStack( constchar );
    stringAtStack += "::isModified";
    return stringAtStack;


int main()

    const std::string firstConstStringObj = getConstCharAndModifyItWithANewString("Hi!");
    std::string secondStringObj = "Hi!";

    std::printf(" firstConstStringObj(%s)(%p)\nsecondStringObj(%s)(%p)\n\n", firstConstStringObj.c_str(), firstConstStringObj.c_str(), secondStringObj.c_str(), secondStringObj.c_str());

【讨论】:

【参考方案2】:

如documentation:中所述

从 c_str() 中获得的指针可能会通过以下方式失效:

将对字符串的非常量引用传递给任何标准库函数,或者 在字符串上调用非常量成员函数,不包括 operator[]、at()、front()、back()、begin()、rbegin()、end() 和 rend()。

所以你使用了无效的指针,因为析构函数是一个非常量成员函数并且没有在上面列出。

【讨论】:

【参考方案3】:

你有典型的未定义行为。 printf 试图取消引用 firstConstCharPointer 因为 %sfirstConstCharPointer 指向已被销毁的数据,因为与此指针的生命周期关联的 std::string 在赋值后停止:

const char* firstConstCharPointer = getConstCharAndModifyItWithANewString("Hi!").c_str();
// temporary std::string returned from getConstCharAndModifyItWithANewString destroyed, pointer becomes dangling.

【讨论】:

【参考方案4】:

指针在您的平台上是相同的,因为firstConstCharPointer 是一个悬空指针,它指向已释放的内存。

这是因为getConstCharAndModifyItWithANewString返回的std::string在赋值表达式const char* firstConstCharPointer = ...;之后被销毁了。

所以当你创建一个新的std::string 对象时,编译器选择使用与之前的std::string 对象相同的内存位置,因此指针是相同的。

例如,在我的平台上,指针是相同的,它们不在Ideone 中。

【讨论】:

以上是关于std::string::c_str() 覆盖函数返回的前一个的主要内容,如果未能解决你的问题,请参考以下文章

深拷贝 std::string::c_str() 到 char * [重复]

std::string::c_str() 结果的生命周期是多少?

std::string::c_str() 结果的生命周期是多少?

为啥我仍然可以在字符串范围之外访问 std::string::c_str() 返回的 char 指针? [复制]

使用 std::string.c_str() 作为另一个方法的参数时的段错误

C++ 中的 StringStream/c_str() 损坏