本文目錄
一、對星號 * 的總結
在我們目前所學到的語法中,星號主要有三種用途:
- 表示乘法,例如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訪問二維數組中的每個元素
-
p指向數組 a 的開頭,也即第 0 行;p+1前進一行,指向第 1 行。
-
*(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 -
*(p+1)+1表示第 1 行第 1 個元素的地址。
-
((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