第十章 字符串
10.1 字符串字面量
1 字符串字面量(也就是字符串常量),是由一对双引号括起来的一个字符序列。无论双引号里有多少字符,都代表字符串字面量。
2 “a”是字符串字面量,而‘a’是字符常量,两者不同。
3 字符串实际是由若干有效字符且以字符’\0’作为结尾的一个字符序列。
10.2 字符串的存储
1 字符串的存取用字符型数组来实现。但一个字符型数组中存储的不一定就是一个字符串,只有当其最后一个元素是’\0’时才表示字符串。
2 字符串结束标志’\0’也表示也占一个字节的内存,但是不计入字符串的实际长度。
3 字符串的初始化允许的一种形式:
char str[6]={‘h’,’e’,’l’,’l’,’0’,’\0’};
也可以省略数组长度的声明(因为长度是显然的,但是注意不能省略’\0’):
char str[ ]= {‘h’,’e’,’l’,’l’,’0’,’\0’};
或者是以字符串常量来初始化字符数组:
char str[ ]={“hello”};
也可以直接省略花括号:
Char str[ ]=”hello”;
4 永远注意应该留有猪狗的存储空间以便于存储字符串的结束标识,因此,字符数组的大小永远比字符串的实际字符数多一。
5 二维数组是按行存储的,因此必须告知系统第一行的长度,而当初始化列表提供的字符串长度小于每行长度时,系统自动为后面的单元分配”\0”。
10.3 字符指针
1 字符指针是指向字符型数据的指针变量。
2 字符串字面量本身代表的就是存放它的常量存储区的首地址。因此只需将该首地址赋值给指针变量,即可让字符指针指向一个字符串。
3 字符串保留在只读的常量存储中,因此只能修改指针变量的值,不能对指针变量指向的存储单元进行读写操作。
只能修改ptr的值,不能修改ptr指向的存储单元,因此*ptr=’W’;是非法的。
4 如果是将字符串保存在一个数组中,然后用字符指针指向它,这种操作是合法的。如:
char str[10]=”Hello”;
char *ptr=str;
此时再使用 *ptr=’W’ 就是合法的。
5 正确使用指针,就要明确字符串被保存到了哪里以及字符指针指向了哪里。
10.4 字符串的访问和输入/输出
1 可以用下标方法来访问,类似于数组;也可以用字符指针间接访问存储与数组中的字符串,如*(ptr+i)。
2 对于数组名,不能使用自增或自减运算操作使其指向字符串中的某个字符,因为数组名是一个地址常量,其值不能被改变。
3 字符数组的输入和输出有多种方法可以实现。
例10.1见文末
4 函数scanf()按s格式不能输入带空格的字符串。而字符串处理函数gets()可以输入带空格的字符串。
5 gets()以回车符作为字符的终止符,同时将缓冲区的回车符读走,但不作为字符串的一部分。scanf()不读走回车符,回车符依旧留在缓冲区中。
例10.2及各种变形见文末
6 函数puts()用于从括号内的参数给出的地址开始,依次输出存储单元中的字符,当遇到第一个’\0’时输出结束,并且自动输出一个换行符。
7 函数gets()不能限制输入字符串的长度,因此容易引起缓冲区溢出,从而引发安全问题。可以通过用限制字符串长度的函数来解决。
8 如果字符串中要保护双引号,此时就要用到转义字符,也就是在双引号前面加一个斜杠。
例10.3见文末
10.5 字符串处理函数
1 字符串处理函数库提供了许多有用的函数来进行字符串处理操作,要用到这些函数就要包含头文件<string.h>。
例10.4见文末
2 对单个字符进行赋值操作可以用赋值运算符,但赋值运算符不能用于字符串的赋值操作。字符串赋值只能用函数strcp()。
3 比较字符串不能直接使用关系运算符,应该用函数strcmp()来比较字符串的大小,比较的是第一对不相等的字符的大小。
4 数字编码使得字符串的大小比较变得可能。
10.6 向函数传递字符串
1 向函数传递字符串时,既可以用数组作为函数参数,也可以用字符指针作为函数参数。
例10.5见文末
2 通常不使用长度即计数控制的循环来判断数组元素是否遍历结束,而是使用条件控制的循环,利用字符串结束标志’\0’来判断字符串是否遍历结束。
例10.6见文末
3 为防止实参在被调函数中意外被修改,可以在相应的形参前面加上类型限定符const。
10.7 从函数返回字符串指针
1 函数之间的握手(信息交换)是通过函数参数和返回值来实现的。因此可以利用数组或指针作为函数参数来精准把握这些信息。
2 返回指针值的函数的定义方式和以前的定义方式基本相同,只是在函数名前加了一个*,如:
char *MyStrcat(char *dstStr, const char *stcStr);
例10.7见文末
代码
10.1a
从键盘中输入一个人名,并把它显示在屏幕上。方法一
//例10.1a 从键盘中输入一个人名,并把它显示在屏幕上。方法一
#include<stdio.h>
#define N 12 //控制最长人名
int main(void)
{
char name[N];
printf("Enter your name:");
scanf("%s",name);//由于name本来就表示数组首地址,因此不需要再加&
printf("Hello! %s\n",name);
return 0;
}
10.1b
从键盘中输入一个人名,并把它显示在屏幕上。方法二
//例10.1b 从键盘中输入一个人名,并把它显示在屏幕上。方法二
#include<stdio.h>
#define N 12 //控制最长人名
int main(void)
{
char name[N];
printf("Enter your name:");
scanf("%s",name);
printf("Hello! %s\n",name);
scanf("%s",name);//用于读取缓冲区上次未被读走的字符
printf("Hello! %s\n",name);
return 0;
}
10.2a
使用函数gets(),从键盘输入一个带空格的人名,然后把它显示在屏幕上。用数组实现。
//例10.2a 使用函数gets(),从键盘输入一个带空格的人名,然后把它显示在屏幕上。用数组实现。
#include<stdio.h>
#define N 12
int main(void)
{
char name[N];
printf("Enter your name:");
gets(name);//注意形式
printf("Hello! %s\n",name);
return 0;
}
10.2b
使用函数gets(),从键盘输入一个带空格的人名,然后把它显示在屏幕上。用字符指针实现 。
//例10.2b 使用函数gets(),从键盘输入一个带空格的人名,然后把它显示在屏幕上。用字符指针实现 。
#include<stdio.h>
#define N 12
int main(void)
{
char name[N];
char *ptrName=name;//声明了一个字符指针,并且指向数组的首地址
printf("Enter your name:");
gets(name);//注意形式
printf("Hello! %s\n",ptrName);//注意,这时候ptrName前没有*
return 0;
}
10.2c
使用函数gets(),从键盘输入一个带空格的人名,然后把它显示在屏幕上。用更安全的方式实现 。
//例10.2c 使用函数gets(),从键盘输入一个带空格的人名,然后把它显示在屏幕上。用更安全的方式实现 。
#include<stdio.h>
#define N 12
int main(void)
{
char name[N];
char *ptrName=name;//声明了一个字符指针,并且指向数组的首地址
printf("Enter your name:");
fgets(name,sizeof(name),stdin);//限制输入字符串的长度不超过数组大小!!! 多余的会被舍弃
printf("Hello! %s\n",ptrName);//注意,这时候ptrName前没有*
return 0;
}
10.3a
从键盘输入一个带空格的人名,然后在显示人名的前面显示“Hello”,i said to。数组方法实现
//例 10.3a 从键盘输入一个带空格的人名,然后在显示人名的前面显示“Hello”,i said to。数组方法实现
#include<stdio.h>
#define N 12
int main(void)
{
char name[N];
char str[]="\"hello\",i said to";//声明了一个字符型数组,并且在"前面加\实现转义功能。
printf("Enter your name:");
fgets(name,sizeof(name),stdin);//限制输入字符串的长度不超过数组大小
printf("%s %s.\n",str,name);//依次输出两个字符串
return 0;
}
10.3b
从键盘输入一个带空格的人名,然后在显示人名的前面显示“Hello”,i said to。字符指针方法实现
//例 10.3b 从键盘输入一个带空格的人名,然后在显示人名的前面显示“Hello”,i said to。字符指针方法实现
#include<stdio.h>
#define N 12
int main(void)
{
char name[N];
char *ptr="\"hello\",i said to";//声明了一个字符型数组,并且在"前面加\实现转义功能。
printf("Enter your name:");
fgets(name,sizeof(name),stdin);//限制输入字符串的长度不超过数组大小
printf("%s %s.\n",ptr,name);//依次输出两个字符串
return 0;
}
10.4
请编程实现按奥运会参赛国家国名在字典中的顺序对其入场次序进行排序,但假设参赛国不超过150个。
//例10.4 请编程实现按奥运会参赛国家国名在字典中的顺序对其入场次序进行排序,但假设参赛国不超过150个。
#include<stdio.h>
#include<string.h>
#define MAX_LEN 10//字符串的最大长度
#define N 150 //最大参赛国
void SortString(char str[][MAX_LEN],int n);
int main(void)
{
int i,n;
char name[N][MAX_LEN];//使用字符型的二维数组
printf("How many countries?");
scanf("%d",&n);
getchar();//读走缓冲区的回车符
printf("Input their name:\n");
for(i=0;i<n;i++)
{
gets(name[i]);//输入n个字符
}
SortString(name,n);
printf("Sorted results:\n");
for(i=0;i<n;i++)
{
puts(name[i]);//输出排序后字符
}
return 0;
}
void SortString(char str[][MAX_LEN],int n)
{
//交换法实现字符串的排序
int i,j;
char temp[MAX_LEN];//交换辅助变量只需以为数组就可以
for(i=0;i<n-1;i++)
{
for(j=i+1;j<n;j++)
{
if(strcmp(str[j],str[i])<0)//用strcmp()函数实现字符串的比较功能
{
strcpy(temp,str[i]);
strcpy(str[i],str[j]);
strcpy(str[j],temp);//这里的strcpy()就相当于赋值操作
}
}
}
}
10.5a
从键盘输入一个字符串a,将字符串a复制到字符串b中,再输出字符串b,即编程实现字符串处理函数strcpy()的功能,但不使用该函数。
//例10.5 从键盘输入一个字符串a,将字符串a复制到字符串b中,再输出字符串b,即编程实现字符串处理函数strcpy()的功能,但不使用该函数。
#include<stdio.h>
#define N 80
void MyStrcpy(char dstStr[],char srcStr[]);
int main(void)
{
char a[N],b[N];
printf("Input a string:");
gets(a);
MyStrcpy(b,a);//实现复制功能,对应的b是目的字符串,a是源字符串
printf("The copy is:");
puts(b);//输出复制过后的b
return 0;
}
void MyStrcpy(char dstStr[],char srcStr[])
{
int i=0;
while(srcStr[i]!='\0')//如果源字符串没有到结尾
{
dstStr[i]=srcStr[i];//将一个个字符复制过去
i++;
}
dstStr[i]='\0';
}
10.5b
将例10.5 中的功能函数用字符指针实现
//将例10.5a 中的功能函数用字符指针实现
//只需将功能函数改为
void MyStrcpy(char *dstStr,char *srcStr)//数组名就是首地址
{
while(*srcStr!='\0')//指向的源字符不是字符串结束标志时
{
*dstStr=*srcStr;//复制字符
srcStr++;//指向下一个源字符
dstStr++;//指向下一个目的字符
}
*dstStr='\0';//最后一个循环结束后,指到结尾处,还应该加上字符串结束标志
}
10.6a
从键盘任意输入一个字符串,计算其实际字符个数并打印输出,即不用字符串处理函数strlen()就能实现其功能,数组方法。
//例10.6a 从键盘任意输入一个字符串,计算其实际字符个数并打印输出,即不用字符串处理函数strlen()就能实现其功能,数组方法。
#include<stdio.h>
unsigned int MyStrlen(const char str[]);
int main(void)
{
char a[80];
printf("Input a string:");
gets(a);
printf("The length of the string is:%u\n",MyStrlen(a));
return 0;
}
unsigned int MyStrlen(const char str[])
{
int i;
unsigned int len=0;
for(i=0;str[i]!='\0';i++)
{
len++;
}
return len;
}
10.6b
从键盘任意输入一个字符串,计算其实际字符个数并打印输出,即不用字符串处理函数strlen()就能实现其功能,字符指针方法。
//例10.6b 从键盘任意输入一个字符串,计算其实际字符个数并打印输出,即不用字符串处理函数strlen()就能实现其功能,字符指针方法。
#include<stdio.h>
unsigned int MyStrlen(const char *str);
int main(void)
{
char a[80];
printf("Input a string:");
gets(a);
printf("The length of the string is:%u\n",MyStrlen(a));
return 0;
}
unsigned int MyStrlen(const char *pStr)
{
unsigned int len=0;
for(;*pStr!='\0';pStr++)//注意判断条件中有*,而增量无*
{
len++;
}
return len;
}
10.7
不使用字符串处理函数strcat(),编程实现strcat()的功能
//例10.7 不使用字符串处理函数strcat(),编程实现strcat()的功能
#include<stdio.h>
#define N 80
char *MyStrcat(char *dstStr,char *srcStr);
int main(void)
{
char first[2*N];//需要一个足够大的数组
char second[N];
printf("Input the fist string:");
gets(first);
printf("Input the second string:");
gets(second);
printf("The result is:%s\n",MyStrcat(first,second));
return 0;
}
char *MyStrcat(char *dstStr,char *srcStr)
{
char *pStr=dstStr;//保留dstStr的首地址
while(*dstStr!='\0')
{
dstStr++;//一直将dst的指针移动到末尾处
}
for(;*srcStr!='\0';dstStr++,srcStr++)//如果srcStr不结束,则一直将指针移动到结尾
{
*dstStr=*srcStr;//此时dstStr的指针其实是在其末尾处,而srcStr是从头开始将其拼接到一起
}
*dstStr='\0';//添加字符串结束标志
return pStr;//返回此时的首地址,就是返回拼接后的字符串首地址
}