太原网络推广网站,南宁建站程序,wordpress登陆后段后端,简述网站制作方案和主要内容在C语言中#xff0c;结构体#xff08;struct#xff09;和共用体#xff08;union#xff09;都是用来存储不同类型数据的复合数据类型#xff0c;它们在程序设计中具有重要的作用。 推荐阅读#xff1a;操作符详细解说#xff0c;让你的编程技能更上一层楼 1. 结构体…在C语言中结构体struct和共用体union都是用来存储不同类型数据的复合数据类型它们在程序设计中具有重要的作用。 推荐阅读操作符详细解说让你的编程技能更上一层楼 1. 结构体的定义与使用
1.1 结构体的基本概念
结构体struct是C语言中的一种用户自定义的数据类型它允许用户将不同类型的数据组合成一个单一的复合数据类型。结构体中的每个数据元素称为“成员”或“字段”。每个成员可以是不同类型的数据。
结构体的定义方式如下
struct StructName {type member1;type member2;type member3;// ...
};注意不要忘了最后的;
举例下面结构体 包含num,gender,name三个成员成员的数据类型分别是intchar,char 数组
struct StructName {int num;char gender;char name[20];
};1.2 结构体的定义与初始化
定义一个结构体之后我们可以定义该结构体类型的变量并对其进行初始化。结构体变量的声明方式如下
struct StructName varName;结构体的初始化可以通过两种方式进行静态初始化和动态初始化。
1.2.1 静态初始化
静态初始化是在定义结构体变量时直接赋值。
struct Person {char name[50];int age;
};struct Person person1 {John, 25};1.2.2 动态初始化
动态初始化是通过用户输入或运行时计算来初始化结构体的成员。
struct Person person2;
strcpy(person2.name, Alice);
person2.age 30;1.3 访问结构体成员
结构体成员通过点操作符.来访问。
struct Person person1 {John, 25};
printf(Name: %s, Age: %d\n, person1.name, person1.age);1.4 结构体的数组
结构体的数组允许我们创建多个结构体对象。声明结构体数组的方式与普通数组类似。
struct Person people[3] {{John, 25}, {Alice, 30}, {Bob, 22}};访问结构体数组元素时需要使用数组索引和点操作符
printf(Name: %s, Age: %d\n, people[0].name, people[0].age);1.5 结构体作为函数参数
结构体可以作为函数的参数传递。可以通过值传递或者引用传递指针传递传递结构体。
1.5.1 结构体值传递
在函数中对结构体进行值传递时函数接收到结构体的副本对副本的修改不会影响原结构体。
void printPerson(struct Person p) {printf(Name: %s, Age: %d\n, p.name, p.age);
}struct Person person1 {John, 25};
printPerson(person1);1.5.2 结构体指针传递
如果希望在函数内修改结构体的内容可以通过传递结构体指针来引用原结构体。
void updatePerson(struct Person *p) {p-age 28; // 使用箭头操作符访问结构体指针成员
}struct Person person1 {John, 25};
updatePerson(person1);
printf(Updated Age: %d\n, person1.age);1.6 结构体内存对齐与填充
结构体在内存中的存储方式受到内存对齐的影响。为了提高处理器的效率结构体的成员通常会根据其类型进行内存对齐。这意味着有时结构体成员之间可能会有空洞称为“填充”。
可以使用#pragma pack指令或__attribute__((packed))来调整结构体的内存对齐方式。
#pragma pack(push, 1)
struct Example {char a;int b;
};
#pragma pack(pop)对齐的原则 结构体内存对齐与填充Padding是C语言中涉及数据结构存储方式的重要概念。它直接影响到程序的内存使用效率和性能尤其在处理多平台或者低层次的系统编程时需要对这一点有深入的理解。
1. 内存对齐的概念
内存对齐指的是将数据类型按一定的规则存储到内存中的方式。由于现代计算机处理器的存取效率通常要求数据类型按一定的边界对齐存储。也就是说不同类型的数据应该存放在特定的内存地址上这样能够提高处理器访问内存的速度。
2. 内存对齐的原理
在C语言中每个数据类型都有自己的“对齐要求”。对齐要求是指某个数据类型的变量在内存中应存储在某个特定的地址上这个地址通常是该数据类型大小的倍数。
例如
char 类型的数据通常可以存储在任意地址1 字节对齐。int 类型的变量通常要求存储在4字节对齐的地址上即地址必须是4的倍数。double 类型通常要求存储在8字节对齐的地址上即地址必须是8的倍数。
不同编译器可能会有不同的默认对齐方式但是常见的规则是
char 类型1字节对齐。short 类型2字节对齐。int 类型4字节对齐。double 类型8字节对齐。
3. 填充Padding
填充是指为了满足对齐要求在结构体成员之间或结构体末尾插入空闲字节以确保每个成员的数据按照其对齐要求存储。
举个例子考虑一个结构体包含 char 和 int 类型的成员
struct Example {char a; // 1 字节int b; // 4 字节
};假设系统对 int 类型要求4字节对齐那么在 char a 后面会有 3 个字节的填充以确保 b 在 4 字节对齐的位置开始存储。这是因为 b 需要在内存中存储在地址是4的倍数的位置。
因此结构体的内存布局可能如下
| char a | padding | padding | padding | int b |这个结构体的总大小将会是 8 字节而不是 5 字节。这样b 的起始地址就符合 4 字节对齐的要求。
4. 内存对齐与填充的规则 结构体成员对齐 每个成员都必须存储在一个地址上这个地址必须是该成员类型对齐要求的倍数。 结构体总对齐 结构体的对齐要求是结构体中最大对齐要求成员的对齐要求。例如如果结构体中有 char 和 double 成员那么结构体的对齐要求就是 double 的对齐要求通常是 8 字节。 结构体大小 结构体的大小是根据最大对齐要求来计算的。结构体的大小通常是结构体总内存的最小倍数这个倍数是结构体内最大成员对齐的倍数。
5. 示例结构体内存对齐与填充
让我们来看一个例子假设在一个系统中int 类型要求4字节对齐char 类型要求1字节对齐
#include stdio.hstruct Example {char a; // 1 字节int b; // 4 字节char c; // 1 字节
};这个结构体中的 a 需要 1 字节而 b 需要 4 字节的对齐。由于 b 必须从 4 字节对齐的位置开始因此 a 后面会有 3 个字节的填充接着 b 存储。然后 c 占用 1 字节由于 b 的对齐要求结构体的总大小将根据最大对齐需求通常为 4 字节填充。
因此结构体在内存中的布局如下
| char a | padding | padding | padding | int b | char c | padding |结构体总大小为 8 字节。可以通过 sizeof 操作符来查看结构体的实际大小
printf(Size of struct Example: %zu\n, sizeof(struct Example)); // 输出86. 编译器对齐设置
在一些情况下编译器允许通过指令来设置对齐方式。例如GCC 和 Clang 提供了 #pragma pack 指令可以控制结构体的对齐方式。可以通过设置对齐大小来减小结构体的内存占用。
例如在GCC中使用 #pragma pack(1) 可以强制按 1 字节对齐这样就不会有任何填充字节
#pragma pack(1)
struct Example {char a; // 1 字节int b; // 4 字节
};
#pragma pack() // 恢复默认对齐这样结构体将没有填充字节内存布局将是
| char a | int b |举例说明
2. 共用体的定义与使用
2.1 共用体的基本概念
共用体union是一种特殊的数据结构它与结构体类似但与结构体不同的是共用体的所有成员共享相同的内存空间。即同一时刻共用体只能存储一个成员的值。这使得共用体能够节省内存空间。
共用体的定义格式如下
union UnionName {type member1;type member2;type member3;// ...
};2.2 共用体的定义与初始化
定义一个共用体并初始化时通常初始化其中的第一个成员。
union Data {int i;float f;char c;
};union Data data1;
data1.i 10;
data1.f 3.14; // 此时 data1.i 的值会被覆盖2.3 访问共用体成员 由于共用体的成员共享相同的内存位置因此只能访问最后存储的成员。当一个成员被赋值时其他成员的值将被覆盖。
union Data data1;
data1.i 10;
printf(Integer: %d\n, data1.i);data1.f 3.14;
printf(Float: %.2f\n, data1.f); // 访问 float 类型成员2.4 共用体的应用场景
共用体主要用于节省内存特别是在需要存储不同类型数据但在任何时刻只需要其中之一的场合。常见的应用场景包括
多种类型的数据共享同一内存空间。处理不同类型数据的协议解析。
2.5 共用体与结构体的区别
内存分配结构体中的每个成员都有自己独立的内存空间而共用体的所有成员共享同一块内存空间。用途结构体适用于需要存储多个不同类型数据的场合而共用体适用于需要存储不同类型数据但在同一时刻只需要其中一个的场合。
3. 结构体与共用体与指针的结合
3.1 结构体指针
结构体指针是指向结构体类型变量的指针。通过结构体指针可以访问结构体的成员。结构体指针通常与malloc动态内存分配结合使用。
3.1.1 声明与初始化
struct Person {char name[50];int age;
};struct Person *ptr;
ptr (struct Person *)malloc(sizeof(struct Person)); // 动态分配内存strcpy(ptr-name, John);
ptr-age 25;3.1.2 访问结构体成员
通过结构体指针访问成员时使用箭头操作符-。
printf(Name: %s, Age: %d\n, ptr-name, ptr-age);3.2 共用体指针
与结构体指针类似我们也可以创建共用体指针通过指针来访问共用体的成员。
3.2.1 声明与初始化
union Data {int i;float f;char c;
};union Data *ptr;
ptr (union Data *)malloc(sizeof(union Data));ptr-i 10;3.2.2 访问共用体成员
与结构体指针类似共用体指针也使用箭头操作符-来访问成员。
printf(Integer: %d\n, ptr-i);3.3 结构体与共用体混合使用
结构体和共用体也可以混合使用以满足更复杂的需求。例如我们可以在结构体中包含一个共用体或者在共用体中使用结构体。
struct Person {char name[50];int age;
};union Data {struct Person p;int i;
};union Data data;
data.p.age 30;
strcpy(data.p.name, Alice);printf(Name: %s, Age: %d\n, data.p.name, data.p.age);4.结论
结构体和共用体是C语言中非常强大的数据结构。结构体允许你将不同类型的数据组织在一起而共用体通过共享内存来节省空间。在实际开发中理解这两者的使用场景和优缺点并掌握它们与指针的结合是编写高效和内存优化代码的关键。
通过本篇文章的学习希望你能够全面理解结构体与共用体的定义、使用方式及其在指针方面的应用从而更好地应对C语言编程中的复杂问题。