#include <iostream>
#include <string>
using namespace std;
int main()
{
string s="12345";
const char* p=s.c_str(); //用C语言的方式定义一个指针p,使它指向C++中字符串s
//c_str()函数就是C++ string类中为了兼容指向C语言字符串而提供的一个函数
cout<<p<<endl;
s.append("abcd");
cout<<p<<endl;
return 0;
}
想想这个程序会输出什么呢?const char* p=s.c_str(); 这句代码已经定义了一个指针使它指向s字符串,所以你是不是觉得程序的输出会是:12345 12345abcd呢?
好来看一下实际的程序输出:
delphi@delphi-vm:~$ ./a.out
12345
12345
delphi@delphi-vm:~$
程序的两次输出都为12345,嗯?这个就有点奇怪呢哈?
我们来看一下实际的原因吧。
在C++string类中,维护了一个m_cstr的指针,这个指针第一次指向"12345"这偏内存区域,其中const char* p也指向"12345"这遍内存区域。然而,经过s.append("abced")这句代码的时候,m_cstr就以及指向了"12345abced"这遍内存区域了,但是const char* p还是指向原来的"12345"这遍原来的内存区域(原来的内存区域以及被释放了,其实p以及成为野指针了),所以2次打印就输出一样的结果了。来我们来吧程序稍微的改改,就可以得到了正确的结果。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s="12345";
const char* p=s.c_str();
cout<<p<<endl;
s.append("abcd");
p=s.c_str();//重新让指针p指向m_cstr所指向的内存区域,就可以得到正确的结果
cout<<p<<endl;
return 0;
}
输出结果为:
delphi@delphi-vm:~$ g++ test.cpp
delphi@delphi-vm:~$ ./a.out
12345
12345abcd
再来看一个很奇诡的例子:
#include <iostream>
#include <string>
using namespace std;
int main()
{
const char* p="12345";
string s="";
s.reserve(10); //将s字符串所代表的的存储空间重置为10个大小的字节
for(int i=0;i<5;i++)
{
s[i]=p[i];
}
if(!s.empty())
{
cout<<s<<endl; //是不是很期待输出 12345呢??
}
return 0;
}
原因分析:
在for执行之前,m_cstr指向一篇10字节的内存区域,字符串长度为0。然后for循环执行后,m_cstr所指向的内存区域的前5个字节的确变成了,12345,但是它的长度m_length还是0,所以输出结果就是什么都没有的原因了。
#include <iostream>
#include <string>
using namespace std;
int main()
{
const char* p="12345";
string s="";
s.reserve(10);
for(int i=0;i<5;i++)
{
s[i]=p[i];
}
if(!s.empty())
{
cout<<s<<endl;
}
for(int i=0;i<5;i++)
{
cout<<s[i]<<endl;// 输出1 2 3 4 5
}
cout<<s.length()<<endl;// 输出 0
return 0;
}
总结:这2个例子告诉我们,C++标准库中的string类,通过一个指针来保存一个一遍内存空间,其实它的内部也有一个成员变量来保存这偏内存空间所存放字符串的长度。还有一点就是用C++编程(面向对象)就尽量避开用C语言(面向过程)的编程习惯。