C++中string.c_str()函数的一些疑问,寻求高手帮忙

FZNPLY 2011-08-29 07:37:19
测试源码如下:

#include <iostream>
#include <string>

#include <cstdio>
#include <cstring>

using namespace std;

void myFunction(const char *p_ch)
{
string str("Function");
printf("函数里面,字符串为: %s\n", str.c_str());
p_ch = str.c_str();
}

int main()
{
const char * const_pcstr_t;

myFunction(const_pcstr_t);
printf("函数外面,字符串为: %s\n", const_pcstr_t);


{
string str("Block");
printf("块作用域里面,字符串为: %s\n", str.c_str());
const_pcstr_t = str.c_str();
}
printf("块作用域外面,字符串为: %s\n", const_pcstr_t);


char* pcstr_t = NULL;

try
{
pcstr_t = new char[10];
}
catch(bad_alloc)
{
cout<< "执行new 操作,分配空间失败."<< endl;
return 1;
}
strcpy(pcstr_t , "Heap");
printf("堆空间删除之前,字符串为: %s\n", pcstr_t);

delete [] pcstr_t;
printf("堆空间删除之后,字符串为: %s\n", pcstr_t);


int i_t = 0;//查看地址可以知道,字符串是存放在堆上,i_t存放在栈上
printf("堆中分配的地址:%x, string内部返回的地址:%x, 局部变量(栈上分配的)地址:%x\n",
reinterpret_cast<unsigned int>(pcstr_t),
reinterpret_cast<unsigned int>(const_pcstr_t),
reinterpret_cast<unsigned int>(&i_t)
);

return 0;
}


g++编译后程序输出如下:

函数里面,字符串为: Function
函数外面,字符串为: U��WVS�O
块作用域里面,字符串为: Block
块作用域外面,字符串为: Block
堆空间删除之前,字符串为: Heap
堆空间删除之后,字符串为:
堆中分配的地址:97c2040, string内部返回的地址:97c2034, 局部变量(栈上分配的)地址:bf9d4b8c
...全文
739 26 打赏 收藏 转发到动态 举报
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
FZNPLY 2011-08-29
  • 打赏
  • 举报
回复
最后附上修正后的代码,和程序运行结果:
#include <iostream>
#include <string>

#include <cstdio>
#include <cstring>

using namespace std;

void myFunction(const char * & const_pcstr_t)
{
string str("Function");
printf("函数里面,字符串为: %s\n", str.c_str());
const_pcstr_t = str.c_str();
}

int main()
{
const char * const_pcstr_t = NULL;

myFunction(const_pcstr_t);
printf("函数外面,字符串为: %s\n", const_pcstr_t);


{
string str("Block");
printf("块作用域里面,字符串为: %s\n", str.c_str());
const_pcstr_t = str.c_str();
}
printf("块作用域外面,字符串为: %s\n", const_pcstr_t);


char* pcstr_t = NULL;

try
{
pcstr_t = new char[10];
}
catch(bad_alloc)
{
cout<< "执行new 操作,分配空间失败."<< endl;
return 1;
}
strcpy(pcstr_t , "Heap");
printf("堆空间删除之前,字符串为: %s\n", pcstr_t);

delete [] pcstr_t;
printf("堆空间删除之后,字符串为: %s\n", pcstr_t);


int i_t = 0;//查看地址可以知道,字符串是存放在堆上,i_t存放在栈上
printf("堆中分配的地址:%x, string内部返回的地址:%x, 局部变量(栈上分配的)地址:%x\n",
reinterpret_cast<unsigned int>(pcstr_t),
reinterpret_cast<unsigned int>(const_pcstr_t),
reinterpret_cast<unsigned int>(&i_t)
);

return 0;
}

在g++运行的结果:

函数里面,字符串为: Function
函数外面,字符串为: Function
块作用域里面,字符串为: Block
块作用域外面,字符串为: Block
堆空间删除之前,字符串为: Heap
堆空间删除之后,字符串为:
堆中分配的地址:8c17040, string内部返回的地址:8c17034, 局部变量(栈上分配的)地址:bfd2639c

谢谢大家
FZNPLY 2011-08-29
  • 打赏
  • 举报
回复
其次,18楼 的jackyjkchen指出了:
gcc的string析构函数没有什么分支,就是因为引用计数的需要,不能在释放后改写内存,而普通的数组就没这个限制。
这就解释了为什么string对象析构之后,仍然能够访问其返回的字符串。而普通数组delete之后就无法访问了。

我虽然不知道如何实现string对象如何实现下面的功能的:
根据引用计数,在delete(释放)空间之后,而不改写内存。

但是依然觉得自己学习到了很多东西,

第二个10分理应给与18楼 的jackyjkchen。

谢谢大家,分虽少,也代表我的心
FZNPLY 2011-08-29
  • 打赏
  • 举报
回复
首先,我自己的问题,

函数声明void myFunction(const char * & const_pcstr_t)
原来声明是void myFunction(const char * const_pcstr_t)
10楼的luciferisnotsatan找出此问题,这样函数返回前输出字符串内容和返回后输出的字符串内容都一样,
这和块作用域內,块作用域外,输出字符串内容一样是一致的,

这些说明了一个问题,string对象析构以后,仍然能够通过指针访问str.c_str()返回的C风格字符串,

不论是块作用域导致string对象的析构,还是函数调用返回导致的string对象的析构。

所以10分给与10楼的luciferisnotsatan
jackyjkchen 2011-08-29
  • 打赏
  • 举报
回复
[Quote=引用 22 楼 fznply 的回复:]

jackyjkchen,18楼,你说的对,第二个我没有使用string,而是char*,所以出现了那个奇怪的现象。
你说的这句话:
gcc的string析构函数没有什么分支,就是因为引用计数的需要,不能在释放后改写内存,而普通的数组就没这个限制。
我觉得最是切中问题要害。

我不知道如何实现,根据引用计数,在delete(释放)空间之后,而不改写内存?
[/Quote]

具体实现不清楚,我没研究过STL源码,告诉你也是蒙你的,这种标准行为之外的东西,不要去太关注
FZNPLY 2011-08-29
  • 打赏
  • 举报
回复
jackyjkchen,18楼,你说的对,第二个我没有使用string,而是char*,所以出现了那个奇怪的现象。
你说的这句话:
gcc的string析构函数没有什么分支,就是因为引用计数的需要,不能在释放后改写内存,而普通的数组就没这个限制。
我觉得最是切中问题要害。

我不知道如何实现,根据引用计数,在delete(释放)空间之后,而不改写内存?
FZNPLY 2011-08-29
  • 打赏
  • 举报
回复
19楼,刚才在10楼不是你给我提供的方法吗?当函数这样声明后,
void myFunction(const char * & const_pcstr_t)
通过这样的调用语句:myFunction(const_pcstr_t);
这个时候const_pcstr_t已经不是NULL了,已经重新指向了str.c_str()返回的位置了
所以之后的一句:printf("函数外面,字符串为: %s\n", const_pcstr_t);
因该能够避免你说的为NULL吧,而且我在g++下测试已经成功
FZNPLY 2011-08-29
  • 打赏
  • 举报
回复
嗯,我明白你说的意思,就像你说的,string使用了引用计数、写时拷贝,很可能不同情景下的析构有所区别,
我还是想知道,这些是如何做到的,想求你解释下其中的原理。

如果我想实现一个类,用这个类初始化一个对象,当把这个对象析构了,我仍然能访问从这个对象返回的一个C风格数组,当然也想string一样,不会出现内存的泄漏。

我是从这个里面得到了一些启发,突然想实现这样一个功能,不知道从何入手,求助各位高手,谢谢你的指点,
luciferisnotsatan 2011-08-29
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 jackyjkchen 的回复:]

引用 10 楼 luciferisnotsatan 的回复:

引用 8 楼 fznply 的回复:

3楼,不好意思啊,我刚才忘记初始化那个指针了,下面这样就应该没问题了吧
const char * const_pcstr_t = NULL;

这回应该会崩掉。
void myFunction(const char *p_ch)
这个只改变了形参p_ch的指向,没有改变实参 ……
[/Quote]
恩,vc的printf实现里有判断,如果是NULL,就直接输出(null),而没去访问NULL地址。
vc里直接访问NULL,也一样崩。
jackyjkchen 2011-08-29
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 jackyjkchen 的回复:]

引用 15 楼 fznply 的回复:

jackyjkchen,12楼,你用的是VC吧,可能和g++不同,但是我这里出现的情况是:
string str对象析构后,仍然可以访问str.c_str()返回的那个字符串
而我自己new的char数组,delete之后,再访问那个字符串,发现内容是空了。


没明白我的意思?就是告诉你别纠结这个事情了。其实g++和vc都没有违反标准。
……
[/Quote]

看错了,第二个你根本没用string,而是char*吧,那就更好解释了

gcc的string析构函数没有什么分支,就是因为引用计数的需要,不能在释放后改写内存,而普通的数组就没这个限制
jackyjkchen 2011-08-29
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 fznply 的回复:]

jackyjkchen,12楼,你用的是VC吧,可能和g++不同,但是我这里出现的情况是:
string str对象析构后,仍然可以访问str.c_str()返回的那个字符串
而我自己new的char数组,delete之后,再访问那个字符串,发现内容是空了。
[/Quote]

没明白我的意思?就是告诉你别纠结这个事情了。其实g++和vc都没有违反标准。

gcc的string使用了引用计数、写时拷贝,很可能不同情景下的析构有所区别
FZNPLY 2011-08-29
  • 打赏
  • 举报
回复
14楼,你说的那个问题,10楼的好友已经给我指出了解决方法,谢谢
FZNPLY 2011-08-29
  • 打赏
  • 举报
回复
jackyjkchen,12楼,你用的是VC吧,可能和g++不同,但是我这里出现的情况是:
string str对象析构后,仍然可以访问str.c_str()返回的那个字符串
而我自己new的char数组,delete之后,再访问那个字符串,发现内容是空了。
jackyjkchen 2011-08-29
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 luciferisnotsatan 的回复:]

引用 8 楼 fznply 的回复:

3楼,不好意思啊,我刚才忘记初始化那个指针了,下面这样就应该没问题了吧
const char * const_pcstr_t = NULL;

这回应该会崩掉。
void myFunction(const char *p_ch)
这个只改变了形参p_ch的指向,没有改变实参 const_pcstr_t
改成
void myFunction……
[/Quote]
不过VS2010不会崩,运行时做了处理了,直接输出(null)
FZNPLY 2011-08-29
  • 打赏
  • 举报
回复
luciferisnotsatan 你说的对,这样输出的结果
myFunction(const_pcstr_t);//函数里输出“Function”
printf("函数外面,字符串为: %s\n", const_pcstr_t);//函数外输出“Function”
jackyjkchen 2011-08-29
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 fznply 的回复:]

嗯,你说的我同意,如果都在g++测试应该会是同样的结论吧,但是块作用域中
{
string str("Block");
printf("块作用域里面,字符串为: %s\n", str.c_str());//输出“Block”
const_pcstr_t = str.c_str();
}
printf("块作用域外面,字符串为: %s\n", const_pcstr_t);//……
[/Quote]
块作用域也是一样啊,未定义行为,我这里就没数据了
FZNPLY 2011-08-29
  • 打赏
  • 举报
回复
嗯,你说的我同意,如果都在g++测试应该会是同样的结论吧,但是块作用域中
{
string str("Block");
printf("块作用域里面,字符串为: %s\n", str.c_str());//输出“Block”
const_pcstr_t = str.c_str();
}
printf("块作用域外面,字符串为: %s\n", const_pcstr_t);//输出“Block“
string对象str已经析构,他也应该释放了空间,但是输出內容没有变化,而下面的删除后,输出字符串却发生变化了。
strcpy(pcstr_t , "Heap");
printf("堆空间删除之前,字符串为: %s\n", pcstr_t);//输出heap

delete [] pcstr_t;
printf("堆空间删除之后,字符串为: %s\n", pcstr_t);//输出空“”
luciferisnotsatan 2011-08-29
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 fznply 的回复:]

3楼,不好意思啊,我刚才忘记初始化那个指针了,下面这样就应该没问题了吧
const char * const_pcstr_t = NULL;
[/Quote]
这回应该会崩掉。
void myFunction(const char *p_ch)
这个只改变了形参p_ch的指向,没有改变实参 const_pcstr_t
改成
void myFunction(const char *&p_ch)
jackyjkchen 2011-08-29
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 fznply 的回复:]

6楼,你说堆上的内容没有改变,为什么下面这句话,输出为空?
printf("堆空间删除之后,字符串为: %s\n", pcstr_t);
[/Quote]
堆上的数据有没有改变不是标准规定好的,编译器愿意改不愿意改,什么情况会改都不确定。

VC是改的。

但有一点是确定的,地址在delete之后绝不会变
FZNPLY 2011-08-29
  • 打赏
  • 举报
回复
3楼,不好意思啊,我刚才忘记初始化那个指针了,下面这样就应该没问题了吧
const char * const_pcstr_t = NULL;
FZNPLY 2011-08-29
  • 打赏
  • 举报
回复
6楼,你说堆上的内容没有改变,为什么下面这句话,输出为空?
printf("堆空间删除之后,字符串为: %s\n", pcstr_t);
加载更多回复(6)

64,282

社区成员

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

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