【C语言程序与设计】字符数组与字符串

目录

1.字符型数据

1.1字符型数据的存储

1.2字符型数据输入问题

1.3处理字符的函数

2.字符串与字符数组

2.1统计空格

2.2字符数组初始化

2.3字符串的输入输出

3.字符串处理函数

3.1string.h中的字符串处理函数

3.2stdio.h中的字符串函数

4.字符串数组

1.1字符型数据的存储
在C语言中,字符和整数之间存在着密切的关系,一个单引号括起来的字符常量表示的是机器字符集里该字符对应的整数值,例如'z'表示整数值122。C语言把字符当成整数进行处理,毕竟所有字符都是以二进制形式进行编码的,而将二进制代码作为整数处理比较简单。
字符类型像整数类型一样也分为符号型(signed)和无符号型(unsigned)。C语言标准没有说明char默认的类型是有符号型还是无符号型。有的编译器默认char有符号型,有的编译器默认char为无符号型。Visual C++编译器将char默认有符号型。通常有符号字符型的取值范围为-128~127,而无符号型的取值范围为0~255.char类型参与整数运算时应该注意避免类型溢出。

解决问题:字符加密
输入一个英文小写字母和整数 k (k<26),将英文字母加密并输出。加密思想:将每一个字母加一个序列 k,即用此字母在英文字母表中其后面的第 k 个字母代替它,如果定义这个char型变量为 ch,用公式表示此加密过程为:ch=ch+k.如果字母为’a’时,则后一个祖母就是’a’,也就是字母表形成了一个圆。

输入样例:
z 25

输出样例:
y

解题思路:
若ch+k<=‘z’,则ch+k即ch对应的密码,否则,ch对应的密码为ch+k-26。这一思路完全正确,但是下面的代码确实错误的。

#include<stdio.h>
int main(void)
{
    
    
	char ch;
	int k;
	sacnf("%c%d",&ch,&k);
	ch=ch+k;
	if(ch>'z')
	{
    
    
		ch=ch-26;
	}
	printf("%c\n",ch);
	return 0;
}

运行结果:

输入:
z 25

输出:

问题分析:
原因是char 类型溢出。在所用编译器VC++6.0中,char默认为signed char类型,表示范围是-128~+127。‘z’+25的值是145,存入char类型变量ch时发生类型溢出。将程序段:

ch=ch+k;
if(ch>‘z’)
{
ch=ch-26;
}

改为:

if(ch+k>‘z’)
{
ch=ch+k-26;
}
else
{
ch=ch+k;
}

就可以避免存储时的类型溢出。
第一个代码中的类型溢出发生在赋值阶段,在计算表达式ch+k的运算过程中不会发生类型溢出,因为编译器把算数运算符中涉及的char类型都提升为int类型。其实有很多的类型溢出是发生在计算阶段,例如语句:

k = 123456*123456/123456;

虽然结果不会超出int范围,但是在计算过程中,中间结果超出int范围,也将发生溢出。

1.2字符型数据的输入问题
scanf()和getchar()函数都可以用来读入单个字符,但是与整型和浮点型数据的读入不同,所有输入都是合法的字符输入。也就是在读入字符前scanf()函数不会跳过空白字符,如果下一个字符是空格,scanf()和getchar()会把读到的空格提前存入适应的变量。这是编程新手最常遇到的一个问题。
若有以下输入语句

scanf(“%c%c%c”,&ch1,&ch2,&ch3);

如果输入为

a b c

结果是将字符’a’存入ch1,第一个空格存入ch2,字符’b’存入ch3,可能并不符合编程者的原意,正确的输入格式是:

abc

如果输入格式之间必须有空格,则应该修改为scanf()中的格式控制:

scanf(“%c %c %c”,&ch1,&ch2,&ch3);

又如,如果在程序中需要先读入一个整数存入变量n,然后读入一个字符存入变量ch,输入格式如下:

5
M

该如何用scanf()函数实现呢?若写为:

scanf(“%d”,&n);
scanf(“%c”,&ch);

存入ch中将是数字5之后的回车符。
解决方法1:

调用getchar()函数将数字5之后的回车符读入
scanf(“%d”,&n);
getchar();//将缓冲区的回车符读入
scanf(“%c”,&ch);

解决方法2:

使用赋值抑制符“*”来跳过所读入的回车符:
scanf(“%d”,&n);
scanf(“%*c%*c”,&ch);//*用%*c将缓冲区的回车符读入,但是不存储

以上方法不足之处是:getchar()或%*c只能处理掉一个字符,如果输入5之后除了回车还有空格等其他空白符,如何跳过多个空白符?
解决方法3:

scanf格式串中的空格符
scanf格式串中的空格符意味着跳过零个或者多个空白字符(空格,回车,制表符等)。为了使scanf()函数在读入字符前跳过空白字符,需要在格式串中的%c之前加上一个空格:
scanf(“%d”,&n);
scanf(" %c",&ch);

也可以在格式串中的%d之后加入一个空格,控制scanf()在读入上一个之后跳过所有空白符,以免空白符被%c读入。

scanf(“d “,&n);
scanf(”%c”,&ch);

1.3处理字符的函数
前面已经讲过如何将小写字母转变为大写字母:

if(ch>=‘a’&&ch<=‘z’)
{
ch=ch-32
}

其实可以使用库函数toupper()来完成上述操作,即

ch = toupper(ch)

在前面判断字符是字母还是数字的字符分类统计中,也可以使用库函数isalpha()来判断一个字符是否是字母,用isdigit()来判断一个字符是否是数字。
toupper()函数,isalpha()函数,isdigit()函数是在ctype.h 头文件中声明的字符处理的函数。
toupper()函数会检测参数ch是否是小写字母,如果是,它会把ch转换成大写字母返回,否则则返回参数的值。
isdigit()函数可以判断参数ch是否属于数字,如果是,返回一个非零整数(表示真),否则返回0(表示假)。
除了上述函数以外,ctype.h头文件还声明了其他几个字符函数,如下表:

函数 功能
tolower() 转换为小写字母
toupper() 转换为大写字母
islower() 判断是否为小写字母
isupper() 判断是否为大写字母
isalpha() 判断是否为大写字母或者小写字母
isdigit() 判断是否为十进制数字(’0‘-’9‘)
isxdigit() 判断是否为十六进制数字(’0‘-’9‘,’a’-‘f’,‘A’-‘F’)
isblank() 判断是否为空白字符(空格,‘\t’)

2.字符串与字符数组
所谓的字符串,一般是指多个字符组成的序列。字符串字面量是双引号括起来的任意字符序列(包括转义字符,如换行符’\n’)。例如:

“Hello World"

C语言本身并没有”字符串“这种数据类型。通常是用一个字符数组来存放一个字符串。字符串与普通字符数组的区别是:字符串的末尾有一个空字符‘\0’。

2.1统计空格
试试看;统计空格个数
输入一行字符(字符个数不超过80),以回车结束,输出其中空格的个数。程序的运行效果如下:

请输入一行字符:
abc d e f g
其中空格总数为5

解题思路:
(1)定义字符数组str[81]。
(2)读入一行字符,存入字符数组str。
(3)扫描整个数组,统计空格个数。

3include<stdio.h>
int main(void)
{
    
    
	char str[81];
	int i,count=0;
	gets(str);
	for(i=0;str[i]!='\0';i++)
	{
    
    
		if(str[i]==' '
		{
    
    
			count++;
		}
	}
	printf("%d",count);
	return 0;
}

代码说明:
在循环执行时,扫描整个数组以统计出空格数’ '的数量,直到最后遇到字符‘\0’为止。

2.2字符数组的初始化
一维数组的定义、引用、初始化方法都适用于字符数组,例如:
定义一维数组str,数组长度为80:

char str[80]

定义并初始化字符数组:

char t[5] = {‘H’,‘a’,‘p’,‘p’,‘p’,‘y’};

输出数组所有元素:

for(i=0;i<5;i++)
{
putchar(t[i]);
}

字符串是由有效字符和字符串结束符’\0’组成。字符串可以按照以下方式声明并切初始化:

char name[15] = {‘W’,‘a’,‘n’,‘g’,‘L’,‘i’,‘\0’};
char name[15] = “WangLi”;

也可以省略数组大小,系统自动计算数组长度,大小为数组初值表的字符总数加1,最后一个元素存入空字符,例如:

char str[] = “Hello World”;

系统将会为数组str分配长度为12的存储空间,并且总动在最后一个有效字符的下一个位置存储’\0’。

2.3字符串的输入和输出
C语言中,对字符串的操作就是对字符数组的操作,但是对字符串的操作与对普通数组的操作有一定的区别。
普通字符数组中的数组元素的个数是确定的,一般用下标控制循环访问数组元素。
字符串中没有显示给出有效字符的个数,只规定在字符串结束符’\0’之前的字符都是字符串的有效字符,一般用结束符’\0’来控制循环。循环条件:s[i]!=‘\0’。
(1)用%s整体进行输入输出,格式描述字符串中使用转换字符串“%s"。

char name[15];
scanf(“%s”,name);
printf(“%s”,name);

注意:用scanf()函数以%s格式读入的数据不能含有空白符,所有空白符都会被当作数据结束的标志,当输入”Zhang san"时,只能将“Zhang"存入name,因此不能达到预期的效果。
(2)用get()和puts()函数
一般格式是:
gets(字符数组名);
功能是读取字符串,并且存放在指定数组中,遇到换行符或者文件结束标志时结束读入。换行符不作为读取串的内容,读取的换行符被转换为字符串结束标志’\0’。
puts(字符串数组名)
功能是将存储在字符数组的字符串输出到标准终端(屏幕)。puts输出字符串时要从数组首地址开始读取自负,遇到字符串结束符’\0’才停止。
注意:puts(s)的作用与语句printf(“%s\n”,s)的作用基本相同,puts()函数只能输出字符串,不能输出数值活进行格式变换,puts()函数在输出在输出字符串后会自动输出一个回车符。

3.字符串处理函数
C语言提供了丰富的字符串处理函数,大致可以分为字符串的输入、输出、合并、修改、比较、转换、复制、搜索等几类,使用这些函数可大大减轻编程的负担。

3.1、string.h中的字符串处理函数
求字符串长度函数strlen()
调用格式:strlen(str)
功能:测试字符串长度。函数返回值就是str中字符的个数。例如:

char str[10] = “China”;
printf(“%d”,strlen(str));

printf(“%d”,strlen(“China”))

程序的输出为:5.

3.2、字符串复制函数strcpy()或strncpy()
调用格式:strcpy(字符数组1,字符串2)
功能:复制第二个参数的内容到第一个参数中。第一个参数必须有足够的内存来接受第二个参数,第二个参数可以是含有结束符‘\0’的字符串(字符数组或字符串常量)。
调用格式:strcpy(字符数组1,字符串,n)
功能:复制第二个参数中最多n个字符到第一个参数值中。
例如:

char s1[10],s2[] = “Beijing”;
strcpy(s1,s2);

4.字符串数组
4.1字符串数组的表示形式
在C语言中,字符数组有两种表示方法:
可以用二维字符数组来表示字符串,格式是:char strs[row][col];。例如:

char strs[4][6]={“hello”,“world”,“c”,“java”};//char 类型的二维数组
//注意,也不支持先声明再赋值,即即下面的赋值方式是错误的
char strs[4][6];
strs={“hello”,“world”,“c”,“java”};

其中的row表示二维数组中的行,表示表示有几个字符串;而col表示二维数组中的列,表示存放字符串的最大长度。

第二种:
可以用一维的字符指针数组来保存字符串,格式是:char* strs[len];。例如:

char* strs[4]={“hello”,“world”,“c”,“java”};//char* 类型的一维数组
//注意,也不支持先声明再赋值。即下面的赋值方式是错误的
char* strs[4];
strs={“hello”,“world”,“c”,“java”};
该一维数组中的每一个元素都是一个char 类型的地址:
第一个这个指针str[0]指向第一个字符串中的第一个字符。即str[0]等于’h’。
第二个指针strs[1]指向第二个字符串中的第一个字符。即
strs[1]等于’w’。

每个strs[i]都表示一个字符串,指针都指向对应字符串的第一个字符。
因此实际上这个一维数组并不存放真正的字符串的地址。

从键盘输入字符串数组
有时候我们需要批量从键盘输入字符串,无论是二维数组还是一维字符指针数组,都可以通过循环输入字符串的方式来给字符串数组赋值,而从键盘输入字符串的方式有scanfgets两种,根据情况选择即可:
二维字符数组表示的字符串数组的批量赋值

#include <stdio.h>
int main()
{
    
    
	char strs[4][10];//声明字符二维数组。注意指定字符串个数和字符串最大长度
	//通过循环批量输入字符串并赋值字符串数组
	for(int i=0;i<4;i++)
	{
    
    
		scanf("%s",strs[i]);
		//或者使用gets()函数
		//gets(strs[i]);
	}
	for(int i=0;i<4;i++)
	{
    
    
		printf("%s\n",strs[i])
	}
}
	

一维字符指针数组表示的字符串数组的批量赋值

#include <stdio.h>
#include <malloc.h>
int main()
{
    
    
	//声明一维字符指针数组
	char *strs[4];
	//通过循环批量输入字符串并赋值给字符串数组
	for(int i=0;i<4;i++)
	{
    
    
		//声明字符串,并为其分配内存空间
		char *str=(char*)malloc(sizeof(char)*10);
		gets(str)//使用gets函数从键盘中输入
		str[i]=str//赋值
		//或者使用scanf()函数
		//scanf("%s",str)
		//注意,我们不能直接使用gets(strs[i]);
		
	}
	for(int i=0;i<4;i++)
	{
    
    
	printf("%s\n",strs[i]);
	}
}

访问字符串数组中的字符串
如果要访问二维字符数组声明的字符串数组中的字符串,可以通过两种方式来访问(其中i和j都是指数组的下标,一个是横坐标,另外一个是纵坐标,都是从0开始的)

下标,格式是字符串数组名[i][j]。例如,strs[i][j] 数组名加下标,格式((字符串数组名+i)+j)`。

修改字符串数组中的字符串的字符
如果是二维数组声明的字符串数组,可以通过如下两种方式来修改字符(其中i和j都是数组地下标,一个是横坐标,零一个是纵坐标,都是从0开始)
下标,格式是字符串数组名[i][j]='新字符'
数组名加下标,格式是:*(*(字符串数组名+i)+j)='新字符'
但是对于一维指针数组声明的字符串数组,里面的字符串是无法修改的,而字符串里面的字符更是不能修改的

遍历字符串数组中的所有字符串
即遍历字符串数组,无论是二位字符串数组声明的字符串数组还是一维字符指针声明的字符串数组,都可以通过for循环等循环语句来遍历数组,然后通过下标(例如,strs[i])或者数组名加下标(例如,*(strs+i))的访问字符串

猜你喜欢

转载自blog.csdn.net/z2004cx/article/details/128465661