函數指針與指針函數


一、函數指針

  先看一個例子:

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#define ERROR 0

#define OK 1

int add(int a,int b){

      return a+b;

  }
int multiply(int a,int b){   
   return a*b;   } int main()
{   int (*fun)(int,int);   fun=add;    printf("%d\n",fun(5,6)); //5+6=11   fun=multiply;    printf("%d\n",fun(5,6));//5*6=30
   return 0;   }

  函數指針的本質是一個指針,該指針變量的值是地址,指向某一個函數的地址,所以它是指向函數的指針。函數的定義是存在於代碼段,因此,每個函數在代碼段中,也有着自己的入口地址,函數指針就是指向代碼段中函數入口地址的指針。
  函數指針的聲明方法為:
    返回值類型   ( * 指針變量名) ([形參列表]);
  
注1:“返回值類型”說明函數的返回類型;“(指針變量名 )”中的括號不能,括號改變了運算符的優先級。若省略整體則成為
  一個函數說明,說明了一個返回的數據類型是指針的函數;后面的“形參列表”表示指針變量指向的函數所帶的參數列表。例如:
    int func(int x); /* 聲明一個函數 */
    int (*f) (int x);
/* 聲明一個函數指針 */

f=func; /* 將func函數的首地址賦給指針f ,  f(x)==func(x)等價,為什么還要用函數指針呢?比如+ - * /四個函數,主函數要對兩個數進行計算,但不確定那鍾運算,那代碼就等着不寫嗎,不,可以先寫,用函數指針f寫好代碼,將來想用+,則將其賦值給f(類似數組傳參數傳的是地址,函數指針就是用來接收某一函數的地址,間接調用該函數,函數名太固定了,不靈活,函數指針相當於一個變量,其它函數可以賦值給它*/回想高中知識f(x)表示一函數,具體是什么函數就看你怎么賦值,程序中,我可以先用f(x)參與代碼編寫,將來需要f(x)實現什么功能,就賦值給它相應的功能代碼即可。

  或者使用下面的方法將函數地址賦給函數指針:

    f = &func; //一般用上面的,如同數組不用取址了

  賦值時函數func不帶括號,也不帶參數由於func代表函數的首地址,因此經過賦值以后,指針f就指向函數func(x)的代碼的首地址。

  注2:函數括號中的形參可有可無,視情況而定。

  如果覺得函數名直接調和函數指針調用函數沒什么卻別,我們來看有一段二叉樹中序遍歷非遞歸的代碼來理解。 

Status  visit(TElemType e)

  {

    printf("%d ", e);

    return OK;

  }

Status InOrderTraverse( BiTree T, Status (*visit)(TElemType e ))
{

  InitStack( S ); p = T;
  while( p || !StackEmpty( S ))
  {

    if( p )// 根指針進棧,遍歷左子樹
    {

      Push( S, p );
      p = p>pLChild;

    }
    else // 根指針出棧,訪問根結點,遍歷右子樹
    {

      Pop( S, p );

      if( !(*Visit)( p->data))

        return ERROR;

      p = p->pRChild; // 右子樹

    }

  }

  return OK;

}

其中作為形參的status (*visit)(TElemType e )這個函數指針 代表了訪問節點的方法 這里使用函數指針的主要原因就是此時只是訪問結點,但是不知道是將來是打印或者是別的什么操作,而我們命名函數時的習慣是將名字起為函數的作用,但是這里不知道具體的訪問的操作,因此寫visit函數指針更加合理。所以,可以使代碼的封裝性和邏輯性更加合理

還有一個作用就是可以將實現同一功能的很多個模塊統一起來標識,這樣一來更容易后期的維護,系統結構更加清晰。或者歸納為:便於分層設計、利於系統抽象、降低耦合度以及使接口與實現分開。 

可以看下面對冒泡排序的封裝,讓整個代碼的層次和模塊化有了質的提升

#include<iostream>
using namespace std;
void sort(int arr[], int size, bool(*cmp)(int,int));
bool up(int a, int b);
bool down(int a, int b);
int main()
{
  int arr[10];
  for (int i = 0;i < 10;++i)
  cin >> arr[i];

  sort(arr, 10,down);

  for (int i = 0;i < 10;++i)
  cout << arr[i] << " ";
  return 0;
}
void sort(int arr[],int size,bool(*cmp)(int,int))//簡單冒泡排序
{
  int temp;
  for (int i = 0;i < size-1; ++i)
  {
    for (int j = i;j < size-1;++j)
    {
      if (cmp(arr[i], arr[j + 1]))//cmp是一個函數指針,變量,功能是比較,是從小到大排序還是從大到小排序不知道, 主函數把up復制給它,它就是up;把down賦值給它,它就down;
       {
        temp = arr[i];
        arr[i] = arr[j + 1];
        arr[j + 1] = temp;
       }
    }
  }
}
bool up(int a, int b)
{
  if (a > b)
    return 1;
  else
    return 0;
}
bool down(int a, int b)
{
  if (a > b)
    return 0;
  else
    return 1;
}

 

二、指針函數

  指針函數是一個函數,返回某種類型指針的地址,使其指向某個地址單元。
  返回指針的函數,一般定義格式為:
    類型標識符 *函數名(參數表)
    int *f(x,y);
  其中x,y是形式參數,f是函數名,調用后返回一個指向整型數據的地址指針。f(x,y)是函數,其值是指針。
  如:char *ch();表示的就是一個返回字符型指針的函數,請看下面的例題:
  例:將字符串1(str1)復制到字符串2(str2),並輸出字符串2.
  #include "stdio.h"
  void main()
  {
    char *ch(char *,char *); //子函數在main下,所以先聲明后使用
    char str1[]="I am glad to meet you!";
    char str2[]="Welcom to study C!";
    printf("%s",ch(str1,str2));
  }
  char *ch(char *str1,char *str2)
  {
    int i;
    char *p;
    p=str2
    if(*str2==NULL) exit(-1);
    do
      {
        *str2=*str1;
        str1++;
        str2++;
      }while(*str1!=NULL);
    return(p);
  }
通過分析可得:
  函數指針是一個指向函數的指針,或者說是一個指針變量可以用來指向一個函數;而指針函數是一個返回值為指針的函數只不過返回值類型為指針型


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM