资源简介
(共35张PPT)
C 语言程序设计
2023
翻转课堂实用教程
7.3 字符数组
1
2
3
一维字符数组
字符串和字符数组的关系
字符串结束符
字符数组的输入和输出方式
字符串处理函数
二维字符数组
知识点
案例7.3.1
案例7.3.2
案例7.3.3
案例分析
练习字符数组
的使用
练习题
1、一维字符数组
存放了字符的数组就是字符数组。只有一个下标的就是一维字符数组。
定义、初始化、引用都与其他类型的一维数组是一样的。
举例:
char t[8] = {‘H’,’e’,’l’,’l’,’o’,’ ’,’C’,’!’};
等价于:t[0] = ‘H’,t[1] = ‘e’,t[2] = ‘l’,t[3] = ‘l’,t[4] = ‘o’,t[5] = ‘ ’,t[6] = ‘C’,t[7] = ‘!’。
char str[9] = {‘H’,’e’,’l’,’l’,’o’,’ ’,’C’,’!’,’\0’};
等价于:str[0] = ‘H’,str[1] = ‘e’,str[2] = ‘l’,str[3] = ‘l’,str[4] = ‘o’,str[5] = ‘ ’,str[6] = ‘C’,str[7] = ‘!’,str[8] = ‘\0’
较t数组,多了一个‘\0’字符,该字符的ASCII值为0
在C语言的字符串处理中,‘\0’字符作为字符串的结束。
7.3.1字符数组知识点
字符数组名还是字符数组的地址,即首字符的地址。
2、字符串与字符数组的关系
字符串就是一组连续的字符的集合,它有一个结束标志‘\0’。
使用一维字符数组来存储字符串。
一维字符数组并不一定是字符串,主要在于一维字符数组中是否在所有字符后保存一个结束符‘\0’。
举例:
若要保存”Hello C!”这个字符串常量,除了 Hello C!这8个字符外,还有一个结束符‘\0’
char str[9] = {‘H’,’e’,’l’,’l’,’o’,’ ’,’C’,’!’,’\0’};
7.3.1字符数组知识点
2、字符串与字符数组的关系
举例:
char t[8] = {‘H’,’e’,’l’,’l’,’o’,’ ’,’C’,’!’};
char str[9] = {‘H’,’e’,’l’,’l’,’o’,’ ’,’C’,’!’,’\0’};
字符数组str的有效长度:8
7.3.1字符数组知识点
str和t数组的区别是什么?
t数组没有保存‘\0’,它仅是一个字符数组.
str不仅是一个字符数组,它还可以作为一个字符串。
str的实际大小:9
3、字符串结束符
访问内存中的一个字符串,需要知道 。'\0'意味着字符串的结束。
char str[9] = {‘H’,’e’,’l’,’l’,’o’,’ ’,’C’,’!’,’\0’};
C语言在处理字符串时,会从前往后逐个扫描字符,直到遇到第一个'\0'就认为到达了字符串的末尾,结束处理。
7.3.1字符数组知识点
字符串的地址+字符串的结束符
#include
int main(){
int i;
char s[17] = "Hello C program!";//先给所有元素赋初值
printf("%s\n",s);
s[0] = 'H';
s[1] = 'a';
s[2] = 'p';
s[3] = 'p';
s[4] = 'y';
s[5] = '\0';
for(i=0;i<16;i++){ //输出s字符数组中所有元素
putchar(s[i]);;
}
printf("\n");
printf("%s\n",s); //将s作为一个字符串来处理
return 0;
}
运行结果
举例,理解结束符
7.3.1字符数组知识点
Hello C program!
Happy C program!
Happy
'\0'在输出时不显示内容。
4、字符数组的输入和输出
分为两种:
7.3.1字符数组知识点
①逐个元素的处理
② 整个数组一次性处理
4、字符数组的输入和输出
7.3.1字符数组知识点
①逐个元素的处理
涉及到的函数为:scanf()、printf()、getchar()、putchar(),使用这四个函数时需要添加stdio.h。
在scanf()和printf()中使用格式控制说明符%c来进行单个字符的输入和输出。
getchar()读取单个字符,并且只能读取单个字符。
putchar()输出单个字符,并且只能输出单个字符。
举例:
char ch;
scanf("%c",&ch); //从键盘中读取一个字符,并存到变量ch中
printf("%c",'A'); //输出一个字符'A',也可输出一个字符变量
ch = getchar(); //从键盘中读取一个字符,并存到变量ch中
putchar(ch); //输出一个字符变量,也可以输出字符常量
4、字符数组的输入和输出
7.3.1字符数组知识点
② 整个数组一次性处理
涉及到的函数为:scanf()、printf()、gets()、puts()。
在scanf()和printf()中使用格式控制说明符%s来进行字符串的输入和输出。
scanf遇到空格、tab键或者回车便停止读取字符。
gets()读取一行字符串。gets()在读取时,只要不遇到换行符就会一直读取,即使遇到多个空格,也不会停止读取。
puts()输出字符串并换行。
举例:
char str[10]={'\0'};
scanf("%s",str);
gets(str);
puts(str);
4、字符数组的输入和输出
7.3.1字符数组知识点
② 整个数组一次性处理
scanf()遇到空格、制表符tab或者换行符停止读取;
gets()只会在遇到换行符’\n’停止读取。
区别
#include
int main(){
int i;
char s[40];
scanf("%s",s);
printf("%s\n",s);
return 0;
}
运行结果:
We are C language learners.↙
We
使用scanf()读取字符串
#include
int main(){
int i;
char s[40];
gets(s);
printf("%s\n",s);
return 0;
}
运行结果:
We are C language learners.↙
We are C language learners.
gets()读取字符串
5、字符数组串处理函数
7.3.1字符数组知识点
常见的处理函数,包含在string.h头文件中。
strlen() 计算字符串有效长度
strcmp()字符串比较
strcat()字符串拼接函数
strcpy()字符串拷贝函数
strstr()字串查找函数
需要#include
5、字符数组串处理函数
7.3.1字符数组知识点
(1)strlen计算字符串有效长度
字符串包含两个部分:一组连续的字符序列和结束符’\0’,strlen()返回的是连续字符的个数。
#include
int main(){
char str1[30] = {"Hello C language!"};
char str2[30] = {"language"};
char str3[10] = "C++";
printf("%d\n",strlen(str1));
printf("%d\n",strlen(str2));
printf("%d\n",strlen(str3));
return 0;
}
运行结果:
18
8
3
5、字符数组串处理函数
7.3.1字符数组知识点
(2)strcmp字符串比较函数
strcmp(str1,str2)用于比较两个字符串大小,自左往右逐个字符的ASCII码值进行比较,直到出现不同的字符或者遇到‘\0’,其中str1和str2为字符串。
0 (当str1和str2中的字符完全一样时,str1等于str2)
strcmp(str1,str2) 返回 正数 (当str1>str2)
负数 (当str15、字符数组串处理函数
7.3.1字符数组知识点
(2)strcmp字符串比较函数
举例:
#include
int main(){
char str1[] = "Abc"; //如果对全部元素赋值,数组长度可以省略
char str2[] = "Abc";
char str3[] = "Acd";
char str4[] = "Bbca";
char str5[] = "Bbc";
printf("str1 VS str2 is %d\n",strcmp(str1,str2));
printf("str1 VS str3 is %d\n",strcmp(str1,str3));
printf("str3 VS str4 is %d\n",strcmp(str3,str4));
printf("str4 VS str5 is %d",strcmp(str4,str5));
return 0;
}
运行结果:
str1 VS str2 is 0
str1 VS str3 is -1
str3 VS str4 is -1
str4 VS str5 is 1
5、字符数组串处理函数
7.3.1字符数组知识点
(3)strcat字符串拼接函数
strcat(str1,str2)用于将str2拼接到str1后面,并返回str1,其中str1和str2为字符串。
注意:
该函数要求str1的字符数组长度要够长,能够放下str1的有效字符+str2的有效字符+一个结束符。
5、字符数组串处理函数
7.3.1字符数组知识点
(3)strcat字符串拼接函数
举例:
#include
int main(){
char str1[30] = {"Hello"};
char str2[5] = {" C"};
char str3[10] = {" language!"};
strcat(str1,str2);
printf("str1: %s\n",str1); //str1中拼接了str2的内容
printf("str2: %s\n",str2); //str2字符串内容不变
strcat(str1,str3);
printf("str1: %s",str1);
return 0;
}
运行结果:
str1: Hello C
str2: C
str1: Hello C language!
5、字符数组串处理函数
7.3.1字符数组知识点
(4)strcpy字符串拷贝函数
strcpy(str1,str2)用于将str2中的内容拷贝到str1中,会清除str1中原有的字符。
注意:
str1字符数组的大小要足够大能容纳str2数组中的字符,否则出现越界的现象。
5、字符数组串处理函数
7.3.1字符数组知识点
(4)strcpy字符串拷贝函数
举例:
#include
int main(){
char str1[30] = {"Hello"};
char str2[30] = {"Language"};
printf("before: str1: %s\n",str1);
strcpy(str1,str2);
printf("after : str1: %s\n",str1);
return 0;
}
运行结果:
before: str1: Hello
after : str1: Language
5、字符数组串处理函数
7.3.1字符数组知识点
(5)strstr字串查找函数
strstr(str1,str2)用于判断str1是否含有str2这个字符串。
如果有,str2则被称为str1的子串。该函数返回指向str1中str2第一个出现的位置的指针;
如果str2不是sr1的子串,则返回NULL。
5、字符数组串处理函数
7.3.1字符数组知识点
(5)strstr字串查找函数
举例:
#include
int main(){
char str1[30] = {"Hello C language!"};
char str2[30] = {"language"};
char str3[10] = "C++";
char *p = strstr(str1,str2);
printf("%s\n",p); //p指向str1中从'l'开始的"language"子串
printf("%d",strstr(str1,str3) == NULL);//str3不是str1的子串,所以strstr返回NULL
return 0;
}
运行结果:
language!
1
6、二维字符数组
7.3.1字符数组知识点
二维字符数组,可以理解为多个一维字符数组,如果一维字符数组中存放的是学生姓名,那二维数组就可以存放多个学生的姓名,其初始化、引用都和二维数组一样。
举例:
char names[3][20] = {{“Peter”},{“Tom”},{“Emily”}};
3行20列,表示3个一维字符数组(每个数组最多20个字符),可以保存3个字符串。
name[0]
name[1]
name[2]
案例7.3.1:查找特定的字符
要求:本例包含两行输入,输入第一行是一串以回车符结束的字符串str(不超过50个字符),第二行是要查找的字符ch。如果在给定的字符串str中找到了字符ch,则输出在str中最先出现ch的下标(从0开始计数);若未找到,则输出“Not Found!”
7.3.2字符数组案例分析
str数组的长度应该定义为多少?
#include
int main(){
int i,flag = 0; // flag为0表示没有找到
char str[51]={'\0'},ch;
gets(str);
ch = getchar();
for(i=0;str[i]!='\0';i++)
{
if(ch == str[i]){
flag = 1;
printf("The first index of %c is %d ",ch,i);break;
}
}
if(flag == 0){
printf("Not Found!");
}
}
运行结果:
Life is like a box of chocolates↙
l↙
The first index of l is 8
运行结果
案例7.3.1程序代码
7.3.2字符数组案例分析
案例7.3.2:删除重复数字字符
要求:输入是一串以回车符结束的字符串str(不超过80个字符),编写程序将str字符串重复的数字字符删除,其他字符原样输出。
问题分析:
(1)定义长度为10的number数组来保存某个数字是否出现过。
比如,若6出现过,则number[6]为1,若6没有出现,则number[6]为0;
所以number[0]、number[1]、number[2]…number[9]依次表示数字0、1、2…9是否出现过,元素值为1表示出现过,元素值为0表示没出现过。
(2)在后续输出str时,对于其中的数字字符,先转化为相应的数字,再判断number[该数字]的值是否为0,为0表示没出现过,便可以直接输出。
7.3.2字符数组案例分析
#include
int main(){
int i,number[10] = {0}; //标记某个数字是否出现过
char str[81] = {'\0'};
gets(str);
for(i=0;str[i]!='\0';i++){
if(str[i]>='0' && str[i]<='9'){
int index = str[i]-'0'; // 数字字符转对应数字
if(number[index] == 0){
number[index] = 1;
putchar(str[i]);
}
}else
putchar(str[i]);
}
return 0;
}
运行结果:
输入数据:
ad2f3adj3fea2in4zz5zv4↙
输出:
ad2f3adjfeain4zz5zv
运行结果
案例7.3.2程序代码
7.3.2字符数组案例分析
案例7.3.3: 提取身份证号中的出生年月,进行转化后输出
要求: 输入一个整数n,随后输入n个身份证号,最后输入一个offset。为了保密的要求,请提取身份证号中的出生年月日,身份证号为18位,其中7-16位为出生年月,对出生年月日进行如下处理,如’0’—>’9’、’1’—>’8’、’2’—>’7’、’3’—>’6’、’4’—>’5’,’5’—>’4’、
’6’—>’3’、’7’—>’2’、’8’—>’1’、’9’—>’0’,最后输出处理过的身份证号。
7.3.2字符数组案例分析
#include
int main(){
int i,n,k;
/*定义了19行19列二维字符数组,每一行19个字符=身份证号18个字符+结束符'\0',19行可以保存19个人的身份证号*/
char identity[19][19];
//n保存身份证号的个数
scanf("%d",&n);
for(i=0;i//identify[i]为一个一维字符数组,保存第i个身份证号
scanf("%s",identity[i]);
}
for(i=0;i//将identity[i]中第7~14位出生日期进行处理
for(k=6;k<=13;k++){
//得到当前数字字符对应的数值
int num = identity[i][k] - '0';
identity[i][k] = '0' + (9-num);
}
printf("%s\n",identity[i]);
}
return 0;
}
案例7.3.3程序代码
7.3.2字符数组案例分析
输入:
3↙
330103200110121513↙
330602199007191214↙
330104200106111313↙
输出:
330103799889871513
330602800992801214
330104799893881313
运行结果
7.3.3字符数组练习题
课堂练习题7.3.1:字符串的逆序输出。
输入一行字符串,字符个数不超过100个,请将该行字符串逆序输出。
课堂练习题7.3.2:输入为一首4句中文藏头诗,每句诗一行,获取这首诗每句的第一个汉字,并组成一个字符串,将其输出。注:每个汉字占两个字节
课堂练习题7.3.3:按照指定规则对字符串压缩
输入为一行字符串,如果该字符串中某个字符c连续出现了n次,当n≥2时,将这n个连续字符c缩写为nc。
【微视频】一维字符数组的定义和使用
C语言中数组长度在定义时就已确定,在引用数组元素时,如果下标index<0或者index>=数组长度,就会出现下标越界情况,访问到数组以外的内存。
编译器不会对下标是否越界进行检查。如果代码中在引用数组时存在下标越界的情况,编译可以通过,但在运行时可能会出现不可预测的结果。
7.4 数组的越界
#include
int main() {
int i;
int a[3];
a[0] = 1;
a[1] = 2;
a[2] = 3;
a[3] = 4; //下标越界访问
for (i = 0; i <= 3; i++)
{
printf("%d ", a[i]);
}
return 1;
}
数组下标越界访问
7.4 数组的越界
实际输出为:1 2 3 3
(本例在Dev C++上运行)
运行结果
预期的输出为:1 2 3 4
为什么是这样的
运行结果呢?
有一些编译器(本例在Dev C++上运行)地址分配方式:数组和循环变量分配在一起,如下图所示:
a[3]属于越界访问,它的地址是a[2]后面的那个地址,访问的其实就是变量i,运行下面代码就可以看出来。
数组下标越界访问
7.4 数组的越界
a[0]
a[2]
i
a[1]
#include
int main() {
int i;
int a[3];
a[0] = 1; a[1] = 2; a[2] = 3; a[3] = 4; //下标越界访问
printf("the address of a[0] is %p\n",&a[0]);
printf("the address of a[1] is %p\n",&a[1]);
printf("the address of a[2] is %p\n",&a[2]);
printf("the address of a[3] is %p\n",&a[3]);
printf("the address of i is %p\n",&i);
return 1;
}
运行结果:
the address of a[0] is 000000000062FE10
the address of a[1] is 000000000062FE14
the address of a[2] is 000000000062FE18
the address of a[3] is 000000000062FE1C
the address of i is 000000000062FE1C
a[3]和i地址一样
总结:
当程序中存在越界访问数组元素时,如果该程序对越界访问的内存具有操作权限,该程序可以正常运行,但会发生不可控的结果(如上面的实例)。如果该程序对越界访问的内存没有操作权限,或者该内存没有被分配,程序将会出现崩溃现象。所以在访问数组元素时,要尤其注意下标不要越界。
7.4 数组的越界
谢谢观看
程序设计基础
展开更多......
收起↑