函式指標、回撥函式、動態記憶體分配、檔案操作

GQH1000發表於2024-11-04

函式指標

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函式需要檔案指標作為引數,檔案關閉後檔案指標成為野指標,必須恢復成空指標。

相關文章