目录
在讲结构体之前,我们用C和指针的一段话来引入。
定义结构体并初始化
创建普通结构体
struct tag {
int a;
char b;
float c;
};
int main()
{
struct tag s1 = { 1,'a',1.1 };
struct tag s2 = { 2,'b',2.2 };
//此时我不想按照顺序赋值,就得按照下面这种做法
struct tag s5 = { .b = 'c',.a = 3,.c = 3.3 };
}
创建嵌套的结构体
struct tag
{
int x;
int y;
};
struct Data
{
int sum;
struct tag p;
};
int main()
{
struct Data d = { 200,{10,11} };
return 0;
}
结构成员的访问操作符
结构成员的直接访问
#include <stdio.h>
struct tag
{
int x;
int y;
};
struct Data
{
int sum;
struct tag p;
};
int main()
{
struct Data d = { 200,{10,11} };
printf("%d %d %d", d.sum, d.p.x, d.p.y);
return 0;
}
结构体成员的间接访问
结构体指针
对于结构体类型的s1
struct Stu* ps=&s1;//取出s1的地址
printf(%s %d %f\n",ps->name,ps->age,pa->score);
结构的特殊声明
匿名结构体类型
#include <stdio.h>
struct
{
int a;
char b;
float c;
}x;
int main()
{
return 0;
}
针对于匿名结构体类型来说,他只能使用一次。
即使两个匿名结构体成员类型一模一样,在编译器看来,他们是两种不同的类型。
结构体重命名
针对于结构体来说,我们可以对其进行重新命名
typedef struct
{
int a;
char b;
float c;
} S;
int main()
{
S s1 = { 0 };
return 0;
}
结构体的自引用
结构体包含一个同类型的结构体的地址我们就称为自引用
结构体的自引用
struct Node
{
int data;//存放数据
struct Node* next;//存放同类型的结构体的地址
};
int main()
{
return 0;
}
结构体自引用不能匿名
typedef struct
{
int data;//存放数据
struct Node* next;//存放同类型的结构体的地址
}Node;
int main()
{
return 0;
}
结构体的自引用不能写成匿名的
结构体的内存对齐
#include <stdio.h>
struct S1
{
char c1;
char c2;
int i;
};
struct S2
{
char c1;
int i;
char c2;
};
int main()
{
printf("%d\n", sizeof(struct S1));
printf("%d\n", sizeof(struct S2));
return 0;
}
为什么大小是有差异的呢?
我们先看一下结构体的对齐规则:
⾸先得掌握结构体的对⻬规则:
- 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
- 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。
- VS 中默认的值为 8
- Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩
3.结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的
整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构
体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。
然后我们再看一下C和指针的讲解:
接下来我们打开画图:(被迫展现优秀的画图)
ok,知道了内存对齐,那么内存对齐的作用是什么呢?
1.平台原因 (移植原因)
2.性能原因
修改默认对齐数
#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1
struct S
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的对⻬数,还原为默认
int main()
{
printf("%d\n", sizeof(struct S));//6
return 0;
}
结构体传参
#include <stdio.h>
struct S
{
int data[1000];
int num;
};
void print1(struct S t)
{
printf("%d %d\n", t.data[0], t.num);
}
void print2(struct S* ps)
{
printf("%d %d\n", ps->data[0], ps->num);
}
int main()
{
struct S s = { {1,2,3,4,5},100 };
print1(s);
print2(&s);
return 0;
}
对于上述的两个函数,我们认为print2更加好,因为他无论目标有多大,他只会传递一份地址,空间不浪费。
位段
什么是位段?
位段的声明和结构是类似的,有两个不同:
1.位段的成员必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以 选择其他类型。
2.位段的成员名后边有⼀个冒号和⼀个数字。
位段的出现就是为了节省空间。
位段对计算机底层的实现还是比较重要的。
若有收获,就点个赞吧