C语言“浅谈结构体“

文章介绍了C语言中结构体的概念,包括如何声明和定义结构体,以及结构体重命名。文中详细阐述了结构体成员的初始化方法,通过实例展示了使用点号.和箭头->访问结构体成员的区别。此外,文章重点讨论了结构体的内存对齐机制,解释了为何两个成员相同的结构体大小可能不同,并给出了内存对齐的规则。最后提到了通过#pragmapack调整对齐数的方法。
摘要由CSDN通过智能技术生成

前言:

        在初学者学习C语言,前期一般都会使用内置类型也就是(int char doublu),但是如果我们要表现一些差异化的物体,单单一个Int和char 已经无法表示。在这个时候就会用到我们的结构体了,也就是平时说的自定义类型。

        你可以把结构体理解为内置类型和自定义类型的结合体,他是由各种类型组成的!

他是这些成员的集合,每个成员可以是不同的类型

1.结构体的声明 

typedef struct Stu
{
 char name[20];//名字
 int age;//年龄
 char sex[8];//性别
}Stu;

这就是一个结构体的声明,我们要表示一个学生的时候,不能单单用一个int或者char 来表示,人是一个多样的物种,需要具体表示一个学生,我们需要用他的名字,年龄,性别等等来表示,所以我们就需要不同类型的集合表示。

定义: 

struct Stu
{
	char name[20];
	int age;
	char sex[6];
}s1;//定义的全局变量

struct Stu s2;//全局变量

int main()
{
	struct Stu s3;//局部变量
	return 0;
}

 看上面代码,如果没有给结构体前重命名(typedef),那么结构体后面的s1就是一个全局的变量,s2.s3前面就是类名(struct Stu)。当然,还有一种是结构体重命名后的定义,看下方代码

typedef struct Stu
{
	char name[20];
	int age;
	char sex[6];
}Stu;//结构体重命名为Stu

Stu s2;//全局变量

int main()
{
	Stu s3;//局部变量
	return 0;

 我们看到上方代码与之前有一些不同,当结构体重命名后,结构体定义的时候,就直接用重命名后的类名去定义对象。

初始化:

struct Stu
{
	char name[20];
	int age;
	char sex[6];
};

int main()
{
	struct Stu s1 = { "zhangsan",18,"nan" };
	struct Stu s2 = { {"zhangsan"}};
	return 0;
}

s1用{ }大括号按照顺序全部初始化,s2只是初始化了一个,剩下的默认初始化为0; 

2.结构体访问 

我们要访问结构体对象中的成员时候,我们就需要用到两个操作符好   .  与  ->  那么他们有什么不同呢,看下面代码

struct Stu
{
	char name[20];
	int age;
	char sex[6];
};
void Print(struct Stu* s2)
{
	printf("%d", s2->age);
}
int main()
{
	struct Stu s1 = { "zhangsan",18,"nan" };
	printf("%d\n", s1.age);
	Print(&s1);
	return 0;
}

 我们看到了两个操作符运用的不同场景,但是他们有什么区别呢?

 操作符  .   :如果我们直接用结构体对象(s1)去访问成员那么就用  .  s1.成员名

 操作符  ->  :  如果我们用结构体指针(s2)去访问成员那么就用  ->  s1->成员名

其实理解起来很简单,如果是指针就用-> 结构体对象就用 .

在这里我要说一个需要注意的点,如果结构体要作为参数传参,那么尽量传递他的地址,这样会减少开销,如果传结构体对象,那么接收的时候就要产生拷贝,如果结构体过大,就会产生大量的开销。

3.结构体的大小 

        结构体大小是一个老生常谈的问题了,他也是一个非常非常热门的考点!为啥一个计算大小能成为考点呢?

        这就不得不说结构体的内存对齐机制了,内存对齐.有两个原因:1.平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特 定类型的数据,否则抛出硬件异常。

        2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

总结一下:

        内存对齐就是以空间换时间的做法,通过内存对齐提高效率

struct S1
{
	char c1;
	int i;
	char c2;
};
struct S2
{
	char c1;
	char c2;
	int i;
};

int main()
{
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	return 0;
}

我们可以看到上面的结构体成员是一摸一样的,要说不同,就只有顺序上的不同。那么大家觉得他们的大小是否一样呢?

 执行后,我们发现两个结构体大小居然是不同的,那么为什么会出现这样的情况呢?

我们要看懂上面的计算过程,我们就要了解内存对齐的规则:

        1. 第一个成员在与结构体变量偏移量为0的地址处。

        2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8

        3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍

        4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。 

 首先大小都是从0开始计算,下一个要对齐到对齐数整数倍的地方(对齐数 与 该成员大小的较小值)如果对齐不上中间就会补上空,计算完后结构体的大小后必须成员最大对齐数的整数倍,

不够也得补上空!!

修改默认对齐数:

#pragma pack(4)
struct S1
{
	char c1;
	int i;
	char c2;
};
#pragma pack()

 那么,在这里上面的#pragma pack(4)是什么意思呢,其实也很简单,就是把默认对齐数修改为4(vs默认的对齐数是8),那下面的#pragma pack()又是什么呢?他表示的就是还原默认对齐数(8)

上就是全部代码,如有错误和不足的地方。欢迎大家指正,下一节我还会细谈一下谈下结构体,请大家关注一下!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值