自定义类型:结构体

1.1 结构体基础

1.2 结构体的声明

1.3 结构体的定义

1.4 结构体的自引用

1.5 结构体的储存

1.1 结构体基础

在结构体之前,我们已经熟知 int, char, short, long, float, double等, 用来修饰变量,但对于很多物品的属性,我们光用以上的类型修饰不了。比如 一个人的基本信息(姓名, 年龄, 身高, 体重), 一本书的基本信息 (书名, 作者, 价格)等,这时候就需要我们创建一个自定义类型: 结构体来修饰这些拥有多个属性的变量。

1.2 结构体的声明

因为结构体是我们自己创建, 使用时需要我们来进行声明,有哪些属性, 属性的类型为什么。

比如创建一个学生:形式为

struct stu
{
    char name[20];// 姓名
    char ID [15];// 学号
    int age;// 年龄
    char sex[5];// 性别

};//这里的分号不能省略

注:我们需要在使用前,进行声明

1.2.1 结构体的特殊声明

能否对声明结构体时,省略结构体的标签?

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

在某种情况下,可以进行以上省略,但此代码会有缺陷

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

struct
{
    char a;
    int b;
    float c;
}*p;

p = &a 

在这种情况下,是否合法?

警告:在这种情况下,编译器会把以上两种声明当做两个不同的个体,即使两个声明里,所有的类型一一对应,依然是非法的。

如仍要使用,只能声明一个这种结构体,并且只能使用一次。

但不建议使用这种省略结构体标签的代码。

1.3 结构体的定义

声明类型之后,该如何定义和使用结构体

struct stu
{
    char name[20];
    char id[15];
    char sex[5];
    int age;
}a;         // 声明的同时,定义

//  定义结构体变量
struct stu b;

//  定义的同时, 初始化
struct stu c = {"zhangsan", "1234567", "nan", 20};

对于结构体, 可以使用嵌套定义

struct stu
{
    int a;
    int b;
};


struct node
{
    int x;
    struct stu p;
}i = {1, {3, 4}};

1.4 结构体的自引用

在结构体中, 是否能引用该结构体本身的成员?

答案是可以,但需要引用正确

struct stu
{
    int a;
    struct stu next;
};
//  是否可行?

如果结构体小,自引用次数少,语法是没有错误,但前面两个条件一旦数量增大,

sizeof(struct stu) 的大小为多少?  内存会被占用很大

所以不推荐上述代码

正确引用

// 正确引用

struct stu
{
    int a;
    struct stu *next;
};

指针的大小在不同环境下分别为 4 / 8,即使结构体大,自引用次数多,但因为是指针,自引用所用字节都为 4 / 8。

1.5 结构体的储存

我们已经知道怎么声明和定义结构体,但结构体是如何在内存中开辟空间的

#include <stdio.h>
int main()
{
	struct p1
	{
		char a;
		int b;
		short c;
	};

	struct p2
	{
		int a;
		char b;
		short c;
	};

	printf("p1的大小%d\n", sizeof(struct p1));
	printf("p2的大小%d\n", sizeof(struct p2));

	return 0;
}

由运行结果知,对于结构体中使用了相同的类型,但空间大小不一样 。这其中有什么原因呢?

其实在这里面涉及到了结构体的内存对齐

1.5.1 结构体的内存对齐

对齐规则

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

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

对齐数 = 该成员的大小与编译器默认对齐数的较小值

        vs中默认对齐数为 8

3.结构体大小为最大对齐数的最大整数倍。

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

图示:p1

同时p2同理

这也是内存大小为什么不一样的原因

1.5.2 为什么要内存对齐

总体上:用空间换取时间

大部分参考资料表示:

1.平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特
定类型的数据,否则抛出硬件异常。
​2.性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访
问。

1.5.3 修改默认对齐数

我们可以用#pragma pack( x ) (x为自己修改的对齐数)来修改

如需还原编译器默认对齐数, 只需 #pragma ()便可修改

1.6 结构体传参

有两种方式, 传值, 传址

#include <stdio.h>

struct stu
{
	char name[20];
	char id[15];
	char sex[5];
	int age;
};


void print1(struct stu p1)
{
	printf("%s %s %s %d\n", p1.name, p1.id, p1.sex, p1.age);
}


void print2(struct stu* p1)
{
	printf("%s %s %s %d\n", p1->name, p1->id, p1->sex, p1->age);
}


int main()
{

	struct stu p1 = { "zhangsan", "1234567", "nan", 20 };
	// 传值
	print1(p1);
	print2(&p1);

	return 0;
}

 

print1 和 print2 均能正常编译

但在选择时, 应选择哪一种?

答案很显然, 选择print2(传址)

因为在结构体很大的时候,传值需要开辟一个相同空间大小的内存,所以结构体传值是很浪费内存的, 但传址,指针的大小永远为 4 / 8 个字节,不会占用更多的空间。

综上所述, 在结构体传参时,我们要选择传址,传指针。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值