结构体(声明、初始化、内存对齐、如何传参)

结构基础知识

聚合数据类型能够同时存储超过一个的单独数据。C提供了两种类型的聚合数据类型,分别是数组和结构体。数组是相同元素的集合,它的每个元素是通过下标引用或指针间接访问的。结构体也是一些值的的集合,这些值称为它 的成员,但一个结构的成员可能具有不同的类型。数组元素可以通过下标访问,这是因为数组元素长度相同,但在结构体中并非如此,由于每个成员的类型可能不同,那么长度也就可能不同,所以就不能通过下标来访问。但是结构体成员都有自己的名字,他们是通过名字访问的。另外,结构体在表达式中使用时,不能被替换为指针。结构体变量也无法使用下标来选择特定的成员。

结构体的声明

结构体(声明、初始化、内存对齐、如何传参)_第1张图片

这个结构体创建了结构体变量x,结构体包含了2个成员:整型a,字符ch。

结构体声明哪些可以省略?

标签A可以省略,那么就是匿名结构体类型,但不建议。在声明时可以没有结构体变量x,如果有,那么该变量为全局变量。如果在main函数中,就是局部变量。

如下:

结构体(声明、初始化、内存对齐、如何传参)_第2张图片

struct A  x;

int          x;

struct A 相当于int ,也就是类型,属于结构体类型,x是结构体变量。

结构体(声明、初始化、内存对齐、如何传参)_第3张图片a[20]包含了20个结构,p是一个指针,它指向结构。

上面两个结构都忽略了结构体标签(如A),那么这个语句成立吗?p=&x?

警告:这两个声明被编译器当做两种截然不同 的类型,即使成员相同。所以,变量a,p,x,类型不同,不可p=&x。

结构体(声明、初始化、内存对齐、如何传参)_第4张图片

注:用typedef后,simple是个类型名,可以创建变量。

访问结构体成员

通过.操作符访问成员:

结构体(声明、初始化、内存对齐、如何传参)_第5张图片

通过->进行访问:

结构体(声明、初始化、内存对齐、如何传参)_第6张图片

两者结果为:10  zhangsan

结构体自引用

我们已经知道在结构体中可以包含数组,指针,标量,结构体...那么结构体里面能不能包含该结构体本身呢?

结构体(声明、初始化、内存对齐、如何传参)_第7张图片

不可以,因为不知道结构体大小,无法开辟空间存y。

正确的结构体自引用如下:

结构体(声明、初始化、内存对齐、如何传参)_第8张图片

因为struct stu *y是指针类型,即为4个字节,可以开辟空间。

结构的不完整声明

偶尔,必须声明一些相互之间存在依赖的结构。即,一个结构包含了另一个结构的一个或多个成员。和自引用一样,至少一个结构必须在另一个结构中以指针的形式存在。问题在于声明部分:如果每个结构体都引用了其他 结构的标签,哪个结构应该首先声明?

这个问题的解决方案是使用不完整声明,它声明一个作为结构标签的标识符。然后,把这个标签用在不需要知道这个结构的长度的声明中,如声明指向这个结构的指针。

结构体(声明、初始化、内存对齐、如何传参)_第9张图片

结构体变量的定义和初始化

结构的初始化方式和数组初始化很相似。一个位于一对花括号内部,由逗号分隔的初始值列表可用于结构的初始化。这些值根据结构成员列表的顺序写出。

结构体(声明、初始化、内存对齐、如何传参)_第10张图片

注:数组不可以整体赋值,可整体初始化。同理,结构体不可整体赋值,可以整体初始化。结构体地址数值等于第一个成员地址数值。

结构体内存对齐

为什么存在内存对齐?

1.平台原因(移植原因)

不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些地址处取某些特定类型的数据,否则就抛出硬件异常。

2.性能原因:

数据结构(尤其是栈)应该尽能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存访问需要访问一次。

图如下:

结构体(声明、初始化、内存对齐、如何传参)_第11张图片

结构体对齐规则:

1.第一个成员在与结构体变量偏移量为0的地址处。

2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数=编译器默认的对齐数与该成员大小的较小值。VS中默认对齐数为8,linux默认对齐数为4.

3.结构体总大小为最大对齐数(每个成员都有一个对齐数)的整数倍。

4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

那么首先看一道题:

结构体(声明、初始化、内存对齐、如何传参)_第12张图片

通过内存对齐规则,答案肯定不是6,答案是12,解释如下:

结构体(声明、初始化、内存对齐、如何传参)_第13张图片

再看其他例子:

结构体(声明、初始化、内存对齐、如何传参)_第14张图片

结果为8。1+1+2+4=8,8是最大对齐数4的倍数。

结构体(声明、初始化、内存对齐、如何传参)_第15张图片

结果为16。8+1+3+4=16,16是最大对齐数4的倍数。

结构体(声明、初始化、内存对齐、如何传参)_第16张图片

结果为32。1+7+16+8=32,32是最大对齐数8的倍数。

结构体传参

我们知道数组传参会发生降级,那么结构体会不会降级呢?

结构体(声明、初始化、内存对齐、如何传参)_第17张图片

结构体传参有两种形式,一个是传结构体,一个是传地址,但是建议选择传地址。因为在栈帧知识中,函数传参,是需要压入栈的,但是如果传结构体对象时,结构体过大,所占空间也就过大,会导致性能下降。

结论:结构体传参,是不会发生降级,要将参数设置为结构体的地址。










你可能感兴趣的:(c语言)