一·结构体类型创建和初始化(前面有提)
结构体:
1.指针的时候用->
2.变量的时候用.
//定义一个学生
struct stu
{
char name[20];
int age;
char id[20];
};
int main()
{
struct stu s={"danika",18,"2023001"};//默认顺序
struct stu x={.id="2023002",.name="jack",.age=19};//指定顺序
return 0;
}
结构体的特殊声明:
在使用结构体时,可以不完全声名(匿名结构体)
struct
{
int a;
char b;
float c;
}
struct
{
int a;
char b;
float c;
}a[20], *p;
此时是省略了结构体标签,在上面的基础上,
p=&x;
就不对了,是因为编译器会把上⾯的两个声明当成完全不同的两个类型,所以是非法的。匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用一次。(温馨提示:以后尽量少用匿名结构体)
二·结构体的自引用
在结构中包含⼀个类型为该结构本⾝的成员是否可以呢?
答案是可以,但是不可以直接引用,否则在计算sizeof(结构体)时就结构体套结构体,无穷尽了,不符合。
可以运用指针,指向该结构体:
struct note
{
int arr[10];
struct note*next;//这样就可以
};
也可以对结构体重命名后使用自引用:
typedef struct Node
{
int data;
struct Node* next;
}Node;
三·结构体内存对齐
1.
VS默认对齐数:8;
Linux中 gcc 没有默认对齐数,对齐数就是成员自身的大小。
2.举例
(会有一些区域用不到浪费了)
总体来说:结构体的内存对齐是拿空间来换取时间的做法。
如何既满足对齐,又节省空间?–>
让占⽤空间小的成员尽量集中在⼀起,如上图s2
3.修改默认对齐数
#pragma pack(1)//设置默认对齐数为1
#pragma pack()//取消默认的对齐数,还原为默认
四·结构体传参
两种方式,但传入地址的那种更好:
1.函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
2.如果传递⼀个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。
3.如果担心传地址改变实参,可在形参前加const。
五·结构体实现位段
开辟完后,为方便理解,可以规定在每个空间从右往左排,如果剩余的空间不够下一个应用,就浪费,继续开辟。
2.举例
struct S
{
char a:3;
char b:4;
char c:5;
char d:4;
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
char类型,一个字节一个字节开辟,a占3个bit,b占4个,以此类推。
所以就算s.a为10,二进制为1010,也只能截3位,010
所以第一个字节中是:0110 010 化为16进制为62.
3.位段的应用(节省空间)
4.注意
内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。
所以不能对位段的成员使用&操作符,这样就不能使用scanf直接给位段的成员输入值,只能是先输入放在⼀个变量中,然后赋值给位段的成员。
struct stu{
int _a : 2;
int _b : 5;
int _c : 10;
int _d : 30;
};
int main(){
int b=0;
scanf("%d",&b);
stu._b=b;
return 0;
}