C語(yǔ)言指針超詳細(xì)講解上篇
前言
本文開(kāi)始指針相關(guān)內(nèi)容的學(xué)習(xí),主要內(nèi)容包括:
- 指針是什么
- 指針和指針類型
- 野指針
- 指針運(yùn)算
- 指針和數(shù)組
- 二級(jí)指針
- 指針數(shù)組
1、指針是什么
指針理解的2個(gè)要點(diǎn):
- 平時(shí)口語(yǔ)中說(shuō)的指針,通常指的是指針變量,是用來(lái)存放內(nèi)存地址的變量
- 指針是內(nèi)存中一個(gè)最小單元的編號(hào),也就是地址
1.1 指針變量
我們可以通過(guò)&(取地址操作符)取出變量的內(nèi)存其實(shí)地址,把地址可以存放到一個(gè)變量中,這個(gè)變量就是指針變量:
int main() { int a = 1;//在內(nèi)存中開(kāi)辟一塊空間 //a變量占用4個(gè)字節(jié)的空間,這里是將a的4個(gè)字節(jié)的第一個(gè)字節(jié)的地址存放在p變量中,p就是一個(gè)指針變量 int* pa = &a;//這里我們對(duì)變量a,取出它的地址,可以使用&操作符 printf("%p\n", &a); printf("%p\n", pa); a = 10; printf("%p\n", &a); return 0; }
利用內(nèi)存和監(jiān)視來(lái)查看&a 和pa的變化:
見(jiàn)下圖所示:
- &a為取變量a的地址:0x00CFFEE0
- pa是指針變量,存放的值是變量a的地址: 0x00CFFEE0
a變量占用4個(gè)字節(jié)的空間,下圖在內(nèi)存中能看到a的地址占用了4個(gè)字節(jié),存放 00 00 00 01
內(nèi)存顯示列數(shù)可根據(jù)自己調(diào)節(jié),4列就是4個(gè)字節(jié)放一行,見(jiàn)下圖:
對(duì)a重新賦值10,變量a的值發(fā)生變化,但地址是不變的。
因此,指針變量,用來(lái)存放地址的變量。(存放在指針中的值都被當(dāng)成地址處理)。
1.2 指針是內(nèi)存中一個(gè)最小單元的編號(hào)
- 指針是內(nèi)存中一個(gè)最小單元的編號(hào),這個(gè)最小單元是一個(gè)字節(jié)
- 經(jīng)過(guò)專家們仔細(xì)的計(jì)算和權(quán)衡后,發(fā)現(xiàn)一個(gè)字節(jié)給一個(gè)對(duì)應(yīng)的地址是比較合適的
- 對(duì)于32位的機(jī)器,假設(shè)有32根地址線,那么假設(shè)每根地址線在尋址的時(shí)候產(chǎn)生高電平(高電壓)和低電平(低電壓)就是(1或者0),32根地址線產(chǎn)生的地址就是:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
...
11111111 11111111 11111111 11111111
內(nèi)存最小單元即一個(gè)字節(jié),與其對(duì)應(yīng)的地址見(jiàn)下圖:
因此,32位的機(jī)器就有2的32次方個(gè)地址。每個(gè)地址標(biāo)識(shí)一個(gè)字節(jié),那我們就可以給 :
2^32 Byte == 2^32/1024 KB == 2^32/1024/1024 MB == 2^32/1024/1024/1024 GB == 4 GB
4 GB的空閑進(jìn)行編址。同樣的方法,那64位機(jī)器,如果給64根地址線,那能編址 8 GB 空間。
- 在32位的機(jī)器上,地址是32個(gè)0或者1組成二進(jìn)制序列,那地址就得用4個(gè)字節(jié)的空間來(lái)存儲(chǔ),所以一個(gè)指針變量的大小就應(yīng)該是4個(gè)字節(jié)
- 在64位機(jī)器上,有64個(gè)地址線,那一個(gè)指針變量的大小是8個(gè)字節(jié),才能存放一個(gè)地址
- 指針是用來(lái)存放地址的,地址是唯一標(biāo)示一塊地址空間的
- 指針的大小在32位平臺(tái)是4個(gè)字節(jié),在64位平臺(tái)是8個(gè)字節(jié)
int main() { int a = 10; int* pa = &a; char ch = 'a'; char* pc = &ch; printf("%d\n", sizeof(pa));//4 printf("%d\n", sizeof(pc));//4 return 0; }
上例說(shuō)明,不管什么類型的指針變量,它的大小就是4個(gè)字節(jié)。因?yàn)榈刂肥?2位0 1表示的,而指針變量就是存放地址的變量,需要4個(gè)字節(jié)存放。跟他的類型無(wú)關(guān)。
2、指針和指針類型
變量有不同的類型,整形,浮點(diǎn)型等。那指針同樣也有類型:
int num = 10; p = #//num的地址保存到 p
要將&num(num的地址)保存到 p 中,我們知道 p 就是一個(gè)指針變量,那它的類型是怎樣的呢?我們給指針變量相應(yīng)的類型
char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;
指針的定義方式是: type + *
- char* 類型的指針是為了存放 char 類型變量的地址:即地址里面的變量是char 類型,占用1個(gè)字節(jié),地址本身4個(gè)字節(jié)
- short* 類型的指針是為了存放 short 類型變量的地址:即地址里面的變量是short類型,占用1個(gè)字節(jié),地址本身4個(gè)字節(jié)
- int* 類型的指針是為了存放 int 類型變量的地址:即地址里面的變量是 int 類型,占用4個(gè)字節(jié),地址本身4個(gè)字節(jié)
2.1 指針±類型
int main() { int n = 10; char *pc = (char*)&n; int *pi = &n; printf("%p\n", &n); printf("%p\n", pc); printf("%p\n", pc+1); printf("%p\n", pi); printf("%p\n", pi+1); return 0; }
指針的類型決定了指針向前或者向后走一步有多大(距離):
- int *向后+1,就是移動(dòng)到下一個(gè) int 類的變量的地址,就是移動(dòng)4個(gè)字節(jié)
- char *向后+1,就是移動(dòng)到下一個(gè) char 類型的變量的地址,就是移動(dòng)1個(gè)字節(jié)
- 指針的類型,是指向地址里存儲(chǔ)數(shù)值的類型
2.2 指針的解引用
指針的類型決定了,對(duì)指針解引用的時(shí)候有多大的權(quán)限(能操作幾個(gè)字節(jié))
2.2.1 int* 類型的解引用
int main() { int a = 0x11223344; int* pa = &a; *pa = 0; return 0;
int* 的指針的解引用就能訪問(wèn)四個(gè)字節(jié):
2.2.2 char* 類型的解引用
int main() { int a = 0x11223344; char* pa = (char*)&a;//&a是int*,所以在這里強(qiáng)制轉(zhuǎn)換 *pa = 0; return 0; }
char* 的指針解引用就只能訪問(wèn)一個(gè)字節(jié):
3、野指針
野指針就是指針指向的位置是不可知的(隨機(jī)的、不正確的、沒(méi)有明確限制的)
3.1 野指針成因
3.1.1 指針未初始化
int main() { int *p;//局部變量指針未初始化,默認(rèn)為隨機(jī)值 *p = 20; return 0; }
3.1.2 指針越界訪問(wèn)
int main() { int arr[10] = {0}; int *p = arr; int i = 0; for(i=0; i<=11; i++)//超過(guò)數(shù)組的元數(shù)個(gè)數(shù) { //當(dāng)指針指向的范圍超出數(shù)組arr的范圍時(shí),p就是野指針 *(p++) = i; } return 0; }
3.1.3 指針指向的空間釋放
int* test() { int a = 10; printf("%d\n", a); return &a;//開(kāi)辟的空間已經(jīng)釋放了 } int main() { int* p = test();//函數(shù)調(diào)用結(jié)束后,開(kāi)辟的內(nèi)存空間釋放了 *p = 100; return 0; }
3.2 如何規(guī)避野指針
- 指針初始化
- 小心指針越界
- 指針指向空間釋放即使置NULL
- 避免返回局部變量的地址
- 指針使用之前檢查有效性
int main() { int *p = NULL;//初始化 //.... int a = 10; p = &a; if(p != NULL) { *p = 20; } return 0; }
總結(jié)
本文學(xué)習(xí)了指針的部分內(nèi)容,下一篇繼續(xù)學(xué)習(xí)指針的內(nèi)容。(鏈接直達(dá))
到此這篇關(guān)于C語(yǔ)言指針超詳細(xì)講解上篇的文章就介紹到這了,更多相關(guān)C語(yǔ)言 指針內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++版本基于ros將文件夾中的圖像轉(zhuǎn)換為bag包
這篇文章主要介紹了C++版本基于ros將文件夾中的圖像轉(zhuǎn)換為bag包,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01C++實(shí)現(xiàn)LeetCode(13.羅馬數(shù)字轉(zhuǎn)化成整數(shù))
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(13.羅馬數(shù)字轉(zhuǎn)化成整數(shù)),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07C語(yǔ)言判斷一個(gè)數(shù)是否是2的冪次方或4的冪次方
本文中我們來(lái)看一下如何用C語(yǔ)言判斷一個(gè)數(shù)是否是2的冪次方或4的冪次方的方法,并且判斷出來(lái)是多少次方,需要的朋友可以參考下2016-06-06MoveWindow() SetWindowPos()的區(qū)別于聯(lián)系
這篇文章主要介紹了VC++中MoveWindow() SetWindowPos()的區(qū)別于聯(lián)系,需要的朋友可以參考下2015-01-01C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單通訊錄系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單通訊錄系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07深入解析C++的WNDCLASS結(jié)構(gòu)體及其在Windows中的應(yīng)用
這篇文章主要介紹了C++的WNDCLASS結(jié)構(gòu)體及其在Windows中的應(yīng)用,WNDCLASS被用來(lái)定義窗口,文中介紹了其諸多屬性,需要的朋友可以參考下2016-01-01