C語言指針關鍵知識點總結

一、對星號 * 的總結

在我們目前所學到的語法中,星號主要有三種用途:

  • 表示乘法,例如int a = 3, b = 5, c; c = a * b;,這是最容易理解的。
  • 表示定義一個指針變量,以和普通變量區分開,例如int a = 100; int *p = &a;。
  • 表示獲取指針指向的數據,是一種間接操作,例如int a, b, *p = &a; *p = 100; b = *p;。

二、各種指針總結

1、數組指針

如果一個指針指向了數組,我們就稱它爲數組指針(Array Pointer)

重點: 數組名是常量,它的值不能改變,而數組指針是變量(除非特別指明它是常量),它的值可以任意改變。也就是說,數組名只能指向數組的開頭,而數組指針可以先指向數組開頭,再指向其他元素。

2、字符串指針

表示方法

第一種方法直接使用字符數組表示字符串,聲明一個指針指向字符數組的首地址:

char str[] = "http://c.biancheng.net";
char *pstr = str;

第二種方法是直接使用一個指針指向字符串(也稱字符串常量):

char *str = "http://c.biancheng.net";

兩種表示方法的區別:

它們最根本的區別是在內存中的存儲區域不一樣,字符數組存儲在全局數據區或棧區,第二種形式的字符串存儲在常量區。全局數據區和棧區的字符串(也包括其他數據)有讀取和寫入的權限,而常量區的字符串(也包括其他數據)只有讀取權限,沒有寫入權限。

3、指針變量做函數參數

優點: 用指針變量作函數參數可以將函數外部的地址傳遞到函數內部,使得在函數內部可以操作函數外部的數據,並且這些數據不會隨着函數的結束而被銷燬。

實例:

void swap(int *p1, int *p2)

4、指針作爲函數返回值

定義:C語言允許函數的返回值是一個指針(地址),我們將這樣的函數稱爲指針函數。

實例:

char *strlong(char *str1, char *str2){
    if(strlen(str1) >= strlen(str2)){
        return str1;
    }else{
        return str2;
    }
}

注意: 用指針作爲函數返回值時需要注意的一點是,函數運行結束後會銷燬在它內部定義的所有局部數據,包括局部變量、局部數組和形式參數,函數返回的指針請儘量不要指向這些數據,C語言沒有任何機制來保證這些數據會一直有效,它們在後續使用過程中可能會引發運行時錯誤。

5、指針數組

定義:如果一個數組中的所有元素保存的都是指針,那麼我們就稱它爲指針數組。

定義形式:

dataType *arrayName[length];

[ ]的優先級高於*,該定義形式應該理解爲:

dataType *(arrayName[length]);

6、二維數組指針

定義實例:

int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} };
int (*p)[4] = a;

括號中的*表明 p 是一個指針,它指向一個數組,數組的類型爲int [4],這正是 a 所包含的每個一維數組的類型。

使用指針p訪問二維數組中的每個元素

  1. p指向數組 a 的開頭,也即第 0 行;p+1前進一行,指向第 1 行。

  2. *(p+1)表示取地址上的數據,也就是整個第 1 行數據。注意是一行數據,是多個數據,不是第 1 行中的第 0 個元素,下面的運行結果有力地證明了這一點:

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

     return 0;
    

    }

    運行結果:
    16

  3. *(p+1)+1表示第 1 行第 1 個元素的地址。

  4. ((p+1)+1)表示第 1 行第 1 個元素的值。很明顯,增加一個 * 表示取地址上的數據。

指針數組和二維數組指針的區別

指針數組和二維數組指針在定義時非常相似,只是括號的位置不同:

int *(p1[5]);  //指針數組,可以去掉括號直接寫作 int *p1[5];
int (*p2)[5];  //二維數組指針,不能去掉括號

7、函數指針

定義:一個函數總是佔用一段連續的內存區域,函數名在表達式中有時也會被轉換爲該函數所在內存區域的首地址,這和數組名非常類似。我們可以把函數的這個首地址(或稱入口地址)賦予一個指針變量,使指針變量指向函數所在的內存區域,然後通過指針變量就可以找到並調用該函數。這種指針就是函數指針

函數指針的定義形式爲:

returnType (*pointerName)(param list);

returnType 爲函數返回值類型,pointerNmae 爲指針名稱,param list 爲函數參數列表。參數列表中可以同時給出參數的類型和名稱,也可以只給出參數的類型,省略參數的名稱,這一點和函數原型非常類似。

實例:

#include <stdio.h>

//返回兩個數中較大的一個
int max(int a, int b){
    return a>b ? a : b;
}

int main(){
    int x, y, maxval;
    //定義函數指針
    int (*pmax)(int, int) = max;  //也可以寫作int (*pmax)(int a, int b)
    printf("Input two numbers:");
    scanf("%d %d", &x, &y);
    maxval = (*pmax)(x, y);
    printf("Max value: %d\n", maxval);

    return 0;
}

三、指針對比

辨析: 僅一個括號之差

int *p1[6];  //指針數組
int *(p2[6]);  //指針數組,和上面的形式等價
int (*p3)[6];  //二維數組指針
int (*p4)(int, int);  //函數指針

常見指針變量的定義

定 義 含 義
int *p; p 可以指向 int 類型的數據,也可以指向類似 int arr[n] 的數組。
int **p; p 爲二級指針,指向 int * 類型的數據。
int *p[n]; p 爲指針數組。[ ] 的優先級高於 *,所以應該理解爲 int *(p[n]);
int (*p)[n]; p 爲二維數組指針。
int *p(); p 是一個函數,它的返回值類型爲 int *。
int (*p)(); p 是一個函數指針,指向原型爲 int func() 的函數。

四、空指針NULL與NUL以及void指針

1、NULL空指針

NULL 是“零值、等於零”的意思,在C語言中表示空指針。從表面上理解,空指針是不指向任何數據的指針,是無效指針,程序使用它不會產生效果。

NULL 是在stdio.h中定義的一個宏,它的具體內容爲:

#define NULL ((void *)0)

(void *)0表示把數值 0 強制轉換爲void *類型,最外層的( )把宏定義的內容括起來,防止發生歧義。從整體上來看,NULL 指向了地址爲 0 的內存,而不是前面說的不指向任何數據。

在進程的虛擬地址空間中,最低地址處有一段內存區域被稱爲保留區,這個區域不存儲有效數據,也不能被用戶程序訪問,將 NULL 指向這塊區域很容易檢測到違規指針。

2、NUL

NUL 表示字符串的結束標誌 ‘\0’,它是ASCII碼錶中的第 0 個字符。NUL 沒有在C語言中定義,僅僅是對 ‘\0’ 的稱呼,不能在代碼中直接使用。

3、void 指針

void 用在函數定義中可以表示函數沒有返回值或者沒有形式參數,void * 表示指針指向的數據的類型是未知的。

void *表示一個有效指針,它確實指向實實在在的數據,只是數據的類型尚未確定,在後續使用過程中一般要進行強制類型轉換。

C語言動態內存分配函數 malloc() 的返回值就是void *類型,在使用時要進行強制類型轉換,請看下面的例子:

#include <stdio.h>

int main(){
    //分配可以保存30個字符的內存,並把返回的指針轉換爲 char *
    char *str = (char *)malloc(sizeof(char) * 30);
    gets(str);
    printf("%s\n", str);
    return 0;
}

運行結果:
c.biancheng.net↙
c.biancheng.net

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章