string的c_str导致的问题

fixopen 2007-10-03 02:12:37
string的c_str()会返回一个C兼容的字符串,也就是所谓的其内容以null终止的字符指针,这儿的关键问题是这个指针的生命周期如何?更精致的也就是说:它什么时候销毁?它只想的内容什么时候失效?我想来想去,觉得这儿很难处理,似乎除了引入一个微型的GC以外,没有什么别的可能。

由于有好多人只希望看代码,我就举点代码作为例子:

string content = "Hello, world!";

mbstowcs(wideContentBufferPointer, wideContentBufferLength, content.c_str(), content.length());

上面的content.c_str()会返回一个char*,也就是一个字符指针,指向一个内存区域,那个内存区域的长度为content.length() + 1,内容content这个字符串的内容后跟一个null。首先,主要的问题是:这个内存区域是不是就是content这个字符串类中包含的那个内容所占据的区域呢?我们可以理智的推断,不是。因为,一、那个区域比这个区域的大小小一。二、content类中内容未必在一个内存区域中。那么,这个内存区域就是c_str分配的了。可是,它在什么时候释放呢?不知道。没有办法知道这个char*会在什么时候释放。真是可怕啊。它不会被释放?这是一个内存泄漏吗?不……,好多人都会疯狂的,应该或许大概编译器会在适当的地方插入释放内存的指令吧。可是这是不可能的,这儿只有在运行时才能知道是不是可以释放了。这儿有GC吗?,应该没有。可是究竟怎么才能完成这个魔幻般的动作呢?有人会猜测,会不会实在string类的析构中释放?我觉得这仍然不可取,看看下面的代码:

char cons* f()
{
string r;
//....
return r.c_str();
}

这段代码会不会出现问题?应该不会吧。

如果会出现问题,那么,我们再看看这样:

//...
string* s = new string();
//...
char* data = 0;
if (condition)
data = s->c_str();
else
data = (char*)malloc(length + 1)
//...a lot of code use data
if (condition)
; //do nothing
else
{
free(data);
data = 0;
}
这儿会有双份的内存泄漏吗?

有人说了,c_str不会分配内存的,你上面的那些分析都不对了。可是问题是:如果c_str()不分配内存,它怎么能给内容最后缀上0?或许它内部就有结尾0?可是如果是这样,我们的string显然不能不是连续的,可是,标准上明确地写着它可以是不连续的。

我没有想到什么?谁给我解答一下。
...全文
3839 26 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
小小蔷薇 2011-02-22
  • 打赏
  • 举报
回复
我来查找问题:
c_str()是不是返回临时对象?
晨星 2007-10-31
  • 打赏
  • 举报
回复
那么

playMedia((rootPath + "/ " + filename).c_str());

!=

char* filename = (rootPath + "/ " + filename).c_str();
playMedia(filrname);

大家觉的这两个模式结果不同很正常?
/////////////////////////////////////////////////
是很正常啊,哪里不正常了。这难道不是C++最基本的临时变量生存期规则吗?
当然,你可以认为C++的临时变量生存期规则很不合理,或者认为某某语言的规则比C++更合理,但那是另外一回事,用C++,当然就得先承认C++的规则,毕竟这个帖子只是讨论string,而不是讨论语言比较。

另外,如果一个对象里返回一个指针或引用在对象失效后仍然可用,那十有八九是你自己需要delete,或者需要需要依赖于GC机制。我觉得那更不爽。
  • 打赏
  • 举报
回复
23楼的 贴的是C++标准
问题不用再争论了.
Leon8086 2007-10-05
  • 打赏
  • 举报
回复
我也很无聊呢……花了2天去emule了个the C++ standard library,我以为自从我上次硬盘down掉之后就不会再去下C++的书了呢……夭寿哦~

const char* string::c_str () const

Returns the contents of the string as a C-string (an array of characters that has the null character '\0' appended).
The return value is owned by the string. Thus, the caller must neither modify nor free or delete the return value.
The return value is valid only as long as the string exists, and as long as only constant functions are called for it

smalllixin 2007-10-04
  • 打赏
  • 举报
回复
不得不说了~~
楼主那么好奇爱研究怎么不去看看string的实现,
如果看不懂就从学C++。
等看懂了就没有这种问题了~
laiwusheng 2007-10-04
  • 打赏
  • 举报
回复
摘录部分源码:

//////////////////////////////////////////////////////////////////
struct Rep {
size_t len, res, ref;
bool selfish;

charT* data () { return reinterpret_cast<charT *>(this + 1); }
charT& operator[] (size_t s) { return data () [s]; }
charT* grab () { if (selfish) return clone (); ++ref; return data (); }
void release () { if (--ref == 0) delete this; }

inline static void * operator new (size_t, size_t);
inline static void operator delete (void *);
inline static Rep* create (size_t);
charT* clone ();

inline void copy (size_t, const charT *, size_t);
inline void move (size_t, const charT *, size_t);
inline void set (size_t, const charT, size_t);

inline static bool excess_slop (size_t, size_t);
inline static size_t frob_size (size_t);

private:
Rep &operator= (const Rep &);
};
//////////////////////////////////////////////////////////////////
//basic_string ::charT *dat;
Rep *rep () const { return reinterpret_cast<Rep *>(dat) - 1; }

static charT eos () { return traits::eos (); }

size_type length () const
{ return rep ()->len; }

//////////////////////////////////////////////////////////////////
extern "C++" {
template <class charT>
struct string_char_traits {
typedef charT char_type; // for users to acquire the basic character type

// constraints

static void assign (char_type& c1, const char_type& c2)
{ c1 = c2; }

//////////////////////////////////////////////////////////////////
void terminate () const
{ traits::assign ((*rep ())[length ()], eos ()); }


const charT* data () const
{ return rep ()->data(); }

public:
const charT* c_str () const
{ if (length () == 0) return ""; terminate (); return data (); }
0黄瓜0 2007-10-04
  • 打赏
  • 举报
回复
void f(const char* s)
{
cout<<s<<endl;
}
int main()
{

string str1="123456789";
string str2="abcdefghijk";
f((str1+"/"+str2).c_str());
const char* s=(str1+"/"+str2).c_str();//operator+一般返回值而不是引用,所以返回的指针是个临时对象所持有的指针.
f(s);//输出乱码,说明临时对象已析构

return EXIT_SUCCESS;
}
universee 2007-10-04
  • 打赏
  • 举报
回复
好,abc我来拉你上岸
0黄瓜0 2007-10-04
  • 打赏
  • 举报
回复
我错了.又在临时对象上翻船了.
0黄瓜0 2007-10-04
  • 打赏
  • 举报
回复
playMedia((rootPath + "/" + filename).c_str());

!=

char* filename = (rootPath + "/" + filename).c_str();
playMedia(filrname);

大家觉的这两个模式结果不同很正常
==============================
这两个不同吗?我看相同.

楼主可以讲下你的理解它它哪里不同.
Leon8086 2007-10-04
  • 打赏
  • 举报
回复
playMedia((rootPath + "/" + filename).c_str());

!=

char* filename = (rootPath + "/" + filename).c_str();
playMedia(filrname);

事实上在VC8.0的STL上,他们就是不一样。至于是谁说的,哪里有这种说法,LZ这么有兴趣,为何不自己翻翻The C++ Standard Library或者C++标准,看他们是怎么规定string的行为的,不就一目了然了,这个最权威。

C++就是这点很不好,有些细节隐藏的过深,要深究某个问题,正确答案还不得不翻这些见了鬼的大部头,不然都是猜测。很多时候我是按我自己的理解使用C++,因为懒得翻书。我自己写代码的时候从来不使用const char* str = (str1+str2).c_str();这种用法,我当然不是很清楚C++如何规定string的行为,不过有一点比较清楚,“谁分配谁回收”这样的行为符合STL容器的设计哲学,很大程度上也符合C++类对象的设计哲学,同时这种假设最安全。为什么c_str()的指针在string的非const函数被调用之后就应该被认为无效呢?这和下面的情况类似:

// vector<char> vec;
vector<char>::iterator ite = vec.begin();
vec.push_back( 'O' );
cout << (*ite) << endl; // ite可能已经无效化了。

不过const char* str = (str1+str2).c_str();这种用法可能牵涉到另一个问题,就是临时变量的生存周期问题。这个我更加懒得去深究了。
healer_kx 2007-10-04
  • 打赏
  • 举报
回复
尽管我没有看帖子,我宁愿站在fixopen这边。
fixopen 2007-10-04
  • 打赏
  • 举报
回复
>>另外一个需要注意的就是c_str的返回值只有在(1)string对象本身失效之前,同时,也在(2)调用任何一个可能修改string对象的函数之前有效。

是吗?谁说的?哪儿有这个说法?

如果如此,显然是析构函数里面释放了。


那么

playMedia((rootPath + "/" + filename).c_str());

!=

char* filename = (rootPath + "/" + filename).c_str();
playMedia(filrname);

大家觉的这两个模式结果不同很正常?
Vitin 2007-10-03
  • 打赏
  • 举报
回复
LZ能够根据编程原理发现并提出问题,这很好。
不过编程有很多技巧,并不明而易见,LZ可能不知道的。

首先肯定,标准库会符合C++标准的需求,并且不会出现LZ提出的问题。
至于为什么会这样,LZ可以阅读标准库的源代码,就清楚原因了。

此外,LZ有几个小小的误解:
1、“content类中内容未必在一个内存区域中”。是的,“未必”,但不是“一定不”。如果有一个标准库中它就是连续的,那么它也是满足标准的。“未必”是为了不给实现设限,如果理解成“一定不”,那还是给实现设限了。同样的,标准说string“可以”是不连续的,但不是说“一定”是不连续的。连续的解仍然符合标准。不要把放宽的需求理解成另一种严厉的需求,从而忽视许多种巧妙的解决方案。
2、“会不会实在string类的析构中释放?我觉得这仍然不可取”。析构是一种改变string的操作,那么为什么不能在析构中释放呢?认为析构以后仍能使用c_str()的返回值,这本身就是一种错误的理解。请注意“Calling any non-const member function for *this can invalidate the pointer”。无论析构后能不能使用,都是符合标准的。因为是“can”,不是“must”或“must not”。只不过此后的使用结果是 Undefined ,除非你阅读源代码,清楚其实现方式。
hjzwl1018 2007-10-03
  • 打赏
  • 举报
回复
楼主担心过多了吧,这些是标准库函数,难道会像你那么假设出现那么严重的后果?那些是大师们写的程序,你只要用,不用管这么多,就想楼上steedhorse所说,"不要做那么多假设".返回栈内存和栈引用是很危险!
晨星 2007-10-03
  • 打赏
  • 举报
回复
可是问题是:如果c_str()不分配内存,它怎么能给内容最后缀上0?或许它内部就有结尾0?可是如果是这样,我们的string显然不能不是连续的,可是,标准上明确地写着它可以是不连续的。
================================
其实我们不需要管那么多,当然“不管”的同义词就是“不要做那么多假设”。
我们只需要知道c_str返回的字符串是有'\0'结尾的,至于这个'\0'是什么时候被追回上的,是不是每种字符串追加的时机都相同,是不是每一个STL的实现追加的方式都相同,我们不需要管,也不应该作相应的假设,更不应该让自己的代码依赖这种假设。
另外一个需要注意的就是c_str的返回值只有在(1)string对象本身失效之前,同时,也在(2)调用任何一个可能修改string对象的函数之前有效。至于这是为什么?同样,尽量别管,也别做假设。

我倒觉得这一切都挺合理的,如果让我来设计string类,在c_str的问题上,结果估计跟这个样子也差不多。
Leon8086 2007-10-03
  • 打赏
  • 举报
回复
char cons* f()
{
string r;
//....
return r.c_str();
}
为什么LZ会认为这个不会有问题?愿闻其详。

LZ,你认为下面的情况,这个指针有意义吗?
char* f()
{
vector<char> vec(2,10);
return &(vec[0]);
};

“string的c_str()返回的指针是由string管理的”,这句话是最有养分的回答了,或者可以补充一下“string(对象)的c_str()返回的指针是由(这个)string(对象负责)管理的”。这个其实跟迭代器的有效性是有点类似的。
iambic 2007-10-03
  • 打赏
  • 举报
回复
string的实现有很多种,楼主可以自己去各套STL代码中寻找答案。
iambic 2007-10-03
  • 打赏
  • 举报
回复
一、返回的是const char*,不是char*。
二、尽量把它当做临时对象来使用。假设分号结束的时候c_str()返回的对象已经非法(虽然实际不是如此)。
yshuise 2007-10-03
  • 打赏
  • 举报
回复
string rt("thsiis");
const char* p = rt.c_str();


楼主是担心c_str()返回的字符申请了新的内存?这是不可能的,因为如果叫客户去释放了一个内存是一件很恐怖的事情。
比如:delete p;

其二:string 仍然用的的stl的内存管理,所以一切同这个内存管理的有关的,都可以不管。


其三:string *p = new string("sss"); 这是客户自己申请的内存,这种不用多说吧,肯定要delete
加载更多回复(6)

65,186

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧