2010/11/3
关键字:C风格字符串
C风格字符串(C-style character string)
以空字符null结束的字符数组.
char c1[] = {'C', '+', '+'}; //不是C风格字符串
char c2[] = "C++"; //是
C风格字符串标准库
必须包含头文件#include<cstring>
cstring是string.h的C++版本,string.h是C语言提供的标准库.
注意这些标准库函数不会检测起字符串参数.使用这些函数是不安全的,强烈建议不要使用.
使用这些函数有下面限制:
1. 参数不能为空
2. 传入的字符串必须以null结尾
3. 一些函数会修改传入的字符串,必须确保被修改的字符串足够大以容纳本函数新生产的字符串.
C风格字符串标准库 |
|
strlen(s) |
返从s开始直到null的长度,不包括null |
strcmp(s1, s2) |
比较两个字符串s1和s2是否相同,若s1和s2相等,返回0,若s1大于s2,返回正数,若s1小于s2,则返回负数. |
strcat(s1, s2) |
将字符串s2接到s1后面,返回s1. |
strcpy(s1, s2) |
将s2复制给s1,并返回s1 |
strncat(s1, s2, n) |
将字符串s2的前n个字符接到s1后面,返回s1. |
strncpy(s1, s2, n) |
将s2前n个字符复制给s1,并返回s1 |
C风格字符串也可以关系操作符比较,但实际上比较的是地址,比较地址是无意义的:
char c1[] = {'C', '+', '+'}; //不是C风格字符串
char c2[] = "C++"; //是
char *p1 = &(c1[1]);
bool b = (c1) > (c2); //实际比较的是地址
b = (c1) > (p1); //实际比较的是地址
也就是说C风格字符串需要比较时必须用strcmp函数.
使用strcat、strcpy等标准库函数时,调用者必须确保目标字符串具有足够的大小.
char cp1[] = {'1', '2', '3'}; //错误,非C风格字符串
char cp2[] = "12345";
char largeStr[3+1+5]; //错误,目标字符串要多一个结尾用以保存null
strcpy(largeStr, cp1); //错误,非C风格字符串不能使用strcpy
strcat(largeStr, " "); //largeStr可能已经溢出
strcat(largeStr, cp2); //largeStr可能已经溢出
修改后
char cp1[] = {'1', '2', '3', 0};
char cp2[] = "12345";
char largeStr[3+1+5+1];
strcpy(largeStr, cp1);
strcat(largeStr, " ");
strcat(largeStr, cp2);
不过最好还是使用strn系列函数,明确指定复制、连接的字符个数
char cp1[] = {'1', '2', '3', 0};
char cp2[] = "12345";
char largeStr[3+1+5+1];
strncpy(largeStr, cp1, 4);
strncat(largeStr, " ", 2);
strncat(largeStr, cp2, 6);
最简便安全的方法是使用string
string cp1 = "123";
string cp2 = "12345";
string largeStr = cp1;
largeStr += " ";
largeStr += cp2;
对于大部分程序,使用string不仅增强安全性,而且效率也有提高,因此尽量避免使用C风格字符串.
动态数组
创建和销毁分别用new name[]和delete[] name.
动态创建的数据如果需要初始化,只能初始化为元素的默认值,即内置类型只能初始化为0,而类类型只能调用输入参数为void的构造函数或默认构造函数.而不可以使用初始化列表.
class A
{
public:
A(){a = 1;}
private:
int a;
};
int main()
{
int *p = new int[10]; //未初始化
int *p1 = new int[10](); //初始化为默认值0
string *pstr = new string[10]; //初始化为空字符串
A *pa1 = new A[10](); //调用输入参数为void的构造函数,a为1
A *pa2 = new A[10]; //调用A的输入参数为void的构造函数,a为1,A没有默认构造函数
B *pb1 = new B[10](); //调用输入参数为void的构造函数,即调用B的默认构造函数,b未初始化
B *pb2 = new B[10]; //调用B的输入参数为void的构造函数即默认构造函数,b未初始化
return 0;
}
如果类未声明输入参数为void的构造函数,则编译器会自动生成默认构造函数,默认构造函数形如A(){},只分配地址,不初始化变量.
string和C风格字符串
有几点需要注意:
string str = "1243";
str = str + "1243"; //错误
str = "1243" + str; //错误
str += "1234"; //正确,这里的+=是string类的重载操作符并不等同于str = str + "1234"
char *p = str.c_str(); //错误
const char *p = str.c_str(); //正确,str.c_str()返回的是string内部数组首地址,不允许被修改
str += "12";
const char *p2 = p; //虽然语法上没问题,但实际可能是错误的,因为p保存的是str之前的内存,str+="12"之后该地址可能无效了,应该将字符串copy到其他内存中
vector和数组
唯一需要知道的是可以用数组初始化vector.
int array[4] = {1,2,3}; //array[3]会被初始化为
vector<int> vec(array, array+4); //初始化方法为vector(begin, end),end为哨兵迭代器,vec中会包含从array开始到array+3的所有元素
指针与多维数组
C++中的多维数组实际是数组的数组.
理解下面的代码即可:
int array[3][4];
int (*p1)[4] = array; //从内向外理解即可.p1是指针,指向int [4]的指针.即p1是指向包含个整型元素的数组的指针
int *p2[4]; //从内向外理解即可.p2是数组,数组中包含个int *元素.等同于int* p[4];
p1 = &(array[2]); //array实际是内层数组的首地址,&array[2]是外层数组的第个元素的首地址
可以用typedef简化理解
typedef int intarray4[4];
for (intarray4 *p=array; p!=array+3; ++p)
{
for (int *q=*p; q!=(*p)+4; ++q)
{
cout << *q << endl;
}
}
intarray4是一种类型,int[4]类型.p就是int[4]数组类型的指针.
对p解引用得到int[4]类型的数组中的第一个元素的值,q指针依次遍历指向4个元素.