学习C(十六)

动态内存分配

堆内存空间的特点:
  手动申请,手动释放。哪怕是程序结束了都不会释放,只有在计算机重启的时候才会被释放
  如何申请堆空间内存:系统提供了2个用来申请堆空间内存的函数
  ①  函数名:malloc
    函数参数:int size
    函数返回值:void* p
  函数功能:向系统申请 size个字节的堆空间内存,然后内部产生一个通用指针p指向了该内存空间的首地址,最后返回p
  通常情况下,size如何决定?根据申请的指针类型有关,例如申请了int类型的堆空间,size就是4。
我们可以使用 valgrind+./a.out来查看程序运行时候的详细的内存管理
②  函数名:calloc
  函数参数:int num,int size
  函数返回值:void
p
  函数功能:向系统申请 num个模块的堆内存空间,每个模块size个字节,然后内部产生一个通用指针void* 指向该段堆内存的首地址,最后返回p
  注意:calloc函数自动初始化申请的内存空间
  注意:通常情况下,人为规定,申请单个模块使用malloc,申请多个模块形成数组,使用calloc

  ③  函数名:realloc
    函数参数:void* ptr,int size
    函数返回值:void* ptr(这个函数必须放在等号的右侧)
    函数功能:将ptr指向的堆空间,重新分配size个大小的堆空间
  注意:reallc频繁的调用,会产生大量的内存碎片。因为realloc并不是直接调用malloc以及free实现,他是直接真的内存进行的操作。
  所以,尽量不要使用realloc,而是自己别写一个堆空间的扩容函数
  如何申请一个堆空间数组
  一直强调:应该将数组看成连续的内存空间
  所以堆空间的数组,只要申请连续的内存空间即可
  系统提供了一个专门初始化内存的函数
  函数名:memset
  函数参数:void* ptr,int value,int size
  返回值:void* ptr
  函数功能:将ptr指向的地址视作首地址,总共size个地址上的值全都设置成value

栈空间和堆空间的区别

#include<stdio.h>
#include<stdlib.h>

int* func(){
	int* p = malloc(sizeof(int));
/*
	注意:这里p依旧是一个栈空间指针变量,只不过该指针指向了堆空间
		所以:p的地址属于栈空间地址,p的值属于堆空间地址
		所以第5行代码产生了2段内存空间,等号左边自动产生了8个字节栈空间内存,等号右边手动申请了4个字节的堆空间内存
*/
	*p = 5;//堆空间的第一个元素被赋值5
	return p;//返回的是堆空间的首地址
	//程序结束,栈空间被释放,而堆空间还在
}

int main(){
	//申请一个int类型的指针,指向一个堆空间
	int* pa = func();
	printf("%d\n",*pa);
	free(pa);
	return 0;
}

堆空间的赋值

#include<stdio.h>
#include<stdlib.h>

int main(){
	int* arr = malloc(sizeof(int)*5);//因为函数malloc返回的是一个通用指针,所以要声明定义一个指针来接收
	arr[0] = 1;
	arr[1] = 2;
	*(arr+2) = 3;
	*(arr+3) = 4;
	arr[4] = 5;
	int i = 0;
	for(i=0;i<5;i++){
		printf("%d ",arr[i]);
	}
	printf("\n");
	return 0;
}

堆空间的初始化

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(){
	//int* arr = malloc(sizeof(int)*5);
	/*
		malloc申请的内存空间,都没有进行过初始化,此时如果对未初始化的指针进行访问,则会有内存错误
	*/
	/*
		系统提供了一个专门初始化内存的函数
		函数名:memset
		函数参数:void* ptr,int value,int size
		返回值:void* ptr
		函数功能:将ptr指向的地址视作首地址,总共size个地址上的值全都设置成value
	*/
	//memset(arr,0,sizeof(int)*5);
	int* arr = calloc(5,sizeof(int));
	printf("%d\n",*arr);
	printf("%d\n",arr[1]);
	printf("%d\n",arr[2]);
	free(arr);
	return 0;
}

扩容

例如:有一个结构体数组,容量为5,当他添加了5个结构体后,数组就被填满,此时,如果需要再次添加数据,则需要对该数组进行扩容。
  但是数组本身的容量无法扩大,所以所谓的扩容就是指申请一个更大容量的数组,然后将原来数组中所有的内容拷贝给新的数组,最后再将指向原来数组的指针全都指向新的数组
  注意,无论如何扩容,如果更大的数组是在栈空间上申请的,一旦函数结束,那么该栈空间就会被释放,这个指向数组的指针就成为野指针。
  原因就在于:栈空间都是自动申请自动释放。
  所以,动态内存分配必须使用到堆内存空间

动态扩容堆空间

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

/*
函数名:expend
函数参数:int* arr-堆空间的首地址,int vol-数组长度
函数返回值:int* arr-堆空间首地址
函数功能:新开辟一个大小为旧堆空间两倍的堆空间,并且把旧堆空间数据复制到新堆空间释放旧堆空间。返回新堆空间的首地址
*/
int* expend(int* arr,int vol){
	int* newArr = malloc(vol*4);//newArr指针指向新的堆空间
	memset(newArr,0,sizeof(int)*vol);//newArr指向的堆空间总共sizeof(int)*vol个字节都置0
	memcpy(newArr,arr,sizeof(int)*vol/2);//因为数组长度vol已经扩大为2倍,除以2才能正确且省时的将旧的堆空间数组复制到新的堆空间空间
	free(arr);//释放旧的堆空间数组,注意:释放的是arr指向的堆空间数组,指针arr并没有消失
	arr = newArr;//把指针arr指向新的堆空间
	return arr;
}

/*
函数名:insert
函数参数:int** p-指向堆空间的指针的地址,int* len,int* vol-数组长度
函数返回值:void
函数功能:向堆空间添加数据
*/
void insert(int** p,int* len,int* vol){
	int i = *len+1;
	if(*len >= *vol){
		printf("数组扩容中\n");
		*vol *= 2;//数组长度翻倍
		*p = expend(*p,*vol);
	}
	(*p)[(*len)] = i;
	(*len)++;
}

int main(){
	int* arr = malloc(20);//用malloc定义一维数组。注意:指向malloc的指针arr不要随便移动
    //int _arr[20];//数组名arr相当于 int* const arr,指针常量,arr的指向被固定。
    int** p = &arr;//指向一维数组的指针
    //int* _p = _arr;//指向一维数组的指针
	int len = 0;//数组下标
	int vol = 5;//数组长度
	int i = 0;
	while(len<20){
		insert(p,&len,&vol);
		for(i=0;i<len;i++){
			printf("%d ",arr[i]);//因为arr为int类型的,偏移一下就移动4个字节,正好可以正确的读取整形数据
		}
		printf("\n");
	}
	return 0;
}

申请堆空间实现冒泡排序

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int IntCmp(const void *pv1,const void *pv2);
int FloatCmp(const void *pv1,const void *pv2);
int BubbleSort(void *pbase,int nmemb,int size,int (*pfCmp)(const void *,const void *));
int main()
{
	int arr1[5] = {13,19,16,27,8};
	float arr2[5] = {1.3,1.9,1.6,2.7,8.0};
	int i = 0;

	//qsort(arr1,5,sizeof(int),IntCmp);
	BubbleSort(arr1,5,sizeof(int),IntCmp);

	for(i = 0;i < 5;i++)
	{
		printf("%d ",arr1[i]);
	}
	printf("\n");
	
	//qsort(arr2,5,sizeof(float),FloatCmp);
	BubbleSort(arr2,5,sizeof(float),FloatCmp);
	for(i = 0;i < 5;i++)
	{
		printf("%.1f ",arr2[i]);
	}
	printf("\n");
	return 0;
}

int FloatCmp(const void *pv1,const void *pv2)
{
	float f1 = *(float *)pv1;
	float f2 = *(float *)pv2;
	
	if(f1 == f2)
	{
		return 0;
	}
	else if(f1 > f2)
	{
		return 1;
	}
	else
	{
		return -1;
	}
}
int IntCmp(const void *pv1,const void *pv2)
{
	return *(int *)pv1 - *(int *)pv2;
}

int BubbleSort(void *pbase,int nmemb,int size,int (*pfCmp)(const void *,const void *))
{
	void * pt = NULL;
	char *pc = (char *)pbase;
	int i = 0;
	int j = 0;

	if(NULL == pbase || nmemb <= 0 || size <= 0 || NULL == pfCmp)
	{
		printf("Input param is invalid\n");
		return -1;
	}
	
	pt = malloc(size);
	if(NULL == pt)
	{
		perror("malloc failed");
		return -1;
	}
	memset(pt,0,size);

	for(i = 0;i < nmemb;i++)
	{
		for(j = 0;j < nmemb - i - 1;j++)
		{
			if(pfCmp(pc+j*size,pc+(j+1)*size) > 0)
			{//交换
				memcpy(pt,pc+j*size,size);
				memcpy(pc+j*size,pc+(j+1)*size,size);
				memcpy(pc+(j+1)*size,pt,size);
			}
		}
	}	

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值