结构体(struct)

为什么需要结构体

为了表示一些复杂的事物,而普通的基本类型无法满足实际要求。

什么叫结构体

把一些基本类型数据组合在一起,形成一个新的复合数据类型,这个叫结构体。

  1. #include <stdio.h>
  2. struct Student
  3. {
  4. int age;
  5. float score;
  6. char sex;
  7. };
  8. int main(void)
  9. {
  10. struct Student st = {80, 66.6, 'F'}; //struct Student为数据类型,
  11. //st为结构体变量
  12. return 0;
  13. }

定义结构体的3种方式

  1. //第一种方式----推荐使用,只是定义了一个新的数据类型,方便定义多个变量
  2. struct Student
  3. {
  4. int age;
  5. float score;
  6. char sex;
  7. };
  8. //第二种方式----过于精简,不能重复定义
  9. struct Student2
  10. {
  11. int age;
  12. float score;
  13. char sex;
  14. }st2;
  15. //第三种方式----过于精简,不能重复定义
  16. struct
  17. {
  18. int age;
  19. float score;
  20. char sex;
  21. }st3;

结构体变量的用法

赋值和初始化

  1. 第一种方式:定义结构体变量的同时,整体赋初值
  2. struct Student st = {10, 66.6, 'F'};
  3. 第二种方式:定义结构体变量后,对每个成员赋初值
  4. struct Student st;
  5. st.age = 10;
  6. st.score = 66.6;
  7. st.sex = 'F';

定义结构体指针变量并赋值

  1. 定义形式:
  2. struct 结构体名 *变量名;
  3. 赋值格式:
  4. struct 结构体名 *变量名 = &结构体变量;
  5. 例子:
  6. struct Student st = {10, 66.6, 'F'};
  7. //struct Student *pst = st; //error
  8. struct Student *pst = &st; //&符号不能少

取出结构体变量中的每个成员

  1. #include <stdio.h>
  2. struct Student
  3. {
  4. int age;
  5. float score;
  6. char sex;
  7. };
  8. int main(void)
  9. {
  10. struct Student st = {10, 33.3, 'F'};
  11. struct Student* pst = &st;
  12. //第一种取出方式
  13. st.age = 20; //结构体变量名.成员名
  14. //第二种取出方式----(更常用)
  15. pst->age = 33; //指针变量名->成员名
  16. return 0;
  17. }
  18. //Note: pst->age在计算机内部会被转换成(*pst).age,等价于st.age。

结构体变量和结构体指针变量作为函数参数传递的问题

结构体变量作为函数的形参时:

  • 对结构体变量进行修改时,必须发送结构体变量的地址;
  • 对结构体变量进行打印输出时,可以发送结构体变量的地址推荐使用,内存占用空间小,执行速度快),也可以发送变量名。

即:推荐使用结构体指针变量作为函数参数传递

结构体变量的运算

结构体变量不能相互加减,也不能相互乘除,但可以相互赋值

  1. 例子:
  2. struct Student
  3. {
  4. int age;
  5. char sex;
  6. char name[100];
  7. };
  8. struct Student st1,st2;
  9. // st1+st2; st1-st2; st1*st2; st1/st2; //都是错误的
  10. st1 = st2; //正确

冒泡排序

  1. 1. 确定一个数组仅需要两个参数;
  2. 2. 冒泡排序的核心是先找最大值或最小值,下面代码是先找最大值。
  3. void sort(int* arr, int len)
  4. {
  5. int i = j = temp = 0;
  6. for(i = 0;i < len - 1;i++) //比较总次数
  7. {
  8. for(j = 0;j < len - 1 - i;j++) //两两比较
  9. {
  10. if(arr[j] > arr[j + 1])
  11. {
  12. temp = arr[j];
  13. arr[j] = arr[j + 1];
  14. a[j + 1] = temp;
  15. }
  16. }
  17. }
  18. }

动态构造存放学生信息的结构体数组

  1. /* 申请动态空间存入学生信息,并进行排序 */
  2. #include <stdio.h>
  3. #include <malloc.h>
  4. // 学生信息结构体
  5. struct Student
  6. {
  7. int age;
  8. float score;
  9. char name[100];
  10. };
  11. // 学生信息录入
  12. void Info_input(struct Student *pArr)
  13. {
  14. int len;
  15. struct Student temp;
  16. printf("请输入学生的个数:");
  17. scanf("%d", &len);
  18. pArr = (struct Student *)malloc(len * sizeof(struct Student)); //struct占用的内存空间大小为所有结构体成员的空间总和
  19. // 信息录入
  20. for (int i = 0; i < len; i++)
  21. {
  22. printf("请输入第%d个学生的信息:\n", i + 1);
  23. printf("age = ");
  24. scanf("%d", &pArr[i].age);
  25. printf("name = ");
  26. scanf("%s", pArr[i].name); //name是数组名,本身是数组的首地址,无需加“&”符号
  27. printf("score = ");
  28. scanf("%f", &pArr[i].score);
  29. }
  30. // 排序
  31. for (int i = 0; i < len - 1; i++)
  32. {
  33. for (int j = 0; j < len - 1 - i; j++)
  34. {
  35. if (pArr[j].score > pArr[j + 1].score)
  36. {
  37. temp = pArr[j];
  38. pArr[j] = pArr[j + 1];
  39. pArr[j + 1] = temp;
  40. }
  41. }
  42. }
  43. // 输出
  44. for (int i = 0; i < len; i++)
  45. {
  46. printf("\n第%d各学生的信息是:\n", i + 1);
  47. printf("age = %d\n", pArr[i].age);
  48. printf("name = %s\n", pArr[i].name);
  49. printf("age = %f\n", pArr[i].score);
  50. printf("\n");
  51. }
  52. }
  53. int main(void)
  54. {
  55. struct Student *st = NULL;
  56. Info_input(st);
  57. free(st);
  58. return 0;
  59. }

枚举(enum)

枚举的定义

把一个事物所有可能的取值一一列举出来。

  1. 定义形式:
  2. enum typeName{ valueName1, valueName2, valueName3, ...... };
  3. ----枚举值默认从0开始,往后逐个加1。初始化定义的时候,可以指定枚举值的数值,一般只定义第一个枚举值即可。
  4. 例子:
  5. enum week{ Mon, Tues, Wed, Thurs, Fri, Sat, Sun };

枚举的用法

  1. /* 查询星期缩写 */
  2. #include <stdio.h>
  3. enum week
  4. {
  5. Mon = 1,
  6. Tues,
  7. Wedn,
  8. Thurs,
  9. Fri,
  10. Sat,
  11. Sun
  12. };
  13. int main(void)
  14. {
  15. enum week day;
  16. printf("请输入你要查询的星期缩写数值:");
  17. scanf("%d",&day);
  18. switch (day)
  19. {
  20. case Mon:
  21. printf("这是Mon");
  22. break;
  23. case Tues:
  24. printf("这是Tues");
  25. break;
  26. case Wedn:
  27. printf("这是Wedn");
  28. break;
  29. case Thurs:
  30. printf("这是Thurs");
  31. break;
  32. case Fri:
  33. printf("这是Fri");
  34. break;
  35. case Sat:
  36. printf("这是Sat");
  37. break;
  38. case Sun:
  39. printf("这是Sun");
  40. break;
  41. default:
  42. printf("error,请输入整型数值!");
  43. break;
  44. }
  45. return 0;
  46. }

使用枚举的注意点

  • 枚举列表中的枚举值标识符的作用范围是全局的,不能再定义同样名字的变量;
  • 枚举值是常量,除了初始化定义时可以指定数值,其余情况下不可对其赋值;
  • 枚举和宏其实非常类似:宏在预处理阶段将名字替换成对应的值,枚举在编译阶段将名字替换成对应的值。我们可以将枚举理解为编译阶段的宏;
  • 枚举值不是变量,不占用内存空间,不能用**&**取枚举值的地址
  • 枚举占用的内存空间仅为一个**int**整型的大小

    枚举的优缺点

  • 优点:限定了取值的范围,使代码更安全

  • 缺点:书写较为麻烦。

    共用体(union)

    共用体的定义形式

    1. union 共同体名
    2. {
    3. 成员列表1;
    4. ...
    5. 成员列表n;
    6. };

    创建共同体的三种方式

    ```c // 第一种方式(推荐使用)——定义共同体后,创建共同体变量 union data { int n; char ch; double f; }; union data a, b, c;

// 第二种方式(过于精简,不能重复定义)——定义共同体的同时,创建共同体变量 union data { int n; char ch; double f; }a, b, c; // 第三种方式(过于精简,一次性使用)——不定义创建共同体变量 union { int n; char ch; double f; }a, b, c;

  1. <a name="zGKGf"></a>
  2. ## 共同体的内存存储分布
  3. ```c
  4. #include <stdio.h>
  5. union data{
  6. int n;
  7. char ch;
  8. short m;
  9. };
  10. int main(){
  11. union data a;
  12. printf("%d, %d\n", sizeof(a), sizeof(union data) );
  13. a.n = 0x40;
  14. printf("%X, %c, %hX\n", a.n, a.ch, a.m);
  15. a.ch = '9';
  16. printf("%X, %c, %hX\n", a.n, a.ch, a.m);
  17. a.m = 0x2059;
  18. printf("%X, %c, %hX\n", a.n, a.ch, a.m);
  19. a.n = 0x3E25AD54;
  20. printf("%X, %c, %hX\n", a.n, a.ch, a.m);
  21. return 0;
  22. }

image.png
小端模式下的存储结构
image.png
大段模式下的存储结构

结构体和共同体的区别

结构体

  • 结构体的各个成员占用不同的内存,成员之间没有相互影响;
  • 结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能存在内存缝隙)

    共同体

  • 共同体的所有成员占用同一段内存,修改一个成员会影响其余所有成员;

  • 共同体占用的内存等于最长的成员占用的内存;
  • 共同体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新成员赋值,会把原来成员的值覆盖掉

    用法示例

    ```c / 设计一个包含共用体的结构体 /

    include

    include

define TOTAL 4

struct memberInfo { char name[20]; int num; char sex; char profession; //作为判断选择union的变量 union S_or_C //共同体保存两个特征——二选一 { float score; char course[20]; } sc; };

int main(void) { struct memberInfo bodys[TOTAL]; //定义一个结构体变量数组,存放多个成员信息 // 输入人员信息 for (int i = 0; i < TOTAL; i++) { printf(“Input data: “); scanf(“%s %d %c %c”, bodys[i].name, &(bodys[i].num), &(bodys[i].sex), &(bodys[i].profession)); if (bodys[i].profession == ‘s’) //判断是否为学生 { scanf(“%f”, &(bodys[i].sc.score)); //输出分数信息 } else { scanf(“%s”, &(bodys[i].sc.course)); //输出课程信息 } fflush(stdin); }

  1. //输出人员信息
  2. printf("\nName\t\tNum\tSex\tProfession\tScore / Course\n");
  3. for (int i = 0; i < TOTAL; i++)
  4. {
  5. if (bodys[i].profession == 's')
  6. { //如果是学生
  7. printf("%s\t%d\t%c\t%c\t\t%f\n", bodys[i].name, bodys[i].num, bodys[i].sex, bodys[i].profession, bodys[i].sc.score);
  8. }
  9. else
  10. { //如果是老师
  11. printf("%s\t%d\t%c\t%c\t\t%s\n", bodys[i].name, bodys[i].num, bodys[i].sex, bodys[i].profession, bodys[i].sc.course);
  12. }
  13. }
  14. return 0;

} ```