0%

结构体

文章时效性提示

本文发布于 506 天前,部分信息可能已经改变,请注意甄别。

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

结构的声明

1
2
3
4
5
struct tag
{
member-list;
}
variable-list;

例如描述一个学生,声明一个结构体类型,通过学生类型来创建学生变量:
——姓名、电话、性别、年龄

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct Stu
{
    char name[20];
    char tele[12];
    char sex[5];
    int age;
}s4,s5,s6;//s4,s5,s6和s3一样,都是全局的结构体变量

struct Stu s3;//创建了s3这个结构体的全局变量

int main()
{
    struct Stu s1;
    struct Stu s2;//创建了s1,s2两种结构体变量(局部变量)
    return 0;
}

特殊的声明

匿名结构体类型

1
2
3
4
5
6
7
8
9
10
11
12
13
struct
{
int a;
char b;
float c;
}x;//创建匿名结构体类型时,必须在后面写上结构体类型变量,否则之后无法创建

struct
{
int a;
char b;
float c;
}a[20],*p;//*p匿名结构体指针

结构体自引用

自引用方式

1
2
3
4
5
struct Node
{
int data;
struct Node* next;
};
1
2
3
4
5
6
7
8
9
10
11
12
typedef struct Node
{
int data;
struct Node* next;
}Node;

int main()
{
struct Node n1;
Node n2;
return 0;
}

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

1
2
3
4
5
6
struct Point
{
int x;
int y;
}p1;//声明类型的同时定义变量p1
struct Point p2;//定义结构体变量p2

定义变量同时赋值:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct S
{
    char c;
    int a;
    double d;
    char arr[20];
};

int main() {
    struct S s = {'c',100,3.14,"hello world"};//结构体成员初始化
    printf("%c,%d,%lf,%s\n",s.c,s.a,s.d,s.arr);//结构体成员的访问
    return 0;
}

打印的结果是:c 100 3.140000 hello world

结构体成员的嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
struct T
{
    double weight;
    short age;
};

struct S
{
    char c;
    struct T st;
    int a;
    double d;
    char arr[50];
};

int main() {
    struct S s = {'c',{55.4,30},100,3.14,"hello world"};
    printf("%lf\n",s.st.weight);//访问嵌套的结构体变量
    return 0;
}

打印的结果是55.400000
嵌套的结构体变量就是在大括号里加大括号

结构体内存对齐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
struct S1
{
    char c1;
    int a;
    char c2;
};

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

int main()
{
    struct S1 s1 = {0};
    printf("%d\n",sizeof(s1));//12
    struct S2 s2 = {0};
    printf("%d\n",sizeof(s2));//8
}

如何计算结构体大小?
——对齐规则
1、第一个成员在与结构体变量偏移量为0的地址处
2、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值
3、结构体的总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
4、如果嵌套了结构体的情况,嵌套结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

为什么要内存对齐?
——性能强,兼容性强

修改默认对齐数

使用#pragma()指令来修改默认对齐数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#pragma pack(4)//设置默认对齐数是4
struct S
{
    char c1;//1
    //7
    double d;//8
};
#pragma pack()//取消设置默认对齐数
int main()
{
    struct S s;
    printf("%d\n",sizeof(s));
    return 0;
}

默认的对齐数是8,#pragma pack(4)修改默认对齐数为4,使原先的结构体s的大小由16变成了12。(中间浪费的字节由8变成了3)

偏移量计算offsetof

size_t offsetof(structName, memberName);
用于计算偏移量,它不是函数,是一个宏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <stddef.h>
struct S
{
    char c;
    int i;
    double d;
};

int main()
{
    printf("%d ",offsetof(struct S, c));
    printf("%d ",offsetof(struct S, i));
    printf("%d ",offsetof(struct S, d));
    return 0;
}

打印的结果是0 4 8

结构体传参

结构体传参,可以传地址,也可以传本身。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>
struct S
{
    int a;
    char c;
    double d;
};

void Init(struct S* ps)
{
    ps->a = 100;
    ps->c = 'w';
    ps->d = 3.14;
}

void print1(struct S tmp)
{
    printf("%d %c %lf\n",tmp.a,tmp.c,tmp.d);
}

void print2(const struct S* ps)
{
    printf("%d %c %lf\n",ps->a,ps->c,ps->d);
}

int main()
{
    struct S s = {0};
    Init(&s);
    print1(s);//传值
    print2(&s);//传址
    return 0;
}

结构体传参,最好传地址,因为传的值有可能过大,系统开销大,通过传地址,可以节约系统开销。