目录
结构体
结构体的声明 定义 及初始化
特殊的结构体
结构体的自引用
结构体的内存对齐
结构体的对齐规则:
offsetof
修改默认对齐数
#pragma pack( )
结构体传参
位段
声明方法
注意事项
位段的内存分配
枚举
联合体(共用体)
声明与定义
特点
联合体的大小
结构体
为什么会出现结构体?结构体能做什么?有什么优点?
且听我慢慢分析。
当我们想了解一本书的基本信息时,我们往往需要这样做:
我们需要敲出四个scanf来输入基本信息,很麻烦;又或者是一个scanf里跟了一堆需要输入的变量, 不美观;此时我们就会想,有没有即简单又美观的方法呢?结构体便应运而生了。
当然我们也可以这样创建变量:
但是请记住,这样创建的是全局变量,而刚才是在函数内创建的bk1,是局部变量。
如果我们觉得struct book bk1这种创建结构体变量的方法太麻烦,总带有struct book,也可以用typedef进行类型重命名:
特殊的结构体因为它没有标签,所以这是匿名结构体,只能用一次。
请问在匿名结构体成员变量相同的情况下,所创建的两个结构体相同吗?
p = &person;
合法吗?
答案是非法的,原因是在编译期看来这是两个不同的类型。
struct Node
{
int age;
struct Node next;
};int main()
{
int size = sizeof(struct Node); //?????能计算出来吗?多大呢?
return 0;
}
我们发现, 结构体struct Node中又嵌套了一个struct Node,而struct Node中还会有,这样下去就无限递归下去了,这没办法运算呀!所以,我们在自引用的时候就发生了错误。
正确方法为:
struct Node
{
int age;
struct Node* next;
};
通过地址找到下一个,不想继续找就放入空指针,才为正确的自引用方式。
结构体的内存对齐
请问,输出的结果是什么呢?
为什么会是8和12呢?为什么只是成员变量先后顺序变了大小就变了呢???这是为什么呢??
答案是,结构体存在内存对齐。
(用空间换取时间)
结构体的对齐规则:1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的值为8
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
接下来我们来讲解上图中的原因:
因此,按照相同的算法,第二个结构体:
和大家说一下,Linux环境下没有默认对齐数,其自身大小便为默认对齐数。
计算结构体成员相对于首地址的偏移量
size_t offsetof( structName, memberName );
头文件:
求嵌套结构体的大小:
讲解:
首先温习一下规则四:
修改默认对齐数4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
#pragma pack( )
结构体传参
struct inform
{
int i;
char a;
char b;
};
struct inform s1 = { 1,'a','b' };void print1(struct inform* s1)
{
;
}void print2(struct inform s1)
{
;
}int main()
{
print1(&s1);
print2(s1);return 0;
}是传参还是传址呢??
答案是:print1,传址。
原因:
函数传参的时候,参数会压栈,会有时间和空间上的系统开销。
如果传递的结构体过大,参数压栈的系统开销比较大,会造成性能下降。
注意事项//位段
struct inform
{
int a : 3;
int b : 4;
int c : 5;
};
①成员必须为int ,unsigned int,signed int
或char,unsigned char, signed char。
②成员名后加冒号和数字
提问:
位段的内存分配printf("%d\n", sizeof(struct inform));
//位段
struct S
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
};
struct S s = { 0 };
int main()
{
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
return 0;
}
说明在VS2019中,字节内部的分配方式是从右向左的
注:位段的存在就是为了节省空间,所以不存在内存对齐
枚举默认值起始值为0,我们也可以给其初始化。
enum week
{
MONDAY = 1,
TUSDAY,
WEDNESDAY,
THUSDAY,
FRIDAY,
SATURDAY,
SUNDAY
};
但是我们不能这样:
enum week day = 2;//2是int类型的 而day是enum week类型的
类型不兼容
同时我们也不能这样:
TUSDAY = 3;
联合体(共用体)
关键字:union
大家看到名字有没有联想到什么呢?为什么叫共用体呢???共用是不是体现在内存中呢?是否有这种感觉呢?
没错,联合体的特点就是成员变量共用同一块内存空间。
union on//联合类型声明
{
int i;
char c;
};
int main()
{
union on cn;//联合变量定义
return 0;
}
特点
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联
合至少得有能力保存最大的那个成员)。
因为共用同一块空间,因此在更改其中一个变量时,会将另一个变量也改掉,因为应用场景往往是使用其中一个变量时,其余变量不使用。
我们先证明一下他们共用同一块空间吧:
因此我们可以利用这个特点,改造一下判断机器大小端的代码:
void check()
{
union ck
{
char c;
int i;
}k;
k.i = 1;
if (k.c == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
}
int main()
{
check();
return 0;
}
运行结果:
联合体的大小union size
{
int i;//对齐数为4
char c;//对齐数为1
//最大对齐数为4 刚好为4的整数倍
};
union size sz;
int main()
{
printf("%d\n", sizeof(sz));//4
return 0;
}
union size
{
short arr[7];//对齐数为数组元素类型 short为2 对齐数为2
int a;//对齐数为1 因此最终大小应该是最大对齐数4的整数倍 为16
};
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)