c++系列文章(10):C风格字符串

  尽管C++支持C风格字符串,但是C++程序中最好还是不要使用它们,因为C风格字符串不仅使用不方便,而且极易引发程序漏洞,是诸多安全问题的根本原因。
  字符串字面值是一种通用结构的实例,这种结构就是C风格字符串。C风格字符串不是一种类型,而是为了表达和使用字符串而形成的一种约定俗成的写法。按此习惯书写的字符串存放在字符数组中并以空字符结束。以空字符结束的意思是在字符串最后一个字符后面跟着一个空字符(’\0’),一般用指针来操作这些字符串。

C标准库String函数

  C语言标准库提供了一组函数用于操作C风格字符串,它们定义在cstring头文件中。

strlen(p); //返回p的长度,空字符不计算在内
strcmp(p1,p2); //比较p1和p2的相等性,如果p1==p2,返回0;p1>p2,返回正值;p1<p2,返回负值
strcat(p1,p2); //将p2附加到p1之后,返回p1
strcpy(p1,p2); //将P2拷贝给p1,返回p1

  这些函数不负责验证其字符串参数,传入此类函数的指针必须指向以空字符作为结束的数组

char ca[] = {'c','+','+'}; //不以空字符结束
cout << strlen(ca) << endl; //严重错误:ca没有以空字符结束,strlen函数可能沿着ca在内的位置不断向前寻找,直到遇到空字符才停止。

  比较标准库string对象时,用普通的关系运算符和相等性运算符就行,但如果用在C风格字符串上,实际比较的将是指针而非字符串本身。要想比较两个C风格字符串需要调用strcmp函数

const char ca1[] = "A string example";
const char ca2[] = "A different string";
if(ca1 < ca2) //未定义,实际上比较的是两个const char*的值,这两个指针指向的并非同一个对象,所以将得到未定义的结果

  连接或拷贝C风格字符串也与标准库string对象的同类操作差别很大。正确的方法是使用strcat和strcpy函数,不过要想使用这两个函数,还必须提供一个用于存放结果字符串的数组,该数组必须足够大以便容纳下结果字符串及末尾的空字符

string largeStr = s1 + " " + s2; //标准库string对象可以直接进行相加
ca1 + ca2; //错误,试图将两个指针相加
// 如果我们计算错了largeStr的大小将引发严重错误
strcpy(largeStr,ca1); //把ca1拷贝给largerStr
strcat(largeStr," "); //在largeStr的末尾添加一个空格
strcat(largeStr,ca2); //把ca2连接到largeStr后面

  一个潜在的问题是,我们在估算largeStr所需的空间时不容易估准,而且largeStr所存的内容一旦改变,就必须重新检查其空间是否足够。这类代码充满了风险而且经常导致严重的安全漏洞,对于大多数应用来说,使用标准库string要比使用C风格字符串更安全、更高效。

与旧代码的接口

很多C++程序在标准库出现之前就已经完成了,它们肯定没有用到vector和string,而且有一些C++程序实际上就是与C语言或其他语言的接口程序,当然也无法使用C++标准库。因此现代C++程序必须提供一组功能,将vector和string与数组和C风格字符串进行衔接。

混用string对象和C风格字符串

  任何出现字符串字面值的地方都可以用空字符结束的字符数组来替代
    允许使用以空字符结束的字符数组来初始化string对象或为sting对象赋值
    在string对象的加法运算符中允许其中一个运算对象是以空字符结束的字符数组;
    在string对象的复合运算中允许使用以空字符结束的字符数组作为右侧的运算对象;
  但上述性质反过来就不成立:如果程序的某处需要一个C风格字符串,无法直接用string对象来代替它。例如不能用string对象直接初始化指向字符的指针,而需要借助c_str函数。c_str函数的返回值是一个C风格的字符串,也就是说该函数的返回结果是一个指针,该指针指向一个以空字符结束的字符数组,而这个数组所存的数据恰好与那个string对象的一样。结果指针的类型是const char*,从而确保我们不会改变字符数组的内容。我们无法保证c_str函数返回的数组一直有效,如果后续的操作改变了s的值就可能让之前返回的数组失去效用

char *str = s; //错误,不能用string对象初始化char*
const char *str = s.c_str(); //正确 

使用数组初始化vector对象

  前面介绍过,不允许使用一个数组为另一个内置类型的数组赋初值,也不允许使用vector对象初始化数组。相反,允许使用数组初始化vector对象,要实现这一目的,只需指明拷贝区域的首元素地址和尾后地址就可以

int int_arr[] = {0,1,2,3,4,5};
vector<int> ivec(begin(int_arr),end(int_arr)); //ivec有6个元素,分别是int_arr中对应元素的副本
vector<int> subVer(int_arr+1,it_arr+4); //subVer有3个拷贝元素:int_arr[1],int_arr[2],int_arr[3]

  用于创建ivec的两个指针实际上指明了用来初始化的值在数组int_arr中的位置,其中第二个指针应指向待拷贝区域尾元素的下一位置。

建议

现代的C++程序应当尽量使用vector和迭代器,避免使用内置数组和指针;应该尽量使用string,避免使用C风格的基于数组的字符串

扫描二维码关注公众号,回复: 10999399 查看本文章
发布了19 篇原创文章 · 获赞 6 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/hjc132/article/details/104514918