结构体(C语言进阶)(一)

目录

前言

1、结构体声明

1.1 结构体基本概念

1.2 结构体声明

1.3 特殊的结构体声明

1.3.1 匿名结构体声明

1.4 结构体自引用

1.5 结构体变量的定义和初始化

1.6 结构体内存对齐

1.7 修改默认对齐数

1.8 结构体传参

总结


前言

        C语言除了有其内置类型,还有自定义类型。因为在实际运用中,我们可能会遇到一类数据,它的内容是由多种数据组成的,例如:一个学生的个人信息,它就包含了姓名、年龄、学号等等。这些数据并不是统一的类型,因此不能用C语言本身的数组去定义,那如果我们需要将其放在一个变量中时,我们就可以运用到结构体了。结构体的出现就是用来处理这样的内容数据不统一的变量,这给了编程者很大的操作空间,使得编程更加灵活自由。

        接下来我们就详细介绍一下结构体吧。

1、结构体声明

1.1 结构体基本概念

        结构体是一些值得集合,这些值称为成员变量。结构体的每个成员可以是不同类型的变量。类比数组,数组的成员变量的类型必须相同,而结构体没有要求。

1.2 结构体声明

        举个例子:

        

        这是一个“学生”结构体的定义,它的类型为struct stu,struct是结构体声明的前缀,stu是自己定义的类型名,因为是包含“学生”信息的结构体,因此我们用来stu。定义好类型名之后,我们用一个大括号阔住接下来的内容,并且在大括号后面加上“;”。然后我们来看大括号里面的内容,上面这个例子我大括号里面放了了四个变量,分别用来存放:姓名;年龄;性别;学号。如果你想定义其他的也是可以的,内容是可以自己灵活改变的。这样,我们就算是定义好了一个结构体类型,它可以用来表示学生的基本信息。

        注意,上面仅只是建立了一个类型,还没有正真建立结构体变量。相当于我们只是建立了一个类似于:int;float;double这样的东西,后面还需要用具体的变量去承载。类比int,我们定义int类型的变量,还得想一个变量名,比如“p”,那我们定义方式就是:int p=0;那这里p就是一个整型的变量。同理结构体也一样,上面的操作只是相当于写了一个“int”,还需要想一个变量名,假如“p”,定义时像这样:struct stu p={ 0 };这样,才算定义出了一个结构体变量p。

        例如:

        这是两种声明方法,可以在结构体建立时就声明变量p1,也可先定义类型,后再声明p2。这两种方法的区别是,前者定了全局变量,后者定义的是局部变量。

1.3 特殊的结构体声明

1.3.1 匿名结构体声明

        例如:

        声明结构体类型时,没有名字。这种类型的结构体只能用一次,而且是马上建立马上用:

        像这样,定义一个s1的结构体之后,就再也无法使用这个匿名结构体。

        匿名结构体特点:对于不同的匿名结构体,即使其内容一模一样,计算机还是会认为他们不是一个类型,如果用两个内容相同的匿名结构体,一个定义变量,一个定义指针,当指针指向变量时,计算机会报错。

1.4 结构体自引用

        结构体内部包含自己。看一个例子:

        对于这个结构体,第一个数据是一个整型,第二个数据定义的居然是指向自己的指针类型,这样的话,我们去访问这个结构体时,可以在它内部发现指向自己的指针,这就是结构体的自引用。

        想一个问题:我们能不能在结构体里面直接定义一个本身类型的结构体变量呢?我们为什么要定义指针呢?

        原因:因为如果我们在一个结构体里面直接定义它本身类型的变量,那我们将无法预估这个结构体的大小,因为每次计算这个结构体第二个参数的大小时,又会进入到一个结构体里面,而那个结构体的第二个参数还是结构体,是一个死循环。所以我们在结构体自引用时,只定义自身类型 的指针,就能避开这个问题。

        为什么要这么用?

        这个用法很重要,有了这样一个功能,才能实现链表建立。是数据结构很重要的内容。

        注意:结构体的自定义是无法通过匿名结构体实现的。

1.5 结构体变量的定义和初始化

        这个在前面其实已经讲到一部分,下面看几种定义的例子:

        p1和p2定义时未初始化,p3定义时进行了初始化。这三种方式都定义了结构体变量,p3在定义时进行了初始化。

1.6 结构体内存对齐

        先看一个例子,思考如下结构体占用几个字节:

#include <stdio.h>
struct S1
{
	char c1;
	int c2;
	char c3;
};
int main()
{
	printf("%d", sizeof(struct S1));
	return 0;
}

        答案:

        答案是12。那么这是为什么呢?为什么不是6?这就涉及到我们揭晓来要讲的:结构体内存对齐。

        内存中如何开辟结构体空间?

        首先了解结构体的对其规则:

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

2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
(VS中默认的值为8)

3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍
处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数
倍。

        就拿上面的结构体举例,讲解其在内存中的存放情况:

struct S1
{
	char c1;
	int c2;
	char c3;
};

        只有vs有默认对齐数,其他平台是没有默认对齐数的,其他平台对齐数就是数据本身的大小。

        为什么存在内存对齐?

        1、平台原因

        不是多有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些地址处取某些特定的数据类型,否则抛出硬件异常。

        2、性能原因:

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

        总的来说:

        结构体的内存对齐是拿空间换取时间的做法。

在我们大致了解对齐规则后,我们需要了解,结构体定义的时候,位置的不同会影响其空间的大小。让占用空间小的数据尽量放在一起,这样可以减少结构体空间的浪费。

1.7 修改默认对齐数

        在vs中默认对齐数是8,但是它可以被修改。

        使用#pragma

        看例子:

#include <stdio.h>
#pragma pack(4)
struct S
{
	int i;
	double d;
};
#pragma pack()

int main()
{
	printf("%d\n", sizeof(struct S));

	return 0;
}

        用#pragma pack(4)将默认对齐数改成了4,结果从16变成了12。

        后面的#pragma pack()是用来恢复默认对齐数。

1.8 结构体传参

        直接看例子:

#include <stdio.h>
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* p)
{
	for(int i=0;i<10;i++)
	printf("%d\n", p->data[i]);
}
int main()
{
	print1(s);
	print2(&s);
	return 0;
}

        结构体传参有两种方式,第一种print1是直接将值传送过去(传值调用);第二种print2是传地址(传址调用)。我们认为第二种更好,因为直接传值的话,参数是需要压栈的,会有时间和空间的系统开销,如果传值一个过大的结构体,会导致系统的性能下降。

总结

        本篇详细讲解了结构体的定义,希望对你有所帮助。

  • 74
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

彭逍遥

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

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

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

打赏作者

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

抵扣说明:

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

余额充值