函数指针、回调函数、动态内存分配、文件操作

函数指针

C语言里函数也有地址,函数名称可以用来表示函数的地址,函数指针可以用来记录函数的地址,函数指针也需要先声明然后才能使用,函数指针的声明可以根据函数声明变化得到,函数指针也分类型,不同类型的函数指针适合与不同类型的函数捆绑。

#if 0
/*
 *
 *函数指针
 *
 * */
#include<stdio.h>
int add (int num,int num1){
	return num + num1;
}
int main (){
	int (*p_func)(int,int) = NULL; 		//函数指针声明语句
	p_func = add;
	p_func(4,7);
	printf("num = %d\n",p_func(4,7));
	printf("add = %p",add);
	return 0;
}

#elif 0
/*
 *
 *四则运算
 *
 * */
#include<stdio.h>
int add (int num,int num1){
	return num + num1;
}
int sub (int num,int num1){
	return num - num1;
}
int mul (int num,int num1){
	return num * num1;
}
int div (int num,int num1){
	 return num / num1;
}
int main (){
	int num = 0,num1 = 0,result = 0;
	char opr = 0;
	char buf[10] = {0};
	int (*p_func)(int,int) = NULL;
	printf("输入考题");
	fgets(buf,10,stdin);
	num = buf[0] - '0';
	num1 = buf[2] - '0';
	opr = buf[1];
	switch (opr){
		case '+':
			//result = add(num,num1);
			p_func = add;
			break;
		case '-':
			//result = sub(num,num1);
			p_func = sub;
			break;
		case '*':
			//result = mul(num,num1);
			p_func = mul;
			break;
		case'/':
			//result = div(num,num1);
			p_func = div;
			break;
	}
	result = p_func(num,num1);
	printf("result = %d\n",result);
	return 0;
}
#endif

回调函数

可以通过函数指针调用它所捆绑的函数,函数指针可以作为形式参数使用,能作为实际参数使用的函数叫做回调函数

/*
 *
 *回调函数
 *
 * */
#include<stdio.h>


/*void print (int *p_num,int size){
	int num = 0;
	for (num = 0;num <= size - 1;num++){
		printf(" %d ",*(p_num + num));
	}
	printf("\n");
}*/


void print_cb (int *p_num){
	printf("%d ",*p_num);	
}
void neg_cb (int *p_num){
	*p_num = 0 - *p_num;
}
void for_each (int *p_num,int size,void (*p_func)(int *)){ 			
	int num = 0; 										//分离功能
	//void (*p_func)(int *) = print_cb;
	for (num = 0;num <= size - 1;num++){
		//print_cb(p_num + num);
		p_func(p_num + num);
	}
}
int main (){
	int arr[] = {1,2,3,4,5};
	//print(arr,5);
	for_each(arr,5,print_cb);
	printf("\n");
	for_each(arr,5,neg_cb);
	for_each(arr,5,print_cb);
	printf("\n");
	return 0;
}

动态内存分配

可以在程序运行的时候临时决定需要分配多少存储区,这种分配存储区的方式叫动态内存分配,为了管理动态分配内存,需要使用一组标准函数,为了使用这些标准函数需要包含stdlib.h头文件。

malloc

malloc可以动态分配一组连续的字节,这个函数需要一个整数类型的参数表示希望分配的字节个数,它的返回值就是分配好的第一个字节的地址,如果分配失败则返回值是NULL,函数把返回值记录在无类型指针的存储区里,需要强制类型转换成有类型指针然后再使用。

free

计算机不会主动回收动态分配内存,在使用完动态分配内存之后必须使用free函数把他们还给计算机,这叫做内存释放,只有动态分配内存才可以释放,一起分配的内存一起释放。这个函数需要第一个字节的地址作为参数,如果使用指针作为参数调用free函数则释放完内存后必须把指针恢复成空指针。

/*
 *动态内存分配
 *
 * */
#include<stdio.h>
#include<stdlib.h>
int main (){
	int *p_num = (int *)malloc(5 * sizeof(int));
	if (p_num){
		//使用动态分配内存
		free(p_num);
		p_num = NULL; 		//使用完后必须初始化为空指针
	}
	return 0;
}

调用函数可以使用被调用函数动态分配的存储区。

练习:编写函数从键盘得到一个水平长方形的位置,并把这个位置传递给调用函数,编写函数计算一个水平长方形的中心点位置并把这个位置传递给调用函数,这两个位置都记录在动态分配内存里

/*
 *动态内存分配练习
 *
 * */
#include<stdio.h>
#include<stdlib.h>
typedef struct {
	int row;
	int col;
} pt;
typedef struct {
	pt pt1;
	pt pt2;
} rect;
rect *read (void){
	rect *p_r = (rect *)malloc(sizeof(rect));
	if (p_r){
		printf("请输入水平长方形位置:");
		scanf("%d%d%d%d",&(p_r->pt1.row),&(p_r->pt1.col),&(p_r->pt2.row),&(p_r->pt2.col));
	}
	return p_r;
}
pt *midpt (const rect *p_r){
	pt *p_pt = (pt *)malloc(sizeof(pt));
	if (p_pt){
		p_pt->row = (p_r->pt1.row + p_r->pt2.row) / 2;
		p_pt->col = (p_r->pt1.col + p_r->pt2.col) / 2;
	}
	return p_pt;
}
int main (){
	pt *p_mid = NULL;
	rect *p_r = read();
	if (!p_r){
		return 0;
	}
	p_mid = midpt(p_r);
	if (!p_mid){
		free(p_r);
		p_r = NULL;
		return 0;
	}
	printf("中心点位置(%d,%d)\n",p_mid->row,p_mid->col);
	free(p_mid);
	p_mid = NULL;
	free(p_r);
	p_r = NULL;
	return 0;
}

calloc

calloc函数也可以动态分配存储区,这个函数可以把动态分配的所有存储区内容都设置成0,这个函数也需要包含stdlib.h头文件这个函数需要两个参数,第一个参数表示要分配的存储区个数,第二个参数表示单个存储区的大小,

这个函数的返回值也是分配好的第一个存储区的地址,这个函数也可能失败,如果失败则返回NULL。

realloc

realloc函数可以调整一段动态分配内存里的存储区个数,尽量少使用这个函数。

文件操作

所有的文件都采用二进制的方式记录数字,如果文件里所有二进制数据都来自字符则这种文件叫文本文件,文本文件以外的文件都叫做二进制文件,文本文件可以看作是特殊的二进制文件。

C语言里提供了两个操作文件的方法:

一种方法是只能操作文本文件,另外一种方法可以操作所有文件。第一种方式叫做文本方式,第二种方式叫二进制方式。

文件操作基本步骤

1.打开文件(fopen)

2.操作文件(fwrite/fread)

3.关闭文件(fclose)

/*
 *文件操作代码框架
 *
 * */
#include<stdio.h>
int main (){
	FILE *p_file = fopen("a.bin","w");
    
	/*if (p_file){	//第一种格式
		//使用文件
		fclose(p_file);
		p_file = NULL;
	}*/
    
	if (!p_file){	//第二种格式
		return 0;
	}
	//使用文件 
	fclose(p_file);
	p_file = NULL;
	return 0;
}

fopen

fopen函数需要两个参数

1.需要打开文件的路径

2.打开文件的方式(决定程序里可以对文件做什么操作)

打开方式有如下选择:

r” 只能查看文件内容不能修改,只能从文件头开始查看,如果文件不存在则打开会失败

r+” 比“r”多了修改功能

w” 只能修改文件内容不能查看,只能从文件头开始修改,如果文件不存在就创建文件

​ 如果文件已经存在就删除文件里的所有内容

w+” 比“w”多了查看功能

a” 只能修改文件内容不能查看,只能在文件末尾追加新内容,如果文件不存在就创建文件

​ 如果文件已经存在不会修改文件原有内容

a+” 比“a”多了查看功能

b” 也是一钟打开方式,这个打开方式可以和上面任何一种混合使用

​ 如果程序里采用二进制方式操作文件就应该在打开方式里包含“b”

fopen函数的返回值是一个地址,因该记录在文件指针里,程序里只能用文件指针表示一个已经打开的文件,fopen函数有可能失败,如果失败返回值是NULL。

fclose

一旦完成对文件的所有操作后必须使用fclose函数关闭文件,fclose函数需要文件指针作为参数,文件关闭后文件指针成为野指针,必须恢复成空指针。

posted @ 2024-11-04 05:33  GQH1000  阅读(18)  评论(0编辑  收藏  举报