目录
结构体可以嵌套在其他结构体中,形成复合结构。这对于表示更复杂的数据结构很有用。
struct Date {
int day;
int month;
int year;
};
struct Person {
char name[50];
int age;
struct Date birthdate;
};
// 使用结构体嵌套
struct Person person1;
person1.birthdate.day = 1;
person1.birthdate.month = 1;
person1.birthdate.year = 1990;
结构体位域允许你在一个结构体中指定每个成员占用的位数。这对于节省内存空间和处理硬件寄存器等场景很有用。
struct StatusRegister {
unsigned int errorFlag : 1;
unsigned int overflowFlag : 1;
unsigned int reserved : 30;
};
结构体和指针可以相互配合使用,允许对结构体进行动态分配和动态访问
struct Point {
int x;
int y;
};
struct Point *ptrToPoint = (struct Point *)malloc(sizeof(struct Point));
// 使用指针访问结构体成员
ptrToPoint->x = 10;
ptrToPoint->y = 20;
结构体成员在内存中的存储通常受到对齐和填充的影响。你可以使用特定的编译指令(例如#pragma pack
)来控制对齐和填充。
#pragma pack(push, 1) // 设置字节对齐为1
struct PackedStruct {
char c;
int i;
};
#pragma pack(pop) // 恢复默认对齐设置
补充知识:
对齐是指结构体成员在内存中的存储位置相对于结构体的起始地址的偏移。例如,如果一个int
类型的成员要求在4字节对齐,那么它将会从结构体的起始地址处偏移至下一个4的倍数的地址。
struct Example {
char c; // 1字节
int i; // 4字节,按照4字节对齐
double d; // 8字节,按照8字节对齐
};
在这个例子中,如果没有对齐要求,结构体的大小将是1 + 4 + 8 = 13字节。但是,由于对齐的存在,char c
后面会有3字节的填充,使得int i
从4字节对齐,double d
从8字节对齐。因此,结构体的实际大小是16字节。
填充是为了满足对齐要求而在结构体成员之间插入的额外字节。填充确保每个成员都位于其所需的对齐边界上。填充的大小取决于结构体成员的数据类型和对齐要求。
struct Example {
char c1; // 1字节
// 3字节填充
int i; // 4字节
char c2; // 1字节
// 3字节填充
double d; // 8字节
};
在这个例子中,c1
和i
之间有3字节的填充,保证int i
从4字节对齐。类似地,c2
和d
之间也有3字节的填充,保证double d
从8字节对齐。
结构体可以包含灵活数组成员,这允许动态分配数组大小。
struct DynamicArray {
int length;
int array[]; // 灵活数组成员
};
struct DynamicArray *arr = malloc(sizeof(struct DynamicArray) + 5 * sizeof(int));
arr->length = 5;
结构体之间可以进行比较,但需要逐个比较结构体的成员。为了方便比较,可以使用memcmp
函数。
struct Point {
int x;
int y;
};
struct Point p1 = {1, 2};
struct Point p2 = {1, 2};
if (memcmp(&p1, &p2, sizeof(struct Point)) == 0) {
printf("Points are equal\n");
}
C11标准引入了匿名结构体和共用体,允许在不使用结构体或共用体名称的情况下定义它们。
union {
struct {
int x;
int y;
};
struct {
float latitude;
float longitude;
};
} position;
position.x = 10;
position.y = 20;
// 或者
position.latitude = 37.7749;
position.longitude = -122.4194;