C语言进阶——自定义类型——结构体

目录

一.   结构体类型的声明

结构体的声明

特殊的声明

二.   结构的自引用

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

定义

初始化

四.   结构体内存对齐

 未完待续


一.   结构体类型的声明

结构体的声明

结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

struct tag
{
        member-list;
}variable-list;

例如描述一个学生:

struct Stu
{
    char name[20];//名字
    int age;//年龄
    char sex[5];//性别
    char id[20];//学号
}; 

在使用时,需要带上struct,我们也可以使用typedef将其重命名。

typedef struct Stu
{
    char name[20];//名字
    int age;//年龄
    char sex[5];//性别
    char id[20];//学号
}Stu; 

特殊的声明

在声明的时候可以省略掉结构体标签(tag)

struct
{
    int a;
    char b;
    float c;
}x;

当然,这种特殊的声明在一个作用域中只能声明一次

struct
{
    int a;
    char b;
    float c;
}x;
struct
{
    int a;
    char b;
    float c;
}*p;

在上面的两个结构体中,即使结构体成员都是相同的,但编译器依然会将其认作是两个结构体,固而不能使用 p=&x 这种写法



二.   结构的自引用

结构的自引用,就是在结构中包含一个类型为该结构本身的成员

例如在链表中就使用了结构体的自引用

在自引用中,使用的应该是指针类型,而不是该结构体本身的成员

struct Node
{
    int data;
    struct Node* next;
};

 这是因为若是为该结构本身的成员,会不断发生嵌套定义。

typedef struct
{
    int data;
    Node* next;
}Node;

typedef struct Node
{
    int data;
    struct Node* next;
}Node;

而在使用typedef重命名的情况下,我们并不能使用第一种自引用方式,因为重命名是在成员定义之后发生的。



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

定义

struct Point
{
    int x;
    int y;
}p1; 
struct Point p2

两种方式


初始化

struct Point
{
    int x;
    int y;
}
struct Point p3 = {1, 2};

不仅如此,我们还可以乱序进行初始化

struct Point
{
    int x;
    int y;
}
struct Point p3 = {.y=2, .x=1};

当然,若是结构体成员中包含结构体变量,直接使用多层花括号进行初始化即可



四.   结构体内存对齐

1.结构体的第一个成员,对齐到结构体在内存中存放位置的0偏移处。

2.从第二个成员开始,每个成员都要对齐到(一个对齐数)的整数倍处对齐数:结构体成员自身大小和默认对齐数的较小值(VS:默认对齐数数8Linux gcc:没有默认对齐数,对齐数就是结构体成员的自身大小)

3.结构体的总大小,必须是所有成员的对齐数中最大对齐数的整数倍。

4.如果结构体中嵌套了结构体成员,要将嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处。

直接看定义不太好理解,我们来举几个例子

struct S1
{
    char c1;
    int i;
    char c2;
};

首先,第一个成员对齐到0偏移处

之后int 的大小为4个字节,对齐数为4,因此第二个成员需要对齐到4

之后char 的大小为1个字节,对齐数为1,直接对齐到8     

这样大小为9,由于需要为最大对齐数(4)整数倍,所以最后总大小为12

C语言进阶——自定义类型——结构体_第1张图片C语言进阶——自定义类型——结构体_第2张图片C语言进阶——自定义类型——结构体_第3张图片

struct S2
{
    char c1;
    char c2;
    int i;
};

首先,第一个成员对齐到0偏移处

之后char 的大小为1个字节,对齐数为1,直接对齐到1

之后int的大小为4个字节,对齐数为,4,对齐到4   

这样大小为8,由于需要为最大对齐数(4)整数倍,所以最后总大小也是8

 C语言进阶——自定义类型——结构体_第4张图片C语言进阶——自定义类型——结构体_第5张图片C语言进阶——自定义类型——结构体_第6张图片

struct S3
{
    double d;
    char c;
    int i;
};

 首先,第一个成员对齐到0偏移处

之后char 的大小为1个字节,对齐数为1,直接对齐到8

之后int的大小为4个字节,对齐数为4,对齐到12

这样大小为16,由于需要为最大对齐数(8)整数倍,所以最后总大小也是16

C语言进阶——自定义类型——结构体_第7张图片C语言进阶——自定义类型——结构体_第8张图片C语言进阶——自定义类型——结构体_第9张图片

struct S4
{
    char c1;
    struct S3 s3;
    double d;
};

 首先,第一个成员对齐到0偏移处

之后是嵌套的结构体成员,可以看到,最大对齐数为8,所以需要对齐到8

之后double的大小为8个字节,对齐数为8,对齐到24

这样大小为32,由于需要为最大对齐数(8)整数倍,所以最后总大小也是32

C语言进阶——自定义类型——结构体_第10张图片C语言进阶——自定义类型——结构体_第11张图片C语言进阶——自定义类型——结构体_第12张图片

而当内部存在数组时,直接将它们拆分成多个变量进行存储即可

为什么存在内存对齐?

这个问题到现在也没有一个明确的答案,但就大多数参考书所言,主要有两点

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

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

2. 性能原因:

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



五.   修改默认对齐数

#pragma pack(8)//设置默认对齐数为8
#pragma pack()//取消设置的默认对齐数,还原为默认

由于还没学习宏,先记住就行了



六.   结构体传参

建议传地址

函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。

如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降


 

你可能感兴趣的:(c语言进阶,开发语言,c语言)