文章目录
- 自定义类型之结构体
- 结构体
- 结构体类型的声明
- 标题结构的自引用
- 结构体变量的定义和初始化
- 结构体内存对齐
- 结构体传参
- 结构体实现位段(位段的填充&可移植性)
结构体 结构体类型的声明
1.1结构体声明
struct tag
{
member-list;
}variable-list;
1.2结构体特殊声明
//匿名结构体类型
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20], *p;
在声明的时候,可以不完全声明,这就是匿名结构体类型;
但是只能使用一次;
上述代码中,我们尝试将
p = &x;
合法吗?我们觉得相同但编译器会把上面俩个声明当成完全不同的俩个类型。
标题结构的自引用在数据结构里,有一种链表结构;
在这里我们猜想一下,一个结构体中,能否包含一个结构体吗?
struct student{
int data;
struct student STU;
};
如果像这样定义的话,那么sizeof(struct student)的结果是?分析下这个结构体大小,里面包含int型,但是结构体里面又有结构体,这就无法判断。
struct student{
int data;
struct student* STU;
};
这样是不是就可以计算了。
这里说的是不可以直接自己套娃自己,但可以嵌套别的结构体;
struct Stu s = {"zhangsan", 20};//初始化
struct Node
{
int data;
struct Point p;
struct Node* next;
}n1 = {10, {4,5}, NULL};
其实这就是链表的结点,等数据结构理解。
结构体变量的定义和初始化struct Point{
int x;
int y;
};
这是结构体类型,下面是如何初始化:
struct Point{
int x;
int y;
}p1;
struct Point p2;
int main(){
struct Point p3 = {x,y};
return 0;
}
struct Stu //类型声明
{
char name[15];//名字
int age; //年龄
};
struct Stu s = {"zhangsan", 20};//初始化
struct Node
{
int data;
struct Point p;
struct Node* next;
}n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化
结构体内存对齐
在上面其实已经提到了sizeof(结构体)如何计算?
在结构体中包含自己的结构体不可以计算,那么其他呢,即如何计算结构体大小?
结构体内存对齐
//练习1
struct S1
{
char c1;
int i;
char c2;
};
printf("%dn", sizeof(struct S1));
//练习2
struct S2
{
char c1;
char c2;
int i;
};
printf("%dn", sizeof(struct S2));
//练习3
struct S3
{
double d;
char c;
int i;
};
printf("%dn", sizeof(struct S3));
//练习4-结构体嵌套问题
struct S4
{
char c1;
struct S3 s3;
double d;
};
printf("练习4%dn", sizeof(struct S4));
先来假设一波:
struct S1
{
char c1;//大小是1
int i;//大小是4
char c2;//大小是1
};
既然如此,那么大小是6;嗯没错了,开始验证:
先来看下输出结果:
咩? 咩?
这时候我们就要知道,结构体是存在内存对齐的
对齐规则:
- 第一个成员在与结构体变量偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
- VS中默认的值为8
- 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
这就是为何练习一是12了。完了完了对齐这浪费了这么多空间,其实是有原因的,可以自己查阅哦
在设计结构体的时候,我们既要满足对齐,又要节省空间, 让占用空间小的成员尽量集中在一起。
在某些编译器中没有默认对齐数的,例如:gcc;VS中默认是8,当结构体不合适的时候,我们可以自己修改默认对齐数的;
#pragma pack(8)//设置默认对齐数为8 #pragma pack()//回复默认对齐数结构体传参
传参可以传形参,也可传地址:
struct S
{
int data[1000];
int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{
printf("%dn", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
printf("%dn", ps->num);
}
int main()
{
print1(s); //传结构体
print2(&s); //传地址
return 0;
}
形参是实参的零时拷贝,传地址能操作实参;
传地址也不用新开空间临时存放,节约空间,提高效率。
1.位段的成员必须是 整形家族 。
2.位段的成员名后边有一个冒号和一个数字。
struct A
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};
A便是位段,那么A的大小呢,还是和结构体一样吗?
NONONONONONONONONONOINON!
冒号后面是比特位,用完就创建,其实位段有很多问题,比如上面的第一个字节没用完,不够第二个位段使用是舍弃还是用上呢?
所以,
位段的跨平台问题:
- int 位段被当成有符号数还是无符号数是不确定的。
- 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机 器会出问题。
- 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
- 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是 舍弃剩余的位还是利用,这是不确定的
但存在即合理,位段也有其应用场景;以后再来研究吧!
暑期编程PK赛 得CSDN机械键盘等精美礼品!



