深圳網(wǎng)站建設(shè)公司開發(fā)制作網(wǎng)站臺州seo快速排名
大家好,我是蘇貝,本篇博客帶大家了解C語言中令人頭疼的指針,如果大家覺得我寫的不錯的話,可以給我一個(gè)贊👍嗎,感謝??
使用的是VS2019編譯器,默認(rèn)為32位平臺
文章目錄
- ①指針是什么
- ②指針定義與&和*操作符
- 1.指針定義
- 2.&和*操作符
- ③指針類型
- ④ 野指針
- 1.野指針成因
- 2.如何規(guī)避野指針
- ⑤指針運(yùn)算
- 1.指針 + - 整數(shù) = 指針
- 2.指針 - 指針
- 3.指針的關(guān)系運(yùn)算
- ⑥二級指針
- ⑦指針和數(shù)組
- ⑧指針數(shù)組
①指針是什么
指針理解的2個(gè)要點(diǎn):
- 指針是內(nèi)存中一個(gè)最小單元的編號,也就是地址
單元編號 == 地址 ==C語言中也叫: 指針 - 平時(shí)口語中說的指針,通常指的是指針變量,是用來存放內(nèi)存地址的變量
內(nèi)存:
指針變量:
我們可以通過&(取地址操作符)取出變量的內(nèi)存起始地址,把地址可以存放到一個(gè)變量中,這個(gè)變量就是指針變量
要知道
1.內(nèi)存被劃分為一個(gè)個(gè)的內(nèi)存單元,每個(gè)內(nèi)存單元的大小是一個(gè)字節(jié)
2.每個(gè)字節(jié)的內(nèi)存單元都有一個(gè)編號,這個(gè)編號就是地址,地址在C語言中被稱為指針
3.每個(gè)內(nèi)存單元都有唯一的地址來標(biāo)識
4.地址要存儲的話,存放在指針變量中
5.在32位機(jī)器上,地址的大小為4個(gè)字節(jié),所以指針變量的大小也為4個(gè)字節(jié)
在64位機(jī)器上,地址的大小為8個(gè)字節(jié),所以指針變量的大小也為8個(gè)字節(jié)
對第5點(diǎn)進(jìn)行解釋
對于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
每個(gè)地址有32個(gè)比特位,即32/8=4個(gè)字節(jié)
同理:對于64位的機(jī)器,有8個(gè)字節(jié)
那32和64位機(jī)器能編址多大空間呢?
由上方解釋可知,32位機(jī)器就有2的32次方個(gè)地址。每個(gè)地址標(biāo)識一個(gè)字節(jié),那我們就可以給 (2^ 32Byte = 2^ 32/1024KB =2^ 32/1024/1024MB=2^32/1024/1024/1024GB = 4GB) 4G的空閑進(jìn)行編址。
同理,64位機(jī)器就可以給 2^ 64Byte 的空閑進(jìn)行編址。
②指針定義與&和*操作符
1.指針定義
這里我們在討論一下:指針的類型
我們都知道,變量有不同的類型,整形,浮點(diǎn)型等。那指針有沒有類型呢?
準(zhǔn)確的說:有的。當(dāng)有這樣的代碼:
(1) int* p;
(2) char* p;
(3) int** p;
(1)* 代表p是指針變量,int* 是指針變量p的類型,int 是p所指向的類型,即p所指向的是int類型的變量
(2)* 代表p是指針變量,char* 是指針變量p的類型,int 是p所指向的類型
(3)離p最近的 * 代表p是指針變量,int* * 是指針變量p的類型,int* 是p所指向的類型
2.&和*操作符
這里的&是取地址操作符,*是間接訪問操作符
int a=10;
int* p=&a;
p=&a的意思是:用&操作符取出a的地址,放在指針變量p中,指針變量的類型是 a的類型 + 一個(gè) * 號,例如:a是int類型的,p的類型為int * ; a是char類型的,p的類型為char * ……
int a=10;
int *p=&a;
*p=0;
*p=0;意思是通過 *間接訪問操作符解引用找到p指向的那個(gè)變量并將之賦值為0(即將0賦值給a)*p==a
int main()
{int a = 10;//在內(nèi)存中開辟一塊空間存儲aint* p = &a;//用&操作符取出a的地址,放在指針變量p中*p = 20;//p通過*解引用找到a并將之賦值為20,*p==aprintf("a=%d\n", a);return 0;
}
//a=20
③指針類型
先看下面代碼的結(jié)果,正如我們上面所說的,在32位平臺下地址的大小為4個(gè)字節(jié),那既然大家字節(jié)數(shù)都一樣,為什么還要區(qū)分char* ,int* 等類型呢?直接用一個(gè)籠統(tǒng)的類型如all*類型不可以嗎?
答案是不可以。為什么呢?
因?yàn)橹羔橆愋褪怯幸饬x的,指針類型決定了指針進(jìn)行解引用操作時(shí)訪問幾個(gè)字節(jié)
示例1:
因?yàn)橄聢D的p是整型指針,指向的是一個(gè)int類型的變量,所以解引用時(shí)訪問4個(gè)字節(jié),所以在進(jìn)行*p=0操作后,0x11223344全部變成0
示例2:
將上圖的int* p=&a;改為char* p=&a;
因?yàn)橄聢D的p是 char* 指針,指向的是一個(gè)char類型的變量,所以解引用時(shí)訪問1個(gè)字節(jié),所以在進(jìn)行*p=0操作后,0x11223344全部變成0x11223300
示例3:
指針類型決定了指針 +1 / -1跳過幾個(gè)字節(jié)(與指針類型進(jìn)行解引用操作時(shí)訪問的字節(jié)數(shù)相同)
總結(jié)
指針的類型決定了,對指針解引用的時(shí)候有多大的權(quán)限(能操作幾個(gè)字節(jié))。
比如: char* 的指針解引用就只能訪問1個(gè)字節(jié),而 int* 的指針的解引用就能訪問4個(gè)字節(jié),double* 的指針解引用就只能訪問8個(gè)字節(jié)……
④ 野指針
概念: 野指針就是指針指向的位置是不可知的(隨機(jī)的、不正確的、沒有明確限制的)
1.野指針成因
(1). 指針未初始化:
指針未初始化,默認(rèn)為隨機(jī)值
int main()
{int* p;//局部變量指針未初始化,默認(rèn)為隨機(jī)值*p = 20;return 0;
}
(2).指針越界訪問:
數(shù)組arr只有10個(gè)元素,但卻循環(huán)了11次,循環(huán)第11次時(shí),針指向的范圍超出數(shù)組arr的范圍時(shí),p成為野指針
解釋*p++ = i ;
++(后綴自增操作符)的優(yōu)先級高于*(間接訪問操作符),所以p先后置++,再與 * 結(jié)合,但由于后置++是先使用再自增,所以執(zhí)行*p=i操作后再自增,所以數(shù)組首元素被賦值為0后p自增1,使得p指向數(shù)組的第二個(gè)元素(跳了4個(gè)字節(jié),原因在上面指針類型)
int main()
{int arr[10] = { 0 };int* p = arr;//p指向arr的首元素int i = 0;for (i = 0; i <= 10; i++){//當(dāng)指針指向的范圍超出數(shù)組arr的范圍時(shí),p就是野指針*p++ = i;}return 0;
}
(3).指針指向的空間釋放:
a是函數(shù)內(nèi)的局部變量,在函數(shù)調(diào)用結(jié)束后,a所開辟的內(nèi)存空間會被釋放,即使在釋放前將a的地址傳了出去,但p接收地址的時(shí)候a所開辟的內(nèi)存空間已被釋放,此時(shí)再用* 對p進(jìn)行解引用,即使找到了該地址也屬于非法訪問
int* text()
{int a = 10;return &a;
}int main()
{int* p = text();//p就是野指針printf("*p=%d", *p);return 0;
}
2.如何規(guī)避野指針
- 指針初始化(若一開始不知道指針該指向哪,就先指向NULL)
NULL就是0,當(dāng)0作為地址時(shí),用戶程序是不能訪問的
- 小心指針越界
- 指針指向的空間釋放,那就使之指向NULL
- 避免返回局部變量的地址
- 指針使用之前檢查有效性
int main()
{int* p = NULL;//1//....int a = 10;p = &a;if (p != NULL)//5{*p = 20;}return 0;
}
⑤指針運(yùn)算
1.指針 + - 整數(shù) = 指針
int main()
{int arr[9] = { 1,2,3,4,5,6,7,8,9 };//使用指針打印數(shù)組的內(nèi)容int* p = arr;//arr是首元素地址,所以p指向數(shù)組首元素int i = 0;for (i = 0; i < 9; i++){printf("%d ", *(p + i));//p指向數(shù)組首元素,即下標(biāo)為0的元素//p+i指向數(shù)組下標(biāo)為i的元素//*(p+i)是通過*解引用找到下標(biāo)為i的元素//p+i 與p相比,跳過了i*sizeof(int)個(gè)字節(jié)}return 0;
}
//1 2 3 4 5 6 7 8 9
上面arr是首元素地址,指針變量p里面存的也是首元素地址,所以arr==p
因此arr+i == p+i, arr[i] == p[i](下標(biāo)為i的元素的地址)
*(arr+i) == *(p+i) = arr[i]
下面表達(dá)式中,左邊數(shù)組的這種形式在編譯器處理時(shí)會轉(zhuǎn)化為右邊的表達(dá)式,又因?yàn)閇 ] 和+ 一樣只是一個(gè)操作符,所以i寫在[ ]里面或外面都可以
arr[ i ] == *(arr+i) //當(dāng)然,更建議寫成 arr[ i ] 這種形式
i [ arr] == *(i+arr)
拓展:
數(shù)組名是數(shù)組首元素地址,除了以下2種情況:
(1)sizeof(數(shù)組名),此時(shí)的數(shù)組名代表整個(gè)數(shù)組,所以計(jì)算結(jié)果是整個(gè)數(shù)組的大小
(2)&數(shù)組名,此時(shí)的數(shù)組名也代表整個(gè)數(shù)組,取出的是整個(gè)數(shù)組的地址,返回的是數(shù)組首元素的地址,但絕不代表&數(shù)組名==arr(首元素地址),如下:
arr和&arr[0]等價(jià),都是首元素地址,類型是int*,所以+1跳過4個(gè)字節(jié)
&arr是取出了整個(gè)數(shù)組的地址,但返回首元素地址,所以+1跳過整個(gè)數(shù)組即10* 4=40個(gè)字節(jié)
sizeof(arr)計(jì)算結(jié)果是整個(gè)數(shù)組的大小即10* 4=40個(gè)字節(jié)
2.指針 - 指針
指針-指針的前提:兩個(gè)指針指向同一塊區(qū)域,指針類型相同
指針-指針差值的絕對值是:指針和指針之間的元素個(gè)數(shù)
了解了這些之后,我們是不是又多了一種計(jì)算字符個(gè)數(shù)的功能的自定義函數(shù)my_strlen()實(shí)現(xiàn)的方法
點(diǎn)擊鏈接了解:自定義實(shí)現(xiàn)strlen函數(shù)的3種方法
3.指針的關(guān)系運(yùn)算
請看下面代碼:
int main()
{int arr[5];int* vp = NULL;for (vp = &arr[5]; vp > &arr[0];){*--vp = 0;}return 0;
}
若不想一開始就越界,有人可能會修改成這樣的代碼:
int main()
{int arr[5];int* vp = NULL;for (vp = &arr[4]; vp >= &arr[0];vp--){*vp = 0;}return 0;
}
那這兩種方法哪個(gè)更好呢?答案是第一種,第二種在絕大部分的編譯器上是可以順利完成任務(wù)的,然而我們還是應(yīng)該避免這樣寫,因?yàn)闃?biāo)準(zhǔn)并不保證它可行。
標(biāo)準(zhǔn)規(guī)定:
允許指向數(shù)組元素的指針與指向數(shù)組最后一個(gè)元素后面的那個(gè)內(nèi)存位置的指針比較,但是不允許與指向第一個(gè)元素之前的那個(gè)內(nèi)存位置的指針進(jìn)行比較
⑥二級指針
指針變量也是變量,是變量就有地址,那指針變量的地址存放在哪里?
存放在 二級指針 中
*pp 通過對pp中的地址進(jìn)行解引用,這樣找到的是 p , *pp 其實(shí)訪問的就是 p
**pp 先通過 *pp 找到 p ,然后對 p 進(jìn)行解引用操作: *p ,那找到的是 a
int main()
{int a = 10;int* p = NULL;int** pp = NULL;*pp = &a;//等價(jià)于 p = &b;**pp = 30;//等價(jià)于*p = 30;//等價(jià)于a = 30;return 0;
}
⑦指針和數(shù)組
指針就是指針,指針變量就是一個(gè)變量,存放的是地址,指針變量的大小取決與32位平臺還是64位平臺
數(shù)組就是數(shù)組,可以存放一組相同類型的數(shù),數(shù)組的大小取決于元素的類型和個(gè)數(shù)
數(shù)組的數(shù)組名是首元素地址,通過指針可以訪問一個(gè)數(shù)組的每一個(gè)元素
int main()
{int arr[5] = { 1,2,3,4,5 };int* p = arr;//p=arr//arr[i]=p[i]=*(arr+i)=*(p+i)都是下標(biāo)為i的元素int i = 0;for (i = 0; i < 5; i++){printf("%d ", p[i]);//arr[i],*(arr+i),*(p+i)}return 0;
}
//1 2 3 4 5
⑧指針數(shù)組
指針數(shù)組是指針還是數(shù)組?我們知道,整型數(shù)組是數(shù)組,存放的是int 類型的元素;字符數(shù)組是數(shù)組,存放的是char類型的元素……
指針數(shù)組是數(shù)組,是存放指針的數(shù)組
例如:int* arr[5];
arr是數(shù)組名,5代表數(shù)組有5個(gè)元素,int* 代表每個(gè)元素都是一個(gè)整型指針
好了,那么本篇博客就到此結(jié)束了,如果你覺得本篇博客對你有些幫助,可以給個(gè)大大的贊👍嗎,感謝看到這里,我們下篇博客見??