C/C++心得-结构体
先说句题外话,个人认为,基本上所有的高级语言被设计出来的最终目的是降低软件开发难度,提升软件开发人员素质和团队协作能力,降低软件维护的难度。在学习语言的时候,可以从这么方面来推测各种语言语法设计的原因,从来更好的掌握各种编程语言。
总结一下C语言中的数据类型结构:
1.常用基本数据类型(int, char, float, double, short, long等等)
2.数组
3.指针
4.枚举
5.结构体
6.公用体
...
这其中除了共用体不常用外,其他都是实际开发中经常用到的数据结构。其他的之前两篇都有说明,今天来说说枚举和结构体。
(初学者应该养成读代码从main开始读的习惯)
1.枚举
枚举作为一种数据类型,用于声明一组命名的常数,用来说明一种事物的不同类型或属性。实际应用作用的话,个人认为是提高代码的可读性,减小程序的维护难度。
举个例子,一个程序中需要使用变量表示颜色,那么写程序前我们需要先设定标准:0表示白色,1表示红色,2表示黄色,3表示蓝色...,那么程序代码如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 void printColor(int color) 5 { 6 switch (color) 7 { 8 case 0: 9 printf("白色\n"); 10 break; 11 case 1: 12 printf("红色\n"); 13 break; 14 case 2 : 15 printf("黄色\n"); 16 break; 17 case 3: 18 printf("蓝色\n"); 19 break; 20 default: 21 break; 22 } 23 } 24 25 int main(int arg, char *args) 26 { 27 int color0 = 0; // 白色 28 int color1 = 1; // 红色 29 int color2 = 2; // 黄色 30 int color3 = 3; // 蓝色 31 32 printColor(color0); 33 printColor(color1); 34 printColor(color2); 35 printColor(color3); 36 37 getchar(); 38 return 0; 39 }
(关于枚举的例子只是作为说明,这些作为例子有些不太好)如果程序代码量很大的情况下,0,1,2,3的这种数字意义很容易忘记,这时可以用到枚举
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 enum Color 5 { 6 White, // 白色 7 Red, // 红色 8 Yellow,// 黄色 9 Blue // 蓝色 10 }; 11 12 void printColor(int color) 13 { 14 switch (color) 15 { 16 case White: 17 printf("白色\n"); 18 break; 19 case Red: 20 printf("红色\n"); 21 break; 22 case Yellow: 23 printf("黄色\n"); 24 break; 25 case Blue: 26 printf("蓝色\n"); 27 break; 28 default: 29 break; 30 } 31 } 32 int main(int arg, char *args) 33 { 34 int color0 = White; // 白色 35 int color1 = Red; // 红色 36 int color2 = Yellow; // 黄色 37 int color3 = Blue; // 蓝色 38 39 printColor(color0); 40 printColor(color1); 41 printColor(color2); 42 printColor(color3); 43 44 getchar(); 45 return 0; 46 }
对枚举的理解应用暂时就如此,关于枚举的作为属性应用,以及其他方面的应用,有兴趣的可以查查其他资料。
2.结构体
结构体的意义之一同样是增加代码可读性。而且结构体配合指针使得C语言变得非常灵活。
结构体是由一系列具有相同或不同类型的数据组成。
下面用两种方法定义并输出一个学生的信息。
首先是不用结构体和枚举的方法
1 #define _CRT_SECURE_NO_WARNINGS 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 6 int getSex(short sex, char * sexVal) 7 { 8 if (sexVal == NULL) 9 { 10 return -1; 11 } 12 switch (sex) 13 { 14 case 0: 15 strcpy(sexVal, "男"); 16 break; 17 case 1: 18 strcpy(sexVal, "女"); 19 break; 20 default: 21 return -2; 22 break; 23 } 24 return 0; 25 } 26 // 不用结构体和枚举的方法 27 int main(int arg, char * args[]) 28 { 29 int num = 1; 30 char name[32] = "LiLei"; 31 short sexN = 0; 32 char sex[4] = { '\0' }; 33 if (getSex(sexN, sex) != 0) 34 { 35 return -1; 36 } 37 printf("学号:%d,姓名:%s,性别:%s\n", num, name, sex); 38 39 getchar(); 40 return 0; 41 }
下面使用结构体和枚举改造代码:
1 #define _CRT_SECURE_NO_WARNINGS 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 6 typedef enum _Sex 7 { 8 Male, 9 Female 10 }Sex; 11 12 struct Student 13 { 14 int num; 15 char name[32]; 16 short sex; 17 }; 18 19 int getSex(Sex sex, char * sexVal) 20 { 21 if (sexVal == NULL) 22 { 23 return -1; 24 } 25 switch (sex) 26 { 27 case Male: 28 strcpy(sexVal, "男"); 29 break; 30 case Female: 31 strcpy(sexVal, "女"); 32 break; 33 default: 34 return -2; 35 break; 36 } 37 return 0; 38 } 39 40 int main(int arg, char * args[]) 41 { 42 struct Student stu = { 1, "LiLei", Male }; 43 char sex[4] = { 0 }; 44 if (getSex(stu.sex, sex) != 0) 45 { 46 return -1; 47 } 48 printf("学号:%d,姓名:%s,性别:%s\n", stu.num, stu.name, sex); 49 getchar(); 50 return 0; 51 }
可以发现,在没有什么注释的情况下,从Main开始读代码,第二段代码是比较好理解。
使用结构体一般都会使用typedef给结构体起个别名,当然typedef在指针、数组等方面都有应用,使用typedef可以精简代码。下面举个例子
1 #define _CRT_SECURE_NO_WARNINGS 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 6 enum Sex 7 { 8 Male, 9 Female 10 }; 11 12 typedef struct _Student 13 { 14 long num; 15 char name[32]; 16 short sex; 17 } Student; // 使用typedef给结构体起别名 18 19 int main(int arg, char * args[]) 20 { 21 struct _Student stu1 = { 50, "hel", Male }; // 不使用typedef前的定义方式 22 Student stu2 = { 100, "yao", Male }; // 使用typedef的定义方式,主要是省去一个struct 23 24 return 0; 25 }
typedef的用法,先正常定义,然后用别名替代掉原变量(或函数名)名称的位置,如:
1 #define _CRT_SECURE_NO_WARNINGS 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 6 typedef int mI; 7 typedef char mCh; 8 typedef char mStr[32];// 原来定义char数组是 char * str[32]; 这里将str替换为别名 9 typedef int (* name)(int a, int b); // int sum(int a, int b), 因为函数需要以指针形式表示,所以把sum替换为 * name 10 11 int sum(int a, int b) 12 { 13 return a + b; 14 } 15 16 int main(int arg, char *args[]) 17 { 18 mI num = 10; 19 mCh ch = 'x'; 20 mStr str = "hello"; // 这里相当于定义 char str[32] = "hello"; 21 name func = sum; // 函数指针,相当于 int func(int a, int b); func中的执行代码和sum函数相同 22 printf("num:%d,a=%c,str=%s,func(1, 2)=%d\n", num, ch, str, func(15, 20)); 23 getchar(); 24 return 0; 25 }
再说说结构体的长度。结构体的长度要略大于或等于其内部成员的总长度。主要是为了程序速度,在一个结构体内有多个类型的成员的时候,会做一些“对齐”处理,该处理有可能导致结构体变量占用空间变大。
1 #define _CRT_SECURE_NO_WARNINGS 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 6 struct Student1 7 { 8 int num; 9 char name[32]; 10 short sex; 11 }; 12 struct Student2 13 { 14 long long num; 15 char name[32]; 16 short sex; 17 }; 18 19 int main(int arg, char * args[]) 20 { 21 struct Student1 stu1; 22 struct Student2 stu2; 23 printf("int:%d, char:%d, short:%d, longlong:%d\n", sizeof(int), sizeof(char), sizeof(short), sizeof(long long)); // 比较4种数据类型的长度 24 printf("long:%d,int:%d\n", sizeof(struct Student2), sizeof(struct Student1)); // 比较两种结构体的长度 25 printf("stu1:%x,stu2:%x\n", &stu1, &stu2); // 两个结构体变量的地址 26 getchar(); 27 return 0; 28 }
上面这段程序的输出结构为:
int:4, char:1, short:2, longlong:8 long:48,int:40 stu1:eafeb4,stu2:eafe7c
从第一行输出信息来看,我们可以算出结构体Student1声明的变量(int num, char name[32], short sex)的总长度应该为38,而第二行的实际输出结果为40.
同样可以算出结构体Student2声明的变量(long long num, char name[32], short)的总长度应该为42,而实际结果为48.
一个结构体的长度必定是其内部长度最大基本数据类型元素的整数倍,上面Student1和Student2都符合该项(数组不是基本数据类型)。
而且结构体变量的首地址必定能被其内部长度最大基本数据类型元素整除,如上面eafeb4可以被4整除,eafe7c可以被8整除。
3.结构体的一点高级用法
1.通过地址取结构体内成员的值(代码作为示例,真实开发中应该不会这样写)
1 #define _CRT_SECURE_NO_WARNINGS 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 6 enum Sex 7 { 8 Male, 9 Female 10 }; 11 12 struct Student1 13 { 14 int num; 15 char name[32]; 16 short sex; 17 }; 18 struct Student2 19 { 20 long long num; 21 char name[39]; 22 short sex; 23 }; 24 25 int main(int arg, char * args[]) 26 { 27 char * stu1Name = NULL; 28 char * stu2Name = NULL; 29 struct Student1 stu1 = { 100, "LiLei", Male }; 30 struct Student2 stu2 = { 100, "WaLiu", Female }; 31 32 printf("int:%d, char:%d, short:%d, longlong:%d\n", sizeof(int), sizeof(char), sizeof(short), sizeof(long long)); // 比较4种数据类型的长度 33 printf("long:%d,int:%d\n", sizeof(struct Student2), sizeof(struct Student1)); // 比较两种结构体的长度 34 printf("stu1:%x,stu2:%x\n", &stu1, &stu2); // 两个结构体变量的地址 35 printf("stu1:%x,stu2:%x\n", &stu1 + 4, &stu2 + 8); 36 37 stu1Name = (char *)(((char *)&stu1) + 4); // 取stu1的地址,向后偏移4位,这个地址就是name的首地址,将其赋给stu1Name 38 stu2Name = (char *)(((char *)&stu2) + 8); // 取stu2的地址,向后偏移8位,这个地址就是name的首地址,将其赋给stu2Name 39 40 printf("stu1:%s,stu2:%s\n", stu1Name, stu2Name); // 打印出值 41 getchar(); 42 return 0; 43 }
以上代码执行结构为:
int:4, char:1, short:2, longlong:8 long:56,int:40 stu1:107fdb0,stu2:107fd70 stu1:107fe50,stu2:107ff30 stu1:LiLei,stu2:WaLiu
这说明可以通过地址偏移的方式取得结构体变量内部的值。