C++字符串详解
许多程序都大量应用到字符串。C++ 为处理字符串提供了两种不同数据类型:C 字符串和 string 类。
string 类库有许多处理字符串的函数,这些函数可以执行许多实用的和字符串相关的功能,并且提供了编程上的安全防护,而这正是 C 字符串处理函数所缺乏的。出于以上理由,你应该会更喜欢使用 string 类而不是 C 字符串。
尽管如此,每个 C++ 程序员都应该对C字符串有足够的了解。string 类构建于 C 字符串之上,所以,了解 C 字符串有助于理解 string 类。此外,还有很多程序是在 string 类加入到 C++ 标准之前编写的,这样的程序需要能理解 C 字符串的程序员来维护它们。最后,程序员如果需要编写和维护底层代码,例如 string 类库或操作系统的一部分,则必须使用 C 字符串来表示字符串数据。
C 字符串是存储在连续内存位置中的字符序列,并以 null 字符结尾。回想一下,null 字符是 ASCII 码为 0 的字符。在程序中,null 字符通常写成
无论 C 字符串以 3 种形式中的哪一种出现在程序中,它始终是以 null 字符结尾的字符数组,并由指向数组中第一个字符的指针表示。换句话说,C 字符串的类型就是以下形式的:
当编译器遇到诸如 "Bailey" 这样的字符串常数时,它分配一个由 7 个字符组成的数组,在数组的前 6 个条目中存储 "Bailey" 的 6 个字符,然后将 null 字符存储在最后一个条目中,如图 1 所示。然后,编译器将数组的第一个字符的地址(char * 类型)作为字符串常数的值。
图 1 字符串常数的数组
下面的程序说明了一个事实,即字符串常数被编译器认为是一个类型为 const char * 的值。关键字 const 表示编译器不希望程序员改变字符串常数的内容。
请注意,在这种情况下,编译器已将所有字符串常数存储在连续内存位置的程序中。
例如,如果 C 字符串长度最多为 19 个字符,则需要分配一个包含 20 个字符的数组,代码如下:
定义为数组的 C 字符串可以通过 3 种方式赋予其值:使用字符串常数进行初始化,从键盘或文件中读取字符,或者一次一个字符地将字符复制到数组中来。以下是一些初始化的例子:
我们知道,可以使用输入和输出流类的各种对象、运算符和成员函数来读取和写入定义为数组的 C 字符串。存储为程序员定义数组的 C 字符串可以使用标准下标符号进行处理。
下面的程序就是一个例子。它一次输出一个字符串,当它找到 null 终止符时停止。它使用了 getline 成员函数来读取要输出的字符串。
这两者之间的区别在于,在第一种情况下,用于存储字符串的数组由编译器隐式分配,而在第二种情况下,数组由程序员明确分配。
表示 C 字符串的第 3 种方法是使用指向 char 的指针指向一个 C 字符串,该字符串的存储已经通过其他两种方法之一分配了。以下是以这种方式使用 C 字符串的一些示例:
例如,来看以下代码:
string 类库有许多处理字符串的函数,这些函数可以执行许多实用的和字符串相关的功能,并且提供了编程上的安全防护,而这正是 C 字符串处理函数所缺乏的。出于以上理由,你应该会更喜欢使用 string 类而不是 C 字符串。
尽管如此,每个 C++ 程序员都应该对C字符串有足够的了解。string 类构建于 C 字符串之上,所以,了解 C 字符串有助于理解 string 类。此外,还有很多程序是在 string 类加入到 C++ 标准之前编写的,这样的程序需要能理解 C 字符串的程序员来维护它们。最后,程序员如果需要编写和维护底层代码,例如 string 类库或操作系统的一部分,则必须使用 C 字符串来表示字符串数据。
C 字符串是存储在连续内存位置中的字符序列,并以 null 字符结尾。回想一下,null 字符是 ASCII 码为 0 的字符。在程序中,null 字符通常写成
'\0'
。程序中通常使用整数 0 或常量 NULL 来表示 null 字符。因此,以下所有语句都会将 null 字符存储到字符变量中:char ch1, ch2, ch3; ch1 = '\0'; ch2 = 0; ch3 = NULL;由于数组是一系列连续的存储位置,它们存储相同类型的值,所以 C 字符串实际上是一个以 NULL 结尾的字符数组。C 字符串可以按以下 3 种形式之一出现在程序中:
- “硬编码”字符串文字。
- 程序员定义的字符数组。
- 指向字符的指针。
无论 C 字符串以 3 种形式中的哪一种出现在程序中,它始终是以 null 字符结尾的字符数组,并由指向数组中第一个字符的指针表示。换句话说,C 字符串的类型就是以下形式的:
char *
也就是说,C 字符串的类型是指向 char 的指针。字符串常数
字符串常数作为用双引号括起来的字符序列直接写入程序中。例如:
"What is your name?"
"Bailey"
当编译器遇到诸如 "Bailey" 这样的字符串常数时,它分配一个由 7 个字符组成的数组,在数组的前 6 个条目中存储 "Bailey" 的 6 个字符,然后将 null 字符存储在最后一个条目中,如图 1 所示。然后,编译器将数组的第一个字符的地址(char * 类型)作为字符串常数的值。
图 1 字符串常数的数组
下面的程序说明了一个事实,即字符串常数被编译器认为是一个类型为 const char * 的值。关键字 const 表示编译器不希望程序员改变字符串常数的内容。
//This program demonstrates that string literals are pointers to char. #include <iostream> using namespace std; int main() { //Define variables that are pointers to char const char *p, *q; // Assign string literals to the pointers to char p = "Hello "; q = "Bailey"; // Print the pointers as C-strings! cout << p << q << endl; // Print the pointers as C-strings and as addresses cout << p << " is stored at " << int (p) << endl; cout << q << " is stored at " << int(q) << endl; // A string literal can be treated as a pointer! cout << "string literal stored at " << int ("literal"); return 0; }程序输出结果:
Hello Bailey
Hello is stored at 4952101
Bailey is stored at 4952108
string literal stored at 4952130
请注意,在这种情况下,编译器已将所有字符串常数存储在连续内存位置的程序中。
程序员定义的字符数组
字符串常数只能保存硬编码到程序中的 C 字符串。要使用从键盘或文件中读取字符的 C 字符串,必须明确定义一个数组来保存 C 字符串的字符。在这样做时,还应该确保在数组中为终止的 null 字符分配一个附加条目。例如,如果 C 字符串长度最多为 19 个字符,则需要分配一个包含 20 个字符的数组,代码如下:
const int SIZE = 20;
char company[SIZE];
定义为数组的 C 字符串可以通过 3 种方式赋予其值:使用字符串常数进行初始化,从键盘或文件中读取字符,或者一次一个字符地将字符复制到数组中来。以下是一些初始化的例子:
const int SIZE = 20; char company[SIZE] = "Robotic Systems, inc"; char corporation [] = "C. K. Graphics";釆用通过字符串常数初始化一个数组的方式时,数组定义中数组的大小是可选的。如果没有指定,则编译器会将其大小设置为比初始化字符串中的字符数多一个(因为要为 null 终止符留出空间)。
我们知道,可以使用输入和输出流类的各种对象、运算符和成员函数来读取和写入定义为数组的 C 字符串。存储为程序员定义数组的 C 字符串可以使用标准下标符号进行处理。
下面的程序就是一个例子。它一次输出一个字符串,当它找到 null 终止符时停止。它使用了 getline 成员函数来读取要输出的字符串。
// This program cycles through a character arrayA displaying each element until a null terminator is encountered. #include <iostream> using namespace std; int main() { const int LENGTH = 80; // Maximum length for string char line[LENGTH]; // Array of char // Read a string into the character array cout << "Enter a sentence of no more than " << LENGTH-1 << " characters : \n"; cin.getline(line, LENGTH); cout << "The sentence you entered is:\n"; // Loop through the array printing each character for(int index = 0; line[index] != '\0'; index++) { cout << line[index]; } return 0; }程序输出结果:
Enter a sentence of no more than 79 characters :
C++ is challenging but fun!
The sentence you entered is:
C++ is challenging but fun!
指向char的指针
正如前面所看到的,C 字符串可以表示为字符串常数或字符数组。这两个方法都将分配一个数组,然后使用该数组的地址作为指向 char 的指针来实际表示字符串。这两者之间的区别在于,在第一种情况下,用于存储字符串的数组由编译器隐式分配,而在第二种情况下,数组由程序员明确分配。
表示 C 字符串的第 3 种方法是使用指向 char 的指针指向一个 C 字符串,该字符串的存储已经通过其他两种方法之一分配了。以下是以这种方式使用 C 字符串的一些示例:
char name[] = "John Q. Public"; char *p; p = name; //指向已有的C字符串 cout << p << endl; // 打印 p = " Jane Doe"; //指向其他C字符串 cout << p << endl; // 打印使用指针变量来表示 C 字符串的一个主要优点是能够使指针指向不同的 C 字符串。使用指向 Char 的指针作为 C 字符串的另一种方法是定义该指针,然后将其设置为指向由 new 运算符返回的动态分配的存储。下面的程序对此进行了演示:
// This program illustrates dynamic allocation of storage for C-strings. #include <iostream> using namespace std; int main() { const int NAME_LENGTH = 50; // Maximum length char *pname = nullptr; // Address of array // Allocate the array pname = new char[NAME_LENGTH]; // Read a string cout << "Enter your name: "; cin.getline(pname, NAME_LENGTH); //Display the string cout << "Hello " << pname; //Release the memory delete[ ] pname; return 0; }程序输出结果:
Enter your name: George
Hello George
例如,来看以下代码:
char *pname; cout << "Enter your name: "; cin >> pname;以上代码是错误的,因为程序试图将一个字符串读入 pname 指向的内存位置,而 pname 则并未正确初始化。
所有教程
- C语言入门
- C语言编译器
- C语言项目案例
- 数据结构
- C++
- STL
- C++11
- socket
- GCC
- GDB
- Makefile
- OpenCV
- Qt教程
- Unity 3D
- UE4
- 游戏引擎
- Python
- Python并发编程
- TensorFlow
- Django
- NumPy
- Linux
- Shell
- Java教程
- 设计模式
- Java Swing
- Servlet
- JSP教程
- Struts2
- Maven
- Spring
- Spring MVC
- Spring Boot
- Spring Cloud
- Hibernate
- Mybatis
- MySQL教程
- MySQL函数
- NoSQL
- Redis
- MongoDB
- HBase
- Go语言
- C#
- MATLAB
- JavaScript
- Bootstrap
- HTML
- CSS教程
- PHP
- 汇编语言
- TCP/IP
- vi命令
- Android教程
- 区块链
- Docker
- 大数据
- 云计算