1 / 82

第 4 章 复杂数据类型

第 4 章 复杂数据类型. 为了解决比较复杂的问题,本章介绍 C 语言提供的复杂数据类型。 4.1 数组 4.2 指针 4.3 结构体 4.4 共用体 4.5 枚举类型 4.6 类型定义符 typedef. 4.1 数组. 4.1.1 数组的概念 4.1.2 一维数组 4.1.3 二维数组 4.1.4 字符维数. s[0]. s[1]. s[2]. ……. s[29]. 数组 s. 4.1.1 数组的概念.

yorick
Download Presentation

第 4 章 复杂数据类型

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第4章 复杂数据类型 为了解决比较复杂的问题,本章介绍C语言提供的复杂数据类型。 4.1 数组 4.2 指针 4.3 结构体 4.4 共用体 4.5 枚举类型 4.6 类型定义符typedef

  2. 4.1 数组 4.1.1 数组的概念 4.1.2 一维数组 4.1.3 二维数组 4.1.4 字符维数

  3. s[0] s[1] s[2] …….. s[29] 数组s 4.1.1 数组的概念 如果有30个互不关联的数据,可以分别把它们存放在30个变量中。但是如果这些数据是有内在联系的,是具有相同属性的(如30个学生的成绩),则可以把这组数据看作一个有机的整体,称为“数组”,用一个统一的名字代表这组数据。例如可用s代表学生成绩这组数据,s就是数组名。一个数组在内存中占一片连续的存储单元,数组s在内存中的存放情况如右图所示。

  4. 数组名代表一组数据,那么用什么方法来区分这组数据中的个体数据呢?从图4-1可以看出,s[0]代表第1个学生的成绩、s[1]代表第2个学生的成绩、…、s[29]代表第30个学生的成绩。s右下脚的数字0、1、…、29用来表示数据在数组中的序号,称为下标。数组中的数据称为数组元素。概括地说,数组就是有序数据的集合。要寻找一个数组中的某一个元素必须给出两个要素,即数组名和下标。数组名和下标唯一地标识一个数组中的一个元素。数组名代表一组数据,那么用什么方法来区分这组数据中的个体数据呢?从图4-1可以看出,s[0]代表第1个学生的成绩、s[1]代表第2个学生的成绩、…、s[29]代表第30个学生的成绩。s右下脚的数字0、1、…、29用来表示数据在数组中的序号,称为下标。数组中的数据称为数组元素。概括地说,数组就是有序数据的集合。要寻找一个数组中的某一个元素必须给出两个要素,即数组名和下标。数组名和下标唯一地标识一个数组中的一个元素。

  5. 1. 定义一维数组 数组同变量一样,也必须先定义、后使用。 1维数组是只有1个下标的数组,定义形式如下: 数据类型 数组名[常量表达式] 比如 float s[30];int tp[31]; (1)“数据类型”是指数组元素的数据类型。 (2)数组名,与变量名一样,必须遵循标识符命名规则。 (3)“常量表达式”必须用方括号括起来,指的是数组的元素个数(又称数组长度),它是一个整型值,其中可以包含常数和符号常量,但不能包含变量。 比如 int n=30; int a[n] 是错误的。 (4)数组元素的下标从0开始。 4.1.2 一维数组定义、引用和初始化

  6. 2 数组元素的引用 引用数组中的任意一个元素的形式: 数组名[下标表达式] 1.“下标表达式”可以是任何非负整型数据,取值范围是0~(元素个数-1)。 特别强调:在运行C语言程序过程中,系统并不自动检验数组元素的下标是否越界。因此在编写程序时,保证数组下标不越界是十分重要的。 2.1个数组元素,实质上就是1个变量,它具有和相同类型单个变量一样的属性,可以对它进行赋值和参与各种运算。 3.在C语言中,数组作为1个整体,不能参加数据运算,只能对单个的元素进行处理。

  7. 例4.1 输入10个整数,按输入时相反的顺序输出这10个数。 分析:用一个一维数组来存储输入的10个整数,通过从后往前访问数组元素的方法即可实现10个数的逆序输出。 #include <stdio.h> void main() { int i,data[10]; for(i=0;i<10;i++) scanf("%d",&data[i]); for(i=9;i>=0;i--) printf("%d ",data[i]); } 运行结果: 1 2 3 4 5 6 7 8 9 10  10 9 8 7 6 5 4 3 2 1

  8. 例4.2 输入10名学生某门课程的成绩,要求把高于平均分的那些成绩打印出来。 • 分析:用一个一维数组来存储10名学生的成绩,通过访问所有数组元素可计算出总成绩进而求出平均分。将每个学生的成绩逐个和平均分相比较,即可把高于平均分的成绩打印出来。 #include <stdio.h> void main() { int i,score[10]; int sum=0,aver; for(i=0;i<10;i++) { scanf("%d",&score[i]); sum=sum+score[i]; }

  9. aver=sum/10; printf("平均分为:%d\n",aver); printf("高于平均分的成绩为:\n"); for(i=0;i<10;i++) if(score[i]>aver) printf("%4d",score[i]); } 运行结果: 60 70 80 78 76 56 98 87 67 61 平均分为:73 高于平均分的成绩为:80 78 76 98 87

  10. 3. 一维数组元素的初始化 初始化格式: 数据类型 数组名[常量表达式]={初值表} (1)如果对数组的全部元素赋以初值,定义时可以不指定数组长度(系统根据初值个数自动确定)。如果被定义数组的长度,与初值个数不同,则数组长度不能省略。比如 int a[10]={0,1,2,3,4,5,6,7,8,9}; int a[]={1,2,3,4,5}; (2)“初值表”中的初值个数,可以少于元素个数,即允许只给部分元素赋初值。 int a[10]={0,1,2,3,4};

  11. 4.1.3 二维数组的定义、引用和初始化化 如果说一维数组在逻辑上可以想象成一行长表或矢量,那么二维数组在逻辑上可以想象成是由若干行、若干列组成的表格或矩阵。例如以下5名学生的成绩表格就形成了一个二维数组。

  12. 上表中的每一行表示了某个学生的三门课程的成绩。上表中的每一行表示了某个学生的三门课程的成绩。 如果用一个score数组来存放上面表格中的成绩, score数组各元素的名称和其所在位置如下所示: score[0][0] score[0][1] score[0][2] score[1][0] score[1][1] score[1][2] score[2][0] score[2][1] score[2][2] score[3][0] score[3][1] score[3][2] score[4][0] score[4][1] score[4][2] 这个数组应该有5行3列,每个元素都具有两个下标用来 表示该元素在数组中的位置。第一个下标表示元素的行号, 第二个下标表示元素的列号。因此,可以通过指定行号和 列号来对不同的数组元素进行存取和引用。

  13. 1. 二维数组的定义 二维数组定义的一般形式是: 类型说明符 数组名[常量表达式1][常量表达式2] 其中,“类型说明符”是指数组的数据类型,也就是每个数组元素的类型。“常量表达式1”指出数组的行数,“常量表达式2”指出数组的列数,它们必须都是正整数。 例如 int score[5][3]; 定义了一个5行3列的二维数组score,用于存储上面提到的5个人3门课程的成绩。 虽然在逻辑上可以把二维数组看作是一张表格,但在计算机内部,二维数组的所有元素都是一个接着一个排列的。C编译程序按行主顺序存放数组元素;也就是说先放第一行上的元素,接着放第二行上的元素,依次把各行的元素放入一串连续的存储单元中。数组score在内存中的排列顺序如下图所示。

  14. score[0][0] score[0][1] score[0][2] score[1][0] score[1][1] score[1][2] … score[4][0] score[4][1] score[4][2] 二维数组score在内存存放情况

  15. 2. 二维数组元素的引用 引用二维数组元素的形式为: 数组名[行下标表达式][列下标表达式] 1.“行下标表达式”和“列下标表达式”,都应是整型表达式或符号常量。 2.“行下标表达式”和“列下标表达式”的值,都应在已定义数组大小的范围内。假设有数组score[5][3],则可用的行下标范围为0~4,列下标范围为0~2。 3.对基本数据类型的变量所能进行的操作,也都适合于相同数据类型的二维数组元素。

  16. 例4.3 参照上面的学生成绩表,输入5个学生3门课程的成绩并计算每个学生的平均分。 分析:可设一个二维数组score[5][3]存放五个人三门课的成绩,再设一个一维数组v[5]存放每个学生的平均分。 #include <stdio.h> void main() { int i,j,s=0,v[5],score[5][3]; for(i=0;i<5;i++) { for(j=0;j<3;j++) { scanf("%d",&score[i][j]); s=s+score[i][j]; }

  17. v[i]=s/3; s=0; } printf("5个人的平均成绩分别为:"); for(i=0;i<5;i++) printf("%4d",v[i]); } 运行结果: 78 89 76↙ 90 78 67↙ 87 87 94↙ 66 77 65↙ 66 96 77↙ 5个人的平均成绩分别为: 81 78 89 69 80

  18. 3. 二维数组的初始化 (1)按行分段赋值可写为: int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; (2)按行连续赋值可写为: int a[3][4]={ 1,2,3,4,5,6,7,8,9,10,11,12}; 对于二维数组初始化赋值还有以下说明: (1)可以只对部分元素赋初值,未赋初值的元素自动取0值。 例如: int a[3][3]={{1},{2},{3}}; 是对每一行的第一列元素赋值,未赋值的元素取0值。 赋值后各元素的值为: 1 0 0 2 0 0 3 0 0 (2)如对全部元素赋初值,则第一维的长度可以不给出。 例如: int a[3][3]={1,2,3,4,5,6,7,8,9}; 可以写为: int a[][3]={1,2,3,4,5,6,7,8,9};

  19. 4.1.4 字符数组与字符串 1.字符数组的定义 定义格式 char 数组名[长度] 定义一维字符数组 char 数组名[行长][列长] 定义二维字符数组 例如 char c[10]; c[0]=‘I’;c[1]=‘’;c[2]=‘a’;c[3]=‘m’; c[4]=‘’;c[5]=‘a’;c[6]=‘’;c[7]=‘b’; c[8]=‘o’;c[9]=‘y’;

  20. c[0] c[1] c[2] c[3] c[4] c[5] c[6] C h i n a \0 \0 数组c在内存中的存放情况 2.字符数组的初始化 字符数组也允许在定义时作初始化赋值。如: char c[5]={ ’C’,’h’,’i’,’n’,’a’}; 把5个字符分别赋给c[0]到c[4]5个元素。 如果花括号中提供的初值个数大于数组长度,则在编译时系统会提示语法错误。如果初值个数小于数组长度,则只将这些字符赋给数组中前面那些元素,其余元素由系统自动定义为空字符(即’\0’)。如: char c[7]={ ’C’,’h’,’i’,’n’,’a’}; 则数组在内存中的存放情况如下图所示。

  21. 3字符数组的引用 例4.4 字符数组的逐个引用 #include <stdio.h> void main() { char c[12]=“I am a boy”; int i; for(i=0;i<10;i++) printf(“%c”,c[i]); printf(“\n”); } 运行结果: I am a boy

  22. 4. 字符串和字符串结束标志 在C语言中没有专门的字符串变量,通常用一个字符数组来存放一个字符串。前面介绍字符串常量时,已说明字符串总是以’\0’作为串的结束符。因此当把一个字符串存入一个数组时,也把结束符’\0’存入数组,并以此作为该字符串是否结束的标志。有了’\0’标志后,就不必再用字符数组的长度来判断字符串的长度了。 C语言允许用字符串的方式对数组作初始化赋值。 例如: char c[]={"C program"}; 或去掉{}写为: char c[]="C program"; 上面的数组c在内存中的实际存放情况如下图所示:

  23. 5.字符数组的输入与输出 • (1)、字符数组的输入 • 输入形式:char c[10]; • scanf(“%s”,c); • 说明: 输入项应使用数组名,不得以“&”开始。 空格、Tab、回车仍将作为字符串分隔符。 • 例如: char str[13]; • scanf(“%s”,str); • 若输入“How are you!”,则只存入“How”。 • 又如:scanf(“%s%s%s”,str1,str2,str3); • 若输入“How are you!”,则“How”、“are”、“you!”分别存入str1、str2、str3中。

  24. (2)、字符数组的输出 • 输出形式 • char c[10]=“China”; • printf(“%s”,c); • 说明: 输出项也应使用数组名。 被输出的字符数组应以“\0”作为结束标记,否则输出错误。 仅输出“\0”前的字符。

  25. 6.字符串处理函数 • (1)、字符串输出函数 • 格式:puts(字符串) • 功能:将字符串送终端输出。 • 说明: 字符串可以使用字符串常量、字符数组或字符串函数。 字符串应以“\0”作为结束标志。 • 例如: puts(“Hello”); • puts(str); //其中str为字符数组名

  26. (2)、字符串输入函数 格式:gets(字符数组) 功能:将键盘输入的字符串存入字符数组中。 例如: char str[30]; gets(str); puts(str); (3)、字符串连接函数 • 格式:strcat(字符数组,字符串) • 功能:将字符串连接在字符数组后,并将结果存入字符数组中。 • 例如: char str1[20]=“Good ”; • char str2[5]=“bye!”; • puts(strcat(str1,str2)); • 说明: 字符数组应足够大。 两个字符串均应以“\0”作为结束标志。

  27. (4)、字符串复制函数 • 格式:strcpy(字符数组,字符串) • 功能:将字符串复制到字符数组中。 • 例如: char str1[10]; • strcpy(str1,“Hello”)); • 说明: 字符数组应足够大。 字符串尾部应有“\0”,该标志将一同复制。 本函数不可用赋值语句实现。例如:str1=“Hello”

  28. (5)、字符串比较函数 格式:strcmp(字符串1,字符串2) 功能:将字符串1与字符串2中的字符自左向右按ASCII码值逐个进行比较,直到“\0”或遇不同字符为止。 返回值: 0 ——两串完全相同 正数 ——字符串1>字符串2 负数 ——字符串1<字符串2 例如: char str1[40],str2[40]; gets(str1); gets(str2); if(strcmp(str1,str2)==0) puts(“Yes”); else puts(“No”);

  29. (6)、测试字符串长度函数 格式:strlen(字符串) 功能:返回字符串的有效字符个数。 例如: char str[10]=“China”; printf(“%d”,strlen(str)); 结果为:5

  30. 4.2 指 针 指针是C语言中的重要概念,也是C语言的重要特色。使用指针,可以使程序更加简洁、紧凑、高效。 4.2.1 指针和指针变量的概念4.2.2 指针变量的定义与应用 4.2.3 指针变量和 数组 4.2.4 字符串指针变量和字符串 4.2.5 指针数组和多级指针

  31. 4.2.1 指针和指针变量概念 C程序中每一个实体,如变量、数组都要在内存中占有一个可标识的存储区域。每一个存储区域由若干个字节组成,在内存中每一个字节都有一个“地址” ,一个存储区域的“地址”指的是该存储区域中第一个字节的地址(或称首地址)。 如执行以下说明后 int a=3; double b=4.9; int *a_pointer=&a; 内存分配情况如右图所示: … 2010 a 3 2014 4.9 b … 3000 a_pointer 2010 … 内存中的a、b、a_pointer

  32. 在程序中一般是通过变量名来对内存单元进行存取操作的。其实程序经过编译以后已经将变量名转换为变量的地址,对变量值的存取都是通过地址进行的。比如,printf(”%d”,a);的执行是这样的:根据变量名与地址的对应关系(这个对应关系是在编译时确定的),找到变量a的地址2010,然后从2010开始的四个字节中取出数据即变量的值3,把它输出。这种通过变量地址存取变量值的方式称为“直接访问”方式。在程序中一般是通过变量名来对内存单元进行存取操作的。其实程序经过编译以后已经将变量名转换为变量的地址,对变量值的存取都是通过地址进行的。比如,printf(”%d”,a);的执行是这样的:根据变量名与地址的对应关系(这个对应关系是在编译时确定的),找到变量a的地址2010,然后从2010开始的四个字节中取出数据即变量的值3,把它输出。这种通过变量地址存取变量值的方式称为“直接访问”方式。 还有一种“间接访问”方式,把变量a的地址存放在另一个变量a_pointer中, 如果想得到a的值,可以先访问a_pointer,得到a_pointer的值2010,再通过地址2010找到它所指向的存储单元单元的值即数值3。 指针:变量的地址。 指针变量: 用来存放另一个变量的地址(即指针)的特殊变量。

  33. 4.2.2 指针变量的定义及其运算 1. 指针变量的定义 其一般形式为: 类型说明符 *变量名; 比如 int *p1; double *p2; 例4.5 分析以下程序的运行结果。 #include <stdio.h> void main() { int *p1; float *p2; double *p3; char *p4; printf("%d,%d,%d,%d\n",sizeof(p1),sizeof(p2),sizeof(p3),sizeof(p4)); } 运行结果:4,4,4,4

  34. 2. 指针变量运算符 (1)取地址运算符(&) 在c中,“&”称为取地址运算符,是单目运算符,其功能是 取变量的地址。 例如 p=&num; 将num的地址存储到p中 (2)指针运算符(*) 在c中,“*”称为指针运算符或者间接访问运算符,是单目运算符,用来表示指针变量所指向的变量

  35. 例4.6 下面的程序说明指针变量是如何工作的。 #include <stdio.h> void main() { int a=10; int *p; p=&a; printf("a=%d\n",a); printf("p=%x\n",p); printf("&a=%x\n",&a); printf("*p=%d\n",*p); printf("&p=%x\n",&p); } 运行结果: a=10 p=12ff7c &a=12ff7c *p=15 &p=12ff78

  36. 例4.7 分析以下程序的运行结果。 #include <stdio.h> void main() { int a=3,b=5,*pa,*pb,*p; pa=&a;pb=&b; printf("%d,%d,%d,%d\n",a,b,*pa,*pb); p=pa;pa=pb;pb=p; printf("%d,%d,%d,%d\n",a,b,*pa,*pb); } 运行结果: 3,5,3,5 3,5,5,3

  37. pa a pa a &a 3 p &b 3 p pb b &a pb b &b 5 &a 5 pa、pb指针变量指向情况

  38. 3. 指针变量的初始化 与其他变量一样,指针变量在定义的同时,也可以对其赋初值,称为指针变量的初始化。 指针变量初始化的一般格式如下: 基类型 *指针变量名=初始地址值; 例如: float x; float *px=&x; 4. 指针变量的运算 可以在指针变量上进行的运算有赋值 、关系和一些受限的算术运算。指针变量的值可赋给同类型的另一指针变量,同类型的指针可以比较是否相等。指针变量可以加上或减去某个整数值,一个指针变量的值也可以减去另一个指针变量的值。

  39. 数组a a[0] (&a[0]) a → 1 (&a[1]) a+1→ 21 a[1] (&a[2]) a+2→ 13 a[2] 18 a[3] (&a[3]) a+3→ … … 4.2.3 指针变量和数组 1、数组指针 一个数组通常包含若干个数组元素,在内存中占用连续若干个存储单元。每个元素均有其相应的地址(指针),第一个元素的地址(指针)即数组的起始地址,也称为该数组的指针。数组名可作为该数组的指针。如右图所示

  40. 数组a p → a[0] p+1→ a[1] p+2→ a[2] p+3→ a[3] … … 2、数组指针的定义与指向 定义方法与步骤: 定义数组→定义指针变量→指向 例如: ① int a[10]; int *p; p=a;(或p=&a[0];) ② int a[10]={0,1,2,3}; int *p=a;(或p=&a[0];) ③ int a[10],*p=a; 数组指针定义后如右图所示。

  41. 例4.8 多种方法访问数组元素 (1) 下标法。 #include <stdio.h> void main() { int a[3]={10,20,30}; int i; for(i=0;i<3;i++) printf("%3d",a[i]); printf("\n"); } 运行结果:10 20 30

  42. (2) 通过数组名计算数组元素地址,找元素的值。 #include <stdio.h> void main() { int a[3]={10,20,30}; int i; for(i=0;i<3;i++) printf("%3d",*(a+i)); printf("\n"); } 运行结果:10 20 30

  43. (3) 通过指针变量计算数组元素地址,找元素的值。 #include <stdio.h> void main() { int a[3]={10,20,30}; int *p=a,i; for(i=0;i<3;i++) printf("%3d",*(p+i)); /*(p+i)也可写成p[i]*/ printf("\n"); } 运行结果:10 20 30

  44. (4) 用指针变量指向数组元素 #include <stdio.h> void main() { int a[3]={10,20,30}; int *p=a; for(p=a;p<a+3;p++) printf("%3d",*p); printf("\n"); } 运行结果:10 20 30

  45. 4.2.4 字符串指针变量和字符串 1. 字符串常量的表示 字符串常量是用双引号括起来的字符序列, 例如“Good bye”就是一个字符串常量。 它由8个字符序列组成。 字符串常量的长度是指该字符串中的字符个数。 但在实际存储区域中,C编译器还自动给字符串序列的末尾 加上一个空字符’\0’,以此来标志字符串的结束。 因此,一个字符串常量所占用的存储区域的字节数总比 它的字符个数多一个字节。

  46. p G o o d b y e \0 在C语言中,操作一个字符串常量的方法有: (1)把字符串常量放在一个字符数组中,例如: char s[]="Good bye"; 数组s共有9个元素组成,其中,s[8]的内容是 ’\0’。 (2)用字符指针变量指向字符串, 然后通过字符指针变量来访问字符串存储区域 2. 字符串指针变量的定义和使用 char *p; p="Good bye"; 使p指向字符串常量中的首字符’G’,如下图所示。

  47. 例4.12 分析以下程序的运行结果。 #include <stdio.h> void main() { char *str="Good bye"; str=str+3; printf("%s",str); } 运行结果: d bye

  48. 例4.13 分析以下程序的运行结果。 #include <stdio.h> void main() { char a[]="Good bye",*str=a; str=str+3; *str='A'; printf("%s\n",str); } 运行结果: A bye

  49. 3 字符指针变量与字符数组的比较 虽然用字符串指针变量和字符数组都能实现字符串的存储和处理,但两者是有区别的,不能混为一谈。 (1)存储内容不同:字符指针变量中存储的是字符串的首地址,而字符数组中存储的是字符串本身。 例如,char *pointer,str[100]; 则编译系统给pointer分配4个字节存放它所指向的字符串首地址,给str分配则分配了100个字节来存放字符。 (2)赋值方式不同:对字符指针变量,可采用下面的赋值语句赋值 char *pointer; pointer="This is a example";

  50. 而字符数组,虽然可以在声明时初始化, 但不能用赋值语句整体赋值,只能逐个元素地赋值。 下面的用法是非法的: char array[20]; array="This is a example"; (3)指针变量的值是可以改变的,如: 例4.14 改变指针变量的值。 #include <stdio.h> #include <string.h> void main() { char *p="hello world"; p=p+6; puts(p); } 运行结果: world

More Related