C++指针的应用
C++指針?文章中我們介紹了指針的基本概念和應用簡介。我們有提到指針可以使用在鏈表、隊列和二叉樹,等等。但是這些都會比較復查,后面"數據結構” 時,我們會用專門的章節來講解這些知識。
這篇文章,詳細的探討一下指針和其他關聯的具體應用。
1. 指針和數組的關系?
在C語言中,指針與數組之間的關系十分密切。實際上,許多可以用數組完成的工作都可以使用指針來完成。一般來說,用指針編寫的程序比用數組編寫的程序執行速度快,但另一方面,用指針實現的程序理解起來稍微困難一些。
我們先聲明一個數組:
int a[10];// 聲明一個int類型的數組,這個數組有10個元素我們可以用 a[0]、a[1]、...、a[9] 來表示這個數組中的10個元素,這10個元素是存儲在一段連續相鄰的內存區域中的。
接下來,我們再聲明一個指針:
int *p; // 聲明一個int類型的指針變量p 是一個指針變量,指向內存中的一個區域。如果我們對指針 p 做如下的初始化:
p = &a[0]; // 對指針進行初始化,p將指向數組 a 的第 1 個元素 a[0]我們知道,對指針進行自增操作會讓指針指向與當前元素相鄰的下一個元素,即 *(p + 1) 將指向 a[1] ;同樣的, *(p + i) 將指向 a[i] 。因此,我們可以使用該指針來遍歷數組 a[10] 的所有元素。可以看到,數組下標與指針運算之間的關系是一一對應的。而根據定義,數組類型的變量或表達式的值是該數組第 1 個元素的地址,且數組名所代表的的就是該數組第 1 個元素的地址,故,上述賦值語句可以直接寫成:
p = a; // a 為數組名,代表該數組最開始的一個元素的地址很顯然,一個通過數組和下標實現的表達式可以等價地通過指針及其偏移量來實現,這就是數組和指針的互通之處。但有一點要明確的是,數組和指針并不是完全等價,指針是一個變量,而數組名不是變量,它數組中第 1 個元素的地址,數組可以看做是一個用于保存變量的容器。更直接的方法,我們可以直接看二者的地址,并不一樣:
#include "stdio.h" int main(){int x[10] = {1,2,3,4,5,6,7,8,9,0};int *p = x;printf("x的地址為:%p\n",x);printf("x[0]的地址為:%p\n",&x[0]);printf("p的地址為:%p\n",&p); // 打印指針 p 的地址,并不是指針所指向的地方的地址p += 2;printf("*(p+2)的值為:%d\n",*p); // 輸出結果為 3,*(p+2)指向了 x[2]return 0; }結果如下:
可以看到, x 的值與 x[0] 的地址是一樣的,也就是說數組名即為數組中第 1 個元素的地址。實際上,打印 &x 后發現,x 的地址也是這個值。而 x 的地址與指針變量 p 的地址是不一樣的。故而數組和指針并不能完全等價。
2. 指針數組
指針是一個變量,而數組是用于存儲變量的容器,因此,指針也可以像其他變量一樣存儲在數組中,也就是指針數組。指針數組是一個數組,數組中的每一個元素都是指針。聲明一個指針數組的方法如下:
// 聲明一個指針數組,該數組有10個元素,其中每個元素都是一個指向int類型的指針 int?*p[10];在上述聲明中,由于 [] 的優先級比 * 高,故 p 先與 [] 結合,成為一個數組 p[];再由 int * 指明這是一個 int 類型的指針數組,數組中的元素都是 int 類型的指針。數組的第 i 個元素是 *p[i],而 p[i] 是一個指針。由于指針數組中存放著多個指針,操作靈活,在一些需要操作大量數據的程序中使用,可以使程序更靈活快速。
3. 數組指針
數組指針是一個指針,它指向一個數組。聲明一個數組指針的方法如下:
int (*p)[10]; // 聲明一個數組指針 p ,該指針指向一個數組由于 () 的優先級最高,所以 p 是一個指針,指向一個 int 類型的一維數組,這個一維數組的長度是 10,這也是指針 p 的步長。也就是說,執行 p+1 時,p 要跨過 n 個 int 型數據的長度。數組指針與二維數組聯系密切,可以用數組指針來指向一個二維數組,如下:
#include "stdio.h"int main(){int arr[2][3] = {1,2,3,4,5,6}; // 定義一個二維數組并初始化int (*p)[3]; // 定義一個數組指針,指針指向一個含有3個元素的一維數組 p = arr; // 將二維數組的首地址賦給 p,此時 p 指向 arr[0] 或 &arr[0][0]printf("%d\n",(*p)[0]); // 輸出結果為 1p++; // 對 p 進行算術運算,此時 p 將指向二維數組的下一行的首地址,即 &arr[1][0]printf("%d\n",(*p)[1]); // 輸出結果為5return 0; }?4. 結構體和指針
1) 簡單介紹一下結構體
結構是一個或多個變量的集合,這些變量可能為不同的類型,為了處理的方便而將這些變量組織在一個名字之下。由于結構將一組相關的變量看做一個單元而不是各自獨立的實體,因此結構有助于組織復雜的數據,特別是在大型的程序中。聲明一個結構的方式如下:
struct message{ // 聲明一個結構 messagechar name[10]; // 成員int age;int score; };typedef struct message s_message; // 類型定義符 typedef s_message mess = {"tongye",23,83}; // 聲明一個 struct message 類型的變量 mess,并對其進行初始化 /* 另一種更簡便的聲明方法 */ typedef struct{char name[10];int age;int score; }message;可以使用?“結構名.成員” 的方式來訪問結構中的成員,如下:
2) 結構體指針
結構指針是指向結構的指針,以上面的結構為例,可以這樣定義一個結構指針:
s_message *p; // 聲明一個結構指針 p ,該指針指向一個 s_message 類型的結構 *p = &mess; // 對結構指針的初始化與普通指針一樣,也是使用取地址符 &C語言中使用?“->” 操作符來訪問結構指針的成員,舉個例子:
#include "stdio.h"typedef struct{char name[10];int age;int score; }message; int main(){message mess = {"tongye",23,83};message *p = &mess;printf("%s\n",p->name); // 輸出結果為:tongyeprintf("%d\n",p->score); // 輸出結果為:83return 0; }?5. 指針和函數的關系
C語言的所有參數均是以“傳值調用”的方式進行傳遞的,這意味著函數將獲得參數值的一份拷貝。這樣,函數可以放心修改這個拷貝值,而不必擔心會修改調用程序實際傳遞給它的參數。?
1) 指針作為函數的參數
傳值調用的好處是是被調函數不會改變調用函數傳過來的值,可以放心修改。但是有時候需要被調函數回傳一個值給調用函數,這樣的話,傳值調用就無法做到。為了解決這個問題,可以使用傳指針調用。指針參數使得被調函數能夠訪問和修改主調函數中對象的值。用一個例子來說明:
#include "stdio.h" // 參數為普通的 int 變量 void swap1(int a,int b) {int temp;temp = a;a = b;b = temp; } // 參數為指針,接受調用函數傳遞過來的變量地址作為參數,對所指地址處的內容進行操作 // 最終結果是,地址本身并沒有改變,但是這一地址所對應的內存段中的內容發生了變化,即x,y的值發生了變化 void swap2(int *a,int *b) {int temp; temp = *a;*a = *b;*b = temp; } int main() {int x = 1,y = 2;swap1(x,y); // 將 x,y 的值本身作為參數傳遞給了被調函數printf("%d %5d\n",x,y); // 輸出結果為:1 2swap(&x,&y); // 將 x,y 的地址作為參數傳遞給了被調函數printf("%d %5d\n",x,y); // 輸出結果為:2 1return 0; }2) 指向函數的指針
在C語言中,函數本身不是變量,但是可以定義指向函數的指針,也稱作函數指針,函數指針指向函數的入口地址。這種類型的指針可以被賦值、存放在數組中、傳遞給函數以及作為函數的返回值等等。聲明一個函數指針的方法如下:
返回值類型 (* 指針變量名)([形參列表]);
int (*pointer)(int *,int *); // 聲明一個函數指針上述代碼聲明了一個函數指針 pointer ,該指針指向一個函數,函數具有兩個 int * 類型的參數,且返回值類型為 int。下面的代碼演示了函數指針的用法:
#include "stdio.h" #include "string.h"// 聲明一個函數 str_comp,該函數有兩個 const char 類型的指針,函數的返回值為 int 類型 int str_comp(const char *m,const char *n); // 聲明一個函數 comp ,注意該函數的第三個參數,是一個函數指針 void comp(char *a,char *b,int (*prr)(const char *,const char*)); int main() {char str1[20]; // 聲明一個字符數組char str2[20]; // 聲明并初始化一個函數指針,且返回值為 int 類型int (*p)(const char *,const char *) = str_comp; gets(str1); // 使用 gets() 函數從 I/O 讀取一行字符串 gets(str2);comp(str1,str2,p); // 函數指針 p 作為參數傳給 comp 函數return 0; }int str_comp(const char *m,const char *n) {// 庫函數 strcmp 用于比較兩個字符串if(strcmp(m,n) == 0) return 0;elsereturn 1; }/* 函數 comp 接受一個函數指針作為它的第三個參數 */ void comp(char *a,char *b,int (*prr)(const char *,const char*)) {if((*prr)(a,b) == 0)printf("str1 = str2\n");elseprintf("str1 != str2\n"); }推薦閱讀:
專輯|Linux文章匯總
專輯|程序人生
專輯|C語言
我的知識小密圈
嵌入式Linux
微信掃描二維碼,關注我的公眾號?
總結
- 上一篇: 数据库变为可疑_SQL Server 2
- 下一篇: 学习C++,知识点太多记不住怎么办?