https://www.toutiao.com/article/7213990045064184372/?log_from=475a0a623534c_1679754477361 什么是结构体 https://www.toutiao.com/article/7213990045064184372/?log_from=c3b7a9a1150be_1681705946225 在C语言中,结构体是不同数据类型的元素的集合。该结构用于创建用户定义的数据类型。该结构也被称为“ C语言自定义类型”。换句话说,结构体是不同类型数据的集合。这种数据类型的名字是由用户自主定义的。通常结构体用于将不同数据类型的元素组合成一个组。结构体中定义的元素称为结构成员。在前面我们学习过基础的数据类型int float char 等,都只能用来表示基础的数据类型,那么要怎么来表示复杂的数据类型呢?比如下信息: 定义5个数组,然后通过数组下标一致性原则去描述上述表格数据是否可行? 当然没得问题,如下代码: #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int ids[10] = { 0 }; char names[10][10] = { 0 }; char sexs[10][3] = { 0 }; int ages[10] = { 0 }; int scores[10] = { 0 }; ids[0] = 100; strcpy(names[0], "欧阳疯"); strcpy(sexs[0], "男"); ages[0] = 18; scores[0] = 666; return 0; } 看起来还不错,实际上很繁琐,在排序需要交换两者数据的时候极其繁琐,既然学生信息有很多,那么能不能定义一个学生类型呢?如果能,直接通过学生访问该学生的所有信息就很方便了! C语言结构体创建为了定义结构体,您必须使用 struct 语句。struct 语句定义了一个包含多个成员的新的数据类型,struct 语句的格式如下:
那么对于上面的学生的信息,就可以用如下结构体表示学生结构体类型: #include <stdio.h> #include <stdlib.h> #include <string.h> struct Student { int id; //学号 char name[10]; //姓名 char sex; //性别 int age; //年龄 int score; //总分 }; int main() { return 0; } 结构体中所有数据成员组成一个整体,形成一个新的数据类型,而不同变量则是零散内存,毫无关联,如下图: C语言结构体访问结构体中的数据必须要通过结构体变量访问,访问方式有以下两种:
结构体变量的创建 结构体类型已经声明,如何使用结构体类型定义结构体变量呢?有以下方法(typedef别名创建后续再讲):
如下测试代码: #include <stdio.h> #include <stdlib.h> struct MM { char name[20]; int age; double score; }mm; //在声明结构体类型的同时定义变量mm int main() { //先声明结构体类型再定义结构体变量 //struct MM: 类型 //girl: 变量名 struct MM girl; return 0; } 结构体变量的初始化 在定义结构体变量的同时通过{}的方式为每一个成员变量进行赋初值,赋初值主要有以下几种方式:
如下测试代码: #include <stdio.h> #include <stdlib.h> struct MM { char name[20]; int age; double score; }; int main() { //全部初始化,顺序必须一致 struct MM girl = {"girl",18,99.1}; //部分初始化:未初始化部分自动初始化为0 struct MM mm = { "gril" }; //全部初始化为0 struct MM zero = { 0 }; //初始化指定的成员(可以初始化任意成员,不必遵循定义顺序) struct MM beauty = { .age = 18,.name = "beauty" }; //用另一个结构体变量初始化 struct MM woman = girl; return 0; } 结构体数组 一个结构体变量可以存放一个学生的一组信息,可是如果有 10 个学生呢?难道要定义 10 个结构体变量吗?难道上面的程序要复制和粘贴 10 次吗?很明显不可能,这时就要使用数组。结构体中也有数组,称为结构体数组。它与前面讲的数值型数组几乎是一模一样的,只不过需要注意的是,结构体数组的每一个元素都是一个结构体类型的变量,都包含结构体中所有的成员项。 struct Student stus[10]; 这就定义了一个结构体数组,共有 10 个元素,每个元素都是一个结构体变量,都包含所有的结构体成员。 示例程序| 从键盘输入 5 个学生的基本信息,如学号、姓名、年龄、性别,将年龄最大的学生的基本信息输出到屏幕 #include <stdio.h> struct Student { int id; char name[10]; int age; char sex; }; int main() { struct Student stus[10]; printf("input stu>:\n"); for (int i = 0; i < 5; i++) { scanf("%d %s %d %c", &stus[i].id, stus[i].name, &stus[i].age, &stus[i].sex); } struct Student maxStu = stus[0]; for (int i = 0; i < 5; i++) { if (maxStu.age < stus[i].age) { maxStu = stus[i]; } } printf("-------------Max---------------\n"); printf("%d %s %d %c\n", maxStu.id, maxStu.name, maxStu.age, maxStu.sex); return 0; } 程序测试结果如下: 当然对于这种表格数据操作有很多,例如排序,查找,文件保存等。详细参见结构体数组写管理系统。 结构体指针 当一个指针变量指向结构体时,我们就称它为结构体指针。C语言结构体指针的定义形式一般为:struct 结构体名 *变量名; 如下测试代码: #include <stdio.h> #include <stdlib.h> #include <assert.h> struct MM { char name[20]; int age; double score; }; int main() { struct MM girl = {"girl",18,99.1}; struct MM* pMM = NULL; //结构体指针指向结构体变量 pMM = &girl; //指针用->访问 printf("%s\t%d\t%.1lf\n", pMM->name, pMM->age, pMM->score); //*pMM等效girl printf("%s\t%d\t%.1lf\n", (*pMM).name, (*pMM).age, (*pMM).score); //结构体指针动态内存申请 struct MM* pArray = (struct MM*)malloc(sizeof(struct MM)*3); assert(pArray); for (int i = 0; i < 3; i++) { pArray[i] =(struct MM){ "张三",18,99.9 }; printf("%s\t%d\t%.1lf\n", pArray[i].name, pArray[i].age, pArray[i].score); } return 0; } 程序测试结果如下: 基本上普通指针能做的,结构体指针一样的。只是在访问数据的时候需要剥洋葱(通过->访问每个数据)。当然也可以当做函数参数和返回值。 C语言位段C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为位段。利用位段能够用较少的位数存储数据。基本语法如下: struct 结构体名 { 类型 位段名1 : 位段大小; 类型 位段名2 : 位段大小; 类型 位段名3 : 位段大小; 类型 位段名4 : 位段大小; ... }; C语言标准规定,只有有限的几种数据类型可以用于位段。
如下测试代码: #include <stdio.h> #include <stdlib.h> #include <assert.h> struct BitField { unsigned char a : 1; unsigned char b : 4; unsigned char c : 3; }; int main() { //初始化 struct BitField bit = { 1,2,3 }; //输出 printf("first:%d %d %d\n", bit.a, bit.b, bit.c); //赋值 //10 1位有效去掉最高位 bit.a = 2; //10100 4位有效去掉最高位 bit.b = 20; //1000 3位有效去掉最高位 bit.c = 8; //再次输出 printf("last:%d %d %d\n", bit.a, bit.b, bit.c); } 程序测试结果如下: 位段注意项:
C语言结构体嵌套在一个结构体内包含另一个结构体作为其成员,当然一般嵌套可以理解为一类数据的封装。访问的话逐步剥洋葱即可,定义方式有两种写法。
示例程序以结构体变量当做数据成员方式嵌套 | 给学生增加一个出生日期,包含年月日 #include <stdio.h> #include <stdlib.h> #include <assert.h> struct Date { short year; short month; short day; }; struct Student { int id; char name[10]; struct Date birth; //出生日期 }; int main() { //数据完整的情况,{}可有可无 struct Student baby = { 1001,"baby",{2008,3,17} }; printf("%d\t%s\t%d-%d-%d\n", baby.id, baby.name, baby.birth.year, baby.birth.month, baby.birth.day); return 0; } 示例程序直接把结构体定义在另一个结构体内| 给学生增加一个出生日期,包含年月日 #include <stdio.h> #include <stdlib.h> #include <assert.h> struct Student { int id; char name[10]; struct Date { short year; short month; short day; }birth;//出生日期 }; int main() { //数据完整的情况,{}可有可无 struct Student baby = { 1001,"baby",{2008,3,17} }; printf("%d\t%s\t%d-%d-%d\n", baby.id, baby.name, baby.birth.year, baby.birth.month, baby.birth.day); return 0; } C语言结构体内存对齐什么是内存对齐 从理论上讲,对于任何变量的访问都可以从任何地址开始访问,但是事实上不是如此,实际上访问特定类型的变量只能在特定的地址访问,这就需要各个变量在空间上按一定的规则排列,而不是简单地顺序排列,这就是内存对齐。通俗点讲就是厕所建坑位需要合理排布对齐,不然可能会存在只有半个坑的情况。 为什么要内存对齐
当然我们会不会算内存对齐其实并不重要,因为对于一个结构体占用内存一个sizeof即可搞定,重要的是大家要知道如何设计代码可以让内存更小,毕竟在特殊开发场景,内存占用是非常值得关注的。例如,网络传输,嵌入式等。 内存对齐规则 C语言标准并没有规定内存对齐的细节,而是交给具体的编译器去实现,但是对齐的基本原则是一样的。
如下测试代码: #include <stdio.h> #include <stdlib.h> //会不会算出结果不重要,重要是学会怎么写可以少内存即可 //按照从小到大即可 字符和整形写一块 struct Data1 { double score; //8 char name[3]; //补一位和int组成8位 int age; }; struct Data2 { char name[3]; //补 5位 double score; //8位 int age; //补4位 }; struct Data3 { char name[9]; //8+1 +3 int num; //4 double score; //8 int age; //4+3 5 char tel[3]; }; struct Data4 { int age; char name[3]; int num; }; struct Data5 { char name[5]; char num; }; struct Data6 { char name[5]; int* p; //32位按照4个字节对齐,64位按照8位对齐 }; struct Data7 { struct Data4 data; //12 //char name[3]; //3+1 double score; //8 int age; //8 }; union Data8 { char name[20]; double score; }; int main() { struct Data1* p = (struct Data1*)malloc(sizeof(struct Data1)); printf("%zd\n", sizeof(struct Data1)); //16 printf("%zd\n", sizeof(struct Data2)); //24 //8 1w 8w printf("%zd\n", sizeof(struct Data3)); //32 printf("%zd\n", sizeof(struct Data4)); //12 printf("%zd\n", sizeof(struct Data5)); //6 printf("%zd\n", sizeof(struct Data6)); //16 //C语言不允许空的结构体 printf("%zd\n", sizeof(struct Data7)); //32 printf("%zd\n", sizeof(union Data8)); //24 return 0; |
|
来自: 山峰云绕 > 《C语言数据结构描述Windows程序设计》