09_动态内存分配

知识点1【函数指针 作为 函数的形参】
案例1:

#include<stdio.h>

int my_add(int a,int b)

{

	return a+b;

}

int my_sub(int a,int b)

{

	return a-b;

}

int my_mul(int a,int b)

{

	return a*b;

}


//定义一个函数  实现上述函数的功能


int my_calc(int a,int b, int (*fun_pointer)(int,int) )

{

	return fun_pointer(a,b);

}

void test01()

{

	printf("%d\n",my_calc(10,20,my_add));

	printf("%d\n",my_calc(10,20,my_sub));

	printf("%d\n",my_calc(10,20,my_mul));


}

int main(int argc,char *argv[])

{

	test01();

	return 0;

}

运行结果:
在这里插入图片描述知识点2【malloc函数 和 free函数】
malloc函数

#include<stdlib.h>

void *malloc(unsigned int num_size);

形参:num_size需要申请空间大小的字节数。

返回值:

    成功:返回空间的起始地址

    失败:NULL

特点:

    1、对于malloc的返回值 一般要强制类型转换

    2、malloc申请的空间 内容不确定  一般使用memset进行清空

   3、多次调用malloc 第1次malloc 和 第2次malloc的地址不一定连续     

free函数

void free(void *addr);

释放堆区空间

案例1:从堆区申请 一个int空间

void test02()

{

	int *addr = NULL;


	addr = (int *)malloc(sizeof(int));

	if(addr == NULL)//申请失败了

	{

		printf("malloc err\n");

		return;

	}


	printf("*addr=%d\n", *addr);//不确定的值

	

	//对堆区空间 清0

	memset(addr, 0, sizeof(int));

	printf("*addr=%d\n", *addr);//0


	//对addr的空间 就行写 或 读

	*addr = 1000;//写


	printf("*addr=%d\n", *addr);//1000 读



	//释放堆区空间 空间使用权限的回收 是否对空间内容清0 这是不确定的

	free(addr);

}

int main(int argc,char *argv[])

{

	test02();

	return 0;

}

运行结果:在这里插入图片描述案例2:从堆区申请一个数组 数组的大小 由用户决定

1、从键盘获取 用户要申请的数组大小

2、根据大小 从堆区申请空间

3、对空间的读写操作

4、释放该空间

void test03()

{

	int n = 0;

	int i=0;

	int *arr=NULL;

	

	//1、获取用户大小

	printf("请输入元素的个数:");

	scanf("%d", &n);

	

	//2、根据大小从堆区申请空间

	arr = (int *)malloc(n*sizeof(int));

	if(NULL == arr)

	{

		//perror 错误输出

		perror("mallac");

		return;

	}


	//对arr的读写操作

	printf("请输入%d个int数据\n",n);

	for(i=0;i<n; i++)

	{

		scanf("%d", arr+i);

	}


	//遍历数组

	for(i=0;i<n;i++)

	{

		printf("%d ", arr[i]);

	}

	//释放空间

	free(arr);

}

int main(int argc,char *argv[])

{

	test03();

	return 0;

}

案例3:从堆区申请一个数组 数组的大小 由用户决定(函数版本)

int get_n(void)

{

	int n = 0;

	printf("请输入元素的个数:");

	scanf("%d", &n);

	return n;

}

int* get_addr(int n)

{

	return (int *)malloc(n*sizeof(int));

}

void my_input_array(int *arr, int n)

{

	int i=0;

	//记得将arr指向的空间清0

	memset(arr,0,n*sizeof(int));


	//获取键盘输入

	printf("请输入%d个int数据\n",n);


	for(i=0;i<n; i++)

	{

		scanf("%d", arr+i);

	}

}

void my_print_array(int *arr, int n)

{

	int i=0;

	for(i=0;i<n;i++)

	{

		printf("%d ", arr[i]);

	}

	printf("\n");

}

void test04()

{

	int *arr=NULL;

	int n = 0;


	//得到用户输入的元素个数

	//1、获取用户大小

	n = get_n();

	

	//定义函数 给arr申请堆区空间

	arr = get_addr(n);

	if(arr == NULL)

	{

		perror("get_addr");

		return;

	}


	//对空间读写操作

	my_input_array(arr, n);



	//对空间数组遍历

	my_print_array(arr, n);


	//释放空间

	free(arr);


}

int main(int argc,char *argv[])

{

	test04();

	return 0;

}

运行结果:
在这里插入图片描述知识点3【calloc函数】

#include<stdlib.h>

void * calloc(size_t  nmemb,size_t  size);

参数:

    1、nmemb 申请的数据块数

    2、size  每一块大小

    3、所以申请总大小=nmemb * size  

返回值:

    成功:返回空间的起始地址

    失败:返回NULL

特点:申请的空间自动清零    

案例:

void test04()

{

	int n = 0;

	int i=0;

	int *arr=NULL;

	

	//1、获取用户大小

	printf("请输入元素的个数:");

	scanf("%d", &n);

	

	//2、根据大小从堆区申请空间

#if 0

	arr = (int *)malloc(n*sizeof(int));

	if(NULL == arr)

	{

		//perror 错误输出

		perror("mallac");

		return;

	}

	memset(arr,0,n*sizeof(int));//清零

#endif


#if 1

	arr=(int *)calloc(n, sizeof(int));//自动清零  不需要使用memset

	if(NULL == arr)

	{

		//perror 错误输出

		perror("calloc");

		return;

	}

#endif

	//对arr的读写操作

	printf("请输入%d个int数据\n",n);

	for(i=0;i<n; i++)

	{

		scanf("%d", arr+i);

	}


	//遍历数组

	for(i=0;i<n;i++)

	{

		printf("%d ", arr[i]);

	}


	//释放空间

	free(arr);


}

知识点4【realloc动态追加或减少空间】

#include<stdlib.h>

void* realloc(void *s, unsigned int newsize);

功能:
在原先s指向的内存基础上重新申请内存,新的内存的大小为 new_size 个 字节,如果原先内存后面有足够大的空间,就追加,如果后边的内存不 够用,则realloc函数会在堆区找一个newsize个字节大小的内存申请,将原先内存中的内容拷贝过来,然后释放原先的内存,最后返回新内存的地址。
参数:s:原先开辟内存的首地址 newsize:新申请的空间的大小
返回值:新申请的内存的首地址
注意:一定要保存 realloc的返回值

void test06()

{

	int *arr = NULL;

	int n = 0;

	int i=0;

	int n_new = 0;


	//1、获取用户大小

	printf("请输入元素的个数:");

	scanf("%d", &n);

	

	arr=(int *)calloc(n, sizeof(int));//自动清零  不需要使用memset

	if(NULL == arr)

	{

		//perror 错误输出

		perror("calloc");

		return;

	}


	printf("请输入%d个int数据\n",n);

	for(i=0;i<n; i++)

	{

		scanf("%d", arr+i);

	}


	//遍历数组

	for(i=0;i<n;i++)

	{

		printf("%d ", arr[i]);

	}


	//再追加5个元素

	printf("请输入新增的元素个数:");

	scanf("%d", &n_new);

	arr = (int *)realloc(arr, (n+n_new)*sizeof(int));


	printf("请输入新增的%d个int数据\n",n_new);

	for(i=n;i<(n+n_new); i++)

	{

		scanf("%d",arr+i);

	}


	for(i=0;i<(n+n_new);i++)

	{

		printf("%d ", arr[i]);

	}

	printf("\n");


	free(arr);

}

int main(int argc,char *argv[])

{

	test06();

	return 0;

}

运行结果:在这里插入图片描述知识点5【堆区空间使用的注意事项】

void test08()

{

	int *p2 = NULL;

	int *p3 = NULL;

	//1、指向堆区空间的指针变量 不要随意的更改指向

	int *p=(int *)calloc(1,sizeof(int));

	int num = 10;

	p = #//p指向num  导致calloc申请的空间泄露


	//2、不要操作已经释放的空间

	p2 = (int *)calloc(1,sizeof(int));

	*p2 = 1000;

	//释放该空间

	free(p2);

	printf("*p2 = %d\n", *p2);//不确定


	//3、不要对堆区空间重复释放

	p3 = (int *)calloc(1,sizeof(int));

	free(p3);

	free(p3);//多次释放

}

知识点6【防止多次释放】

void test09()

{

	int *p = (int *)calloc(1,sizeof(int));


	if(p != NULL)//防止多次释放

	{

		free(p);

		p=NULL;

	}

	


	if(p != NULL)

	{

		free(p);

		p=NULL;

	}

}

知识点7【字符串处理函数】
只要是以str开头的函数 都是遇到’\0’结束
#include<string.h>
1、strlen测量字符换长度
size_t strlen(const char *s)

//s:被测量的字符串首元素地址

//返回值为 字符串的长度 不包含’\0’

//const char *s strlen函数 不会通过s修改s指向的空间内容
案例:

void test01()

{

	char buf1[128]="hehehe";

	char buf2[]="hehehe";

	char buf3[]="hehe\0he";


	// \123 代表一个字符 \hhh 八进制转义h: 0~7

	//\\表示'\'

	char buf4[]="hehe\123\\he";


	//\\x2f表示一个字符  \xdd 十六进制转义 d:0~9 a~f

	char buf5[]="hehe\x2fhe";



	printf("%d\n",sizeof(buf1));//126

	printf("%d\n",strlen(buf1));//6


	printf("%d\n",sizeof(buf2));//7

	printf("%d\n",strlen(buf2));//6


	printf("%d\n",sizeof(buf3));//8

	printf("%d\n",strlen(buf3));//4


	printf("%d\n",sizeof(buf4));//9

	printf("%d\n",strlen(buf4));//8

	printf("%s\n",buf4);

	

	printf("%d\n",sizeof(buf5));//8

	printf("%d\n",strlen(buf5));//7


	char buf6[]="\0hehe\0hehe";

	printf("%d\n",strlen(buf6));//0

	return;	

}

作业:自定义一个my_strlen函数测量字符串长度(不能在函数里面使用strlen)

int my_strlen(const char *s);

2、strcpy strncpy字符串拷贝(重要)
strcpy
原型:char *strcpy( char *dest, const char *src )
功能:把src所指向的字符串复制到dest所指向的空间中
返回值:返回dest字符串的首地址
注意:遇到’\0’会结束,只是’\0’也会拷贝过去

void test02()

{

	char src[]="hello string";

	//保证dst足够大

	char dst[128]="";


	strcpy(dst,src);


	printf("dst=%s\n",dst);//"hello string"

}
void test02()

{

	char src[]="hello\0string";

	//保证dst足够大

	char dst[128]="";



	strcpy(dst,src);


	printf("dst=%s\n",dst);//"hello"

}

实现strcpy

char *my_strcpy(char *dst,const char *src)

{

#if 0

	do

	{

		*dst = *src;

		dst++;

		src++;

	}while(*src != '\0');

	*dst='\0';

#endif

	while(*dst++ = *src++);

}

void test02()

{

	char src[]="hello\0string";

	//保证dst足够大

	char dst[128]="";

	my_strcpy(dst,src);


	printf("dst=%s\n",dst);//"hello"

}

strncpy
原型:char *strncpy( char *dest, const char *src,int num)
功能:把src指向字符串的前num个复制到dest所指向的空间中
返回值:返回dest字符串的首地址
注意:’\0’不拷贝

void test03()

{

	char src[]="hello string";

	//保证dst足够大

	char dst[128]="";


	strncpy(dst,src,3);

	printf("dst=%s\n",dst);//"hel"

}
void test03()

{

	char src[]="hello";

	//保证dst足够大

	char dst[128]="";


	strncpy(dst,src,30);

	printf("dst=%s\n",dst);//"hello"

}

3、strcat字符串的拼接

char *strcat(char *dest, const char *src);

将src的字符串 拼接到 dst的末尾(dst第一个’\0’的位置)

void test04()

{

	char src[]="world";

	char dst[128]="hello";


	strcat(dst,src);


	printf("%s\n",dst);//"helloworld"

}
void test04()

{

	char src[]="wor\0ld";

	char dst[128]="hello";


	strcat(dst,src);


	printf("%s\n",dst);//"hellowor"

}

strncat拼接前n个

void test04()

{

	char src[]="world";

	char dst[128]="hello";


	strncat(dst,src,2);


	printf("%s\n",dst);//"hellowo"

}

作业:自定义my_strcat函数 不许使用 strcat

char *my_strcat(char *dest, const char *src);

4、strcmp字符串的比较 (整个字符串的比较)
int strcmp(const char *s1, const char *s2);

功能:将s1和s2指向的字符串 逐个字符比较

返回值:

>0: 表示s1 >  s2

<0:  表示s1 <  s2

==0: 表示s1 ==  s2
void test05()

{

	char s1[]="hehe haha";

	char s2[]="hehe xixi";

	char s3[]="hehe haha";


	if(strcmp(s1,s2) >0 )

	{

		printf("s1 > s2\n");

	}

	else if(strcmp(s1,s2) <0 )

	{

		printf("s1 < s2\n");

	}

	else if(strcmp(s1,s2) == 0)

	{

		printf("s1 == s2\n");

	}


	if(strcmp(s1,s3) >0 )

	{

		printf("s1 > s3\n");

	}

	else if(strcmp(s1,s3) <0 )

	{

		printf("s1 < s3\n");

	}

	else if(strcmp(s1,s3) == 0)

	{

		printf("s1 == s3\n");

	}

}

运行结果:在这里插入图片描述strncmp 字符串局部比较

void test05()

{

	char s1[]="hehe haha";

	char s2[]="hehe xixi";

	


	if(strncmp(s1,s2,4) >0 )

	{

		printf("s1 > s2\n");

	}

	else if(strncmp(s1,s2,4) <0 )

	{

		printf("s1 < s2\n");

	}

	else if(strncmp(s1,s2,4) == 0)

	{

		printf("s1 == s2\n");

	}

	

}

运行结果:在这里插入图片描述5、strchr字符查找函数

char *strchr(const char *s, int c);

//从字符串s中查找第一次c字符出现的地址

//没有找到 返回NULL

void test06()

{

	char str[]="www.1000phone.com";

	char *ret = NULL;


	ret = strchr(str,'o');

	if(ret != NULL)

	{

		*ret = '#';

	}

	printf("str=%s\n",str);//"www.1000ph#ne.com";

}

(了解)

void test06()

{

	char str[]="www.1000phone.com";

	char *ret = NULL;


	while((ret = strchr(str,'o')) && (*ret = '#') );

	

	printf("str=%s\n",str);//"www.1000ph#ne.c#m";

}

重要 啥都写 了解
6、strstr字符串查找

char *strstr(const char *s1, const char *s2);

//从s1中查找字符串s2 返回第一次s2出现的地址

//查找失败 返回NULL

void test07()

{

	char s1[]="www.sex.777.sex.com";

	char s2[]="sex";

	

	char *ret = NULL;

	ret = strstr(s1,s2);

	if(ret == NULL)

	{

		return;

	}

	printf("%s\n",ret);//"sex.777.sex.com"

}
void test07()

{

	char s1[]="www.sex.777.sex.com";

	char s2[]="sex";

	

	char *ret = NULL;


	while(1)

	{

		ret = strstr(s1,s2);

		if(ret == NULL)

		{

			break;

		}

		memset(ret,'#', 3);

	}

	

	printf("%s\n",s1);//"www.###.777.###.com"


}



发布了20 篇原创文章 · 获赞 0 · 访问量 28

猜你喜欢

转载自blog.csdn.net/weixin_45992045/article/details/104676459