关键字:string类 字符串 C-风格字符串 C库字符串函数
字符串:存储在内存的连续字节中的一系列字符。
C++处理字符串的方式有两种:
C-风格字符串:由一系列字符组成,以空值字符结尾。
由字符串的定义——存储在连续字节中的一系列字符,我们可以将字符串存储在char数组中,其中每个字符都位于自己的数组元素中。
请对比下面两个字符数组声明:
char dog[8] = {'b', 'e', 'a', 'u', 'x', ' ', 'I', 'I'}; //不是字符串
char cat[8] = {'f', 'a', 't', 'e', 's', 's', 'a', '\0'}; //是字符串
上面这两个数组都是char数组,但只有第二个数组是字符串。
空字符对C-风格字符串而言至关重要。例如,C++有很多处理字符串的函数,其中包括cout使用的那些函数。它们都逐个地处理字符串中的字符,直到达到空字符为止。如果使用cout显示上面的cat这样的字符串,则将显示前7个字符,发现空字符后停止。但是,如果使用cout显示上面的dog数组(它不是字符串),cout将打印出数组中的8个字母,并接着将内存中随后的各个字节解释为要打印的字符,直到遇到空字符为止。由于空字符(实际上是被设置为0的字节)在内存中很常见,因此这一过程将很快停止。但尽管如此,还是不应将不是字符串的字符数组当作字符串来处理。
在cat数组示例中,这是一个冗长乏味的将数组初始化为字符串的工作,我们接下来介绍另一种将字符数组初始化为字符串的方法——只需使用一个用引号括起的字符串即可,这种字符串被称为字符串常量:
char bird[11] = "Mr. Cheeps"; //空字符'\0'隐式的被包括在字符串常量内 char fish[] = "Bubbles"; //让编译器自己计算char数组长度
用引号括起的字符串隐式地包括结尾的空字符,因此不要显式地包括它。此外,各种C++输入工具通过键盘输入,将字符串读入到char数组中,将自动加上结尾的空字符。
C-风格字符串与常规char数组之间的一个重要区别:字符串有内置的结束字符,而char数组只有以空值字符结尾才是字符串。
一个keypoint是应确保数组足够大,能够存储字符串中所有字符——包括空字符,所以使用字符串常量初始化字符数组时让编译器计算元素数目更为安全。
接下来,我们探讨为什么字符串常量"S"与字符常量'S'为何不能互换:
- 字符常量是字符串编码的简写表示,在ASCII系统上,'S'只是83的另一种写法;
- "S"不是字符常量,它表示的是两个字符(字符S和\0)组成的字符串,此外,"S"实际上表示的是字符串所在的内存地址。
上面都是C-风格字符串的常规使用,接下来,我们将探讨一下C库字符串函数。
现在我们想知道字符数组word中的字符串是不是mate,将测试代码写成如下形式:
word == "mate"
数组名word是数组的地址,字符串常量也是"mate"的地址。因此,上面的关系表达式不是判断两个字符串是否相同,而是查看它们是否存储在相同的地址上。
由于C++将C-风格字符串视为地址,因此如果使用关系运算符来比较它们,将无法得到满意的结果。
此时,我们就应该使用C-风格字符串库中的strcmp()函数来比较。该函数接受两个字符串地址作为参数,这意味着参数可以是指针、字符串常量或字符数组名。
下面是示例程序:
1 // compstr1.cpp -- comparing strings using arrays 2 #include <iostream> 3 #include <cstring> // prototype for strcmp() 4 int main() 5 { 6 using namespace std; 7 char word[5] = "?ate"; 8 9 for (char ch = 'a'; strcmp(word, "mate"); ch++) 10 { 11 cout << word << endl; 12 word[0] = ch; 13 } 14 cout << "After loop ends, word is " << word << endl; 15 return 0; 16 }