指針是C語言最重要也是最難理解的部分,它在我們平時的工作中無處不在。
今天我們繼續(xù)來看看指針的剩下的知識總結(jié)吧!上一批的話可以在主頁看到哦~
5 指針與結(jié)構(gòu)體
一個指針,它指向的可以是一個結(jié)構(gòu)體類型,這稱為結(jié)構(gòu)體指針。而一個結(jié)構(gòu)體,它的成員中也可以有指針成員。
struct{ char *name; //姓名 int num; //學(xué)號 int age; //年齡 char group; //所在小組 float score; //成績} stu1 = { "Tom", 12, 18, 'A', 136.5 }, *pstu = &stu1;
上面的代碼中,定義了一個結(jié)構(gòu)體變量stu1。這個變量中有一個指針變量name。還定義了一個結(jié)構(gòu)體指針pstu。
我們想要通過結(jié)構(gòu)體指針訪問結(jié)構(gòu)體成員一般有以下兩種方式:
(*pstu).name;pstu->name;
6 指針與const:常量指針與指針常量
初學(xué)者常常對這兩個概念搞錯。首先,我認為需要理解這里說的常量是什么意思。常量就是只可讀不可修改的。那常量指針和指針常量到底哪個是只可讀不可修改的呢?是指針還是指針指向的內(nèi)容?這里有一個方法,能讓你迅速明白哪個是不可修改的。就是在聲明時,以星號(*)為界,分成兩部分,星號左邊的和星號右邊的。const在哪邊,那個就是只可讀不可修改的。以下面這個代碼為例,我們來分析一下:以星號(*)為界,星號左邊是char,沒有const關(guān)鍵詞,所以它指向的內(nèi)容不是常量。然后,我們看星號的右邊是const ptr,所以我們可以說ptr是一個常量。所以,這行代碼聲明了一個是常量的指針但是指向的內(nèi)容不是常量。即這個是一個指針常量。
char* const ptr = "just a string";
類似的,我們也可以分析下面的代碼:
// Neither the data nor the pointer are const//char* ptr = "just a string"; // Constant data, non-constant pointer//const char* ptr = "just a string"; // Constant pointer, non-constant data//char* const ptr = "just a string"; // Constant pointer, constant data//const char* const ptr = "just a string";
6.1 指針常量(Constant Pointers)
指針常量(Constant Pointers): 它的本質(zhì)是一個常量,只不過這個常量是指針。由于指針是只可讀不可修改的,所以這個指針不能指向別的地址了,但是該地址里的內(nèi)容還是可以改變的。指針常量的聲明格式如下:
* const 例如: int * const ptr;
我們來看下序:
#includeint main(void){ int var1 = 0, var2 = 0; int *const ptr = &var1; ptr = &var2; printf("%d ", *ptr); return 0;}
上面這段程序中:
我們首先定義了兩個變量var1,var2;
然后,定義了一個指針常量ptr,并且指向了var1
接著,試圖讓ptr指向var2
最后,打印出指針ptr指向的地址的內(nèi)容
讓我們來運行一下這個程序:
main.c: In function 'main':main.c9: error: assignment of read-only variable 'ptr' ptr = &var2; ^
我們看到這個程序編譯報錯了:試圖對只讀(read-only)變量ptr進行賦值。所以,一旦我們定義了指針常量,那這個指針就不能指向其他變量了。
但是我們還是可以修改指向的地址里的內(nèi)容的:
#includeint main(void){ int var1 = 0; int *const ptr = &var1; *ptr = 10; // OK printf("%d ", *ptr); // 10 return 0;}
6.2 常量指針(Pointer to Constants)
常量指針(Pointer to Constants):它的本質(zhì)是一個指針,只不過它指向的值是常量(只可讀,不可修改)。由于指向的是一個只可讀不修改的值,所以指針不能通過它存儲的地址間接修改這個地址的值,但是這個指針可以指向別的變量。
常量指針的聲明格式如下:
const* 例如: const int* ptr;
還是有一段程序:
#includeint main(void){ int var1 = 0; const int* ptr = &var1; *ptr = 1; printf("%d ", *ptr); return 0;}
我們還是來分析一下這個程序:
我們定義了一個變量 var1,并且初始化為0
然后我們定義了一個指針常量ptr,并且將它指向了var1
接著,試圖通過指針ptr來改變var1的值
最后,打印出ptr指向的地址的內(nèi)容。
我們進行編譯:
main.c: In function 'main':main.c10: error: assignment of read-only location '*ptr' *ptr = 1; ^
編譯報錯也很明顯: *ptr是一個只讀的。所以不能通過ptr來修改var1的值。
但是,我們可以將ptr指向其他的變量:
#includeint main(void){ int var1 = 0; const int* ptr = &var1; printf("%d ", *ptr); // 0 int var2 = 20; ptr = &var2; // OK printf("%d ", *ptr); // 20 return 0;}
6.3 指向常量的常量指針
理解了上面兩種類型的話,理解這個就很容易了。指向常量的常量指針是指這個指針既不能指向其他的地址也不能通過地址修改內(nèi)容。
它的聲明格式如下:
const* const 例如: const int* const ptr;
同樣,下面一段程序,我想你一定知道哪里編譯錯誤了。
#includeint main(void){ int var1 = 0,var2 = 0; const int* const ptr = &var1; *ptr = 1; ptr = &var2; printf("%d ", *ptr); return 0;}
編譯結(jié)果:
main.c: In function 'main':main.c10: error: assignment of read-only location '*ptr' *ptr = 1; ^main.c9: error: assignment of read-only variable 'ptr' ptr = &var2; ^
7 指針與函數(shù)
7.1 函數(shù)指針
指針與函數(shù)相結(jié)合有兩種情況:指針函數(shù)、函數(shù)指針。
指針函數(shù),它的本質(zhì)是一個函數(shù),它的返回值是一個指針。
int * func(int x, int y);
函數(shù)名本身就是一個指針(地址),這個地址就是函數(shù)的入口地址。
#includeint sum(int a, int b){ return a + b;} int main(){ printf("%p ", sum); return 0;}
輸出:
0000000000401550
而函數(shù)指針,它的本質(zhì)是一個指針。只不過它存的地址恰好是一個函數(shù)的地址罷了。
函數(shù)指針變量定義的格式一般是:
返回值 (*變量名)(參數(shù)列表)
比如:
#includeint sum(int a, int b){ return a + b;} int main(){ printf("%p ", sum); int (*psum)(int, int); // 函數(shù)指針變量,參數(shù)名可以省略 psum = sum; printf("%p ", psum); return 0;}
輸出:
00000000004015500000000000401550
可以發(fā)現(xiàn),兩者地址相等。
函數(shù)指針類型的定義:
typedef 返回值 (* 類型名)(參數(shù)列表);復(fù)制代碼
比如:
typedef int(*PSUM)(int, int);PSUM pSum2 = sum;PSUM pSum3 = sum;?
這樣的好處就是,首先通過typedef定義一個函數(shù)指針類型PSUM,定義完后,PSUM就相當(dāng)于一種新的類型,可以用此類型去定義其他函數(shù)指針變量,就不用每次都使用int(*pSum)(int, int);來定義一個函數(shù)指針變量。
#includeint sum(int a, int b){ return a + b;}int func2(int a, int b){ return a - b;}typedef int (*PFUNC) (int, int);int main(){ int (*psum)(int, int); psum = sum; printf("psum(4, 5):%d ", psum(4, 5)); PFUNC p2 = func2; printf("p2(5, 2):%d ", p2(5, 2)); p2 = sum; printf("p2(5, 2):%d ", p2(5, 2)); return 0;}
輸出:
psum(4, 5):9p2(5, 2):3p2(5, 2):7
7.2 回調(diào)函數(shù)
說到函數(shù)指針,那還有一個概念不得不提——回調(diào)函數(shù)。因為在實際的項目代碼中實在是太常見了。
回調(diào)函數(shù)就是一個通過函數(shù)指針調(diào)用的函數(shù)。如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個函數(shù),當(dāng)這個指針被用來調(diào)用其所指向的函數(shù)時,我們就說這是回調(diào)函數(shù)。
那為什么要使用回調(diào)函數(shù)呢?或者說使用回調(diào)函數(shù)有什么好處呢?回調(diào)函數(shù)允許用戶把需要調(diào)用的方法的指針作為參數(shù)傳遞給一個函數(shù),以便該函數(shù)在處理相似的事情時,可以靈活的使用不同的方法。
怎么使用回調(diào)函數(shù):
#includeint Callback_1(int a) ///< 回調(diào)函數(shù)1{ printf("Hello, this is Callback_1: a = %d ", a); return 0;} int Callback_2(int b) ///< 回調(diào)函數(shù)2{ printf("Hello, this is Callback_2: b = %d ", b); return 0;} int Callback_3(int c) ///< 回調(diào)函數(shù)3{ printf("Hello, this is Callback_3: c = %d ", c); return 0;} int Handle(int x, int (*Callback)(int)) // 注意這里用到的函數(shù)指針定義{ Callback(x);} int main(){ Handle(4, Callback_1); Handle(5, Callback_2); Handle(6, Callback_3); return 0;}
如上述代碼:可以看到,Handle()函數(shù)里面的參數(shù)是一個指針,在main()函數(shù)里調(diào)用Handle()函數(shù)的時候,給它傳入了函數(shù)Callback_1()/Callback_2()/Callback_3()的函數(shù)名,這時候的函數(shù)名就是對應(yīng)函數(shù)的指針,也就是說,回調(diào)函數(shù)其實就是函數(shù)指針的一種用法。
8 二維指針
二維指針,或者二級指針。就是指向指針的指針。比如:
#includeint main(){ int a = 10; int *pa = &a; int **ppa = &pa; printf("%p, %p, %p, %p, %p", a, pa, *pa, ppa, *ppa); return 0;}
輸出如下:
000000000000000A, 000000000061FE14, 000000000000000A, 000000000061FE08, 000000000061FE14
從輸出結(jié)果也可以看到,pa存的內(nèi)容*pa= 000000000000000A,剛好與a的地址相同。而ppa存的內(nèi)容*ppa= 000000000061FE14也剛好等于pa的地址。它們之間的內(nèi)存關(guān)系可以用如下的圖表示:
8.1 命令行參數(shù)
處理命令行參數(shù)是指向指針的指針的一個用武之地。
一般main函數(shù)具有兩個形參。第一個通常稱為argc,它表示命令行參數(shù)的數(shù)目。第2個通常稱為argv,它指向一組參數(shù)值。由于參數(shù)的數(shù)目并沒有內(nèi)在的限制,所以argv指向這組參數(shù)值(從本質(zhì)上來說是一個數(shù)組)的第一個元素。這些元素的每個都是指向一個參數(shù)文本的指針。如果程序需要訪問命令行參數(shù),main函數(shù)在聲明時就要加上這些參數(shù)。
【int main(int argc, char **argv)
舉例:
#includeint main(int argc, char *argv[]){ printf("argc: %d ", argc); // 打印參數(shù),直到遇到NULL指針。程序名被跳過 while (*++argv != NULL) { printf("%s ", *argv); } return 0;}
在windows上執(zhí)行: est2.exe hello world
argc: 3helloworld
注意,如果命令行中傳遞的一個參數(shù)包括空格,就需要用 ""將參數(shù)括起來,比如:
. est2.exe "hello word"
則上面的代碼將輸出:
argc: 2hello word
9 結(jié)束語
本文關(guān)于指針的講解就結(jié)束了。我相信你一定對指針有更深入的了解。
-
C語言
+關(guān)注
關(guān)注
180文章
7604瀏覽量
136683 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4327瀏覽量
62569 -
指針
+關(guān)注
關(guān)注
1文章
480瀏覽量
70551
原文標(biāo)題:C語言基礎(chǔ)知識:最核心的—指針!知識總結(jié)(第二部分)
文章出處:【微信號:cyuyanxuexi,微信公眾號:C語言編程學(xué)習(xí)基地】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論