(C语言)深度剖析结构体


在这里插入图片描述

导语

在C语言中,有很多的类型,例如int,short,long,char,double,float等等,它们属于内置类型,除开这些,C语言也允许我们自定义类型,我们今天要学习的就是自定义类型中的一个重要的知识点,结构体。

1. 结构体类型的声明

1.1 基础知识

结构是一些值的集合,这些值称为成员变量。结构的每个成员可以由不同的类型组成

1.2 结构的声明

我们可以用结构体来描述一个学生,一个学生的组成成员有哪些呢?
我们知道,有姓名,性别,年龄,学号等,各个成员组合起来构成了结构体。

struct student
	{ 
	    //成员变量
		char name[20];//名字
		int age;//年龄
		char sex[5];//性别
		char id[20];//学号
	};  //分号不能丢

1.3 特殊的声明(匿名结构体类型)

我们在声明结构体的时候,可以不完全的声明
例如

int main()
{
	struct
	{
		int age;
		char name[20];
		char sex[5];
	}s1;

	struct
	{
		int age;
		char name[20];
		char sex[5];
	}*p;
	
	return 0;
}

这两个结构在声明的时候忽略了标签,这样是可以的,但是我们在上面代码的基础上,加上这行代码,会出现什么呢?

	p = &s1;

有的小伙伴可能已经知道答案了,对,编译器会报错。
在这里插入图片描述
虽然这两个结构的成员完全相同,但是编译器会把它们两个当作完全不同的类型,所以会出现错误,这对于我们来说是不方便的,所以我们在声明结构体的时候一定要加上标签。

1.4 结构的自引用

我们想要在结构中包含一个类型为该结构本身的成员,可以吗?
答案是可以。我们先来看一下错误示范

struct Node
{
      int data;
      struct Node next;
      //这样直接引用是不对的,会导致无限循环
}

正确的自引用:

struct Node
{
      int data;//4个字节
      struct Node* next;//4或8个字节
      //大小确定了,结构体也就确定了
}

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

有了结构体类型,变量如何定义和初始化呢,so easy.

    struct student
	{
		char name[30];
		int age;
		char sex[5];
		char id[20];
	}p1;               //声明类型的同时定义变量p1
	struct student p2; //定义结构体变量p2  

    //初始化:定义变量的同时赋值
	struct student p3 = {"zhangsan",18,"nan",220206401};
	
	struct Node
	{
		int data;
		struct student p;
		struct Node* next;
	}n1 = {10,{"zhangsan",18,"nan",220206401},NULL };    //结构体嵌套初始化

	struct Node n2 = { 20,{"lihua",18,"nan",220206435},NULL };//结构体嵌套初始化

1.6 结构体内存对齐

我们已经了解结构体的基本使用了,那么它的大小该如何计算呢?
我们先学习一下计算的规则,再来几道题目练练手

1.结构体的第一个成员永远都放在0偏移处
2.从第二个成员开始,以后的每一个成员都要对齐到某个对齐数的整数倍处
这个对齐数是:成员自身大小和默认对齐数的较小值
备注:VS的默认对齐数是8,gcc环境下没有默认对齐数,对齐数默认就是成员本身大小
3.当成员全部存放后,结构体的总大小必须是所有成员的对齐数中最大对齐数的整数倍,如果不够,则会浪费空间对齐。
4.如果嵌套了结构体,嵌套的结构体成员要对齐到自己成员的最大对齐数的整数倍处。
整个结构体的大小,必须是最大对齐数的整数倍,最大对齐数包含嵌套结构体成员中的对齐数

例题:

struct s1
{
    int a; // 4 8 4  
    char c; // 1 8 1
    //和默认对齐数相比,取小,则a取4,c取1
};
struct s1 s;

在这里插入图片描述

struct s1
{
    char c; 
    int a;  // 4 8 4  
};
struct s1 s;

在这里插入图片描述

struct s2
{
   double a;
   char b; // 1 8 1
   int c;//4 8 4
};
struct s2 s;

在这里插入图片描述
就展示这么多了,大家自己多多练习,熟能生巧。
通过例题,可以得到一个小技巧:创建结构体时将占用空间小的类型凑在一起,可以节省空间。

1.7 修改默认对齐数

VS的默认对齐数是8,好烦呀,怎么办呢?其实我们可以修改结构体的默认对齐数,只需用#pragma这个预处理指令即可。如下:

#include<stdio.h>
#pragma pack(1) //修改默认对齐数为1
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.8 结构体传参

直接上代码

#include<stdio.h>
struct S
{
    int data[1000];
    int size;
};
//传值
void print(struct S s)
{
    printf("%d", s.size);
}
//传址
void print(struct S* sp)
{
    printf("%d", sp->size);
}
int main()
{
    struct S s1;
    print1(s1);
    print2(&s1);
}

这里有个小问题,是使用print1好还是print2好?
使用print2比较好,因为print1是传值调用,print2是传址调用,传址调用比传值调用更省时间和空间。

结语

学习完结构体后,我们可以通过写一些小程序来练练手,例如通讯录,检验一下自己结构体的学习程度,下一篇内容也是关于通讯录,有问题欢迎大家交流!
在这里插入图片描述

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

MyPersist.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值