目錄
- 指針介紹與基本用法
- 雙重指針
- 函數指針
- 空指針與野指針
- 函數參數的指針傳遞
- 最后
指針一般在C/C++語言學習的后期接觸,這樣就導致指針給新手一種高深莫測、難以掌握的刻板印象。但實際上指針的使用很簡單,并且還能夠極大的提高程序的靈活性,幫助我們輕松實現復雜的功能。
指針介紹與基本用法
?指針是什么?和上一章將的變量一樣,指針也是一種變量罷了。與普通變量不同的是,指針變量存儲的內容是地址數據,像什么0xffee之類的十六進制數據,并且它也有自己的地址,可以通過取地址符號&
進行取址。下面這個代碼定義了一個空指針a并且輸出a的地址:
#include<iostream>
#include <cstring>
using namespace std;int main(){
char* a = NULL;
cout << &a <<endl; //輸出0xe3c49ff878
return 0;
}
?指針也是一個變量,就像int類型里面存儲整數,float存儲小數一樣,指針也有自己的使命:存儲其他變量的地址。因此,不能給指針賦數字或者字符,指針內只能裝載其它變量的地址數據。
?指針的語法為:type * ptr_name
例如int * a;float *b
等啊,前面的type表明了這個指針存儲的地址值對應的變量的類型。int類型就只能存儲int類型變量的地址,float類型就只能存儲float類型變量的地址。
#include<iostream>
#include <cstring>
using namespace std;int main(){int a = 1;float b = 0.01;int * i_Ptr = &a;float * f_Ptr = &b;char* c_Ptr = &a ; //報錯:"int *" 類型的值不能用于初始化 "char *" 類型的實體C/C++(144)
return 0;
}
?指針使用的精髓與最強大的地方在于取值符*
,通過*
實現了根據地址找到指針指向的變量。這個功能真的很振奮人心,是int,float等其他變量類型所沒有的功能。看下面這個例子,*ptr = variable
#include<iostream>
#include <cstring>
using namespace std;int main(){int a = 1;float b = 0.01;int * i_Ptr = &a;float * f_Ptr = &b;//通過*,找到了對應地址的變量a,bcout << *i_Ptr <<endl; //輸出:1cout << *f_Ptr <<endl; //輸出 0.01//再通過地址來修改對應變量的值,此時*i_Ptr = a,*f_Ptr = b*i_Ptr = 2; *f_Ptr = 0.02;cout <<a <<endl; //輸出2cout <<b<<endl; //輸出0.02
return 0;
}
?為了進一步說明取值符*
的作用,繪圖一張如下:揭示了指針的指向變量的功能
雙重指針
?上面這張圖最后出現了一個指向指針的指針,我們也稱這種指針叫雙重指針(另外還有三重、四重等,學會了雙重指針其余的都是一樣的)。雙指針即指針內存儲的值(指向的變量的地址)是另一個指針的地址。例如上圖的d,有d=&p,則*d = p。
?雙指針的作用在于,通過雙指針(d)可以在函數中調用以及修改指針(p)指向的變量(通過*d = p這個方法可以修改指針p的值從而改變指向對象,以及**d = *p =a,從而修改指針p指向的變量a的值)
下面這個是通過**d修改雙重指針指向的指針(b)所指向的對象(a)的值,把a的值改成了10
#include<iostream>
using namespace std;
void func1(float** d){**d = 10; //通過**d改變指針指向對象的值
}
int main(){float a = 1;float *b = &a;func1(&b); cout << a<<endl; //輸出a=10,即通過**d = a實現修改指向指針的值return 0;
}
下面這個是通過*d修改雙重指針指向的指針(b)所指向的對象(a),把指向的對象從a轉成了b
#include<iostream>
using namespace std;
float c = 2;
void func1(float** d){*d = &c; //通過*d改變指針指向的對象
}
int main(){float a = 1;float *b = &a; //此時 b指向afunc1(&b); //函數更改了b指向的對象cout << *b <<endl; //輸出2,即通過*d = b,實現修改指向指針所指向的對象;return 0;
}
函數指針
?函數指針顧名思義就是指向一個函數的指針,通過函數指針可以調用函數。事實上,函數名本身就是一個函數指針,其存儲著函數的入口地址,寫一段小試驗打印函數指針地址內容如下:
#include<iostream>
using namespace std;void func1(int a,int b)
{cout << (a+b) <<endl;
}int main()
{cout << (void*)func1 <<endl; //輸出0x7ff701251450return 0;
}
?函數指針主要由函數返回值類型以及函數參數類型來定義。語法為返回值類型(*指針名)(參數1類型,參數2類型...)
例如指向前面代碼片段的函數指針可以被定義為void(*p)(int,int)
。函數指針存儲函數入口地址,最大的功能就是將函數作為參數傳入另一個函數,示例如下:
#include<iostream>
using namespace std;void func1(int a,int b)
{cout << (a+b) <<endl;
}void func2(void (*f)(int,int),int c)
{f(1,3);cout << c <<endl;
}int main()
{void(*p)(int,int);p = func1;func2(p,3); //輸出 4 3//直接func2(func1,3)也行return 0;
}
空指針與野指針
?空指針是指不指向任何變量的指針,C++中常用NULL
宏來對其進行定義。空指針的作用就是初始化指針,例如在進行內存分配前,我們可能需要先把指針給定義出來,此時空指針不指向任何對象,被初始化為0。來看這樣一個例子:
#include<iostream>
using namespace std;
int main(){int* a; //野指針cout<< a <<endl; //輸出0x7f6de082ba50cout<< *a <<endl; //輸出 2int* b = NULL; //空指針cout<<b<<endl; //輸出0cout<<*b <<endl; //輸出Segmentation fault (core dumped)return 0;
}
?我們把未初始化,或者指向無效對象的指針叫做野指針,例如上面例子中的指針a。a指向了一個完全隨機的內存位置,這樣就有可能造成越界訪問的問題,進而導致程序崩潰。反觀指針b被定義為空指針,其值為0,并且如果此時訪問空指針,操作系統會報錯,對內存數據進行保護,減小了程序崩潰的風險。
函數參數的指針傳遞
?指針的一個重要的應用就是函數參數的指針傳遞,即傳入函數的參數不是變量本身而是變量的地址。如果傳入的是變量那么這種傳參方式叫值傳遞,編譯器會拷貝一份參數值到函數內部。這樣一來是無法修改對應的變量,因為只傳入了值,沒有對應地址;二來拷貝需要占用內存空間。
通過指針傳遞的方法,可以在不使用返回值的前提下(使用返回值的方法會產生數據拷貝增大函數內存開銷),通過取值操作符*來直接改變變量的值。并且由于傳入的是變量地址,并不會函數本身不會占用太多的內存空間。二者對比的測試函數如下:
#include<iostream>
using namespace std;
float c = 2;//值傳遞
void func1(float d){d = 10; //實際上只是函數內部創建了一個局部變量float d;
}
//指針傳遞
void func2(float* d){*d = 10; //通過*d改變指針指向的對象
}int main(){float a = 1;float *b = &a; //此時 b指向afunc1(a);cout << a <<endl; //輸出 a = 1,并沒有改變a的值。func2(&a); //函數可以直接改變a的值cout << a <<endl; //輸出10,即修改指向指針所指向的對象的值;return 0;
}
雙重指針的作用也是類似,可以實現在函數中改變傳入的指針變量,例如改變指向對象,改變內存分配等。上一節雙重指針的兩個實例可以仔細研讀一下。這里給一個通過雙重指針實現自定義內存分配的例子:
#include<iostream>
using namespace std;
float c = 2;//函數功能:為指針分配內存空間
void func1(float **d){*d = new float[2]; //*d相當于傳入的指針a
}int main(){float *a = NULL;//定義了一個空指針cout <<a[1]<<endl; //越界訪問,程序出錯func1(&a); cout << a[1] <<endl; //輸出一個隨機值return 0;
}
最后
?最后想說一下,關于指針上面的內容只是基礎,后續還有關于指針數組和數組指針的一些內容,我將會放在數組那一章再介紹。