结构体的声明
结构体的基础知识
结构体是一种自定义类型,它由多种基本类型组成。结构体的每个成员可以是不同类型的变量。
结构的声明
struct tag //类型声明 { member-list; //结构体成员变量 }variable-list; //创建的结构体变量
例如描述一个人:
struct person { int id[20]; //身份证号码 char name[10];//姓名 char sex[5];//性别 };
特殊声明
在声明结构的时候,可以不完全的声明。
struct { int a; char c; }n; //匿名结构体类型 struct { int a; char c; }*p;
上面两个结构体都省略了名称,且结构体成员一样,是否相等?
struct { int a; char c; }n; struct { int a; char c; }*p; int main() { p = &n; return 0; }
显然编译器会把上面的两个声明当成完全不同的两个类型
结构体变量的定义以及初始化
struct book //类型声明 { char name[20]; int price; }a1; // 声明类型的同时定义变量a1 struct book a2; //定义结构体变量a2 struct book a3 = { "剑指offer",99 }; //初始化:定义变量的同时赋初值。 struct person { char name[20]; int age; struct book a4; }b1 = { "张三",85,{"0基础学习C语言",66}}; //结构体嵌套初始化 struct person b2 = { "李四",48,{"可口可乐如何制作",28} }; //结构体嵌套初始化
结构体的自引用
自引用也就是自己引用自己,那么在一个结构中能不能引用一个该结构本身的成员呢?
struct person { char name[20]; int age; struct person a1; }; int main() { printf("%zd", sizeof(struct person)); return 0; }
显然person的大小无法计算。
如何正确使用呢?
之前我们学过指针,它的大小是固定的4/8个字节,那么我们可以试试指针
struct person { char name[20]; int age; struct person *next; }; int main() { printf("%zd\n", sizeof(struct person)); return 0; }
这样大小就能够进行计算了。
还有一点需要注意的是使用typedef自定义类型名时引用的结构体不能省略struct,例如:
typedef struct person { char name[20]; int age; struct person* next; //这个struct不能够省略 }person;
省略后就会报错。
结构体内存对齐
计算下面结构体的大小
struct a { char a; char b; int c; }; struct b { char i; int n; char p; }; struct c { char c1; struct b b1; //结构体嵌套问题 double d; }; int main() { printf("%zd\n", sizeof(struct a)); printf("%zd\n", sizeof(struct b)); printf("%zd\n", sizeof(struct c)); return 0; }
前面两个结构体它们的结构体成员变量数目是一样的,但是它们所占内存大小不一样,这就涉及了一个特别热门的考点:结构体内存对齐。
如何计算?
首先得掌握结构体的对齐规则:
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的值为8
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
例如:
struct b { char i; int n; char p; double c; }; int main() { printf("%zd", sizeof(struct b)); return 0; }
因此此结构体占用内存空间的大小为24字节。
灰色部分为浪费掉的空间。
- 为什么存在内存对齐?
- 平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。 - 性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
总结:拿内存空间换取时间。
修改默认对齐数
之前我们说过VS中默认的值为8,这个其实是可以进行修改的。
- #pragma 这个预处理指令,是可以修改我们的默认对齐数的。
例如:
#pragma pack(1) //设置默认对齐数为1 struct b { char i; int n; char p; double c; }; #pragma pack() //取消设置的默认对齐数,还原为默认 struct c { char i; int n; char p; double c; }; int main() { printf("%zd\n", sizeof(struct b)); //14 printf("%zd\n", sizeof(struct c)); //24 return 0; }
总结:结构在对齐方式不合适的时候,我么可以自己更改默认对齐数。
结构体传参
struct person { char name[15]; int age; char sex[5]; }; void print1(struct person a1) //传值 { printf("%s %d %s\n", a1.name, a1.age, a1.sex); } void print2(struct person* a2) //传地址 { printf("%s %d %s", a2->name, a2->age, a2->sex); } int main() { struct person a = { "马哈藤",55,"男"}; print1(a);//传结构体 print2(&a);//传结构体地址 }
传值与传地址,首选传地址。
形参是实参的一份临时拷贝因此当传的内容特别大时,会很浪费空间,导致性能下降,然而传地址,就会很好的解决问题!
结论:
结构体传参的时候,要传结构体的地址。