本章內(nèi)容是指針的內(nèi)容,有哪些地方寫的不好還請(qǐng)多多指點(diǎn)。????
站在用戶的角度思考問(wèn)題,與客戶深入溝通,找到天峨網(wǎng)站設(shè)計(jì)與天峨網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:成都做網(wǎng)站、網(wǎng)站設(shè)計(jì)、外貿(mào)營(yíng)銷網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、域名注冊(cè)、雅安服務(wù)器托管、企業(yè)郵箱。業(yè)務(wù)覆蓋天峨地區(qū)。
首先說(shuō)一下指針的初級(jí)知識(shí)點(diǎn)什么是指針。
按傳統(tǒng)的方式來(lái)講:
在計(jì)算機(jī)科學(xué)中,指針(Pointer)是編程語(yǔ)言中的一個(gè)對(duì)象,利用地址,它的值直接指向
(points to)存在電腦存儲(chǔ)器中另一個(gè)地方的值。由于通過(guò)地址能找到所需的變量單元,可以
說(shuō),地址指向該變量單元。因此,將地址形象化的稱為“指針”。意思是通過(guò)它能找到以它為地址
的內(nèi)存單元。
內(nèi)存單元是什么呢?就好比現(xiàn)實(shí)生活中我們的房間,不就是哪個(gè)單元哪個(gè)房間號(hào)嘛。
我們用畫圖的形式來(lái)展示。
指針
指針是個(gè)變量,存放內(nèi)存單元的地址(編號(hào))。
用代碼方式表示:
#include
int main()
{
int i = 9;//向內(nèi)存申請(qǐng)一塊空間
int* p = &i;//&i 取出i的地址 放到p變量中 而p就是一個(gè)指針變量 它指向的是i變量所在的地址
return 0;
}
簡(jiǎn)單的來(lái)說(shuō): 指針就是個(gè)變量,這個(gè)變量是用來(lái)存放地址的。(存放到指針變量中的值都將被當(dāng)作地址處理)。
比如說(shuō): int* p = 12;
數(shù)據(jù)在內(nèi)存中都是地址的形式存放的,而在內(nèi)存中地址是以4位16進(jìn)制和8位16進(jìn)制表示的,而12的16進(jìn)制不就是字母C嗎。
上面一行代碼解釋為:定義一個(gè)整型指針變量,把12這個(gè)值當(dāng)作地址賦值給變量p。%p以地址的形式打印所以是 --> 0000000C。
對(duì)于32位機(jī)器來(lái)說(shuō),我們假設(shè)有32根地址線,每根地址線在尋找地址的時(shí)候產(chǎn)生一個(gè)電信號(hào)正電或者負(fù)電(1/0)
那么32根地址線產(chǎn)生的地址是:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
00000000 00000000 00000000 00000010
......
2的32次方個(gè)地址。
每個(gè)地址標(biāo)識(shí)一個(gè)字節(jié),那么就可以給(2^32Byte == 2^32/1024KB == 2^32/1024/1024MB == 2^32/1024/1024/1024GB == 4GB)
4GB的空閑編址。同樣的方法放到64位機(jī)器上,編址有TB,是這么多嗎
。
這里我們就明白了:
由此我們得出結(jié)論:
在此之前我們學(xué)過(guò)基本類型有:整型、字符型、浮點(diǎn)型等。既然指針也是變量,那指針是不是也會(huì)有不同的類型呢?答案是肯定的!
指針也是變量,所以也會(huì)有該對(duì)應(yīng)的指針類型。下面舉幾個(gè)例子:
#include
int main()
{
int a = 10;//整型
char b = 'w';//字符型
float c = 3.14;//單精度浮點(diǎn)型
double d = 123.789;//雙精度浮點(diǎn)型
int* pa = &a;//整形指針
char* pb = &b;//字符指針
float* pc = &c;//浮點(diǎn)型指針
double* pd = &d;//浮點(diǎn)型指針
return 0;
}
以上代碼就列舉了部分指針類型。
下面我們來(lái)看一段代碼
sizeof()是計(jì)算數(shù)據(jù)大小的操作符,單位是字節(jié),可以看到不管是什么類型的指針都是4個(gè)字節(jié)。誒~既然大小都是4個(gè)字節(jié),那我們定義不同類型的指針又有什么意義呢?那我們何不造一個(gè)通用類型的指針呢?當(dāng)然不行呢。你看我們每個(gè)人每天都要吃飯,雖然說(shuō)有不同的方式可以讓自己吃飽,但是為什么要這么多不同的食品讓我們填飽肚子呢?直接造一種可以讓人吃飽的食品不就好了。這個(gè)時(shí)候大家又知道了不同的食品有不同的作用,有些要補(bǔ)維生素A呀或者是維生素B呀等等。同樣的指針類型也會(huì)有它不同的意義呀!
下面我們繼續(xù)看段代碼
我們看到內(nèi)存上,a的地址,后面是變量a的數(shù)據(jù)存儲(chǔ)方式 -->小端存儲(chǔ)方式。當(dāng)我們把a(bǔ)的地址存放到變量pa中,我們通過(guò)地址去改變a的值,請(qǐng)看下面
右下角我們看到pa變量的值變成了a的地址,而且a的值也被改成了20。好這個(gè)看不出什么,下面我們看另外一段代碼
跟上面一樣,我只是改變了指針的類型,改變指針類型后我們看到*pa=20執(zhí)行之后,為什么右上角的值變成了 14 46 13 00呢?
通過(guò)觀察,指針類型不同,操作的字節(jié)數(shù)就不同,可以看到int* 指針改變的是4個(gè)字節(jié),而char* 指針改變的是1個(gè)字節(jié)。
由此我們得出結(jié)論:
指針類型決定了,對(duì)指針解引用的時(shí)候有多大權(quán)限(能操作幾個(gè)字符)。比如說(shuō):char* 的指針解引用就只能訪問(wèn)一個(gè)字節(jié),而int*的指針解引用能訪問(wèn)4個(gè)字節(jié)。
指針類型意義1:
指針類型決定了指針解引用時(shí),能訪問(wèn)的空間大小。
我們繼續(xù)看下面一段代碼的運(yùn)行結(jié)果:
可以看到int類型指針p的地址與p+1的地址之間差了4 而char類型指針pc和pc+1的地址之間差了1。雖然說(shuō)是以數(shù)組來(lái)舉例子,不過(guò)就算不用數(shù)組舉例也是一樣的。
指針類型意義2:
指針類型決定了指針+1或者-1有多大距離(指針的步長(zhǎng))。也就是字節(jié)
這有啥用呢?
可以知道我們創(chuàng)建了一個(gè)整型數(shù)組,這個(gè)數(shù)組有10個(gè)元素全部初始化成0了。數(shù)組名代表數(shù)組首元素的地址,把數(shù)組首元素的地址傳給變量p通過(guò)循環(huán)給數(shù)組賦值。
結(jié)果很明顯,通過(guò)指針+整數(shù)訪問(wèn)數(shù)組的元素,然后再給數(shù)組元素賦值。那么我們把指針類型改一下呢會(huì)出現(xiàn)什么效果?
它改變的是什么?搜嘎斯內(nèi),它只改變了整型數(shù)據(jù)中一個(gè)字節(jié)的內(nèi)容。這是一個(gè)字節(jié)一個(gè)字節(jié)的訪問(wèn),沒(méi)改變指針類型之前呢,沒(méi)改變之前是4個(gè)字節(jié)4個(gè)字節(jié)的訪問(wèn)所以啊。
以此類推,想怎樣訪問(wèn)就用什么樣類型的指針。
總結(jié)一下指針類型的作用:
- 指針類型決定了指針解引用(訪問(wèn)字節(jié))的權(quán)限有多大。
- 指針類型決定了指針向前走或者向后走有多大(距離,步長(zhǎng))。
野指針,大家看到會(huì)想起啥?野狗?野貓?它們都是沒(méi)得家的沒(méi)得主人,到處流浪。那么野指針是啥?
先按傳統(tǒng)的說(shuō)法:
野指針就是指針指向的位置是不可知的(隨機(jī)的、不正確的、沒(méi)有明確限制的)
那為什么會(huì)產(chǎn)生野指針這玩意兒呢?
接下來(lái)我們看看野指針形成的原因。
1.指針未初始化
#include
int main()
{
int* p ;//定義一個(gè)局部的指針變量,局部變量不初始化的話,默認(rèn)是隨機(jī)值
*p = 10;//*p是對(duì)指針的解引用操作,這里非法訪問(wèn)內(nèi)存
//使用了未初始化的局部變量p
//使用未初始化的內(nèi)存p
return 0;
}
2.指針越界訪問(wèn)
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i <= 10; i++)
{
//當(dāng)指針指向的范圍超出數(shù)組arr的范圍時(shí),p就是野指針
*p = i;
p++;
}
return 0;
}
3.指針指向的空間釋放
int* work()
{
int a = 20;//函數(shù)調(diào)用后變量a銷毀了
return &a;//所以此時(shí)a的地址雖然說(shuō)還是那地址,但是指向的那塊空間已經(jīng)還給了操作系統(tǒng)
}
int main()
{
int* p = work();
//對(duì)p指針的解引用 去訪問(wèn)一個(gè)已經(jīng)還給操作系統(tǒng)的空間 那片空間說(shuō)不定操作系統(tǒng)已經(jīng)存放其他內(nèi)容 而且這也是屬于非法訪問(wèn)內(nèi)存
*p = 30;
return 0;
}
說(shuō)了這么多的野指針成因,那我們應(yīng)該怎樣有效的避免野指針的產(chǎn)生呢?
#include
int main()
{
//當(dāng)不知道p應(yīng)該初始化為什么地址的時(shí)候,可以初始化NULL
int* p = NULL;//初始化指針
return 0;
}
指針運(yùn)算有3種形式:
int main()
{
int arr[10] = { 20,21,22,23,24,25,26,27,28,29 };
int* p = arr;
int* end = arr + 9;
while (p <= end)
{
printf("%d\n", *p);
p++;//指針+整數(shù) p+=1;
}
return 0;
}
可以知曉,指針+整數(shù) 在整型數(shù)組中訪問(wèn)下一個(gè)元素,既然可以+那肯定也能-
指針-整數(shù)
指針-指針,聯(lián)系之前學(xué)過(guò)的內(nèi)容,指針不就是地址嘛,那指針-指針 不就是 地址 - 地址。指針+指針沒(méi)啥意義,就和日期一樣,日期+日期有啥用嘛?日期-日期可以,日期+天數(shù)也可以。但是日期加日期就沒(méi)啥意義了。
#include
int main()
{
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int* p = arr;
int* end = arr + 9;
printf("%d\n", end - p);
return 0;
}
代碼運(yùn)行如下:
運(yùn)行結(jié)果是9,我們發(fā)現(xiàn)從arr[0]到arr[9]之間剛好就是9個(gè)元素。
試試arr - (arr +9)這不是-9嘛看看代碼運(yùn)行效果:
結(jié)果也是-9,雖然數(shù)字是相同了,但是符號(hào)卻不相同,而數(shù)組中是隨著數(shù)組下標(biāo)的增長(zhǎng)地址是由低到高的。內(nèi)存中地址也是由低到高。
因此我們?nèi)缫枰玫皆氐膫€(gè)數(shù),應(yīng)該用高地址- 低地址。
注意事項(xiàng):
指針-指針的前提是:兩個(gè)指針指向的是通一塊空間
給大家來(lái)個(gè)實(shí)際點(diǎn)的:
#include
//函數(shù)遞歸方法
int me_strlen(char*str)
{
if(*str != '\0')
return 1 + me_strlen(str + 1);
else
return 0;
}
int me_strlen(char* str)
{
char* p = str;//p指針指向 str 變量 str 指向的是 字符串首字符的地址
while(p != '\0')//字符串長(zhǎng)度是找'\0'
{
p++;
}
return p - str;//指針-指針 就是中間元素個(gè)數(shù)
}
int main()
{
char arr[] = "hello world";
int ret = me_strlen(arr);//傳字符串首字符的地址
return 0;
}
指針關(guān)系運(yùn)算,我們知道關(guān)系運(yùn)算是啥,關(guān)系運(yùn)算不就是 > < >= <= != ==
用代碼舉個(gè)例子吧:
#include
int main()
{
int arr[5];
int *p;
for( p = &arr[5]; p > arr[0])
{
*--p = 0;
}
return 0;
}
這個(gè)代碼不太標(biāo)準(zhǔn),看著有點(diǎn)難受。修改一下
#include
int main()
{
int arr[5];
int *p;
for( p = &arr[4]; p > arr[0]; p--)
{
*p = 0;
}
return 0;
}
這個(gè)代碼是什么意思呢?咱們畫圖理解一下
該代碼的意思是:取出arr數(shù)組后面一個(gè)元素的地址,判斷該地址是否大于數(shù)組首元素的地址,如果大于那就把p變量所指向的內(nèi)容改為0,然后在自減,也就是往arr[0]那邊走,再以同樣的方式賦值,直到p的地址要小于或者是等于arr[0]循環(huán)就結(jié)束。
但是請(qǐng)注意:
允許指向數(shù)組元素的指針與指向數(shù)組最后一個(gè)元素后面的那個(gè)內(nèi)存位置的指針比較,但是不允許與指向第一個(gè)元素之前的那個(gè)內(nèi)存位置的指針進(jìn)行比較。
如果還不明白請(qǐng)自己畫圖理解一下。
指針和數(shù)組的關(guān)系:指針是地址嘛,而數(shù)組名是數(shù)組首元素地址。 我們舉個(gè)例子
有圖有真相(doge),可以看到arr和&arr[0]的地址是一樣的,所以說(shuō),數(shù)組名就是數(shù)組首元素地址。
#include
int main()
{
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int* p = arr;//p存放的就是數(shù)組首元素地址
return 0;
}
但是數(shù)組名就一定是數(shù)組首元素地址嗎?NONONO。也有例外啦!請(qǐng)記住下面2點(diǎn)
- sizeof(數(shù)組名) --> 只有數(shù)組名沒(méi)有其他操作符 數(shù)組名表示整個(gè)數(shù)組,計(jì)算的是整個(gè)數(shù)組的大小,單位是字節(jié)。
- &數(shù)組名 -- > 取出的是整個(gè)數(shù)組的地址 數(shù)組名表示整個(gè)數(shù)組
其他情況下,數(shù)組名就是首元素地址。
首元素的地址和數(shù)組的地址有啥區(qū)別呢?
靠,這不一樣嘛(手動(dòng)狗頭)?雀食哈,這打印出來(lái)都一樣,但是意義卻截然不同呢,不信再舉個(gè)例子:
看到區(qū)別沒(méi),arr是數(shù)組名,代表首元素地址,+1 因?yàn)閿?shù)組類型是int 而int類型指針+1 不就是跳過(guò)4個(gè)字節(jié) 剛好差4。
而&arr,arr代表整個(gè)數(shù)組,+1 跳過(guò)了一個(gè)數(shù)組,兩個(gè)地址之間差 40個(gè)字節(jié)。
既然可以把數(shù)組名當(dāng)成地址存放到一個(gè)指針中,我們可以使用指針來(lái)一個(gè)個(gè)的訪問(wèn)。
#include
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%p <==> %p\n", &arr[i], p + i);
*(p + i) = i;
}
return 0;
}
效果:
所以 p+i 其實(shí)計(jì)算的是數(shù)組 arr 下標(biāo)為i的地址 。
那我們就可以直接通過(guò)指針來(lái)訪問(wèn)數(shù)組 。
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;//數(shù)組名
int sz = sizeof(arr)/sizeof(arr[0]);
int i = 0;
for(i=0;i *(p+2)
//arr[2] --> *(arr+2) --> *(2+arr) --> 2[arr]
//arr[2]<==> *(arr+2) <==> *(p+2) <==> *(2+p) <==> *(2+arr) == 2[arr]
//2[arr] <==> *(2+arr)
return 0;
}
二級(jí)指針,有沒(méi)有覺(jué)得好高級(jí)。之前了解到指針本質(zhì)上還是變量嘛,只是用來(lái)存放地址的變量而已。既然是變量那它是不是也會(huì)有地址?
那當(dāng)然,那么存放指針變量的地址就叫二級(jí)指針咯。畫圖舉個(gè)例子就知道了:
這樣是不是好理解一些。
#include
int main()
{
int a= 1;
int* p = &a;//p是指針變量,一級(jí)指針
int** pa = &p;//pa也是指針變量,二級(jí)指針
return 0;
}
說(shuō)完了什么是二級(jí)指針,那二級(jí)指針如何使用呢?其實(shí)和一級(jí)指針的使用是類似的。
*pa 通過(guò)對(duì)pa的地址解引用,找到p, *pa其實(shí)訪問(wèn)的就是p。
int b = 1;
*pa = &a;//等價(jià)于 p = &a;
**pa先通過(guò) *pa找到p,然后對(duì)p進(jìn)行解引用操作: *p,找到a
**pa = 30;
//等價(jià)于*pa = 30;
//等價(jià)于a = 30;
我們學(xué)過(guò)整型數(shù)組、字符型數(shù)組。那么指針有沒(méi)有數(shù)組呢?
答案肯定是有的。還記得數(shù)組的定義是什么嗎?數(shù)組是一組相同類型元素的集合。那么一組相同類型的指針?lè)旁谝黄疬@算不算是數(shù)組呢?
這肯定是數(shù)組,因?yàn)樗鼭M足數(shù)組的定義。那么指針數(shù)組又是怎么樣的呢?
首先指針數(shù)組的定義:一組相同類型的指針的集合。
int arr[10];//這是整型數(shù)組,數(shù)組有10個(gè)元素,數(shù)組中每個(gè)元素都是整型。
char arry[5];//這是字符型數(shù)組,數(shù)組有5個(gè)元素,數(shù)組中每個(gè)元素都是字符型。
#include
int main()
{
int a = 1;
int b = 2;
int c = 3;
int* pa = &a;
int* pb = &b;
int* pc = &c;
return 0;
}
這段代碼中,a、b、c均為整型變量,pa、pb、pc均為整形指針。3個(gè)整型變量用3個(gè)整型指針來(lái)接收豈不是浪費(fèi)空間了?那我們干脆把這3個(gè)整型指針?lè)旁谝黄鸾M成一個(gè)整型指針數(shù)組不就好了嘛,誒指針數(shù)組這不就出來(lái)了嘛。
首先定義一個(gè)數(shù)組 int arr[10]; 既然每個(gè)類型都是指針,那么把數(shù)組的類型改一下不就好了嘛。 int* arr[10]; 這時(shí)我們就說(shuō)這個(gè)數(shù)組是指針數(shù)組嘛,這是一個(gè)整型指針數(shù)組,該數(shù)組有10個(gè)元素且每一個(gè)元素都是整型指針。那么我們畫圖展示一下:
用代碼的形式:
int main()
{
int arr[10];//整形數(shù)組 - 存放整形的數(shù)組就是整形數(shù)組
char ch[5];//字符數(shù)組 - 存放的是字符
//指針數(shù)組 - 存放指針的數(shù)組
int* parr[5];//整形指針的數(shù)組
char* pch[5];//字符型指針數(shù)組
return 0;
}
想必大家看了這幾幅圖和代碼,對(duì)指針數(shù)組應(yīng)該有大概的了解了吧。
感謝大家的收看,以上都是鄙人學(xué)習(xí)的個(gè)人理解如果有哪些地方說(shuō)錯(cuò)了或者是沒(méi)有講明白,還請(qǐng)大家多多指點(diǎn)指點(diǎn)。謝謝大家!?。?/p>
這部分是鄙人對(duì)指針的初步了解后面進(jìn)階的會(huì)在【指針其實(shí)也沒(méi)有那么難—進(jìn)階指針】這篇中講解。