首页 > 其他分享 >【C语言】自定义类型:结构体(建议收藏!!!)

【C语言】自定义类型:结构体(建议收藏!!!)

时间:2024-06-04 10:29:01浏览次数:26  
标签:位段 struct 自定义 int 收藏 char printf C语言 结构

结构体

前言

我们已经学了很多数据类型,列如int, char, float, double等。但还不能满足需求,在我们实际应用中,每一种变量进行一次声明,再结合起来显然是复杂的,列如一位学生的信息管理,他可能有,性别 (char),姓名(char),学号(int)成绩(float)等多种数据。如果把这些数据分别单独定义,就会特别松散、复杂,难以规划,因此我们需要把一些相关的变量组合起来,以一个整体形式对对象进行描述,因此我们需要自定义一种类型来规划它们,于是就有了结构体类型。

1、什么是结构体

结构体就是把多种类型组合起来的集合体。多种类型可以是int, float,char,指针等。可以想一下,数组是相同类型的多个元素组成的集合;而结构体是多种类型组成的集合。两者有区别,不可混淆。

2、结构体类型的声明

形式:

struct 类型名
{
		结构体成员列表;
}

如:定义一个学生类型的结构体

struct Stu
{
	char name[20];//姓名
	char sex[5];//性别
	int age;//年龄
	char id[20];//身份证
};

这段代码就完成了结构体的声明。

3、结构体变量的创建和初始化

结构体变量创建
方式一:在声明结构体的同时创建变量。

struct Stu
{
	char name[20];
	char sex[5];
	int age;
	char id[20];
}s1;

这里创建了学生类型的结构体变量s1。
方式二:struct Stu s1;

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

int main()
{
	struct Stu s1;//结构体变量创建
	return 0;
}

结构体的初始化
结构体初始化有两种方式,看如下代码:

//结构体声明
struct Stu
{
	char name[20];
	char sex[5];
	int age;
	char id[20];
};
int main()
{
	//顺序初始化
	struct Stu s1 = { "小敏", "女", 18, "202413230" };
	//指定顺序初始化
	struct Stu s2 = { .age = 20, .id = "202413232", .name = "晓东", "男" };
	return 0;
}

4、结构成员访问操作符

有两种,分别是:" . “成员操作符和”->"成员操作符

struct Stu
{
	char name[20];
	char sex[5];
	int age;
	char id[20];
};
int main()
{
	//顺序初始化
	struct Stu s1 = { "小敏", "女", 18, "202413230" };
	//.操作符访问
	printf("name:%s\n", s1.name);
	printf("sex:%s\n", s1.sex);
	printf("age:%d\n", s1.age);
	printf("id:%s\n", s1.id);
	//指定顺序初始化
	struct Stu s2 = { .age = 20, .id = "202413232", .name = "晓东", "男" };
	//->操作符访问
	struct Stu* p = &s2;
	printf("name:%s\n", p->name);
	printf("sex:%s\n", p->sex);
	printf("age:%d\n", p->age);
	printf("id:%s\n", p->id);
	return 0;
}

5、结构体内存对齐

对齐规则:

  1. 结构体的大小一定是最大成员的整数倍。
  2. 成员的偏移量一定是当前成员的整数倍。
  3. . 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构
    体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

代码展示:

struct S1
{
	char c1;
	int i;
	char c2;
};
struct S2
{
	int i;
	char c1;
	char c2;
};
struct S3
{
	double d;
	char c1;
	int i;
};
struct S4
{
	char c1;
	struct S3 s3;
	double d;
};
int main()
{
	printf("%zd\n", sizeof(struct S1));//输出12
	printf("%zd\n", sizeof(struct S2));//输出8
	printf("%zd\n", sizeof(struct S3));//输出16
	printf("%zd\n", sizeof(struct S4));//输出32
	return 0;
}

6、存在内存对齐的原因

  1. 平台原因 (移植原因):
    不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定
    类型的数据,否则抛出硬件异常。
  2. 性能原因:
    数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要
    作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地
    址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以
    ⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两
    个8字节内存块中。

简单来说,结构体的内存对⻬是拿空间来换取时间的做法。以此来提高效率。

所以设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间。方法:让占⽤空间⼩的成员尽量集中在⼀起。
代码展示:

struct S1
{
	char c1;
	char c2;
	int i;
};
struct S2
{
	char c1;
	int i;
	char c2;
};
int main()
{
	printf("%zd\n", sizeof(struct S1));//8
	printf("%zd\n", sizeof(struct S2));//12
	return 0;
}

结构体S1和S2类型的成员都一样,但是S1占的空间小于S2,这就是让占⽤空间⼩的成员尽量集中在⼀起的效果。

7、结构体传参

传参方式:

  • 结构体传参(类似传值方式)
  • 结构体地址传参
    看如下代码:判断那种传参方式更好
struct S
{
	int data[1000];
	int num;
};
struct S s = { {1,2,3,4},100 };
struct S* ps = &s;
//结构体传参
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(ps);
	return 0;
}

输出结果:
在这里插入图片描述
答案是:print2函数更好
原因:函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。如果传递⼀个结构体对象的时候,结构体过⼤,参数压栈的的系统开销⽐较⼤,所以会导致性能的下降。总之,结构体传参的时候,要传结构体的地址。

8、结构体实现位段

位段定义: 位段是C语言中结构体的一种数据类型。位段允许在结构体中定义具有指定位数的成员,这些成员可以占用结构体变量内部的连续比特位。位段的声明和结构体的声明是类似的。位段是基于结构体的,位段的出现就是为了节省空间。

与结构体的两个不同点:

  1. 位段的成员必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以选择其他类型。
  2. 位段的成员名后边有⼀个冒号和⼀个数字。这个数字代表了该成员变量在结构体内占用的bit位数。它用来限定成员变量的范围和存储空间。

位段中的位指的是二进制位(bit位),如下面的_a成员变量就占两个比特位,_b成员变量就占5个比特位。
列如:A就是一个位段。

//位段声明
struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};
//结构体声明
struct B
{
	int _a;
	int _b;
	int _c;
	int _d;
};
int main()
{
	printf("位段A大小=%zd\n", sizeof(struct A));//8
	printf("结构体B大小=%zd\n", sizeof(struct B));//16
	return 0;
}

位段的内存分配:

  1. 位段的成员可以是 int, unsigned int ,signed int 或者是 char 等类型。
  2. 位段的空间上是按照需要以4个字节( int ) 或者 1个字节( char ) 的⽅式来开辟的。
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。

位段的应用:
下图是⽹络协议中,IP数据报的格式,我们可以看到其中很多的属性只需要⼏个bit位就能描述,这⾥
使⽤位段,能够实现想要的效果,也节省了空间,这样⽹络传输的数据报⼤⼩也会较⼩⼀些,对⽹络
的畅通是有帮助的。
在这里插入图片描述

位段使用的注意事项:
位段的一些成员可能共同占用⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位置处是没有地址的。内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。所以不能对位段的成员使⽤&操作符,这样就不能使⽤scanf直接给位段的成员输⼊值,只能是先输⼊放在⼀个变量中,然后赋值给位段的成员

代码展示:

struct A
{
	int _a : 2;
	int _b : 15;
	int _c : 10;
	int _d : 32;
};
int main()
{
	struct A s = { 0 };
	//错误
	/*scanf("%d", &b);*/
	
	//正确做法
	int b = 0;
	scanf("%d", &b);
	s._b = b;
	printf("%d", s._b);
	return 0;
}

总结:
这一章我们学习结构体的声明,变量的创建和初始化,成语访问操作符,传参,内存对齐,位段。后面两个是难点。相信大家都学会了吧!

那么写到这里,本节内容就结束了,这篇博客花费了很长时间,但写完有满满的成就感,希望能帮助到大家,如果文章有不足的地方,欢迎在评论区留言指正,我们一起学习交流! 希望能得到大家的关注、点赞、评论、收藏! 你的支持是我最大的动力!!

标签:位段,struct,自定义,int,收藏,char,printf,C语言,结构
From: https://blog.csdn.net/2301_81193162/article/details/139336546

相关文章

  • C语言之指针进阶(5),sizeof和strlen的数组计算以及指针运算笔试难题详解
    目录前言一、sizeof和strlen的区分比较二、sizeof,strlen与数组的计算三、指针运算,笔试难题解析总结前言    本文作为指针进阶的最后一篇文章,给大家带来了丰富的例题,这其中包括区分比较sizeof和strlen计算各种花样的数组指针表达式,如果你能答对所有的关......
  • 数据在内存中的存储<C语言>
    导言       在计算机中不同类型的数据在计算机内部存储形式各不相同,弄懂各种数据在计算机内部存储形式是有必要的,C语言的学习不能浮于表面,更要锻炼我们的“内功”,将来在写程序的时候遇见各种稀奇古怪的bug时,也便能迎刃而解,所以本文将着重介绍,整数在内存中的存储、大小......
  • vue3中ref绑定自定义组件没有获取到dom?
    问题<template><my-compref="test"/></template><scriptlang="ts"setup>consttest=ref()onMounted(()=>{console.log(test.value)})</script>打印出来的是一个proxy对象解决办法1.如果ref绑定的是一个普通的d......
  • 杨辉三角C语言的超简单解决办法
    #include<stdio.h>#include<stdlib.h>intmain(){intarr[10][10]={0};//十行的杨辉三角intsize=sizeof(arr)/sizeof(arr[0]);//求一共有几行for(inti=0;i<size;i++){for(intj=0;j<=i;j++)//对角线{if(i==j||j=......
  • 浙大翁恺《C语言程序设计》课程笔记
    1.1计算机与编程语言设计算法->编写程序->计算机执行程序执行的两种方式1.解释:借助一个程序(解释器),那个程序能试图理解你的程序,然后按照你的要求让计算机执行2.编译:借助一个程序(编译器),把你的程序翻译成机器语言,然后让计算机执行编程语言本身没有解释型和编译型之......
  • VS Code自定义代码模板
    VSCode自定义代码模板VSCode作为一款轻量级的编辑器,使用体验极佳,具有很多有用的小功能,也有着非常丰富的插件,日常写代码的时候自动补全和各种模板能够有效的提高编码的效率,这里介绍两种我常使用的自定义代码块或者模板的设置方法。集成自定义代码段VSCode自带了用户定......
  • C语言程序设计第二讲:顺序程序设计
    一、数据类型1.基本数据类型C语言中提供了一些基本数据类型,用于表示各种不同类型的数据:整数类型:int:表示整数,通常占用4个字节。shortint:表示短整数,通常占用2个字节。longint:表示长整数,通常占用4或8个字节。longlongint:表示更长的整数,通常占用8个字节。unsignedi......
  • HarmonyOS NEXT星河版之自定义List下拉刷新与加载更多
    文章目录一、加载更多二、下拉刷新三、小结一、加载更多借助List的onReachEnd方法,实现加载更多功能,效果如下:@ComponentexportstructHPList{//数据源@PropdataSource:object[]=[]//加载更多是否ing@StateisLoadingMore:boolean=false......
  • c语言中,结构体变量交换改写为堆空间申请内存
            在这里我实现的功能为:输入三个人的信息,每个人的信息分别为姓名和三个成绩,我分别计算三个人的成绩和,并通过经典的三杯水案例完成对三个人的成绩从小到大的排列打印。重点:     我这里使用的为堆空间申请内存的形式 第一步:        定义一个......
  • 初识C语言(02)—学习笔记
    转义字符转义字符释义\0结束标志\n换行\'打印单引号\"打印双引号\\打印一个反斜杠\t水平制表符\a警告字符,蜂鸣?在书写连续多个问号时使用,防止它们被解析成三字符\dddddd表示1~3个八进制的数字\xdddd表示2个十六进制数字\v垂直......