天天看點

【C語言】指針進階第五站,函數指針

文章目錄

  • ​​1函數指針​​
  • ​​1.1基本形式​​
  • ​​1.2用函數指針來調用函數​​
  • ​​1.3兩個奇葩的代碼​​
  • ​​奇葩代碼1​​
  • ​​奇葩代碼2​​
  • ​​2函數指針數組​​
  • ​​2.1基本形式​​
  • ​​2.2電腦實作​​
  • ​​2.2.1switch語句​​
  • ​​2.2.2函數指針形式​​
  • ​​2.3指向函數指針數組的指針​​
  • ​​3.相關練習題​​
  • ​​結語​​

指針進階第五站????:函數指針

點進我的首頁,可以回顧前四站的内容哦~

1函數指針

​函數也有自己的位址​,函數名/&函數名 就是函數的位址

【C語言】指針進階第五站,函數指針

1.1基本形式

在​​數組指針​​的學習中我們了解到

int arr[5];
int (*pa)[5] = &arr;//pa是數組指針      
  • 指針變量pa的類型是​

    ​int(*)[5]​

那麼函數指針的形式是怎樣的呢?

void test(char* str)
{}

int main()
{
    void (*pt)(char*) = test;
    //pt是一個函數指針
    return 0;
}      
  • pt的類型是​

    ​void (*)(char*)​

下面哪個代碼有能力存放函數的位址呢?

void (*pfun1)();
void *pfun2();      

答:pfun1可以存放

  • pfun1先和*結合,說明pfun1是指針,指針指向的是一個函數,指向的函數無 參數,傳回值類型為void
  • pfun2先和()結合,判斷為一個傳回值為int*類型的函數

那麼,如何書寫一個函數指針呢?

int Add(int x, int y)
{
  return x + y;
}      

以Add函數為例,它有兩個int類型的形參,傳回類型是int

所對應的函數指針就是​

​int(*)(int,int)​

​類型

int (*pf)(int, int) = Add;      

依據以下幾步就能正确寫出函數指針

  • 确定函數的傳回類型
  • 确定函數的參數類型和個數
  • 把函數參數類型裡的變量名去掉,放入括号裡

    ​(int x,int y)​

    ​​去掉x、y,即​

    ​(int,int)​

  • 在前面加上函數的傳回類型
  • 最後加上​

    ​(*)​

    ​,以及函數指針變量名

需要注意的是,​

​(*pf)​

​的括号不能省略,否則編譯器會報錯

去掉括号之後就相當于​函數聲明​,無法指派
【C語言】指針進階第五站,函數指針

1.2用函數指針來調用函數

如下圖所示,當我們定義了一個函數指針後

就可以通過指針來通路原函數

這時候​

​(*pf)​

​​其實就相當于​

​my_test​

【C語言】指針進階第五站,函數指針

我們可以通過函數指針來調用上面提到過的Add函數

int Add(int x, int y)
{
  return x + y;
}

int main()
{
  int (* pf)(int, int) = Add;
  int sum = (*pf)(2,3);
  int sum1 = pf(2, 3);

  int sum2 = Add(2, 3);
  printf("%d\n", sum);
  printf("%d\n", sum1);
  printf("%d\n", sum2);

  return 0;
}      

可以看到,sum和sum1兩種形式都正确調用了該函數

【C語言】指針進階第五站,函數指針

因為我們已經把Add的位址轉給了pf指針,​函數名Add​和​指針pf​實際上是等價的

是以在使用函數指針的時候,可以不帶​

​*​

​使用。但是帶*的時候一定要加括号!

1.3兩個奇葩的代碼

奇葩代碼1

(*(void (*)())0)();      
這裡的0僅為示例,我們在正常使用的時候并不能通路0的位址

看到這個代碼的時候,是不是有點懵?

别急,讓我們來慢慢分析一波!

【C語言】指針進階第五站,函數指針

奇葩代碼2

void (*signal(int , void(*)(int)))(int);      
【C語言】指針進階第五站,函數指針

說人話就是,signal函數内​傳入​了一個​

​void(*)(int)​

​的函數指針,​傳回值​也是一個​

​void(*)(int)​

​的函數指針!

void fun(int num)
{
    printf("fun-->%d\n", num);
}
void ( *signal( int, void(*)(int) ) )(int);

int main()
{
    void(*pf)(int);//定義一個函數指針
    pf = signal(100, fun);
    //為signal函數傳參,并用pf指針接收
    return 0;
}      

​“這個代碼2是真的奇葩,就沒有什麼辦法把他變成人話嗎?(簡化一下)”​

當然有!那就是用typedef函數來給​

​void(*)(int)​

​指針起一個新名字!

typedef void(*pf_t)(int);
//把void(*)(int)命名為pf_t      
void(*p)(int);//p是函數指針變量的名字
typedef void(*pf_t)(int);//pf_t是一個新的類型名      

這樣我們的代碼就能得到簡化

void ( *signal( int, void(*)(int) ) )(int);//源代碼
//簡化後
pf_t siganal(int,pf_t);      

這樣是不是就更容易分辨了?

2函數指針數組

2.1基本形式

既然函數指針也是一個指針類型,那我們就可以用指針數組來存放它

  • 前提:這些函數的​參數類型、傳回類型​一緻
int Add(int x, int y)
{
  return x + y;
}
int Sub(int x, int y)
{
  return x - y;
}
int Mul(int x, int y)
{
  return x * y;
}
int Div(int x, int y)
{
  return x / y;
}
//函數指針數組
int (*pfArr[4])(int, int) = {Add, Sub, Mul, Div};      

相比于分開寫多次函數調用

//多次函數調用
int (*pf1)(int,int) = Add;
int (*pf2)(int, int) = Sub;
int (*pf3)(int, int) = Mul;
int (*pf4)(int, int) = Div;      

函數指針數組可以讓我們以使用數組的形式來通路每個函數

int Add(int x, int y)
{
  return x + y;
}
int Sub(int x, int y)
{
  return x - y;
}
int Mul(int x, int y)
{
  return x * y;
}
int Div(int x, int y)
{
  return x / y;
}

int main()
{
  int (*pfArr[4])(int, int) = {Add, Sub, Mul, Div};//函數指針數組
  int i = 0;
  for (i = 0; i < 4; i++)
  {
    //int ret = (*pfArr[i])(8, 4);
    int ret = pfArr[i](8, 4);

    printf("%d\n", ret);
  }
  return 0;
}      

這樣也簡化了我們的代碼

2.2電腦實作

2.2.1switch語句

  • 目的:實作一個電腦
  • 菜單:用數字來選擇運算類型
  • 方法:以switch/case語句來實作函數調用
  • 結束:用do/while實作多組輸入,以及結束程式
int Add(int x, int y)
{
  return x + y;
}
int Sub(int x, int y)
{
  return x - y;
}
int Mul(int x, int y)
{
  return x * y;
}
int Div(int x, int y)
{
  return x / y;
}


void menu()
{
  printf("**********************************\n");
  printf("*****  1. add     2. sub     *****\n");
  printf("*****  3. mul     4. div     *****\n");
  printf("*****  0. exit               *****\n");
  printf("**********************************\n");
}

int main()
{
  int input = 0;
  int x = 0;
  int y = 0;
  int ret = 0;
  do
  {
    menu();
    printf("請選擇:>");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      printf("輸入2個操作數:>");
      scanf("%d %d", &x, &y);
      ret = Add(x, y);
      printf("ret = %d\n", ret);
      break;
    case 2:
      printf("輸入2個操作數:>");
      scanf("%d %d", &x, &y);
      ret = Sub(x, y);
      printf("ret = %d\n", ret);
      break;
    case 3:
      printf("輸入2個操作數:>");
      scanf("%d %d", &x, &y);
      ret = Mul(x, y);
      printf("ret = %d\n", ret);
      break;
    case 4:
      printf("輸入2個操作數:>");
      scanf("%d %d", &x, &y);
      ret = Div(x, y);
      printf("ret = %d\n", ret);
      break;
    case 0:
      printf("退出電腦\n");
      break;
    default:
      printf("選擇錯誤\n");
      break;
    }
  } while (input);

  return 0;
}      

這種方式需要寫非常多的重複代碼,而且代碼長度很長????

我們可以使用​函數指針​對它進行優化

2.2.2函數指針形式

int main()
{
    int input = 0;
    int x = 0;
    int y = 0;
    int ret = 0;

    int (*pfArr[5])(int, int) = {0, Add, Sub, Mul, Div};
    //pfArr是一個函數指針的數組,也叫轉移表
    do
    {
        menu();
        printf("請選擇:>");
        scanf("%d", &input);
        if (input == 0)
        {
            printf("退出電腦\n");
            break;
        }
        else if (input >= 1 && input <= 4)
        {
            printf("輸入2個操作數:>");
            scanf("%d %d", &x, &y);
            ret = pfArr[input](x, y);
            printf("ret = %d\n", ret);
        }
        else
        {
            printf("選擇錯誤\n");
        }
    } while (input);

    return 0;
}      

這樣就避免了我們在每個case語句裡都寫上​輸入提示、scanf和不同的函數調用​所導緻的代碼備援了

運作試試吧!

【C語言】指針進階第五站,函數指針

2.3指向函數指針數組的指針

​函數指針數組​

​​是一個數組,數組可以用​

​數組指針​

​來存放位址

  • 指向​

    ​函數指針數組​

    ​的指針:是一個指針
  • 該指針指向一個數組,數組的每個元素都是一個​

    ​函數指針​

int Add(int x, int y)
{
    return x + y;
}

int main()
{
    int (*pa)(int, int) = Add;//函數指針
    int (* pfA[4])(int, int);//函數指針的數組
    int (* (*ppfA)[4])(int, int) = &pfA;
    //ppfA 是一個指針,該指針指向了一個存放函數指針的數組

    return 0;
}      

3.相關練習題

定義一個函數指針,指向的函數有兩個int形參并且傳回一個函數指針,傳回的指針指向一個有一個int形參且傳回int的函數?下面哪個是正确的?
A. int (*(*F)(int, int))(int)
B. int (*F)(int, int)
C. int (*(*F)(int, int))
D. *(*F)(int, int)(int)      

一步步分析題目的要求

  • 該函數指針指向的函數有兩個int類型,即​

    ​(int,int)​

    ​,ABCD都有,無法排除
  • 仔細看看,D的類型沒有寫全,直接排除
  • 傳回一個函數指針,該指針指向一個​

    ​有一個int形參且傳回int的函數​

    B是一個函數指針,傳回類型是int,錯誤

    C的傳回值是int*類型,錯誤

  • A選項去掉函數指針F後,剩下​

    ​int (*)(int)​

    ​,​符合題意​

結語

函數指針的知識點是第一次接觸到????

你學廢了嗎?????

下一站????:回調函數

繼續閱讀