1. 程式人生 > >指標陣列,陣列指標,函式指標及應用(回撥函式)

指標陣列,陣列指標,函式指標及應用(回撥函式)

················································索引···························································

  當我們在學習指標與陣列時總會遇到兩個令人容易混淆的概念:陣列指標與指標陣列。
  在這裡陣列指標是指向陣列的指標,其本質為指標指向的物件是陣列。由於陣列的形式多樣所以陣列指標的表達也十分多樣。同理,指標陣列就是存放指標的陣列,其本質為陣列。由於“[ ]”的優先順序高於“ * ”的優先順序,指標陣列與陣列指標的表達可做如下表示:

  • int * p1 [10];
      // 指標陣列
      p1先與“[ ]”結合構成一個包含10個元素的陣列,int*表示的則是陣列的內容。
  • int (* p2)[10];  // 陣列指標  p2先與“ * ”構成指標定義,int表示陣列內容,[10]表示陣列內元素個數。

  由於指向陣列的指標與指向普通整型變數的指標不同,在這裡可以再對“陣列名”與“&陣列名”的關係進行理解。
  在一維陣列中,陣列名錶示指向首元素的首地址,是一個指向普通變數的指標常量,當對其+1時偏移量是一個普通資料型別的記憶體大小。而在陣列名前加上取地址符&後,表示的就是一個指向陣列的指標常量對其+1時偏移量是一個數組的記憶體大小。
  觀察以下程式碼:

int main ()
{
    char arr[5] = {'A','B','C','D'};
    char(*p1)[3] = &arr;   //p1指向一個具有三個字元元素的陣列
    char(*p2)[3] = arr;    //p2也指向一個具有三個字元元素的陣列
    printf("arr = %p\n",arr);
    printf("p1 = %p\n",p1);
    printf("p1+1 = %p\n",p1+1);
    printf("p2+1 = %p\n",p2+1);
    return 0;
}


  在這段程式碼當中,p1,p2都是指向由3個整型元素組成的陣列的指標,但arr卻是一個指向整型資料的指標常量,二者的指向內容雖然不同但是由於當變數作為右值時編譯器只會去取變數的值,所以在程式執行時編譯器會報出如警告卻不會執行失敗。

e:\code\c語言\test\test.c(16) : warning C4048: “char ()[10]”和“char ()[5]”陣列的下標不同
e:\code\c語言\test\test.c(17) : warning C4047: “初始化”: “char ()[10]”與“char ”的間接級別不同

int main ()
{
    char arr[5] = {'A','B','C','D'};
    char(*p1)[10] = &arr;//p1指向一個具有三個字元元素的陣列,與arr指向空間一致
    char(*p2)[10] = arr;
    printf("arr = %p\n",arr);
    printf("p1 = %p\n",p1);
    printf("p1+1 = %p\n",p1+1);
    printf("p2+1 = %p\n",p2+1);
    return 0;
}



  總結程式碼可以看出指向陣列的指標初始化時只是用到了原陣列提供的地址,其訪問的內容大小並不受原陣列arr限制,而受到自己指向的陣列大小的影響即:p1+1的偏移量是10而不是原陣列大小(&arr)的4,p2+1的偏移量也是10而不是原指標指向元素的大小(arr首元素大小)的1。

  對於陣列指標最重要的是要理解其本質是指標,而且其偏移量受到自身指向的陣列大小影響即可。相較之下,指標陣列就很好理解了,不過是本質是陣列且每個陣列元素是指標罷了(不管是指向何種型別的指標在32位平臺下其自身的空間佔用量都是4個位元組)

函式指標

  知道了陣列指標是指向陣列的指標,那麼同理也可以對函式指標進行相同的理解,函式指標就是指向函式的指標了。
  函式在記憶體中佔用一塊地址而且這塊地址也是可以賦給一個指標變數的,也就是說可以通過這個地址訪問到這個函式。與陣列相似,函式名也是指向函式第一條指令的常量指標。所以說函式的呼叫可以通過函式名,也可以通過指向函式的指標來呼叫。函式指標還允許將函式作為引數傳遞給其他函式,也就是回撥函式
  函式指標表現形式:type (*func)(type &,type &)該語句聲明瞭一個指標func,它指向了一個函式,這個函式帶有了2個type型引數並返回一個type的值。
  需要特別注意的是第一個括號一定要寫,如果不寫的話表示式就變成了函式宣告而非函式指標。
  分析以下兩句程式碼,看看是什麼意思:

1.(*(void (*)())0)();
2.void(*signal(int,void(*)(int)))(int);

表示式1:
這裡寫圖片描述
表示式2:
這裡寫圖片描述

  對於函式指標暫時只需要明白其表達方法,以及如何利用函式指標呼叫函式即可。

  在對函式指標有了一定的瞭解之後,函式指標陣列就很好理解了,其意義就是定義一個數組,陣列的內容均是指向函式的指標。
  表示式:例如:int (*arr1[10])();
  arr1先於[]結合,表明其本質是陣列,其指向的型別是int (*)()返回值為int的函式。即函式指標陣列。
  其應用參考:利用函式指標陣列實現計算器;

  當定義了一個函式指標陣列後,能否在定義一個指標用於儲存這個陣列的地址呢?這個指標就是指向函式指標陣列的指標。其表示式為:例如:void (*(*p)[5]) )(void)
  表示 一個指向有5個元素每個元素為指向一個返回值為空的函式的陣列的指標。

#include<stdio.h>
#include<string.h>
void swap(char *p,char *q, int n)
{
    unsigned int i = 0;
    char tmp;
    for(i=0; i<n; i++)
    {
        tmp = *p;
        *p = *q;
        *q = tmp;
        p++;
        q++;
    }
}
int int_cmp(const void *p,const void *q)//實現整型間的排序
{
    return (*(int *)p > *(int *)q);
}
int char_cmp(const void *p,const void *q)//實現字元間的排序
{
    return (*(char *)p > *(char *)q);
}
int str_cmp(const char **p,const char **q)//實現字串排序
{
    return strcmp(*p,*q);
}
void bubble(void *arr,int sz,int wid,int(*cmp)(const void *p,const void *q))
{
    unsigned int i = 0;
    unsigned int j = 0;
    for(i=0;i<sz-1;i++)
    {
        for(j=0;j<sz-i-1;j++)
        {
            if(cmp((char *)arr+wid*j ,(char *)arr+wid*(j+1))>0)
            //函式指標呼叫
            {
                swap((char *)arr+wid*j,(char *)arr + wid*(j+1),wid);
            }
        }
    }
}

int main()
{
    int arr[10] = {0,11,33,22,44,55,66,88,77,99};
    int wid = sizeof(arr[0]);
    int sz = sizeof(arr)/sizeof(arr[0]);
    int i = 0;
    bubble(arr,sz,wid,str_cmp);//第四個引數要根據排序的不同型別進行更改
    for(i=0;i<sz;i++)
    {
        printf("%s\n",arr[i]);
    }
    printf("\n");
    return 0;
}
#include<stdio.h>
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");
}
void calc(int (*pfun)(int, int))
{
    int x = 0;
    int y = 0;
    int ret = 0;
    printf("請輸入要進行計算的值:");
    scanf("%d%d", &x, &y);
    ret = pfun(x, y);
    printf("ret = %d\n", ret);
}

int main()
{
    int input = 0;
    int x = 0;
    int y = 0;
    int ret = 0;
    int (*pfun[5])(int , int) = {0, Add, Sub, Mul, Div};
    //建立函式指標的陣列,陣列中儲存的都是返回值為int的函式的指標。
    do
      { 
        menu();
        printf("請選擇:");
        scanf("%d", &input);
        switch(input)
        {
        case 1:
            calc(pfun[1]);
            //通過陣列呼叫函式的指標進而呼叫函式,可大量節省程式碼篇幅
            break;
        case 2:
            calc(pfun[2]);
            break;
        case 3:
            calc(pfun[3]);
            break;
        case 4:
            calc(pfun[4]);
            break;
        case 0:
            printf("退出\n");
            break;
        default:
            printf("輸入錯誤,請重新輸入\n");
            break;
        }
    }while(input);
    return 0;
}

本文主要用於學習總結之用,其中主要參考資料有《C語言深度解剖》,《C陷阱與缺陷》,《C和指標》。內容中凡有不足疏漏之處歡迎批評指正,謝謝。