• 沒有找到結果。

字符串是一种非数值类型数据,它也是计算机加工处理的对象。C 语言用字符数组和指针 表示字符串,C++仍然支持这两种表示方法,另外,C++还可用字符串类型 string(后面章节 介绍)对象表示字符串,string 类型最终也是用字符数组和指针表示字符串,因此学好前两种 表示方法对学习 string 类型是有帮助的,本节介绍这两种方法。

4.4.1 字符串的概念

若干个字符连续排列起来构成的序列就是字符串。在源程序中要用一对双引号将字符串 括起来,如:"China",这对双引号只充当界限符,不是字符串的成员,仅便于编译系统识别 源程序中的字符串。字符串中字符的个数叫字符串的长度。

程序运行时,字符串在内存中占据一片连续的字节,串中的每个字符占一个字节,运行 中的程序根据串首字符所占字节的地址就可以找到整个字符串;为了让程序检测到串尾字符或 测定字符串的长度,C++语言规定在字符串尾添加一个字符'\0'作为字符串结束标志,这样程序 从串首字符向后找,遇到了字符串结束标志字符'\0'就表示找到了整个字符串,也就识别了字 符串。"China"的存储示意见图 4-14,图中 1000 表示首字符所占字节的地址。

C h i n a \0

1000

图 4-14 字符串存储示意图

字符串可以包括转义字符及 ASCII 码表中的控制字符(以转义字符出现)。字符串可以包 含汉字,如:"中国"、"中国=China"。一个汉字占两个字节,长度计为 2。

输入输出字符串时,仅作为界限标志的双引号和'\0'均不要输入,也不会输出。

4.4.2 字符串的存储表示法

1. 字符数组表示法

字符数组可用来存储字符串,其元素依次存储串中的各字符。内存中字符串尾总附加'\0' 字符,它要占一个字节,所以长度为 L 的字符数组能容纳长度不超过 L-1 的任何字符串,若 试图容纳长度超过 L-1 的字符串,将在编译时出现语法错误或运行时数组越界,必须设法避免。

字符数组名代表首字节地址,通过字符数组名就可以找到存于其中的字符串。用数组元素就可 以找到存于其中的字符。字符数组为字符串提供存储空间,因此字符数组相当于字符串变量。

如有定义:

char s1[]={'s','t','u','d','e','n','t','\0'};

其存储状态如图 4-15 所示:

s1[0] s1[1] s1[7]

s t u d e n t \0 图 4-15 字符数组存储示意图

显然,一个元素存放一个字符。

若通过赋值语句将各字符分别赋给字符数组的各元素,注意串尾要添上' \0'。为简便起见,

在编写程序时,可以直接用字符串初始化字符数组如下:

char s1[8]={"student"};

花括号也可省略,简单地写为 char s1[8]="student";

为避免计算字符串的长度,可以写成:

char s1[]="student";

系统将双引号括起来的字符依次存于字符数组的各个元素,并自动在末尾补上字符串结 束标志字符'\0',并一起存到字符数组中,上面数组 s1 的长度为 8,有效字符只有 7 个。若有 定义:

char s2[]={'s','t','u','d','e','n','t'};

s2的长度为 7,s2 中未存放完整字符串,如执行语句:

cout<<s2;

很可能得不到正确输出结果,该语句将在输出 student 之后继续输出,直至遇到 8 位全 0 代码(即'\0')为止。但写成:

char s2[8]={'s','t','u','d','e','n','t'};

可以得到正确输出,字符数组的初始化也与其他数组的初始化一样,对部分未明确指定 初值的元素,系统自动设定 0 值字符,也正是字符串结束标志符'\0'。

若要表示多个字符串,可以采用二维字符数组,例如:

char str2[][30]={"I am happy!","I am a student."};

用数组存储的字符串,串中字符通过数组元素来引用,例如:

char s[]="I am a student.";

用 s[i]或*(s+i)就能访问数组中字符串的第 i 个字符。

上面说明了用字符数组存储字符串,另一方面也表示可用字符串初始化字符数组。

2. 指针表示法

若将字符串首字符所占字节的地址存于字符基类型的指针变量中,则程序可通过该指针 变量访问字符串或其中的某个字符,因此,指针是字符串的一种表示法,例如:

char *cp ="I am a student.";

定义了指向字符的指针变量 cp,并初始化 cp 使它指向字符串的第一个字符('I')。以后就 可通过 cp 间接访问字符串或其中的某个字符,如*cp 或 cp[0]的值就是'I',用*(cp+i)或 cp[i]就 能访问字符串中的第 i 个字符。有时也称 cp 是指向字符串的指针变量。也可先定义 cp,事后 再赋值,如下:

char *cp;

cp="I am a student.";

这是将字符串首字符所占据字节的地址赋给 cp,而不是将字符串赋给 cp,cp 并不为字符 串提供存储空间。可以把该字符串"I am a student."看作字符串常量,由编译系统为它分配存储 空间。

用字符数组和字符指针变量都能表示字符串并参加各种运算,但两者之间是有区别的。

字符数组元素在内存中占据一片固定的连续内存空间,每个元素可存放一个字符,因此,字符 数组为字符串提供存储空间。在程序运行过程中,字符数组所占的固定内存空间在不同时刻可 存放不同的字符串,所以,字符数组相当于字符串变量。数组名代表这片内存空间的首地址,

是常量指针,因此它只能指向位于这个固定地址内存中的字符串。而字符指针变量并不为字符 串提供存储空间,但同一指针变量在不同时刻可指向内存中不同地址处的字符串,当然也可指 向存于数组中的字符串。既然数组名是指针常量,就不能用一个字符串给一个字符数组赋值,

例如:

char str[50];

str="I am a student.";

编译时将产生错误。但能用一个字符串给一个字符数组初始化。当然,字符数组各个元 素可单独赋值。

4.4.3 字符串的输入与输出

字符串的输入输出总的来说有两种方式:

(1)逐个字符输入输出,即每次输入输出一个字符,用循环结构控制整个串的输入输出。

(2)将整个字符串作为一个整体一次性完成输入输出。

每种方式具体完成输入输出操作又有两种方法,一种是调用标准 C 输入输出(Standard C I/O)函数库中的字符/字符串输入输出函数(scanf、printf、getchar、putchar、gets、puts 等),

C++同样支持这些函数。另一种是用 C++的标准输入输出流对象 cin 和 cout,这种方法使用简 单,举例如下:

设有:

char s1[20]="I am a student." 逐个输出字符语句:

for(i=0;s[i];++i) //循环条件等价于 s[i]!=0 cout<<s1[i]

将字符串一次性输出:

cout<<s1;

两者输出结果都为:

I am a student.

若要从第 i 个字符起输出字符串后面一截,则写成:

cout<<&s1[i]; //s1+i

若指针变量 cp 指向某个字符串,同样可以用上面两种方式输出。

若程序运行时要输入字符串,那么程序必须为它提供存储空间,一般采用一个够长的字 符数组,若有数组定义:

char s2[100];

则逐个字符输入语句:

for(i=0;i<15;++i) cin>>s2[i];

s2[i]= '\0'; //额外补加串结束标志

将字符串作为一个整体一次性输入语句:

cin>>s2;

这种方式输入字符串是以空格符、Tab 控制符、回车符作为终止标志,因此字符串中不能 含这些字符。若要输入以回车键为终止标志的一行字符则用 cin.getline()。

两种输入方式都必须保证输入的字符串长度比数组长度少 1。

上面是用 cout 对象的重载运算符>>和 cin 对象的<<进行输入输出,还可以用成员函数 cout.put()、cin.get()、cin.getline()进行灵活的输入输出控制,具体见后面输入输出流章节。

4.4.4 字符串处理处理函数

为了便于使用字符串,C 语言的字符串函数库提供了丰富的字符串处理函数,C++仍然兼

容它们。这里介绍其中几个比较常用的字符串处理函数。注意,在程序首部加上#include

<string>,其中包含了函数说明。

下面函数调用形式中的参数 str、str1 和 str2,除特别声明外,均是字符数组名、字符(串)

指针变量或字符串常量。

1. 字符串连接函数 strcat()

函数原型:char *strcat(char *, const char *);

调用格式:strcpy(str1,str2);

函数功能:将字符串 str2 连接到 strl 的后面,函数原型中的修饰符“const”说明 str2 串的 内容不会被改变,而不是要求 str2 为字符串常量。

函数调用返回一个指针值,仍为 str1。与 str1 对应的实参一般为数组,正确使用该函数,

要求与 str1 对应的实参尾部有足够剩余空间,以便能容纳 str2 的内容。连接前,str1 和 str2 尾 部都有'\0'。连接后,str1 中的'\0'在连接时被覆盖掉,而在新的字符串有效字符之后保留一个'\0'。

例如:

char s1[30]="this";

char s2[30]=" is"; //或 char *s2=" is";

strcat(s1,s2); //结果存在 s1 中,第一个实参为数组 cout<<s1; //两条语句等价于 cout<<strcat(str1,str2);

将输出:

this is

图 4-16 表示连接前后 str1 与 str2 的内容。

连接前:

S1 T H I S \0

S2 I S \0

连接后:

S1 T H I S I S \0

图 4-16 连接前后 s1 与 s2 的内容变化图 2.字符串拷贝函数 strcpy()和 strncpy()

函数原型:char *strcpy(char *,const char *);

调用格式:strcpy(str1,str2);

函数功能:将字符串 str2 整个复制到字符数组 str1 中,str2 的值不变。

调用该函数时,一般 str1 是字符数组,且 str1 定义得足够大,以便能容纳被拷贝的 str2 的内容。

例如:

strcpy(str1,"Changsha");

在某些应用中,需要将一个字符串的前面一部分拷贝,其余部分不拷贝,调用函数 strncpy() 可实现这个要求。函数调用格式:

strncpy(str1,str2,n);

作用是将 str2 中的前 n 个字符拷贝到 str1(附加'\0')。其中 n 是整型表达式,指明欲拷贝 的字符个数。如果 str2 中的字符个数不多于 n,则该函数调用等价于 strcpy(str1,str2)。

3.字符串比较函数 strcmp() 函数原型:int strcmp(char *,char *);

调用格式:strcmp(str1,str2);

函数调用 strcmp(str1,str2)比较两个字符串大小。对两个字符串自首至尾逐个字符相比较

(按字符的 ASCII 代码值的大小),直至出现不同的字符或遇到'\0'为止。如全部字符都相同,

则认为相等,函数返回 0 值;若出现不相同的字符,则以第一个不相同的字符比较结果为准。

若 str1 的不相同字符小于 str2 的相应字符,函数返回一个负整数(-1);反之,返回一个正整 数(1)。

注意:对字符串不能进行关系运算,必须调用 strcmp(str1,str2)对字符串作比较。

4.求字符串长度函数 strlen() 函数原型:unsigned int strlen(char *);

调用格式:strlen(str);

函数功能:求字符串 str 的长度(不包括'\0')。例如:strlen("right")返回值为 5。

5.子串查找函数 strstr()

函数调用 strstr(str1,str2),在 str1 中查找是否包含子串 str2,若找到,则返回第一次出现

函数调用 strstr(str1,str2),在 str1 中查找是否包含子串 str2,若找到,则返回第一次出现

相關文件