C语言结构体与共用体(下篇) CSDN 花神生涯


位段

#include <studio.h>
struct S
{
//一个字节8个比特位,4个字节4个字节开辟,不足的直接浪费掉
	int a:2;//2个比特位,本来int得占4个字节,也就是32个比特位
	int b:5;//5个比特位
	int c:10;//10个比特位
	int d:30;//30个比特位
} 
int main()
{
	struct S s;//结构体变量的说明
	printf("%d\n",sizeof(s))//输出8
	s.a=10;
	s.b=20
	s.c=3
	s.d=4
	return 0;
}
  • 位段的成员必须是int unsigned int或signed int或者是char类型
  • 位段的成员名后边有一个冒号和一个数字
  • 位段的空间上是按照以4个字节(int)或者1革命字节(char)的方式来开辟的
  • 位段是为了节省空间

枚举enum

//枚举类型定义
enum 变量名 
{
	枚举值列表
};
//例如:
enum sex//定义枚举
{
	MAlE;//枚举的可能选项列表
	FEMALE;
	SECRET;
};
int main()
{
	enum sex s=MALE;//枚举变量的定义并使用
};
//枚举变量说明
enum sex
{
	MAlE;
	FEMALE;
	SECRET;
}a,b,c;//这里的abc是枚举变量,MAlE;FEMALE;SECRET;是枚举常量
//枚举变量的使用
int main()
{
	a=MALE;
};
  • 枚举类型是一种基本数据类型,也就是一一列举的意思
  • 枚举值是常量,不能在对其进行赋值,例如:下方MAlE;FEMALE;SECRET;已经是常量,不能在下方写成MALE=2,等
  • 枚举值默认从0开始,以后逐渐增1
  • 只能把枚举常量赋予给枚举变量
  • 不能把元素的数值直接赋予枚举常量,但可以在枚举中先行更改枚举常量的初始值
  • 如果一定要在下方使用数值,必须使用强制类型转化MALE=(enum sex)2;
  • 枚举标识符不能再用来定义变量,也就是MALE等不能再使用来定义变量,例如:int MALE =3;是错误的
  • 注意:枚举元素使用时是,枚举常量,不要加单,双引号

枚举的优点

  • 增加代码的可读性和可维护性
  • 和# define定义的标识符比较有类型检查,更加严谨
  • 防止了命名污染(封装)
  • 便于调试
  • 使用方便,一次可以定义多个常量

共用体(联合)union

//共用体的定义
union 共用体名
{
	成员列表
};
//例如:
union date
{
	char c;
	int i;
};
int main()
{
	union date u;//拿共用体创建变量
	printf("%d\n",sizeof(u));//输出4
	printf("%p\n",&u);//地址相同,三者共用同一块空间
	printf("%p\n",&(u.c));
	printf("%p\n",&(u.i));
	return 0};
//共用体的定义的同时定义变量
union date
{
	char c;
	int i;
}a,b,c;
int main()
{
	return 0};
  • 与结构体类似,但结构体各个成员都会分配相应的内存空间,而共用体的所用成员 共用一段内存
  • 这样一个联合变量空间的大小,至少是最大成员空间的大小
  • 共用体使用联合覆盖技术,几个成员变量相互覆盖,从而共用一段内存,并且在同一时刻只能使用其中的一个成员变量,
  • 如果对新的成员变量赋值,则原来的成员变量将被覆盖
  • 共用体也是一种特殊的自定义类型

知识扩展

int main()
{
//高字节---->低字节
//int a=0x11223344;
//低地址----------->高地址
//....[][11][22][33][44][][]....大端存储模式->高字节放在低地址
//....[][44][33][22][11][][]....小端存储模式->低字节放在高地址
}

动态内存分配

  • 整个程序包含了申请内存空间,使用内存空间,释放内存空间三个步骤,实现了空间的动态分配

目前内存的使用方式,

  • 1,创建一个变量
    int a=12//局部变量,存放栈区
    int b=12//全局变量,存放静态区
    在这里插入图片描述

  • 2,创建一个数组
    int arr[10];
    局部放在栈区 ,全局放在静态区

  • 上述地开辟空间的方式,空间开辟大小式固定的,数组在申明时,必须指定数组的长度,它所需要的内存在编译时分配

  • 数组的大小必须是常量,如果想用变量表示长度,想对数组的大小做动态说明是不允许的

  • 所需的内存空间取决于实际输入的数据,而这个无法事先确定,为了解决这个问题,C语言提供一些内存管理函数,可以根据需要动态地分配空间

  • 如果size为0,这个标准未定义,有错

分配内存空间函数malloc
  • 为确保内存分配精确,常常与sizeof一起使用
  • 分配一块长度为size字节的连续区域
  • 返回值为该区域的首地址
  • (类型说明符*)用于强制类型转换
#include <stdlib.h>//malloc函数所需要的头文件

//调用形式:
(类型说明符*)malloc(size)//向内存申请size个字节,然后(类型说明符*)用来强制转换
int main()
{
	//向内存申请10个整型的空间
	 int* p=(int*)malloc(10*sizeof(int))
	 //申请10个整型的空间赋给整型指针p,但因为malloc返回类型默认为volid所以用(int*)强制转换
	 if(p==NUll)//如果开辟空间失败,尽量检查是否失败
	 {
	 	printf("%s\n",strerror(errno))
	 	//strerror函数可以把错误码所对应的错误信息打印出来
	 }
	 else//开辟成功
	 {
		 int =0;
		 for(i=0;i<10;i++)
		 	{
			 	*(p+i)=i;//p+i是下标为i的元素地址,*(p+i)找下标为i的元素,把i放在开辟空间中有10个在使用空间
			 }
	 	 for(i=0;i<10;i++)
	 		{
		 		printf("%d\n",*(p+i))
			 }
	 }
	free(p);//释放空间,但存放首地址的p没变
	p=NUll;//主动设置为空指针,防止p被”怀仁“找到
	return 0;
}
释放内存空间函数free
  • 当空间申请的空间不再使用的时候,就应该还给操作系统
  • free函数专门来做动态内存的释放和回收
  • 当程序生命周期到了,也会自动释放
  • 该函数只释放 由malloc 或calloc函数所分配的动态内存空间区域
//释放ptr所指向的一块内存空间
free(void *ptr)
free(p)//谁开辟,谁释放,不回收,有可能内存泄漏
分配内存空间函数calloc
(类型说明符*)calloc(n.size)
//按stu的长度分配两块连续区域,强制转换为指向stu的结构体指针类型。并把首地址赋予指针变量ps
ps=(struct stu*)calloc(2,sizeof(struct stu));
int *ps=(int*)calloc(10,sizeof(int));
  • 在内存动态存储区中分配n块长度为size字节的连续区域
  • 函数的返回值为该区域的首地址
  • calloc会在返回地址之前把申请的空间的每个字节初始化内容默认都为0
  • (类型说明符*)用于强制类型转换
  • 为确保内存分配精确,常常与sizeof一起使用
内存调整函数realloc
  • realloc函数的出现让动态内存管理更加灵活
  • 它可以做到对动态开辟内存大小的调整
  • 这个函数调整原空间大小的基础上,还会将原来内存中的数据移动到新的空间,然后释放旧空间
  • 如果p指向的空间之后有足够的内存空间可以追加,则直接追加,后返回p
  • 如果p指向的空间之后没有足够的内存空间可以追加,则重新找一个新的内存区域,并且把原来内存中数据拷贝回来,释放旧的内存空间 ,最后返回新开辟的内存空间地址
void* realloc(void*ptr,size_t size);
//ptr是要调整的内存大小
//size是调整后的大小
//返回值为调整之后的内存起始位置
int main()
{
	int *p=(int*)malloc(20);
	if(p==NULL)
	{
	printf("%s\n",strerror(errno))}
	}
	else//开辟成功
	 {
		 int =0;
		 for(i=0;i<10;i++)
		 	{
			 	*(p+i)=i;
			 }
	}//截至到这里,只是在使用malloc函数开辟的空间
	//假设开辟的空间不够,希望多20个,这就要用到realloc函数
	int *p2=realloc(p,40); 
	if(p2==NULL{
		printf("开辟失败"}
	}
	else if(p2!=NULL{
		p=ptr;//目的是保持从始至终始终用p维护新的内存
		printf("开辟成功"}
		for(i=5;i<10;i++)
		{
			*(p+i)=i//p+5就是下标是5的元素地址。把5赋给下标是5的元素
		}
		for(i=0;i<10;i++)
		{
			printf("%d",*(p+i))
		}
	}
	//释放旧空间
	free(p);
	//使指针为空
	p=NULL'
	return 0;
}

常见的动态内存错误

  • 对NULL指针的解引用操作
  • 对动态开辟内存的越界访问
  • 对非动态开辟内存使用free释放
  • 使用free释放一块动态开辟内存的一部分
  • 对同一块动态内存多次释放
  • 动态开辟内存忘记释放(内存泄漏)

用指针处理链表

  • 使用动态分配时,每个结点(每次分配的一块连续空间)之间可以是不连续的(节点内是连续的),节点之间的联系可用指针实现
  • 在节点结构体中定义一个成员项,用来存放下一结点的首地址,这个存放地址的成员,常被称作“指针域”,存放数据的各个成员笼统的称为“数据域”
  • 第一个节点的指针域存放第二个节点的首地址…如此串联下去,最后一个结点没有后续结点,其指针域可赋为0,这样的连接方式,在数据结构中称为链表
  • 链表能更好的利用内存,按需分配和释放存储空间
  • 链表中插入或删除一个节点,只需改变某节点的指针域即可
  • 第0个节点称为头节点或链表点,存放首地址,无数据,只是指针变量
  • 每个节点两个域,一个是数据域,存放各种实际数据,一个是指针域,存放下一节点首地址
  • 链表中每一个节点都是同一种结构体类型
#include <studio.h>
#include <math.h>//引用库函数
#define N 3
struct student
{
	long num;
	float score;
	struct student *next;
	//前两个成员num,score组成数据域,
	//后一个成员next构成指针域,它是一个指向stu类型结构体的指针变量
};
void main()
{
	struct student *head,*create(void)
	//建立链表函数 struct student* create(void)
	void plink(struct student *head);
	//输出链表函数 void plink(struct student *head);
	float averf(struct student *head);
	//求平均值函数 float averf(struct student *head);
	int i,len;
	float aver;
	head==NULL;
	head==create();//返回链表头指针
	plink(head);
	aver=averf(head);//返回平均值
	printf("Average=%5.2f",aver);//输出平均值
}
struct student* create()
{
	struct student *head,*p1,*p2;
	int i,len;
	len=sizeof(struct student);
	for(i=1;i<=N;i++)
	{
	p1 =(struct ,student*)malloc(len);
	printf ("Enter num, score:");
	scanf(&ld,%f", &p1->num, &p1->score)
	if(i==1)head=p2=pl;//头节点
	else { p2->next=p1; p2=p1;}//中间节点
	if(i==N) p2->next=NULL;//尾节点
	}
	return (head) ;//返回链表头指针
}
void plink (struct student, *head) //输出各节点
{
	struct student *p;inti;
	for(i=1; i<=N; i++)
	{
		if(i==1)
			p = head;
		else
			p = p->next;
			printf("%d: num=%ld, score=%5.2f\n", p->num, p->score);
	}
	return;
}
fLoet averf(struct student *head)
{
	struct student *p;
	float sum= 0;
	int c =0;
	P = head;//p指向首节点
	while(p !- NULL)
	{
		c++;//c用于统计节点数
		sum = sum+p->score;
		p=p->next;
	}
	return(sum/c);//返回平均值
}

花神博客生涯之C语言(结构体下篇)结束了哦~
接下来会持续更新(⊙o⊙)!
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值