函数的返回值是结构体类型
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include<stdio.h> 4 #include<stdlib.h> 5 6 struct info 7 { 8 char name[50]; 9 int num; 10 }; 11 12 struct info getstruct(char *p, int num) 13 { 14 struct info info1;//创建结构体 15 printf("%x\n", &info1); 16 info1.num = num;//进行赋值 17 strcpy(info1.name, p);//复制字符串 18 printf("%d,%s\n", info1.num, info1.name); 19 return info1;//返回结构体,return有副本机制,保存了info1到另外一段内存 20 } 21 22 main() 23 { 24 struct info MM = getstruct("hello", 10); 25 26 printf("%d,%s\n", MM.num, MM.name); 27 28 system("pause"); 29 };
什么时候传值,什么时候传址呢?
内容很少,可以传值。内容很多,最好传地址,节约时间与空间,因为复制浪费时间和空间。
改变一个变量,需要传入地址,改变一个结构体,需要传入结构体的地址
向函数传递整个结构体变量中的数据,传值
传递结构体变量的地址,传址
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include<stdio.h> 4 #include<stdlib.h> 5 6 struct info 7 { 8 char name[50]; 9 long long phone; 10 }; 11 12 //函数的副本机制,函数的参数新建了一个变量接收值 13 //形参改变,不影响实参 14 void showinfo(struct info info1) 15 { 16 printf("%x\n", &info1); 17 18 strcpy(info1.name, "gao yuan yuan"); 19 info1.phone = 18687654321; 20 21 printf("%s,%lld\n", info1.name, info1.phone); 22 } 23 24 //因为函数的副本机制,所以改变一个外部变量,需要变量的地址 25 void change(struct info *p) 26 { 27 printf("%x\n", &p); 28 29 strcpy((*p).name, "gao yuan yuan"); 30 (*p).phone = 18687654321; 31 32 printf("%s,%lld\n", (*p).name, (*p).phone); 33 } 34 35 main() 36 { 37 struct info lady = { "tang wei",13912345678 }; 38 39 printf("%x\n", &lady); 40 41 printf("%s,%lld\n", lady.name, lady.phone); 42 43 change(&lady); 44 45 printf("%s,%lld\n", lady.name, lady.phone); 46 47 system("pause"); 48 }
//函数的参数,会对数组传地址,不会有副本机制
//为了节约内存,数组传递的是指针
//数组作为函数参数,退化成指针
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include<stdio.h> 4 #include<stdlib.h> 5 6 struct csdn 7 { 8 char user[40]; 9 char password[40]; 10 char email[40]; 11 }; 12 13 //函数的参数,会对数组传地址,不会有副本机制 14 //为了节约内存,数组传递的是指针 15 //数组作为函数参数,退化成指针 16 //void find(struct csdn cdata[5], char *str) 17 void find(struct csdn *cdata, char *str) 18 { 19 printf("find结构体数组有%d个元素\n", sizeof(cdata));//结构体数组作为参数,有4个字节 20 printf("find结构体元素有%d个元素\n", sizeof(struct csdn)); 21 22 int i; 23 24 for (i = 0;i < 5;i++) 25 { 26 if (strcmp(cdata[i].user, str) == 0) 27 { 28 printf("找到%s,%s,%s\n", cdata[i].user, cdata[i].password, cdata[i].email); 29 } 30 } 31 } 32 33 main() 34 { 35 struct csdn cdata[5]= 36 { 37 {"microsoft","1234567890","microsoft@google.com"}, 38 { "google","1234567890","google@google.com"}, 39 { "baidu","1234567890","baidu@google.com"}, 40 { "ali","1234567890","ali@google.com"}, 41 { "tencent","1234567890","tencent@google.com"} 42 }; 43 44 printf("main结构体数组有%d个元素\n", sizeof(cdata)); 45 printf("main结构体元素有%d个元素\n", sizeof(struct csdn)); 46 47 int i; 48 49 for (i = 0;i < 5;i++) 50 { 51 printf("%s %s %s\n", cdata[i].user, cdata[i].password, cdata[i].email); 52 } 53 54 find(cdata, "google"); 55 56 system("pause"); 57 }
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include<stdio.h> 4 #include<stdlib.h> 5 6 #define DB double * 7 typedef double * db; 8 9 main() 10 { 11 DB dp1, dp2;//double *dp1, dp2; 12 13 db dp3, dp4;//double *dp3, *dp4 14 15 printf("%d,%d\n", sizeof(dp1), sizeof(dp2)); 16 17 printf("%d,%d\n", sizeof(dp3), sizeof(dp4)); 18 19 system("pause"); 20 }
#define DB double//替换 #define 被替换的内容 替换的内容
typedef double db;//别名 typedef 原来的类型名 新的别名
//#define 一般加上;,如果加上;,会把;一起替换
//typedef 需要;
结构体字节对齐机制
结构体变量占据的内存单元的个数应当大于等于其内部所有数据成员占据内存单元数的和。
1、C语言引入了字节对齐机制,一般来说,不同的编译器字节对齐机制有所不同,但还是有以下3条通用准则:
(1)结构体变量的大小能够被其最宽基本类型成员的大小所整除;
(2)结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
(3)结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。
2、说明: 字节对齐第3条准则提及最宽基本类型的概念,所谓基本类型是指像char、short、int、float、double这样的内置数据类型。“数据宽度”就是指其sizeof的大小。诸如结构体、共用体和数组等都不是基本数据类型
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include<stdio.h> 4 #include<stdlib.h> 5 6 struct mystruct 7 { 8 short sh; 9 char str[11]; 10 };//2+11=14 11 12 struct info 13 { 14 char ch; 15 char str[10]; 16 };//1+10=11 17 18 struct tel 19 { 20 int tel; 21 char name[30]; 22 };//4+32=36 23 24 struct data 25 { 26 char c; 27 double db; 28 char str[9]; 29 };//8+8+16=32 30 31 struct data1 32 { 33 short sh; 34 double db; 35 char str[9]; 36 };//8+8+16=32 37 38 struct data2 39 { 40 short sh; 41 int ch; 42 char str[19]; 43 };//4+4+20=28 44 45 main() 46 { 47 printf("%d\n", sizeof(struct mystruct)); 48 49 printf("%d\n", sizeof(struct info)); 50 51 printf("%d\n", sizeof(struct tel)); 52 53 printf("%d\n", sizeof(struct data)); 54 55 printf("%d\n", sizeof(struct data1)); 56 57 printf("%d\n", sizeof(struct data2)); 58 59 system("pause"); 60 };
输出结果:
14
11
36
32
32
28
请按任意键继续. . .
共用体大小必须至少包含最大的成员数据,同时可以整除最小成员数据
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include<stdio.h> 4 #include<stdlib.h> 5 6 union data 7 { 8 double db;//8 9 char str[9];//9 10 };//16 11 12 union data1 13 { 14 int i;//4 15 char str[9];//9 16 };//12 17 18 main() 19 { 20 printf("%d\n", sizeof(union data)); 21 22 printf("%d\n", sizeof(union data1)); 23 24 system("pause"); 25 };
输出结果:
16
12
请按任意键继续. . .
结构体定义
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include<stdio.h> 4 #include<stdlib.h> 5 6 struct//第3种方法,无名结构体,限量版发行 7 { 8 char email[50]; 9 char name[50]; 10 char add[100]; 11 int num; 12 int bignum; 13 char tel[20]; 14 char phone[20]; 15 double RMB; 16 }d1, d2, d3;//第3种方法,无名结构体,限量版发行 17 18 //#define DD struct dangdang//第2种方法 19 // 20 //DD 21 //{ 22 // char email[50]; 23 // char name[50]; 24 // char add[100]; 25 // int num; 26 // int bignum; 27 // char tel[20]; 28 // char phone[20]; 29 // double RMB; 30 //}; 31 //DD d1, d2, d3;//第2种方法 32 33 //struct dangdang//第1种方法 34 //{ 35 // char email[50]; 36 //char name[50]; 37 //char add[100]; 38 //int num; 39 //int bignum; 40 //char tel[20]; 41 //char phone[20]; 42 //double RMB; 43 //}; 44 //struct dangdang d1, d2, d3;//第1种方法 45 46 main() 47 { 48 49 system("pause"); 50 };
//指针访问结构体2种形式
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include<stdio.h> 4 #include<stdlib.h> 5 6 struct info 7 { 8 int num; 9 float score; 10 }; 11 12 main() 13 { 14 struct info info1; 15 printf("%d,%f\n", info1.num = 10, info1.score = 29); 16 struct info *p1 = &info1; 17 18 printf("%d,%f\n", (*p1).num, (*p1).score); 19 printf("%d,%f\n", p1->num, p1->score);//指针访问结构体2种形式 20 21 struct info *p2 = (struct info*)malloc(sizeof(struct info)); 22 p2->num = 18; 23 p2->score = 19.8; 24 25 printf("%d,%f\n", (*p2).num, (*p2).score); 26 printf("%d,%f\n", p2->num, p2->score); 27 28 system("pause"); 29 }
结构体变量有副本机制,结构体数组没有,结构体变量内部的数组(如果有数组)也有副本。
为什么需要结构体?
为了表示一些复杂的事物,而普通的基本类型无法满足实际需求。
什么叫结构体?
把一些基本类型数据组合组合在一起形成的一个新的数据类型,这个叫做结构体。
14.2.2结构体类型的变量、数组和指针变量的定义
可以用以下四种方式定义结构体类型的变量、数组和指针变量,推荐用第三种方式。
1 #include<stdio.h> 2 3 struct date 4 { 5 int year, month, day; 6 }; 7 8 /* (1)紧跟在结构体类型说明之后进行定义。不推荐,因为只能够定义一次此类型结构体变量 */ 9 struct student 10 { 11 char name[12]; 12 char sex; 13 struct date birthday; 14 float sc[4]; 15 }std, pers[3], *pstd; 16 17 /* (2)在说明一个无名结构体类型的同时,直接进行定义。不推荐,因为只能够定义一次此类型结构体变量 */ 18 struct 19 { 20 char name[12]; 21 char sex; 22 struct date birthday; 23 float sc[4]; 24 }std, pers[3], *pstd; 25 26 /* (3)先说明结构体类型,再单独进行变量定义。推荐 */ 27 struct student 28 { 29 char name[12]; 30 char sex; 31 struct date birthday; 32 float sc[4]; 33 }; 34 struct student std, pers[3], *pstd; 35 36 /* (4)使用typedef说明一个结构体类型名,再用新类型名来定义变量 */ 37 typedef struct 38 { 39 char name[12]; 40 char sex; 41 struct date birthday; 42 float sc[4]; 43 }STREC; 44 STREC std, pers[3], *pstd; 45 46 main() 47 { 48 49 }
14.2.3给结构体变量、数组赋初值
1 #include<stdio.h> 2 3 struct student 4 { 5 int age; 6 float score; 7 char sex; 8 }; 9 10 main() 11 { 12 struct student st = { 80, 66.6, 'F' }; 13 14 struct student st2; 15 st2.age = 10; 16 st2.score = 88; 17 st2.sex = 'F'; 18 19 printf("%d %f %c\n", st.age, st.score, st.sex); 20 printf("%d %f %c\n", st2.age, st2.score, st2.sex); 21 }
14.2.4引用结构体变量中的数据
1 #include<stdio.h> 2 3 struct student 4 { 5 int age; 6 float score; 7 char sex; 8 }; 9 10 main() 11 { 12 struct student st = { 80, 66.6, 'F' }; 13 struct student * pst = &st; 14 15 st.age = 10; /* (1)通过结构体变量引用 */ 16 pst->age = 88; /* (2)通过指针变量引用,更常用 */ 17 (*pst).age = 99; /* (3)通过指针变量引用 */ 18 }
结构体变量不能相加,不能相减,也不能相互乘除。
允许相同类型的结构体变量之间进行整体赋值。
1 #include<stdio.h> 2 3 struct student 4 { 5 char name[10]; 6 int num; 7 }per1, per2 = { "YANGGM",46 }; 8 9 main() 10 { 11 per1 = per2; 12 }
写一个程序,输入学生的个数,并根据学生的成绩,进行降序排序。
1 #include<stdio.h> 2 3 struct student 4 { 5 int age; 6 char name[100]; 7 float score; 8 }; 9 10 void getdata(struct student * parr, int len) 11 { 12 int i; 13 14 for (i = 0;i < len;++i) 15 { 16 printf("请输入第%d个学生的信息:", i + 1); 17 18 printf("age="); 19 scanf("%d", &parr[i].age); 20 21 printf("name="); 22 scanf("%s", parr[i].name); 23 24 printf("score="); 25 scanf("%f", &parr[i].score); 26 } 27 } 28 29 void outdata(struct student * parr, int len) 30 { 31 int i; 32 33 for (i = 0;i < len;++i) 34 { 35 printf("age=%d,name=%s,score=%f\n", parr[i].age, parr[i].name, parr[i].score); 36 } 37 } 38 39 void sort(struct student * parr, int len) 40 { 41 int i, j; 42 struct student t; 43 44 for (i = 0;i < len - 1;++i) 45 { 46 for (j = 0;j < len - 1 - i;++j) 47 { 48 if (parr[j].score < parr[j + 1].score) 49 { 50 t = parr[j]; 51 parr[j] = parr[j + 1]; 52 parr[j + 1] = t; 53 } 54 } 55 } 56 } 57 58 main() 59 { 60 int len; 61 struct student * parr; 62 struct student t; 63 64 printf("请输入学生的个数:"); 65 scanf("%d", &len); 66 parr = (struct student *)malloc(len * sizeof(struct student)); 67 68 getdata(parr, len); 69 70 printf("\n输入的信息如下:\n"); 71 outdata(parr, len); 72 73 sort(parr, len); 74 75 printf("\n根据学生的成绩排序,使用降序\n"); 76 outdata(parr, len); 77 }
枚举
什么是枚举?
把一个事物所有可能的取值一一列举出来
枚举型常量,可以将数据限定在一定的范围之内,不会出现越界错误,并且刨除多余的数据
枚举的优缺点:
代码更安全
书写麻烦
1 #include<stdio.h> 2 3 enum weekday 4 { 5 mon, tue, wed, thu, fri, sat, sun 6 }; 7 8 void fun(enum weekday i) /* 本函数目的只是期望接受0-6之间的数字,将形参i定义为枚举 */ 9 { 10 switch (i) 11 { 12 case 0:printf("mon\n"); 13 break; 14 case 1:printf("tue\n"); 15 break; 16 case 2:printf("wed\n"); 17 break; 18 case 3:printf("thu\n"); 19 break; 20 case 4:printf("fri\n"); 21 break; 22 case 5:printf("sat\n"); 23 break; 24 case 6:printf("sun\n"); 25 break; 26 } 27 } 28 29 main() 30 { 31 fun(fri); 32 }
数组
优点:
存取速度快
缺点:
需要一个连续的很大的内存
插入和删除元素的效率很低
链表
优点:
插入删除元素效率高
不需要一个连续的很大的元素
缺点:
查找某个位置的元素效率低
专业术语:
首节点:
存放第一个有效数据的节点
尾节点:
存放最后一个有效数据的节点
头结点:
头结点的数据类型和首节点的数据类型一模一样
头结点是首节点前面的那个节点
头结点并不存放有效数据
设置头结点的目的是为了方便对链表的操作
头指针:
存放头结点地址的指针变量
例14.2
对比向函数传递结构体数组名和向函数传递结构体变量名的区别。
1 #include<stdio.h> 2 3 typedef struct 4 { 5 int num; 6 double mark; 7 }REC; 8 9 void sub1(REC x) 10 { 11 x.num = 23; 12 x.mark = 81.5; 13 } 14 15 void sub2(REC y[]) 16 { 17 y[0].num = 12; 18 y[0].mark = 77.5; 19 } 20 21 main() 22 { 23 REC a = { 16,90.0 }, b[] = { 16,90.0 }; 24 sub1(a); 25 printf("A)%d,%5.1lf\n", a.num, a.mark); 26 sub2(b); 27 printf("B)%d,%5.1lf\n", b[0].num, b[0].mark); 28 }
输出结果:
A)16, 90.0
B)12, 77.5
请按任意键继续. . .
例14.3
通过函数返回结构体类型的值。
1 #include<stdio.h> 2 3 typedef struct 4 { 5 int a; 6 char b; 7 }ST; 8 9 ST fun(ST x) /* 函数的返回值类型是结构体类型ST */ 10 { 11 x.a = 99; 12 x.b = 'S'; 13 return x; 14 } 15 16 main() 17 { 18 ST y; 19 y.a = 0; 20 y.b = 'A'; 21 printf("y.a=%d,y.b=%c\n", y.a, y.b); 22 y = fun(y); 23 printf("y.a=%d,y.b=%c\n", y.a, y.b); 24 }
输出结果:
y.a=0,y.b=A
y.a=99,y.b=S
请按任意键继续. . .
例14.4
通过函数的返回值返回指向结构体变量的指针。
1 #include<stdio.h> 2 3 typedef struct 4 { 5 int a; 6 char b; 7 }ST; 8 9 ST * fun(ST x) /* 函数的返回值类型是ST *类型 */ 10 { 11 ST * px; 12 x.a = 100; 13 x.b = 'C'; 14 px = &x; 15 return px; 16 } 17 18 main() 19 { 20 ST y, *p; 21 y.a = 999; 22 y.b = 'X'; 23 printf("y.a=%d y.b=%c\n", y.a, y.b); 24 p = fun(y); 25 printf("(*p ).a=%d (* p).b=%c\n", (*p).a, (*p).b); 26 }
输出结果:
y.a=999 y.b=X
(*p ).a=100 (* p).b=C
请按任意键继续. . .
例14.5
读入五位用户的姓名和电话号码,按姓名的字典顺序排列后,输出用户的姓名和电话号码。
下面的程序中说明了一个名为 USER 的结构体类型,该结构体包含了两个字符串变量用以存放姓名和电话号码。getdata 函数读入五位用户的姓名和电话号码,放入形参 sp 所指结构体数组中。getsort 函数把结构体数组元素中的数据按姓名的字典顺序排列。outdata 函数输出最后的结果。
1 #include<stdio.h> 2 #include<string.h> 3 #define N 5 4 5 typedef struct 6 { 7 char name[20]; 8 char num[10]; 9 }USER; 10 11 void getdata(USER * sp); 12 void getsort(USER * sp); 13 void outdata(USER * sp); 14 15 main() 16 { 17 USER sp[N]; 18 getdata(sp); 19 getsort(sp); 20 outdata(sp); 21 } 22 23 void getdata(USER * sp) 24 { 25 int i; 26 printf("Enter name & phone number:\n"); 27 for (i = 0;i < N;i++) 28 { 29 gets(sp[i].name); 30 gets(sp[i].num); 31 } 32 } 33 34 void getsort(USER * sp) 35 { 36 int i, j, k; 37 USER temp; 38 for (i = 0;i < N - 1;i++) /* 按成员 name 的大小重新排列 sp 数组的元素 */ 39 { 40 k = i; 41 for (j = i + 1;j < N;j++) 42 { 43 if (strcmp(sp[k].name, sp[j].name)>0) 44 { 45 k = j; 46 } 47 temp = sp[k]; /* 结构体整体赋值 */ 48 sp[k] = sp[i]; 49 sp[i] = temp; 50 } 51 } 52 } 53 54 void outdata(USER * sp) 55 { 56 int i; 57 printf("after sorted:\n"); 58 for (i = 0;i < N;i++) 59 { 60 printf("%s,%s\n", sp[i].name, sp[i].num); 61 } 62 }
例14.6
一个简单的链表:
以下程序中所定义的结构体类型 NODETYPE 共有两个成员:成员 data 是整型;成员 next 是指针类型,其基类型为 NODETYPE 类型。
main 函数中定义的变量 a、b、c 都是结构体变量,它们都含有 data 和 next 两个成员;变量 h 和 p 是指向 NODETYUPE 结构体类型的指针变量,它们与结构体变量 a、b、c 中的成员变量 next 类型相同。
1 #include<stdio.h> 2 3 struct node 4 { 5 int data; 6 struct node * next; 7 }; 8 9 typedef struct node NODETYPE; 10 11 main() 12 { 13 NODETYPE a, b, c, *h, *p; 14 a.data = 10; /* 给变量中的 data 域赋值 */ 15 b.data = 20; 16 c.data = 30; 17 h = &a; /* 将结点相连 */ 18 a.next = &b; 19 b.next = &c; 20 c.next = '\0'; 21 p = h; 22 while (p) /* 移动 p,使之依次指向 a、b、c,输出它们 data 域中的值 */ 23 { 24 printf("%d", p->data); 25 p = p->next; /* p 顺序后移 */ 26 } 27 printf("\n"); 28 }