匿名结构体
匿名结构体是指在定义一个结构体变量时,不指定结构体名称,只定义结构体的字段。这种结构体不需要使用类型名就可以直接访问结构体字段,也称为无名结构体。
示例:
在此示例中,我们定义了一个无名结构体类型,包含两个字段:name和age。然后我们创建一个名为person的变量,该变量类型为我们定义的无名结构体类型,并初始化name和age字段。最后,我们可以通过person变量访问该结构体的字段值。
四、结构体的自引用
结构体的自引用指的是结构体类型中包含一个指向自身类型的指针成员变量。这种自引用的结构体类型在树形结构、链表等数据结构的实现中经常用到。要声明一个包含自引用的结构体类型,需要在结构体定义中使用结构体名作为指针成员变量的类型,代码示例如下:
在上述示例代码中,TreeNode结构体定义了一个整型变量val和两个指向自身类型的指针成员变量left和right。这个结构体类型可以用于构造树形结构数据。
五、结构体变量的定义和初始化
下面是结构体变量的定义和初始化示例:
//初始化:定义变量的同时赋初值。
六、结构体内存对齐
我们已经掌握了结构体的基本使用了。 现在我们深入讨论一个问题:计算结构体的大小。
例如:
在这个代码中,可以打印出S1的大小为12,怎么得来的呢,接下来就需要另一个知识点:结构体内存对齐。
结构体内存对齐是为了使结构体中的每个数据成员能够被高效地访问。当结构体的数据成员在内存中被存储时,通常不是按照声明的顺序排列的,还会考虑一些对齐规则。对齐规则通常由编译器或操作系统决定,但也可以通过指令控制。
常见的对齐规则是按照数据类型的大小进行对齐,在结构体中,如果一个数据成员的大小小于对齐值,则会在该数据成员后填充一定的字节,使得下一个数据成员能够按照对齐值对齐。
对齐规则:
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。
VS中默认的值为8
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
例如,考虑下面这个结构体:
struct example
{
char c; // 占用1个字节
int i; // 占用4个字节
short s; // 占用2个字节
double d; // 占用8个字节
};
如果按照对齐规则,结构体的布局可能是这样的:
使用内存对齐可以减小结构体的内存占用,提高读写效率。但需要注意的是,对齐会增加一定的开销,例如填充字节需要占用内存空间,所以如果结构体中数据成员的大小很小,对齐带来的好处可能会被抵消。此外,如果结构体中的数据成员顺序被重新排列,可能导致对齐效果变差。
七、修改默认对齐数
在 C/C++ 中,结构体内存对齐可以通过使用 #pragma pack 指令来修改默认对齐数。
默认情况下,结构体成员会按照其自然对齐方式排列,而结构体本身的大小会按照默认对齐数(通常为 4 字节或 8 字节)和对齐规则进行对齐。如果需要调整结构体的对齐方式,可以使用 #pragma pack 指令来实现。
例如,如果需要将默认对齐数改为 1 字节,可以使用以下代码:
在这个例子中,#pragma pack(1) 指令告诉编译器使用 1 字节对齐方式,因此结构体 MyStruct 中的成员将按照 1 字节对齐方式排列,而结构体本身也会按照 1 字节对齐方式进行对齐。
需要注意的是,修改默认对齐数可能会影响代码的性能,因为它会增加内存访问的负载。因此,只有在必要的情况下才应该使用 #pragma pack 指令来调整对齐方式。
八、结构体传参
在 C/C++ 中,结构体可以作为函数的参数传递。在传递结构体时,可以使用值传递或指针传递两种方式。
值传递是指将整个结构体作为参数传递给函数,这样函数会复制一份结构体,并在函数内部使用。这种方式比较简单,但对于大型结构体来说可能会产生较大的开销,因为需要进行结构体的复制。示例如下:
指针传递是指将结构体的指针作为参数传递给函数,并在函数内部通过指针来访问和修改结构体的成员。这种方式可以避免结构体的复制,但需要注意在函数内部对指针进行检查和处理,以避免空指针和野指针等问题。示例如下:
需要注意的是,在使用指针传递结构体时,需要确保结构体在函数外部的生命周期不会比函数调用的时间短,否则可能会引发未定义行为。