自定义类型:结构体

目录

一、结构体声明

二、结构体变量

2.1结构体的特殊声明

2.2结构体的自引用

三、结构体大小

3.1修改默认对齐数

四、结构体传参

五、结构体实现位段


一、结构体声明

结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

我们来定义一个学生类,有名字、年龄、性别、学号。

//结构体声明,定义一个学生类
struct stu
{
	char name[20];
	int age;
	char sex[5];
	char id[20];

};

int main()
{
	return 0;
}

二、结构体变量

接着上面的学生类,我们来给结构体成员初始化

需要注意需按照结构体成员的顺序初始化。

int main()
{
	//按照结构体成员的顺序进行初始化。
	struct Stu s = { "张三",20,"男",2023000102 };
	printf("name : &s\n", s.age);
	printf("age : %d\n", s.age);
	printf("sex : %s\n", s.sex);
	printf("id : %s\n", s.id);
	
	//按照指定的顺序初始化
	struct Stu s2 = { .age = 18, .name = "lisi", .id = "202332" };
	printf("name : &s\n", s.age);
	printf("age : %d\n", s.age);
	printf("sex : %s\n", s.sex);
	printf("id : %s\n", s.id);


	return 0;
}

2.1结构体的特殊声明

说白了就是不完全的声明。

把结构体类型S省略。

struct
{
	int a;
	char b;
	float c;

}x;

struct
{
	int a;
	char b;
	float c;
}a[20], *p;

警告:

编译器会把上面的两个声明当成完全不同的两个类型,所以是非法的。

匿名的结构体类型,如果没有对结构体类型重命名的话,只能用一次

我们可以重命名来使用

typedef struct
{
	int a;
	char b;
	float c;
}S;

int main()
{
	S s1 = { 0 }, s2 = { 0 };//初始化。

	return 0;
}

2.2结构体的自引用

说白了就是自己包含自己。

比如,定义一个链表的结点:

struct Node
{
	int data;//数据存储
	struct Node* next;//下一个节点
};

上述代码正确吗?

如果正确,那sizeof(struct Node)是多少呢?

仔细分析是不对的,因为一个结构体中再包含一个同类型的结构体变量,这样结构体大小就会无穷的大,是不合理的。

正确的自引用方式:

struct Node
{
	int data;//数据存储
	struct Node* next;//下一个节点的地址
};

另外:

结构体自引用中夹杂了tyoedef对匿名结构体类型重命名,也容易引入问题,如。

typedef struct
{
     int data;
     Node* next;
}Node;

这段代码正不正确呢?

仔细分析是不对的。为什么?如果对Node重命名的话,再结构体内部就提前使用了Node,所以是不正确的,这就是先有鸡还是先有蛋的问题了。如何改正呢?

定义结构体就不要使用匿名结构体了。

typedef struct Node
{
     int data;
     struct Node* next;
}Node;

三、结构体大小

为了计算结构体的大小就需要了解结构体对齐

对齐规则:

1.结构体第一个成员对齐到偏移量为0的地址处。

2.第二个成员变量对齐到第二个成员变量对齐数的整倍数的地址处。

        (对齐数=编译器默认的对齐数 和 该成员变量大小的较小值

        VS 默认值为8,Linux中gcc没有默认对齐数,对齐数就是成员自身的大小。

3.所以成员完成了对齐并不是整个结构体的大小,结构体的总大小为所有成员变量最大对齐数的倍数。

4.如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整倍数处,

        结构体的整体大小同3.

了解了对齐规则,练习一下

先看结构体S1 和 S2.

//练习1
struct S1
{
	char c1;
	int i;
	char c2;
};

//练习2
struct S2
{
	char c1;
	char c2;
	int i;
};

结构体S1,S2的大小为多少呢?是否相等呢?

分析一下:

 

由分析可得处:

        s1 -- 12, s2 -- 8 

仅仅因为顺序不同,结构体大小就不同。

再看两个例子:把s3嵌套再s4中。

了解到这,那为什么会存在内存对齐呢?

其实就是:结构体的内存对⻬是拿空间来换取时间的做法。

3.1修改默认对齐数

我们可以通过预处理指令#pragma,修改编译器对齐数。

四、结构体传参

结构体变量.成员名

结构体指针->成员名

如下例子:

​
struct S
{
	int data[1000];
	int num;
};
struct S s = { {1,2,3,4}, 1000 };
//结构体传参
void print1(struct S s)
{
	printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
	printf("%d\n", ps->num);
}
int main()
{
	print1(s); //传结构体
	print2(&s); //传地址
	return 0;
}

​

上面的print1 和 print2函数那个好些?

答案是:print2

原因:

        函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。

        如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销就比较大,所以导致性能下降。

结论:结构体传参的时候,要传结构体的地址。

五、结构体实现位段

位段是基于结构体的。

先看一下结构体的代码:

struct A
{
	int _a;
	int _b;
	int _c;
	int _d;
};
int main()
{
	printf("%d\n", sizeof(struct A));
	return 0;
}

结构体实现位段

        位段的成员必须是 int , singed int ,unsigned int , char 类型的。(整形家族里的)

        位段后由冒号和一个数字。

再结构体的基础上实现位段:

struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};
A就是⼀个位段类型。那位段A所占内存的大小是多少?
        位段:二进制位。
        2的单位是比特位
  • 33
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值