傳值和傳址,指針函數,函數指針,函數指針作爲參數,函數指針作爲返回值。

有時候,我們定義的函數需要接收用戶傳入的數據,那麼就需要使用到函數的參數。

函數參數的數量可以有多個,

返回值一般表示函數計算後的數值,也可以表示執行結果。

主函數傳入的參數稱爲實參,傳遞到函數體的參數叫做形參。

傳遞到形參後就可以直接在函數體當中使用已經定義過的形參的數據。

函數的設計應該遵從一個函數實現一個功能的原則,

傳入的參數讓函數實現更爲豐富的功能。

類型名就是指定函數的返回值,一個函數實現一個功能通常是要反饋結果的,若函數確實不需要返回值,可以使用void來定義。

函數定義時寫的參數就是形參,形式參數,定義時僅僅是一個佔位符,沒有實際數值,在主函數執行函數時傳遞的具體的數值就是實際參數。

實參和形參的功能就是用於數據傳輸。當函數發生調用的時候,實參的值會傳遞給形參。並且傳遞是具有單向性的。當函數執行黨的時候形參纔會生成具體的空間,纔會分配內存,當函數結束之後就會立即釋放內存。所以形參的變量只有在函數的內部有效,出了函數他什麼也不是。

傳值和傳址

指針也是一個變量,所以可以通過參數(將指針的存值)傳遞給函數。引進指針的參數有什麼意義呢?

因爲作用域的存在,不同的函數是無法直接去訪問對方的變量的,所以形參和實參重名也並不衝突。

當通過傳遞變量實現對一個變量的更改並打印的話,在函數中更改的僅僅是形參,對在main函數的數據沒有影響。

而函數中指針的使用呢就是拿到實參變量的地址,直接對所在的地址的數據進行修改。

這是傳值和傳址的根本區別。

傳遞數組

在主函數中定義的數組,進行實參與形參的傳值時,函數調用處的實參直接書寫數組名就可以了。實際上不是傳輸的整個數組的每個值,並不存在將整個數組作爲參數傳遞的形式,而是將數組的第一個元素的地址進行了傳遞,所以,在函數中對數組元素進行的修改會將主函數中定義的數組的元素一併修改。

指針函數與函數指針

指針函數:

函數的類型實際指的就是函數的返回值。可以返回爲整形、浮點型、等、當然也可以返回指針類型的數據,定義時只需要在函數名前面加一個*就可以了。這就稱爲指針函數。

指針函數產生並返回的是一個帶指針的,比如返回一個字符串,那就是用char類型進行定義的。

譬如我們使用%s進行輸出時需要一個字符串,我們使用函數來進行提供字符串,但是對於字符串沒有可以定義字符串的類型,我們是用char類型的指針來定義的字符串,字符串的約定俗成就是給指針讀到空字符就可以結束,所以我們知道第一個字符地址就可以知道整個字符串,所以我們定義函數返回值類型時候,對於返回字符串類型都是定義的char類型。

用指針變量做返回值我們就稱之爲指針函數。

注意:不要返回局部變量(在函數中定義的變量)的指針!!

函數指針:

是一個指針,指向函數的指針。與數組一樣,定義時我們使用小括號將函數的名字與前面的*號括起來。

函數表示法就是我們平時用的函數,像printf函數,這個函數的名字經過求值之後,就會變成函數的地址。所以呢,在定義了函數指針後,再給他傳遞一個已經被定義的函數名就可以通過該指針進行調用函數了。

比如我們定義一個函數指針,希望通過函數指針來對已經定義的函數進行調用,那麼就是使指針指向函數,此指針的返回值爲函數的返回值,參數爲函數的參數。

譬如定義函數指針fp同過fp調用函數int squre(int ):int (*fp)(int ); 直接讓fp = squre ;就可以。

爲什麼呢?因爲我們定義的指針就規定了此指針只能指向一個參數爲int 返回值爲int的函數。所指函數各方面就是與squre符合,所以就可以直接相等。這個squre經過運算之後就是函數的地址,這個地址被指針存儲了。

函數指針作爲參數:

函數指針也可以被作爲參數進行傳遞。這就實現了在一個函數裏調用另一個函數。

當我們定義了一個函數,函數的參數的類型可以爲指針類型,如果我們爲此指針添加了參數類型與返回值類型,那麼這個指針我們要求它必須指向符合給他定義的參數與返回值的函數,這就是一個函數指針,它必須指向函數。指針的本質就是存儲一個所指的地址,所以傳參時傳遞的就是一個地址,函數的本質也是一個地址,所以傳參時我們就可以將函數名作爲實參傳遞出去,這個指針收到地址後存儲,就是指向了地址對應的函數,那麼我們就成功在一個函數裏通過指針調用了另外一個函數。既然調用的函數是有參數與返回值的,所以我們定義的函數的返回值必須與所調函數一致,還需增加參數值,個數與類型都要與所掉函數的參數一致。

舉個例子,我們定義一個calc函數,希望通過這個函數實現對其他函數如sub,add函數自由的調用。

首先我們定義add函數與sub函數:int add(int ,int );      int sub(int ,int );

這兩個函數的返回值與參數類型都是int,參數都具有兩個。

接下來就是定義calc函數並實現對sub或add的調用了:

剛剛說過,要實現對其他函數的調用,需要定義一個函數指針,來接收不同的函數的地址,接收誰的地址,就調用誰。然後執行函數的返回值類型與參數個數,類型還必須與所調函數的返回值與參數完全一樣。

所以我們這麼定義calc:int calc(int (*fp)(int ,int ),int ,int );

calc函數有三個參數,第一個參數爲函數指針類型的參數,它可以調用其他的函數,第二個、三個爲整形參數,爲調用成功後函數的參數,所調函數進行運算完畢後,必然返回一個值,所以calc的返回值必須與所調函數的返回值一樣,接受了所調函數的返回值再返出calc函數。

當我們在主函數裏調用calc時就是這樣:calc(add,5,8); 實參分別爲add函數的地址,參數5,和參數8。

反正就是函數指針他必須接收函數!!,接收了就能實現指針調用函數。可以是函數指針與函數直接相等,也可以通過傳參進行傳遞。當然成功調用的前提是函數指針的定義(即返回值,參數)都要正確,都要符合所指函數的類型。

當函數指針作爲參數時,就表示它要開始調用函數了!

將函數指針作爲返回值:

什麼意思呢?

比如這個函數的名字叫select,它本身有兩個參數,返回的返回值是一個函數指針,這個函數指針也具有兩個參數,並且其返回值爲整型。那麼這個返回函數指針的select函數就是一個返回函數指針(地址)的函數,既然函數指針(地址)作爲返回值,那麼我們需要再定義一個與返回函數同類型的函數指針來接收它。

這個函數怎麼寫?

int (*select(int ,int ))(int ,int );

這個式子從第一次出現的變量select看起,向右讀,與參數結合形成帶兩個整形參數的函數,碰到括號想向左看,只有一個*,那麼*就是select函數的返回值,select返回一個地址。這個地址可以是一個函數的地址,而最希望得到函數地址的是函數指針,所以我們就定一個函數指針來接收select函數返回的地址,完成函數指針對select返回的函數的調用。

如果我們想把此函數指針放到其他函數裏進行調用,即用其他函數調用這個函數指針作爲返回值形成的函數指針,我們最好新建一個函數指針來接收此函數指針 ,int (*fp)(int ,int ) = int (*select(int,int ))(int ,int)  這樣調用時就不需要使用函數名select而是使用函數指針fp,這樣調用就直觀多了。

說不清楚也容易說錯,下面是使用函數指針作爲返回值完成的計算器代碼:

#include <stdio.h>

int add(int ,int);
int sub(int ,int); 
int (*select(char))(int ,int );  // 定義一個函數指針,指向函數返回的地址 
 
int add(int num1 ,int num2)
{
	return num1 + num2; 
}  
int sub(int num1 ,int num2)
{
	return num1 - num2;
}
//calc方法  調用函數的函數 
int calc(int (*fp)(int ,int),int num1,int num2) 
{
	return (*fp)(num1,num2);
}
//select方法  select的返回值是地址
int (*select(char op))(int ,int)  									 
{
	switch(op)
	{
		case '+' : return add;
		case '-' : return sub;
	}
}

int main()
{
	int num1 ,num2;
	char op;
        int (*fp)(int ,int);  //定義一個函數指針fp來接受select函數返回的地址形成的函數指針

	printf("請輸入一個式子:");
	scanf("%d%c%d",&num1,&op,&num2);

	fp = select(op);
	//根據op決定調用的函數
	printf("%d %c %d = %d",num1,op,num2,calc(fp,num1,num2)); 
	
	return 0;
}

main函數中,在calc調用那裏也可以不用fp接收,直接這麼寫:

int main()
{
	int num1 ,num2;
	char op;
	printf("請輸入一個式子:");
	scanf("%d%c%d",&num1,&op,&num2);
	int (*fp)(int ,int ) = int (*select(char op))(int ,int ); 
	
	//根據op決定調用的函數
	printf("%d %c %d = %d",num1,op,num2,calc(select(op),num1,num2)); 
	
	return 0;
}

簡而言之,以上實現輸入字符就能調用對應方法的原理就是:

函數select根據輸入的字符返回對應方法的函數地址,僅僅返回函數地址不可取,我們需要定義一個函數指針來接收這個地址,由此形成可以指向方法函數的函數指針。而這個函數指針又可以在別的函數中作爲參數被調用,執行調用的函數需要定義相同的參數與返回值供此函數指針使用。

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章