上一篇(初探指針(1))從指針的概念開始入手,依次是:什麼是指針>>指針傳遞>>多級指針>>指針函數。儘管指針遠不於此,但尋常應付想來足夠了。這一次想談談關於指針易混淆的地方。
倒不知是否翻譯之故,一度出現了指針函數和函數指針,指針數組和數組指針,指針常量和常量指針,字符指針數組和二維字符數組……通通被我認爲屬於一個羣體——不用就會忘記系列。其實拋開考試中會故意找茬,實際運用到都算不上太難。但這些術語名字嘛……想起我還小的時候,范冰冰李冰冰傻傻分不清。數組
首先咱們須要理清指針與數組的關係:數組不等於指針,但在某些狀況下兩者等價。函數
定義一個一維數組int a[i] = {...};
,當咱們使用a[i]
的時候,實際上編譯器作了轉換處理,變成了*(a+i)
。ui
一維數組是方便理解的,那麼二維數組呢?
a[i][j]
與*(*(a+i)+j)
具備等價效果,對後者分析以下:spa
有了這個鋪墊,咱們就能夠愉快的開始下面的旅程了。翻譯
前面說到在指針函數中,return的時候注意不要是局部變量的地址,這句話自己是有瑕疵的。但若是就這樣作了,保準兒程序不會出錯。可是等一等,咱們須要這般畏首畏尾嗎?指針
與「猥(畏)瑣(縮)」說不!
通常說來,存放在棧中的變量不能夠被return地址。這是因爲棧中的數據由系統產生,也由系統回收,很難說咱們在return棧中的某個數據的地址時,系統是否是早一步把它幹掉了。code
但這幾個數據是能夠的:全局變量,靜態變量,常量數據,堆中的數據。一個運行的程序裏,這些數據並不存放在棧中,具體在哪裏涉及到內存分配的問題,這裏不作延伸了。regexp
函數指針的根本是指針,它可以指向一個函數。遊戲
定義:int (*p)(int, int);
表示p是一個可以指向函數的指針,而且它指向的函數是int類型,且這個函數有兩個參數,這個兩個參數都是int類型。內存
#include <stdio.h>
int add_xy(int x, int y)
{
return x+y;
}
int main(int argc, char **agrv)
{
int (*p)(int, int);
p = &add_xy;
int result = p(1, 2);
printf("%d\n", result);
return 0;
}
// 輸出
>>3
觸類旁通:char *(*p)(int, char *)
表示p是一個指向函數的指針,這個函數有兩個參數:一個int類型,一個char*類型(char*的指針),而且這個函數返回char *的指針
必定要區分: char *p(int, char *)
,當沒有小括號把指針運算符和指針變量包起來的時候,表示指針函數的聲明,函數的名字爲p
指針數組的根本是數組,即:一個數組裏的成員都是指針
定義:int *p[];
按照優先級順序,這裏p[]
是一個總體,因此能夠看做:int *
—> p[]
示例:
#include <stdio.h>
int main(int argc, char **agrv)
{
int a=1, b=2, c=3;
int *p[3] = {&a, &b, &c}; // 定義一個指針數組,而且初始化
int i = 0;
for(i=0;i<3;i++){
printf("%d\n", *(*(p+i)));
// printf("%d\n", *p[i]); 也能夠這樣寫
}
return 0;
}
// 輸出
>>1
>>2
>>3
數組指針的根本是指針,即:一個指向數組的指針
定義:int (*p)[];
按照優先級順序,*p
是一個總體,因此能夠看做int *p
—> []
示例:
#include <stdio.h>
int main(int argc, char **argv)
{
int a[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int (*p)[3] = a; // 定義一個數組指針,而且初始化
int i = 0, j = 0;
int row = 3, col = 3;
for(i=0; i<row; i++){
for(j=0; j<col; j++){
printf("%d ", *(*(p+i)+j));
// printf("%d ", (*(p+i))[j]); 也能夠這樣寫
}
}
return 0;
}
// 輸出
>>1 2 3 4 5 6 7 8 9
指針常量表示:這個指針爲「只讀」(從始至終只能指向一個地址,能夠經過指針運算符來修改所指向的地址上的數據,必定在定義的時候賦初值)
定義:int *const p;
指針(*)常量(const),也就是說指針運算符在const的前面;const修飾的是p,因此p不能被賦值
#include <stdio.h>
int main(int argc, char **argv)
{
int a = 1;
int *const p = &a;
printf("%d\n", a);
*p = 2;
printf("%d\n", a);
return 0;
}
// 輸出
>>1
>>2
常量指針表示:這個指針指向的地址上的數據爲「只讀」(該指針能夠隨意更改指向的地址,但不能經過取地址運算符來修改所指向的地址上的數據)
定義:int const *p;
常量(const)指針(*),也就是說,const在指針運算符的前面;而且能夠看作const 修飾的是*p,因此*p不能被賦值
示例:
#include <stdio.h>
int main(int argc, char **argv)
{
int const *p = NULL;
int a = 1, b = 2;
p = &a;
printf("%d\n", *p);
p = &b;
printf("%d\n", *p);
return 0;
}
// 輸出
>>1
>>2
字符指針數據,也就是一個數組,裏邊的成員都是char *的指針,即:char *str[] = {"...", "...", ...};
二維字符數組,一個二維數組,裏邊成員都是char類型的字符,即:char str[][n] = {"...", "...", ...};
#include <stdio.h>
int main(int argc, char **agrv)
{
char *names[] = {"Tom", "Alice", "Bob"};
int i = 0;
for(i=0; i<3; i++){
printf("%s ", *(names+i));
// printf("%s ", names[i]); 也能夠這樣寫
}
printf("\n");
char hobbies[][10] = {"swimming", "sleeping", "reading"};
for(i = 0; i<3; i++){
printf("%s ", *(hobbies+i));
// printf("%s ", hobbies[i]); 也能夠這樣寫
}
printf("\n");
return 0;
}
// 輸出
>>Tom Alice Bob
>>swimming sleeping reading
在使用上,兩者是沒差的,但從內存上分析就存在區別。
字符指針數組中的成員都是一個char *的指針,也就是說,它們都是地址,真正的字符串存放在常量區;二維字符數組裏邊存放的都是一個個char類型的字符。建議使用前者,由於字符指針數組存儲字符串要比二維字符數組開銷小。
我記得有一個送命遊戲叫作:韓國女星連連看。其實對我重度臉盲患者,豈止韓國女星,即使是如今的許多大陸女星,我也着實難分辨(其實如今的新興男星我也基本分不清了)。好在能不能區分出明星仍是其次,重要的是區分如下的表達式。若是可以輕易識別,指針也算入門了。
int *p[n];
int (*p)[n];
int (*p)(int, int);
int (*p[])(int, int);
int p(int, int);
int *p(int, int);
int const *p;
int *const p;
int const * const p;