关于结构体类型的讲解

目录

一、结构体类型的声明

二、结构体的自引用

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

1、结构体变量的定义

2、结构体变量的初始化

四、结构体内存对齐

五、修改对齐数

六、结构体传参


一、结构体类型的声明

struct stu
{
	char arr[20];
	int age;
	int hight;
}s1;

其中struct——结构体关键字,stu——结构体标签,是根据自己的需求写,这里我举例为学生,大括号里面的是结构体成员变量,s1——结构体变量,这里注意,虽然在创建结构体的时候,加了大括号后末尾会自动分号“;”,但是还是不要忘记不能丢。

结构体还可以匿名声明,就是不写结构体标签:

struct
{
	char arr[20];
	int age;
	int hight;
}s1;

这种结构体变量也能用,不过只能在大括号后面和分号中间创建变量,在其他地方都不能创建变量,

另外,如果再创建一个一样的结构体,成员变量也一模一样的结构体指针p,是不能取地址s1赋值给p的,因为编译器会认为它们两个是不同的结构体类型。


二、结构体的自引用

结构体是可以包含一个结构体的,但是这个被包含的结构体的大小必须是确定了的,否则这个大的结构体大小也无法确定。

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

像这样结构体中包含自己,那就是无法算出它的大小,如果想包含,就在成员变量里的结构体加一个“*”,表示是一个指针,大小就是4个字节:

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

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

1、结构体变量的定义

struct stu
{
	char arr[20];
	int age;
	int hight;
}s1 = { "王五",19,183 };

struct stu s2 = { "李四",18,175 };

int main()
{
	struct stu s3 = { .arr = "张三",.age = 18,.hight = 180 };
	struct stu s2 = { .age = 19,.hight = 183,.arr = "王五" };
	printf("姓名:%s  年龄:%d  身高:%d\n", s1.arr, s1.age, s1.hight);
	printf("姓名:%s  年龄:%d  身高:%d\n", s2.arr, s2.age, s2.hight);
	printf("姓名:%s  年龄:%d  身高:%d\n", s3.arr, s3.age, s3.hight);
	return 0;
}

我们有三种定义方式:

1、在创建结构体的时候一起创建了结构体变量,例如s1,

2、在main函数外部创建结构体变量,例如s2,

3、在main内部创建的变量,例如s3,

其中s1和s2是全局变量,s3是局部变量。

2、结构体变量的初始化

可以在创建变量的同时一起初始化,也可以在函数内部进行初始化,在函数内部初始化可以打乱顺序,即在括号内指定成员变量进行初始化。


四、结构体内存对齐

我们知道char占1个字节,short占2个字节,int占4个字节,float占4个字节,long占8个字节,double占8个字节,那么一个结构体大小是几呢?

计算方法如下:

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

2其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处,在vs中默认对齐数是8。

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

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

数)的整数倍。

举个例子:

struct stu
{
	char arr;
	int age;
	int hight;
}s1;

 第一个成员变量是char类型,1个字节,比默认对其数8小,所以对齐数为1,从0开始,占1个字节空间。

第二个是int类型,4个字节,默认对齐数是8,而int是4,所以对齐数是4,前面只占了一个字节的空间,不够4的倍数,所以只能浪费空间,从4开始,占4个空间(4,5,6,7),

第三个也是int类型,此时,正好是从8开始分配空间(8,9,10,11)。

从0~11,总共是12个字节,是最大对其数4的倍数,所以该结构体的大小为12。

再来一道题:

struct stu
{
	char arr[3];
	int age;
	int hight;
}s1;

struct A
{
	int a;
	struct stu s1;
}A1;

int main()
{
	printf("%d\n", sizeof(A1));
	//printf("%d\n", sizeof(s1));
	return 0;
}

该输出结果为16,这里要注意两个点:

第一,s1中的char类型是个数组,是占3个字节,经过计算,该结构体的大小为12,

第二、A1中包含s1结构体,算大小的时候,先给a分配4个字节空间,在看s1中的最大对齐数,是4,所以不用浪费空间,直接从4开始分配,分配空间大小就是s1的大小,,所以是4 + 12 = 16个字节。

搞这么麻烦,为什么要这样设置内存对齐呢?

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

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

存访问仅需要一次访问。

那么如何有效的减小空间呢?

我们可以在创建结构体变量时,把结构体成员变量的类型大小从小到大排。


五、修改对齐数

这里我们用到一个预处理指令:#pragma

#include <stdio.h>
#pragma pack(8)//设置默认对齐数为8
struct S1
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
struct S2
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{
	//输出的结果是什么?
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	return 0;
}

 结构在对齐方式不合适的时候,我们可以自己更改默认对齐数。


六、结构体传参

传参和基本数据类型传参一样:

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;
}

传值,相当于是临时拷贝一份,而传址,是把地址传过去,我们优先考虑传址,因为函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销就比较大,所以会导致性能的下降。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值