11.转换为C风格字符串
有三个函数可以将std::string的内容,转换为C风格的字符串或字符数组。
(1)c_str()
const char* c_str() const;
在数据后面故意添加上 '\0' 结束符(但不影响std::string维护的字符串数),然后返回数据的指针。
如果所包含的字符中,原来已经夹有 '\0' , 则调用者将只得到被截断的字符串:
std::string s1("abc\0efg", 7);
cout << "s1.length = " << s1.length() << endl; //7
cout << "s1.c_str is " << s1.c_str() << endl; //abc
c_str()返回的是const char*,所以调用者不能通过它修改std::string的字符内容。
(2)data()
在C++11中,data()和c_str()干一模一样的活,同样返回补上 '\0' 的C风格的
常量字符(const char *);在C++ 11之前,data()不会自动添加 '\0',因此处理起来一定要小心。
【危险】:std::string的c_str()与data()返回值的有效期
c_str()或data()都返回一个const char * 的指针,这个指向的内存并不是一直有效,比如:
std::string s = "I am a programmer.";
char const * pstr = s.c_str();
s.clear(); //清除掉
cout << pstr << endl; //危险
一般这么认为:s.clear()只是将内存清掉,那之前的pstr就变成指向一个空字符串,最后一行代码大不了屏幕什么也没有输出而已。但事实上pstr所指向的内容有可能是空,也有可能是远洋,也有可能已经错乱了。
无论是c_str()还是data(),在原string对象调用了非常量的成员操作之后,就失效了。
在实际项目中,推荐更严格而易于排查的规定:只在同一个语句中使用。
//比如需要给C函数atoi传递一个C风格的字符串,请写成
int a = atoi(s.c_str());
//而不要写成:
char const* pstr = s.c_str();
...
int a = atoi(pstr);
因为,现在“...”的位置,可能是空行,但以后说不定就有哪位不听话的家伙,在那里插入一堆代码(不知不觉地毁掉pstr指针)。
12. 复制子串
c_str()和data()没有复制内存的行为存在,所以高效是必然的,
缺点:
一,是前面提到的危险
二,是它返回的const char *,所以不允许修改内容,这就有了std::string的copy成员函数:
size_t copy(char* s, size_t n, size_t pos = 0) const;
或许copy应该取名为copy_to,其功能体现能更直观点,它就是将内容(不添加 '\0'),复制到入参的char * s中。n则指定最多要复制多少个字符,pos则表示从当前串哪个位置开始复制。返回值是实际复制了几个字符(因为源串的长度可能小于n)。
std::string s("0123456");
char buf[10];
size_t count = s.copy(buf, string::npos, 1);
//别忘了手工添加结束符,因为下一行要输出
buf[count] = '\0';
cout << buf << endl;