指針理解——指針數組、數組指針、指針函數、函數指針


一個存在已久的謠言

源碼

#include <stdio.h>
int main()
{
     int ar[10] = { 1,2,3,4,5,6,7,8,9,10 };
     printf("&ar[0]=%p\n",&ar[0]);
     printf("ar=%p\n", ar);
     printf("&ar=%p\n", ar);
     getchar();
     return 0;
}

運行結果:

根據運行結果,很多人就會得出“數組名就是首元素的地址”這樣錯誤的結論。見代碼

#include <stdio.h>
int main()
{
     int ar[10] = { 1,2,3,4,5,6,7,8,9,10 };
     printf("&ar[0]=%p\n",&ar[0]);
     printf("ar=%p\n", ar);
     printf("&ar=%p\n", ar);
     printf("sizeof(ar)=%d\n", sizeof(ar));
     printf("sizeof(&ar[0])=%d\n", sizeof(&ar[0]));
     getchar();
     return 0;
}

運行結果:

如果 “數組名就是首元素的地址” 結論屬實,那么數組名的大小就是一個指針的大小。事實上,數組名代表整個數組空間。

數組名(ar)本身的確是個地址,在數值上等於數組首元素取地址(&ar[0]),等於對數組名取地址(&ar)。數值上這三個數相等,那只是表象。其實質是地址背后指向內存空間的能力是不同的。

這里面還有個有趣的問題,就是數組作為形參會退化為指針。參考C++——數組形參退化為指針。這也就是為啥,數組作為形參的時候還要再多給一個數組長度參數。

數組指針、指針數組

數組名是地址,與數組首元素地址僅代表自己類型那么大內存不同,數組名內存指向能力非常強。數組名指向整個數組空間。進一步講,對數組名取地址,即就是在對整個數組取地址,則數組的地址自然要用指向數組的指針才能接收,所以,必須定義指向數組的指針類型,即為數組指針。見代碼

#include <stdio.h>
int main()
{
     int ar[10] = { 1,2,3,4,5,6,7,8,9,10 };
     int *p = ar;
  
     /*下面是錯誤代碼
     int **p = &ar;*/
 
     //正確寫法
     int(*pp)[10] = &ar;
     getchar();
     return 0;
}

int *p = ar;  p的類型int *,p指向的類型int

int **p = &ar;  p的類型int** ,p指向的類型int*

int ar[10]的類型,int[10]

int(*pp)[10] = &ar;pp的類型是 int (*)[10],pp指向int [10]類型

數組指針、指針數組重點在於后兩個字。即數組指針本質上是指針,指針數組本質上是數組。這是字面上理解,代碼角度怎么區分呢?

int ar[10] = { 1,2,3,4,5,6,7,8,9,10 };
int(*p1)[10] = &ar;      //數組指針
int* p2[3];              //指針數組

變量(這里是p1,p2)與[ ]優先結合,所以int* p2[3];是指針數組,是一個數組。 要想變成指針,需要使用 ()強制優先結合指針。

總結:

數組指針

首先它是一個指針,它指向一個數組,在32位系統下永遠是占4個字節,至於它所指向的數組占多少字節是不知道的,它是“指向數組的指針”簡稱

對於數組指針,強調的是指針的概念,只不過,指針的能力是用來指向數組類型的,並且其方括號中的數字一定,例如:int (*p)[10],p就是指向數組的指針,其中p指針規定了只能指向整形的數組,並且數組大小只能是10個整形空間,不能多也不能少,多之少之都會認為其指針的能力與指向的實體不符。

指針數組

指針數組,首先它是一個數組,數組的元素都是指針,數組占多少字節由數組本身決定,它是“儲存指針的數組”的簡稱。

對於指針數組,強調的是數組的概念,只不過,數組所保存的類型是指針罷了,其地位跟普通的數組沒有什么區別,都是數組,只不過是大家保存的類型不同而已,因此,我們美名其曰:保存指針的數組就稱其為指針數組, 例如:int *p1[10]

指針數組最典型的例子就是main函數:int main(int argc,char *argv[])  第二個參數是一個字符串指針數組

#include <stdio.h>
int main()
{
     int ar[3] = { 1,2,3 };
     //數組指針指向ar
     int (*p)[3] = &ar;
      //指針數組
     int* q[3] = {&ar[0],&ar[1],&ar[2]};
      char* pstr[] = { "Hello","halo","nihao" };
    getchar();
     return 0;
}

函數指針、指針函數

函數指針

函數指針,首先它是一個指針,只不過,指針所指向的類型是函數,它是“指向函數的指針”的簡稱

#include <stdio.h>
int Max(int a, int b)
{
    printf("%d\n", a > b ? a : b);
     return a > b ? a : b;
 }

//函數指針
int(*pfun)(int, int);
 
void main()
{
     //情形1
     Max(1, 2);
 
     //情形2
     int(*pfun)(int, int);
     pfun = &Max;
     (*pfun)(1, 2);
 
     //情形3
     pfun = Max;
     pfun(1, 2);
 
     getchar();
}

一般來說,我們調動函數往往是通過函數名來進行調動,例如情形1,由於指針強悍與無所不能,只要你能表示出來的,指針都可以想辦法指向,因此,通過指針來調動函數就顯得很自然了,我們把能夠指向函數的指針稱為函數指針,例如情形2,把Max函數的地址賦給了pfun函數指針,在調動時先取值,然后再調動函數,這是一種標准的做法,事實上,由於函數名就是函數的入口地址,本身也充當了地址,因此,我們可以簡化程序,例如情形3,由於指針所指之物為函數,因此它的調動就行如直接運行函數類似了,但是,心里的清楚,情形3的做法實際是情形2的簡寫過程。

注意,一般指針都有其加1的能力,但是,函數指針不允許做這樣的運算。即pfun+1是一個非法的操作。

指針函數

指針函數,首先它是一個函數,只不過,函數所返回的類型是指針類型,它是“返回指針類型的函數”的簡稱。 我們把返回指針類型的函數稱其為指針函數,那就意味着只要返回值為指針,無論是什么類型的指針,都有資格稱為指針函數

//指針函數,返回整形指針
int* fun(int a, int b)
{
    return 0;
}

像這種,函數fun,參數是(int a, int b),返回值是int* 。這種比較明顯

返回函數指針的指針函數

先看一種錯誤的寫法。對於VS2012以后的IDE,這種代碼寫得時候直接顯示紅色〰,根本編譯不過

#include <stdio.h>
int Max(int a, int b)
{
      printf("%d\n", a > b ? a : b);
    return a > b ? a : b;
}

 //指針函數,返回函數指針。但是這種寫法是錯誤的
int(*) (int,int) func(int a, int b, int(*FUN)(int, int))
{
     printf("max value=%d\n", FUN(a, b));
    return FUN;
}

void main()
{
     func(1, 2, Max);
    getchar();
}

正確寫法

#include <stdio.h>
int Max(int a, int b)
{
     printf("%d\n", a > b ? a : b);
    return a > b ? a : b;
}
 
//函數指針
int(*pfun)(int, int);
 
//func這個函數參數是(int a, int b, int(*FUN)(int, int))
//返回值是個指針,這個指針是int (*) (int, int)型函數指針
int(*func(int a, int b, int(*FUN)(int, int))) (int, int)
{
     printf("max value=%d\n", FUN(a, b));
     return FUN;
}

void main()
{
     func(1, 2, Max);
     getchar();
}

 但是這種代碼寫出來太難理解了,可以使用typedef簡化代碼

#include <stdio.h>
int Max(int a, int b)
{
     printf("%d\n", a > b ? a : b);
     return a > b ? a : b;
}

//將函數指針定義成類型
typedef int(*pfun)(int, int);
 
//func這個函數參數是(int a, int b, int(*FUN)(int, int))
//返回值是個指針,這個指針是int (*) (int, int)型函數指針
//int(*func(int a, int b, int(*FUN)(int, int))) (int, int)
pfun func(int a, int b, pfun FUN)
{
    printf("max value=%d\n", FUN(a, b));
    return FUN;
}

void main()
{
     func(1, 2, Max);
     getchar();
}


免責聲明!

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



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