栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C语言自定义类型之结构体篇

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C语言自定义类型之结构体篇

自定义类型之结构体

文章目录
  • 自定义类型之结构体
  • 结构体
    • 结构体类型的声明
    • 标题结构的自引用
    • 结构体变量的定义和初始化
    • 结构体内存对齐
    • 结构体传参
    • 结构体实现位段(位段的填充&可移植性)


结构体 结构体类型的声明

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;嗯没错了,开始验证:

先来看下输出结果:


咩? 咩?
这时候我们就要知道,结构体是存在内存对齐的
对齐规则:

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
  • VS中默认的值为8
  1. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  2. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。


这就是为何练习一是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!

冒号后面是比特位,用完就创建,其实位段有很多问题,比如上面的第一个字节没用完,不够第二个位段使用是舍弃还是用上呢?
所以,
位段的跨平台问题:

  1. int 位段被当成有符号数还是无符号数是不确定的。
  2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机 器会出问题。
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是 舍弃剩余的位还是利用,这是不确定的

但存在即合理,位段也有其应用场景;以后再来研究吧!


暑期编程PK赛 得CSDN机械键盘等精美礼品!
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1015726.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号