程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C語言指針聲明探秘

C語言指針聲明探秘

編輯:關於C語言

前言
我對C指針的理解一直停留在:指針本身是一塊內存,它保存了一塊內存的地址,可以引用,但是最近在讀代碼的時候,各種指針的聲明搞得我異常苦惱,趕緊去學習了一番,也只是明白了最基本的使用,總結如下。

基本知識
指針的基本使用正如前言中說的,先看這樣一段代碼:
[cpp]
#include <stdio.h>  
 
int main() 

        int i = 10; 
        int * p; 
        printf("p的地址:%d\n",&p); 
        printf("未初始化時p的內容:%d\n",p); 
//      printf("未初始化訪問p指向的內存:%d\n",*p); // 這行代碼訪問了個野指針,必然發生段錯誤  
        p = &i; 
        printf("--初始化p完畢--\n"); 
        printf("p裡面保存的地址:%d\n",p); 
        printf("p指向的內存的內容:%d\n",*p); 
        printf("p的大小:%d\n",sizeof(p)); 
        printf("p指向的內存大小:%d\n",sizeof(*p)); 
        return 0; 

#include <stdio.h>

int main()
{
        int i = 10;
        int * p;
        printf("p的地址:%d\n",&p);
        printf("未初始化時p的內容:%d\n",p);
//      printf("未初始化訪問p指向的內存:%d\n",*p); // 這行代碼訪問了個野指針,必然發生段錯誤
        p = &i;
        printf("--初始化p完畢--\n");
        printf("p裡面保存的地址:%d\n",p);
        printf("p指向的內存的內容:%d\n",*p);
        printf("p的大小:%d\n",sizeof(p));
        printf("p指向的內存大小:%d\n",sizeof(*p));
        return 0;
}

輸出結果為:[plain] view plaincopyprint?p的地址:1439276008 
未初始化時p的內容:0 
--初始化p完畢-- 
p裡面保存的地址:1439276020 
p指向的內存的內容:10 
p的大小:8 
p指向的內存大小:4 

p的地址:1439276008
未初始化時p的內容:0
--初始化p完畢--
p裡面保存的地址:1439276020
p指向的內存的內容:10
p的大小:8
p指向的內存大小:4

這就是指針的基本使用,可用下圖來說明:

 


指針與數組
首先看這兩個聲明語句:
[cpp]
char (*a) [100]; 
char* a [100]; 

char (*a) [100];
char* a [100];
第一個是聲明了一個指向有100個char元素的數組的指針(注意和指向數組首地址的char型指針分開);第二個是聲明了一個有100個char*元素的數組,數組裡面裝的是char *。
為了理解,我們來看這樣一段代碼:
[cpp]
#include <stdio.h>  
 
int main() 

        int arr[10][100]; 
        printf("sizeof(arr[0]) = %lu\n", sizeof(arr[0])); 
        printf("sizeof(arr[0][0]) = %lu\n", sizeof(arr[0][0])); 
        int *p; 
        int (*q)[100]; 
        p = &arr[0][0]; 
        q = &arr[0]; 
        printf("p = %d\n",p); 
        printf("q = %d\n",q); 
        printf("sizeof((*p)) = %lu\n", sizeof((*p))); 
        printf("sizeof((*q)) = %lu\n", sizeof((*q))); 
        p++; 
        q++; 
        printf("after add 1, p = %d\n", p); 
        printf("after add 1, q = %d\n", q); 
        return 0; 

#include <stdio.h>

int main()
{
        int arr[10][100];
        printf("sizeof(arr[0]) = %lu\n", sizeof(arr[0]));
        printf("sizeof(arr[0][0]) = %lu\n", sizeof(arr[0][0]));
        int *p;
        int (*q)[100];
        p = &arr[0][0];
        q = &arr[0];
        printf("p = %d\n",p);
        printf("q = %d\n",q);
        printf("sizeof((*p)) = %lu\n", sizeof((*p)));
        printf("sizeof((*q)) = %lu\n", sizeof((*q)));
        p++;
        q++;
        printf("after add 1, p = %d\n", p);
        printf("after add 1, q = %d\n", q);
        return 0;
}
這端代碼運行後結果如下:
[plain]

sizeof(arr[0]) = 400 
sizeof(arr[0][0]) = 4 
p = 1411443800 
q = 1411443800 
sizeof((*p)) = 4 
sizeof((*q)) = 400 
after add 1, p = 1411443804 
after add 1, q = 1411444200 

sizeof(arr[0]) = 400
sizeof(arr[0][0]) = 4
p = 1411443800
q = 1411443800
sizeof((*p)) = 4
sizeof((*q)) = 400
after add 1, p = 1411443804
after add 1, q = 1411444200
因為內存是線性的,C中所謂的二維數組不過是數組的數組,arr這個數組有10個元素,每個元素是一個長度為100的數組,在程序員的腦子裡面,arr是一個有10行100列的二維數組。
代碼裡的p是一個指向int型的指針,q是一個指向“有100個int的int數組”的指針。所以p和q的初始化方式是不同的,但是開始的時候他們都指向了arr這個數組的數組的首地址(初始時是相等的),但是到後面分別執行自增操作之後,因為它們的類型不同,因此根據指針自增運算的含義,他們移動的步長也不相同,p移動了sizeof(int)個字節,而q移動了sizeof(int[100])個字節,於是它們的值也大不相同,可以用下圖來說明:

\
另外要注意的就是字符二維數組的聲明:
[cpp]
include <stdio.h>  
 
int main() 

        char* str[2] = {"liushuai","kobe"}; 
        printf("%s %s\n",str[0],str[1]); 
        return 0; 

#include <stdio.h>

int main()
{
        char* str[2] = {"liushuai","kobe"};
        printf("%s %s\n",str[0],str[1]);
        return 0;
}
輸出結果顯然:
[plain]
liushuai kobe 

liushuai kobe

以上是合法的字符二維數組的聲明,str是一個有兩個元素的數組,每個元素的類型是一個char*,結合上面所講的,應該不難理解。
返回指針的函數和函數指針
來看下面兩個聲明語句:
[cpp]
int* foo(int i); 

int* foo(int i);
這個應該比較好理解,類比著裝有指針的數組的聲明char* a[100],這是個函數聲明,聲明了一個名字為foo的函數,這個函數接受一個類型為int的參數,返回一個指向int型的指針。
再看下面的聲明:
[cpp]

void (*bar)(); 

void (*bar)();
類比著數組的聲明,這個語句聲明了一個指向函數的指針bar,它指向的函數要求返回值為void,且不接受任何參數。這是一個比較簡單的函數的函數指針的聲明。
函數既然可以返回一個指針,那麼一個函數能不能返回一個指向函數的指針呢?答案是肯定的,看,指針是多麼靈活。剛剛接觸可能會有點不適應,我們來看一個例子:
[cpp]
int (*foo(int)) (double*,char); 

int (*foo(int)) (double*,char);
類比著上面的講解,我們知道,這個語句聲明了一個函數foo,它接受一個int類型的參數,返回一個指向函數的指針,要求指向的函數具有這樣的形式:接受一個double類型的指針和char型的變量作為參數,返回一個int類型的值。
我們可以用C中的typedef簡化這個聲明:
[cpp]
typedef int (*ptf) (double*, char); 
ptf foo(int ); 

typedef int (*ptf) (double*, char);
ptf foo(int );

注意:typedef和#define是不同的,typedef是給“這樣”的指針起了一個別名ptf,而不是簡單的進行宏替換。
好吧,我們接著來個更變態的,如果一個函數的參數和返回值都是函數指針,那麼聲明就會更復雜,例如:
[cpp] view plaincopyprint?void (*signal (int sig, void (*func) (int siga)) ) ( int siga ); 

void (*signal (int sig, void (*func) (int siga)) ) ( int siga );
其實慢點分析也不難,我們可以用typedef來簡化:
[cpp]
typedef void (*p_sig) (int); 
p_sig signal(int sig, p_sig func); 

typedef void (*p_sig) (int);
p_sig signal(int sig, p_sig func);
signal這個函數的參數func是一個函數指針,返回了一個函數指針,且兩種指針要求指向的函數具有同一種形式(接受一個int型的參數,返回空值)。
通過函數指針調用函數
還是通過一個例子來說明問題:
[cpp]
#include <stdio.h>  
 
void printMyName(); 
 
int main() 

        void (*f)(); 
        f = printMyName; 
        f(); 
        f = &printMyName; 
        f(); 
        return 0; 

 
void printMyName() 

        printf("liushuaikobe\n"); 

#include <stdio.h>

void printMyName();

int main()
{
        void (*f)();
        f = printMyName;
        f();
        f = &printMyName;
        f();
        return 0;
}

void printMyName()
{
        printf("liushuaikobe\n");
}
是不是很容易呢。注意用“&函數名”和“函數名”初始化一個函數指針都是合法的,因為C中函數名會被轉換為指向這個函數的指針。
指針真是充滿智慧的產物,通過函數指針,可以輕松實現面向對象語言中多態等一些高級特性(例如Java的接口,C++的虛函數),真的太美妙了。
對於大神,這些東西可能都是小兒科,但是本人C真的沒怎麼用過,搞懂了這些,我也很高興了。
最後送大家一句話:
不要因為走得太遠,就忘了自己當初為什麼出發。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved