【C语言】结构体详解

CSDN话题挑战赛第2期
参赛话题:学习笔记

前言

阅读这篇文章,博主会带你认识、定义结构体,还会介绍如何计算结构体大小。

一.认识结构体

结构体是由一批数据组合而成的结构型数据。组成结构型数据的每个数据称为结构型数据的成员

二.结构体声明

struct tag
{
 member-list;
}variable-list;

假如我们想描述一本书,如果用单一的变量我们可能无法涵盖所有的信息。
这时我们可以使用结构体。

struct book
{
 char name[20];//书名
 int date;//出版日期
 char sex[5];//类型
 char id[20];//作者
}; //分号不能丢

三.特殊的声明

3.1 不完全声明
在声明结构体的时候,可以不完全声明,比如匿名结构体类型

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

上面两个结构在定义时省略掉了结构体标签(tag)

3.2 p = &x合法么
注意:虽然结构体内含有的变量相同,但编译器会把它们当成不同的类型,从而导致报错。因此是不合法的。

四.结构体自引用

4.1 结构体能够包含自己么

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

此时编译器会报错,为什么呢?

next是我们定义的的一个结构体变量,需要占用内存,但他的大小是多大呢?next里面又含有一个next,如果真的这样定义,next的大小会变成无穷大,因此编译失败。

那么应该如何编译呢?

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

当结构体自引用的时候,我们使用指针,因为指针的大小是确定的,因此不会出现上面的情况。

4.2 typedef
typedef可以给结构体重命名,比如

typedef struct Node
{
 int data;
}Node;

这样我们在定义结构体变量的时候,就可以使用重定义的名字,会稍微便捷一点。

struct Node num;
Node num;

五.结构体变量定义和初始化

有了前面的铺垫,那如何定义结构体变量其实很简单。

struct Point
{
 int x;
 int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2

可以声明的同时定义如p1,也可以在后面定义如p2.

//初始化:定义变量的同时赋初值。
struct Point p3 = {x, y};
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};//结构体嵌套初始化

当我们在在结构体中嵌套结构体时,定义时注意在{}里面再使用一个{}来给里面的结构体赋值。

六.结构体内存对齐

既然我们已经掌握了结构体的基本使用,那么应该怎么样计算结构体的大小呢?是将所有成员所占的空间相加么?显然不是的。(这是一个考点

计算结构体之前,我们需要先了解一下计算的规则。

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

1.计算结构体大小

  • 练习1
struct S1
{
	 char c1;
	 int i;
	 char c2;
};
printf("%d\n", sizeof(struct S1));

运行结果:
在这里插入图片描述我们画个图来解释这个题:

在这里插入图片描述第一个元素对齐偏移量为0的地址处,char类型占据一个字节,i为int类型,大小为4小于VS默认值8,因此i对齐4,又因为整体大小需要占据最大对齐数(这道题为4)的整数倍处,因此大小为12.

  • 练习2.
struct S3
{
	double d;
	char c;
	int i;
};
struct S4
{
 char c1;
 struct S3 s3;
 double d;
};
printf("%d\n", sizeof(struct S4));

运行结果:
在这里插入图片描述依旧用一个图来解释:

在这里插入图片描述用练习1的方法我们能轻易地算出S3的大小是16,S3里的最大对齐数是8,因此S3对齐到8的位址,d对齐到24的位址,加起来为32是最大对齐数8的倍数。

由此我们还能得出一个结论:
将较小的变量放在一起定义会比较节省空间。

七·.修改对齐数

#pragma pack(1)//设置默认对齐数为1
struct S2
{
 char c1;
 int i;
 char c2;
};

运行结果:
在这里插入图片描述

八.结构体传参

我们都知道函数传参既能传值又能传址,那么当我们想要传递结构体的时候我们应该传哪一种呢?

函数传参的时候,参数需要压栈,会有时间和空间上的系统开销
如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。因此最好传址

最后

如果你觉得博主写的还不错的话,记得点赞收藏加关注,感谢支持!

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

诺伯里-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值