从函数返回'c_str'

8

这是我在网上找到的一个小型库:

const char* GetHandStateBrief(const PostFlopState* state)
{
    static std::ostringstream out;

    // ... rest of the function ...

    return out.str().c_str()
}

在我的代码中,我正在做这件事:

const char *d = GetHandStateBrief(&post);
std::cout<< d << std::endl;

一开始,d 中包含了垃圾数据。后来我意识到从函数中获取的 C 字符串会在函数返回时被销毁,因为 std::ostringstream 是在堆栈上分配的。所以我添加了以下代码:

return strdup( out.str().c_str());

现在我可以从函数中获取所需的文本。

我有两个问题:

  1. 我的理解是正确的吗?

  2. 我后来注意到out(类型为std::ostringstream)是使用静态存储分配的。这不意味着该对象应该一直保留在内存中直到程序终止吗?如果是这样,那么为什么不能访问该字符串?

4个回答

11

strdup在堆上分配了一个字符串的副本,你需要手动释放它(使用free()函数)。如果有选择的话,最好返回std::string

out的静态存储并没有帮助,因为.str()返回一个临时的std::string,在函数退出时会被销毁。


3
你说得对,out是在数据段上分配的静态变量。但是out.str()是在堆栈上分配的临时变量。所以当你执行return out.str().c_str()时,你返回的是指向堆栈临时变量内部数据的指针。请注意,即使字符串不是堆栈变量,c_str也只能“保证在下一次调用字符串对象的非常量成员函数之前不会改变”。
我认为你提出了一个合理的解决方法,假设你不能直接返回一个字符串。

2
哦,"静态变量分配在堆上" - 从未听说过这种东西 :) - Nikolai Fetissov
3
好的,无论是静态的还是其他任何std::string,字符串的字符数据确实存储在堆上。它是与具有全局生命周期的其他变量一样存储在数据段上的字符串描述符。 - Ben Voigt

0

strdup() 返回一个指向堆上内存的 char* 指针。当你使用完它后,需要用 free() 函数释放它,但是这样做是可行的。

在这种情况下,静态局部变量 std::ostringstream out 没有意义,除非返回的 std::string 也是静态的,而你的观察结果表明这并不是真的。


-1
GetHandStateBrief 函数中,变量 out 不需要是静态的。你需要一个显式的 static string 来替换原始调用中创建的临时变量 out.str()
static std::string outStr;
std::ostringstream out;
... rest of function ...
outStr = out.str();
return outStr.c_str();

1
这很危险。在后续调用 GetHandStateBrief 后,返回的 char* 不能保证是有效的。 - Marcelo Cantos
每次调用 GetHandStateBrief 都会使上一次调用返回的指针失效。然而,风险取决于上下文。 - sean e
1
我不会给你点踩,但是在C++中,“在函数中使用静态变量以便返回指针而不仅仅是返回对象”这种方法是一个众所周知的反模式。请参考Scott Meyer的《Effective C++第二版》第23条。 - Timo Geusch
1
我认为这并不比原始代码更糟糕(除了这个能够工作)。在C++程序中使用strdup并不一定更好。 - UncleBens
所有的都是正确的 - 但我会把它留下来,因为它成功地完成了他想做的事情。当 CRT 不共享时,我不得不在 dlls 中使用类似的反模式。另一方面,如果可以返回对象,则同意不应该这样做。 - sean e

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