结构体是c语言中自定义的类型,对于一些内置类型做不到的时候就需要使用的自定义类型,所以了解结构体类型和结构体在内存中对齐方式等知识是至关重要的。
一、结构体类型
结构体内容可以包含多个数据类型,用来描述复杂对象的各种属性。
1.结构体的创建:
注:分号必不可少!!不要丢掉分号!
这两种创建都可以。
2.结构体初始化
结构体初始化要按顺序。
struct A
{
char name[20];
int age;
char sex[5];
};
int main()
{
//struct A a1 = { "zhangsan", 20, "男" };
struct A a2 = { 21,"lisi","女" };
printf("%s %d %s", a2.name, a2.age, a2.sex);
return 0;
}
a1是我正常的初始化,如果像a2一样没有顺序那么打印出来就是这样的,所以一定要按照顺序来初始化,才能保证数据的正确性。
也有一种方法可以不按顺序,如:
struct A
{
char name[20];
int age;
char sex[5];
};
int main()
{
struct A a1 = { "zhangsan", 20, "男" };
struct A a2 = { 21,"lisi","女" };
struct A a3 = { .age=20,.name="张三",.sex="男"};
printf("%s %d %s\n", a1.name, a1.age, a1.sex);
printf("%s %d %s\n", a2.name, a2.age, a2.sex);
printf("%s %d %s\n", a3.name, a3.age, a3.sex);
return 0;
}
3.结构体的空间分配
我们学习内置类型的时候总会学到该类型在内存中所占内存的字节,那么结构体的占内存的字节是多少呢?这个时候我们就要说结构体的内存分配了。
struct A
{
char a;
int b;
char c;
};
struct B
{
char a;
char b;
int c;
};
int main()
{
printf("%d\n", sizeof(struct A));
printf("%d\n", sizeof(struct B));
return 0;
}
结果却不一样,但是我定义的类型一样啊,为什么结果不一样呢?那么我们现在就要说一下结构体是怎么分配内存空间了。
我们先来看例1:struct A,他在内存分配是
有人会疑惑为什么int不会和第一个char a紧挨着?这是因为结构体有他自己的对其规则:
(1). 结构体的第⼀个成员对⻬到相对结构体变量起始位置偏移量为0的地址处
(2).其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。
- VS中默认的值为8
- Linux中没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩
(3). 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍。
(4). 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。
以下是我的理解以及解释:
(1).表示结构体第一个成员对齐的位置一定是0,从图中我们可以看出char a放在了0地址。
(2).成员变量要对齐到地址的整数倍,也就是图中我们int为什么要放在3地址的位置,因为4地址是int(占内存4字节)的整数倍。
(3).当我们计算出结构体的占的空间如果是14呢?那么我们就要找到成员中最大对齐数的整数倍,比如是int(4字节),那么我们就取16个字节。
(4).如果我们发现结构体嵌套了一个结构体,那么我们就要找到那个嵌套的结构体里面成员最大对齐数的整数倍,然后占整个结构体的大小。
例2:
struct A
{
char a;
int b;
char c;
};
struct B
{
struct A a1;
int b;
char c;
};
int main()
{
printf("%d\n", sizeof(struct A));
printf("%d\n", sizeof(struct B));
return 0;
}
例2内容嵌套了一个结构体,那么我们就分析一下例2是怎么分配结构的吧。
经过我们画图,发现他只占了17个字节,那么为什么最后结果是20呢?那么这个时候我们就需要看(4)了。
第一个结构体structA整体占12个字节,且 里面最大的类型是int(占4字节),int 是4,VS的默认对齐数是8,取最小,也就是4,那么我们就需要取对齐数的整数倍也就是4的整数倍20。
4.对齐原因
①.平台的问题。
②.性能的问题,如果没有对齐的话,我可能需要访问空间两次才能拿到一个数,但是我对齐后只需要拿一次即可,但是就是浪费很多的空间。
5.设置对齐数
VS默认的对齐数是8,那么如果我们想改呢?那么我们就需要
#pragma pack(1)//对齐数设置为1
#pragma pack()//还原
三、结构体成员访问操作符
成员访问操作符一共有两种一个是点(.),另一个是箭头(->)。
点用于成员变量,而箭头用于地址。
struct A
{
int *age;
char* name[20];
};
print(struct A*ps)
{
printf("%d", ps->age);
}
int main()
{
struct A a1 = { 20,"zhangsan" };
print(&a1);
return 0;
}
四、结构体实现位段(位域)
位段存在是为了节省空间的,另外位段的成员必须是int/unsigned int/ signed int/ char等正常类型。
它的形式:
一个冒号(:)+数字的形式,主要表示这个变量占多少个bit位!注意是bit位!!不是字节!!!
(一)、位段存在的问题
位段存在很多的不确定性,例如:
1.当开辟内存后,从左向右使用还是从右向左,是不确定,我们一遍假设是从右向左(VS是这样的)
2.下一个成员是否使用剩余不足的空间,是不确定的,VS默认不使用。
3.int是否有符号,是不确定的。
4.位段中最大的位数,是不确定的。
(二)、位段在内存的分配
struct A
{
char _a : 2;
char _b : 4;
char _c : 5;
};
int main()
{
struct A a1 = { 0 };
a1._a = 10;
a1._b = 5;
a1._c = 10;
printf("%d", sizeof(struct A));
return 0;
}
和我们预想的一样占了两个字节。
在内存中是这样显示的,将我们预想的图转化成16进制,与内存显示的一模一样。
注:位段是不能取地址的,
struct A
{
int a : 2;
};
int main()
{
struct A a1 = { 0 };
int b = 0;
scanf("%d", &b);
a1.a = b;
printf("%d", a1.a);
return 0;
}
以上就是结构体的知识总结,喜欢的朋友可以给我点个赞,如果有任何错误,可以评论,感谢大家指点~!