结构体的详解(想要彻底了解结构体,那么看这一篇就够了!)

        前言:在C语言中,我们知道有这么几种基本数据类型,包括整型(char 、 short、 int、long 、long  long),浮点型(float 、 double)。这些都是C语言已经规定好了的数据类型,那么能不能自己定义一个类型呢?事实上是可以的,结构体就是其中的一种。


接下来我会以这五点来使你了解结构体究竟是何物:

目录

1.结构体的定义规则

2.结构体类型的变量创建

(1)定义结构体后直接创建变量

(2)定义结构体后在创建变量

(3)定义结构体后创建局部变量

3.结构体的初始化

(1)定义时后直接进行初始化

(2)定义后逐个赋值

(3)乱序初始化结构体

4.结构体中数据的调用

5.结构体大小

结构体的对齐规则:

(1)不含嵌套结构体:

(2)含义嵌套结构体:


1.结构体的定义规则

        结构体定义由关键字 struct 和结构体名组成,结构体名可以根据需要自行定义。struct 语句定义了一个包含多个成员的新的数据类型,struct 语句的格式如下:

//定义一个结构体
struct Student {
	//在内部创建你需要的数据类型
	char name[10];
	int age;
	char sex[5];
};

上面的代码是我想创建一个类型来描述一个学生的信息,我将学生名字、年龄、性别创建在了一个名叫Student的结构体中,这样一来我们就创建好了自己定义的一个结构体。

注:结构体中的内容可依据需求自行创建,一般来说结构体的创建都是在代码最开始的地方创建,并且要注意在创建好结构体之后要加上 ;(分号)!

2.结构体类型的变量创建

        当一个数据类型创建好了之后,我们就避免不了创建一个变量使用它,如何创建一个结构体类型的变量呢?

(1)定义结构体后直接创建变量

#include<stdio.h>
struct Student {
	char name[10];
	int age;
	char sex[5];
}stu;
int main()
{
	//执行的代码
	return 0;
}

如图,我们定义好结构体后直接在其尾部创建了一个变量。

(2)定义结构体后在创建变量

#include<stdio.h>
struct Student {
	char name[10];
	int age;
	char sex[5];
};
struct Student stu;
int main()
{
	//执行的代码
	return 0;
}

如图,创建好结构体后,我们在创建一个结构体变量。

注:以上的两种创建的结构体变量都是全局变量。

(3)定义结构体后创建局部变量

#include<stdio.h>
struct Student {
	char name[10];
	int age;
	char sex[5];
};
int main()
{
	struct Student stu;
	//执行的代码
	return 0;
}

如图,我们在main大括号中创建了结构体的变量,其为局部变量。

3.结构体的初始化

        结构体的变量创建好了,那么也就避免不了对其进行初始化了,结构体的初始化大致分成三种初始化方式。

还是以上面学生的结构体为例

(1)定义时后直接进行初始化

#include<stdio.h>
struct Student {
	char name[10];
	int age;
	char sex[5];
};
int main()
{
	struct Student stu = { "zhangsan",20,"男" };
	//执行的代码
	return 0;
}

即,直接在后面用 { } 将数据进行初始化。

(2)定义后逐个赋值​​​​​​​

#include<stdio.h>
#include<string.h>
struct Student
{
	char name[10];
	int age;
	char sex[5];
};
int main()
{
	struct Student stu;
	strcpy(stu.name, "zhangsan");
	stu.age = 20;
	strcpy(stu.sex, "男");

	return 0;
}

即,分开一个一个进行赋值。

注:定义好结构体后如果不直接进行初始化,那么在后续的给结构体中内容赋值时不能在一次性全部赋值了。(如图)

(3)乱序初始化结构体

#include<stdio.h>
struct Student {
	char name[10];
	int age;
	char sex[5];
};
int main()
{
	struct Student stu = {.age = 20,.name = "zhangsan",.sex = "男"};
	//执行的代码
	return 0;
}

即,初始化不按照定义时的顺序进行初始化。

4.结构体中数据的调用

        结构体变量创建好了,初始化也初始化好了,那么我们如何调用结构体中的内容呢?调用结构体中的内容我们使用 ( . )操作符。

 例如我想打印一下结构体中的内容:

#include<stdio.h>
struct Student {
	char name[10];
	int age;
	char sex[5];
};
int main()
{
	struct Student stu = {.age = 20,.name = "zhangsan",.sex = "男"};
	printf("%s %d %s", stu.name, stu.age, stu.sex);
	return 0;
}

即,我们想使用结构体中的内容我们只需要用创建变量的名字 . 类型名即可。

当然我们还可以使用->来调用结构体中的内容:

#include<stdio.h>
struct Student
{
	char a;
	int b;
};
void Print(struct Student* p)
{
	p->a = 'Z';
	p->b = 1024;
	printf("%c %d", p->a, p->b);
}
int main()
{
	struct Student stu;
	Print(&stu);
	return 0;
}

也就是说,普通调用的时候我们使用结构体名 . 类型名称调用,而对于结构体的地址而言,我们使用->来调用结构体中的内容。

5.结构体大小

        在C语言中,每一个数据类型都有其对应的大小,通常我们使用sizeof()操作符来计算数据类型的大小,那么自定义结构体的类型大小是多大呢?

首先我们先来看一下这个结构体:

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

一般来说,char为一个字节大小,int为4个字节大小,那么这个结构体的大小应该为6,但是事实并非如此,我们看一下结果:

#include<stdio.h>
struct stu
{
	char a;
	int b;
	char c;
};
int main()
{
	printf("%zd", sizeof(struct stu));
	return 0;
}

这和我们想象中的结果有所出入,那么为什么其大小是12,而不是6呢?这就必须介绍结构体的对齐规则。

结构体的对齐规则:

        1.结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处;

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

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

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

我们直接使用一个例子来举例:

(1)不含嵌套结构体:


以上我们就了解了结构体的大小到底如何进行计算了。

(2)含义嵌套结构体:

以上我们就了解了有嵌套结构体的大小是到底如何进行计算了。

注意:嵌套的结构体计算对齐数时,不需要在和默认对齐数进行比较。

当然我们也可以使用pragma来修改默认对齐数:(如图)

#include<stdio.h>
#pragma pack(1)
struct sin
{
	char a;
	int b;
	char c;
};
#pragma pack()
int main()
{
	printf("%zd", sizeof(struct sin));
	return 0;
}

由上面的计算我们已经知道了此结构体如果默认对齐数是8时大小为12,但是我们使用pragma修改其默认对齐数后其大小为6(读者可以自己进行计算)

那么为什么会出现对齐数呢?主要原因有二:

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

        2.性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。假设一个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用一个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。


以上就是结构体的全部内容了~~~

  • 53
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值