一、概述
有时候在使用字符串时,总把C++的字符串类型和C风格的字符串混淆,或者独立为不同概念,为此特定查找资料,总结两者之间的区别和联系。如有错误,恳请指出。
二、C语言中的字符串
在C语言中,对字符串的处理主要是使用字符数组(以空字符’\0’为结尾)或者是指向字符类型的指针,即用char*,但指针在实现字符串类型是较为麻烦的。用法如下:
//example 1
char str[15]="Hello world!"; //使用字符数组, 也可以char str[] ="Hello world!";
*(str+2)=’L’; //使用*()指定数组修改位置改变第3个字母
char *p= str; //使用指针p指向数组str的首地址
*(p+2)=’L’; //仍可间接修改数组str的第3个字母
char *ptr ="Hello world!"; //使用指针指向一行字符串;
*(ptr+2)='L'; //错误,编译虽通过,但无法改变字符串的第3个字母
这是因为第一个字符串是由数组str开辟的,数组的每个值是可以改变的变量,而第二个指针ptr指向的是字符串常量,而字符串常量是无法改变的,故为了避免误判,指向字符串常量时应该使用const指针。
如 const char *ptr ="Hello world!"
//example 2
char *ptr = new char[15]; //由new在堆中开辟的15个char型空间,ptr指向该空间首地址
ptr = "abcdefg"; //错误,不可再用ptr指向另一行字符串首地址
ptr[0]='A'; //可由ptr[下标]或*(ptr+索引)写或读数据,用后者指针的形式速度较快
*ptr ='a'; //可改变ptr[0]的值
由于是堆分配的内存,故函数调用结束后,不会自行撤销,故需要在函数加入delete []ptr ;语句。
三、C++中的字符串
由于C风格字符串较为复杂,不适合大程序的开发,故C++标准库定义了string类,头文件形式为#include <string>,而头文件<string.h>和<cstring>都是错误的,这两个头文件主要定义处理C风格字符串的一些方法,譬如strlen(), strcpy()等。第一个为C的头文件格式,第 2个为C++的C风格头文件。
先看sting类的原形:
//example 3
class String
{
public:
String(const char *str = NULL);// 普通构造函数
String(const String &other); //拷贝构造函数
~ String(void); //析构函数
String & operator =(const String &other);//赋值函数
private:
char *m_data;// 用于保存字符串
};
//普通构造函数
String::String(const char *str)
{
if(str==NULL)
{
m_data = new char[1]; // 对空字符串自动申请存放结束标志'\0'的//加分点:对m_data加NULL 判断
*m_data = '\0';
}
else
{
int length = strlen(str);
m_data = new char[length+1]; // 若能加 NULL 判断则更好
strcpy(m_data, str);
}
}
// String的析构函数
String::~String(void)
{
delete[] m_data; // 或delete m_data;
}
//拷贝构造函数
String::String(const String &other) // 输入参数为const型
{
int length = strlen(other.m_data);
m_data = new char[length+1]; //对m_data加NULL 判断
strcpy(m_data, other.m_data);
}
//赋值函数
String & String::operator =(const String &other) // 输入参数为const型
{
if(this == &other) //检查自赋值
return *this;
delete[] m_data; //释放原有的内存资源
int length = strlen( other.m_data );
m_data = new char[length+1]; //对m_data加NULL 判断
strcpy( m_data, other.m_data );
return *this; //返回本对象的引用
}
可看成string类含有一个私有的指针char* m_data,由构造函数和拷贝构造函数用new开辟空间,并用strcpy复制传进来的字符串(C风格或string类对象),由析构函数释放内存。
(1)string类的构造函数和析构函数如下:
a) strings; //生成一个空字符串s
b) strings(str) //拷贝构造函数 生成str的复制品,str为string类的对象
c) strings(str,stridx) //将字符串str内“始于位置stridx”的部分当作字符串的初值
d) strings(str,stridx,strlen) //将字符串str内“始于stridx且长度顶多strlen”的部分作为字符串的初值
e) strings(cstr) //将C风格字符串作为s的初值
f)string s(chars,chars_len) //将C风格字符串前chars_len个字符作为字符串s的初值。
g) strings(num,c) //生成一个字符串,包含num个c字符,c是字符型
h) strings(beg,end) //以区间beg;end(不包含end)内的字符作为字符串s的初值
i) s.~string()//销毁所有字符,释放内存
(2)字符串操作函数
a)=,assign() //赋以新值
b)swap() //交换两个字符串的内容
c)+=,append(),push_back() //在尾部添加字符
d)insert() //插入字符
e)erase() //删除字符
f)clear() //删除全部字符
g)replace() //替换字符
h)+ //串联字符串
i)==,!=,<,<=,>,>=,compare() //比较字符串
j)size(),length() //返回字符数量
k)max_size() //返回字符的可能最大个数
l)empty() //判断字符串是否为空
m)capacity() //返回重新分配之前的字符容量
n)reserve() //保留一定量内存以容纳一定数量的字符
o)[ ], at() //存取单一字符
p)>>,getline() //从stream读取某值
q)<< //将谋值写入stream
r)copy() //将某值赋值为一个C_string
s)c_str() //将内容以C_string返回
t)data() //将内容以字符数组形式返回
u)substr() //返回某个子字符串
v)查找函数
w)begin()end() //提供类似STL的迭代器支持
x)rbegin() rend() //逆向迭代器
y)get_allocator() //返回配置器
这些成员函数给string字符串的操作带来了极大的方便。
(3)元素读写
//example 4
string str("abcdefg"); //调用构造函数初始化str
str[0] = 'A'; //使用下标访问,可访问可修改
str.at(0)='A'; //使用成员函数at()可访问可修改
但是注意使用下标一旦越界则不报任何异常,而使用at()则会报异常,当由str.length()函数得到字符串长度(不包含空字符),即可使用for遍历数组,另一种方法是使用迭代器
//example 5
string s = "abcdefg";
for(string::iterator iter = s.begin(); iter!=s.end(); iter++) //向前遍历
{
cout<<*iter<<endl;
}
for(string::reverse_iterator iter = s.rbegin(); iter!=s.rend(); iter++) //向后遍历
{
cout<<*iter<<endl;
}
string类提供了向前和向后遍历的迭代器iterator,迭代器提供了访问各个字符的语法,类似于指针操作,迭代器不检查范围。
用string::iterator或string::const_iterator声明迭代器变量,const_iterator不允许改变迭代的内容。常用迭代器函数有:
const_iterator begin()const;
iteratorbegin(); //返回string的起始位置
const_iterator end()const;
iteratorend(); //返回string的最后一个字符后面的位置
const_iterator rbegin()const;
iteratorrbegin(); //返回string的最后一个字符的位置
const_iterator rend()const;
iteratorrend(); //返回string第一个字符位置的前面rbegin和rend用于从后向前的迭代访问,通过设置迭代器string::reverse_iterator,string::const_reverse_iterator实现
四、C风格字符串和string类的转换
C++提供的由C++字符串得到对应的C风格字符串的方法是使用data()、c_str()和copy(),C风格字符串转为string则用构造函数即可。
//example 6
string var = "abcdefg";
const char *p = var.c_str(); //不加const则不正确,p所指向的字符是不可改变的
string str = "abcdefg";
char p[10];
strcpy(p,str.c_str());
p[0] ='A'; //因为p为长度10数组,故p所指向的值可改变
char *p = new char[str.length()+1];
strcpy(p ,str.data());
delete []p; //使用data()函数
char p[10];
str.copy(p,7);
p[str.length()+1]='\0'; //使用copy函数
data()以字符数组的形式返回字符串内容,但并不添加’\0’。c_str()返回一个以‘\0’结尾的字符数组,而copy()则把字符串的内容复制或写入既有的c_string或字符数组内。