目錄
1、指針是什么?
2. 指針和指針類型
2.1 指針+-整數(shù)
2.2 指針的解引用
3. 野指針
3.1 野指針成因
3.2 如何規(guī)避野指針
4. 指針運(yùn)算
4.1 指針+-整數(shù)
4.2 指針-指針
4.3 指針的關(guān)系運(yùn)算
5. 指針和數(shù)組
?6. 二級(jí)指針
7. 指針數(shù)組
1、指針是什么?
指針是什么?
指針理解的2個(gè)要點(diǎn):
- 1. 指針是內(nèi)存中一個(gè)最小單元的編號(hào),也就是地址,即我們常說(shuō)的指針即地址
- 2. 平時(shí)口語(yǔ)中說(shuō)的指針,通常指的是指針變量,是用來(lái)存放內(nèi)存地址的變量,即指針即變量
總結(jié):指針就是地址,口語(yǔ)中說(shuō)的指針通常指的是指針變量。
那么我們就可以這樣理解:
內(nèi)存
指針變量
我們可以通過(guò)&(取地址操作符)取出變量的內(nèi)存其實(shí)地址,把地址可以存放到一個(gè)變量中,這個(gè) 變量就是指針變量。
#include <stdio.h> int main() { int a = 10;//在內(nèi)存中開(kāi)辟一塊空間 int* p = &a;//這里我們對(duì)變量a,取出它的地址,可以使用&操作符。 //a變量占用4個(gè)字節(jié)的空間,這里是將a的4個(gè)字節(jié)的第一個(gè)字節(jié)的地址存放在p變量 中,p就是一個(gè)之指針變量。 return 0; }
總結(jié):
指針變量,用來(lái)存放地址的變量。(存放在指針中的值都被當(dāng)成地址處理)。
那這里的問(wèn)題是:
- 一個(gè)小的單元到底是多大?(1個(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)生的地址就會(huì)是:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
...
11111111 11111111 11111111 11111111
這里就有2的32次方個(gè)地址。
每個(gè)地址標(biāo)識(shí)一個(gè)字節(jié),那我們就可以給 (2^32Byte == 2^32/1024KB ==2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G的空閑進(jìn)行編址。
同樣的方法,那64位機(jī)器,如果給64根地址線,那能編址多大空間,自己計(jì)算,計(jì)算方法如上所示,此處不再計(jì)算。
這里我們就明白: 在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è)地 址。
總結(jié): 指針是用來(lái)存放地址的,地址是唯一標(biāo)示一塊地址空間的。 指針的大小在32位平臺(tái)是4個(gè)字節(jié),在64位平臺(tái)是8個(gè)字節(jié)。
2. 指針和指針類型
這里我們?cè)谟懻撘幌拢褐羔樀念愋?我們都知道,變量有不同的類型,整形,浮點(diǎn)型等。
那指針有沒(méi)有類型呢? 準(zhǔn)確的說(shuō):有的。 當(dāng)有這樣的代碼:
int num = 10; 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 + * 。
其實(shí): char* 類型的指針是為了存放 char 類型變量的地址。
short* 類型的指針是為了存放 short 類型變量的地址。
int* 類型的指針是為了存放 int 類型變量的地址。 那指針類型的意義是什么?下面我們會(huì)進(jìn)行講解!
2.1 指針+-整數(shù)
#include <stdio.h> 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; }
總結(jié):指針的類型決定了指針向前或者向后走一步有多大(距離)。
2.2 指針的解引用
#include <stdio.h> int main() { int n = 0x11223344; char* pc = (char*)&n; int* pi = &n; *pc = 0; //重點(diǎn)在調(diào)試的過(guò)程中觀察內(nèi)存的變化。 *pi = 0; //重點(diǎn)在調(diào)試的過(guò)程中觀察內(nèi)存的變化。 return 0; }
下圖為執(zhí)行完*pc = 0之后n的內(nèi)存空間
由圖可以看出,我們執(zhí)行完該語(yǔ)句后,只改變了一個(gè)字節(jié)的空間所對(duì)應(yīng)的值!
?下圖為執(zhí)行完*pi = 0之后的內(nèi)存空間,與我們定義的指針類型一致,因?yàn)槲覀兌x的pc是char類型的指針變量,char只占據(jù)四個(gè)字節(jié)的內(nèi)存空間。
?由圖可以看出,我們這次是改變了四個(gè)字節(jié)的空間所對(duì)應(yīng)的值,與我們定義的指針類型所一致,因?yàn)閜i是int 類型的指針變量,int在內(nèi)存中占據(jù)四個(gè)字節(jié)的空間。
總結(jié):
?指針的類型決定了,對(duì)指針解引用的時(shí)候有多大的權(quán)限(能操作幾個(gè)字節(jié))。
比如: char* 的指針解引用就只能訪問(wèn)一個(gè)字節(jié),而 int* 的指針的解引用就能訪問(wèn)四個(gè)字節(jié)。
3. 野指針
概念: 野指針就是指針指向的位置是不可知的(隨機(jī)的、不正確的、沒(méi)有明確限制的)
3.1 野指針成因
1. 指針未初始化
#include <stdio.h> int main() { int* p;//局部變量指針未初始化,默認(rèn)為隨機(jī)值 *p = 20; return 0; }
2. 指針越界訪問(wèn)
#include <stdio.h> int main() { int arr[10] = { 0 }; int* p = arr; int i = 0; for (i = 0; i <= 11; i++) { //當(dāng)指針指向的范圍超出數(shù)組arr的范圍時(shí),p就是野指針 *(p++) = i; } return 0; }
3. 指針指向的空間釋放(后續(xù)會(huì)講,此處暫時(shí)不講)
3.2 如何規(guī)避野指針
- 1. 指針初始化
- 2. 小心指針越界
- 3. 指針指向空間釋放即使置NULL
- 4. 避免返回局部變量的地址
- 5. 指針使用之前檢查有效性
#include <stdio.h> int main() { int* p = NULL; //.... int a = 10; p = &a; if (p != NULL) { *p = 20; } return 0; }
4. 指針運(yùn)算
4.1 指針+-整數(shù)
#include<stdio.h> int main() { int x = 0; int* p = &x; printf("%p\n", p); printf("%p\n", p + 1); char* p2 = (char*)&x; printf("%p\n", p2); printf("%p\n", p2+1); return 0; }
?指針+-整數(shù)實(shí)際上是跨越指針?biāo)缸兞款愋偷淖止?jié)數(shù)乘以我們加的數(shù)字,比如在上面的例子中,我們一開(kāi)始是將整型指針p進(jìn)行了+1操作,然后我們對(duì)p+1進(jìn)行以地址形式打印后,相比原來(lái)的p,增加了4個(gè)字節(jié),同理,后面的p2是char類型的指針,我們對(duì)其進(jìn)行+1操作后,相比原來(lái)的p2的地址,增加了1個(gè)字節(jié)。
4.2 指針-指針
#include<stdio.h> int main() { int arr[5] = { 1,2,3,4,5 }; int* p1 = &arr[4]; int* p2 = &arr[0]; printf("%d", p1 - p2); return 0; }
上面進(jìn)行相減后得出的結(jié)果是4,為什么會(huì)得出4這個(gè)結(jié)果呢?因?yàn)閜1和p2之間相差4個(gè)整型元素,這個(gè)地方為什么我們要強(qiáng)調(diào)是整形元素呢?因?yàn)槲覀冊(cè)谶\(yùn)算符兩側(cè)的指針類型均是整型指針,實(shí)際上指針類型的變量進(jìn)行相減是指針?biāo)淼牡刂愤M(jìn)行相減,將得出的值除以指針?biāo)赶虻目臻g所占據(jù)的字節(jié)數(shù),這樣將大家不是很容易理解,有一點(diǎn)點(diǎn)抽象,我給大家簡(jiǎn)單舉一下例子,比如在上面這個(gè)例子中p1中所存儲(chǔ)的地址值是16,而p2中所存儲(chǔ)的值是0,那么我們將p2減去p1后得出的16除以p1和p2所指向的數(shù)據(jù)類型即整型類型,每個(gè)整型元素所占據(jù)的字節(jié)數(shù)為4,所以16除以4之后的結(jié)果為4,即最終在屏幕上的輸出結(jié)果為4,那么很多小伙伴就問(wèn)了,在上面這個(gè)例子中,我們將兩個(gè)指針的類型進(jìn)行強(qiáng)制轉(zhuǎn)換為char類型后得出的結(jié)果是不是就會(huì)改變?yōu)?6了呢,因?yàn)楦鶕?jù)上面的我給出的推理方式確實(shí)應(yīng)該是這樣的,因?yàn)閏har類型占據(jù)的字節(jié)數(shù)為1,16除以1之后的結(jié)果仍為16,接下來(lái)我們代碼展示一下是不是就像我們的推理一樣,得出的結(jié)果是16.
?同我們的推導(dǎo)一樣,這就印證了我們的推導(dǎo)是正確的!
4.3 指針的關(guān)系運(yùn)算
既然指針變量中所存儲(chǔ)的是地址,地址從本質(zhì)上來(lái)說(shuō)也只是一串?dāng)?shù)據(jù),那么也是可以進(jìn)行比較大小的!此處不再進(jìn)行舉例給大家進(jìn)行展示,因?yàn)椴浑y理解,但是下面會(huì)給大家進(jìn)行強(qiáng)調(diào)一個(gè)要點(diǎn)!
標(biāo)準(zhǔn)規(guī)定:
允許指向數(shù)組元素的指針與指向數(shù)組最后一個(gè)元素后面的那個(gè)內(nèi)存位置的指針比較,但是不允許與 指向第一個(gè)元素之前的那個(gè)內(nèi)存位置的指針進(jìn)行比較。
5. 指針和數(shù)組
我們看一個(gè)例子:
#include <stdio.h> int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,0 }; printf("%p\n", arr); printf("%p\n", &arr[0]); return 0; }
運(yùn)行結(jié)果:
可見(jiàn)數(shù)組名和數(shù)組首元素的地址是一樣的。
結(jié)論:數(shù)組名表示的是數(shù)組首元素的地址。(2種情況除外,數(shù)組章節(jié)講解了)
那么這樣寫(xiě)代碼是可行的:
int arr[10] = {1,2,3,4,5,6,7,8,9,0}; int *p = arr;//p存放的是數(shù)組首元素的地址
既然可以把數(shù)組名當(dāng)成地址存放到一個(gè)指針中,我們使用指針來(lái)訪問(wèn)一個(gè)就成為可能。
例如:
#include <stdio.h> int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,0 }; int* p = arr; //指針存放數(shù)組首元素的地址 int sz = sizeof(arr) / sizeof(arr[0]); for (int i = 0; i < sz; i++) { printf("&arr[%d] = %p <====> p+%d = %p\n", i, &arr[i], i, p + i); } return 0; }
所以 p+i 其實(shí)計(jì)算的是數(shù)組 arr 下標(biāo)為i的地址。
那我們就可以直接通過(guò)指針來(lái)訪問(wèn)數(shù)組。
如下:
int main() { int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; int* p = arr; //指針存放數(shù)組首元素的地址 int sz = sizeof(arr) / sizeof(arr[0]); int i = 0; for (i = 0; i < sz; i++) { printf("%d ", *(p + i)); } return 0; }
?6. 二級(jí)指針
指針變量也是變量,是變量就有地址,那指針變量的地址存放在哪里? 這就是二級(jí)指針 。
對(duì)于二級(jí)指針的運(yùn)算有:
- *ppa 通過(guò)對(duì)ppa中的地址進(jìn)行解引用,這樣找到的是 pa , *ppa 其實(shí)訪問(wèn)的就是 pa .
int b = 20; *ppa = &b;//等價(jià)于 pa = &b;
- **ppa 先通過(guò) *ppa 找到 pa ,然后對(duì) pa 進(jìn)行解引用操作: *pa ,那找到的是 a .
**ppa = 30; //等價(jià)于*pa = 30; //等價(jià)于a = 30;
7. 指針數(shù)組
指針數(shù)組是指針還是數(shù)組?
答案:是數(shù)組。是存放指針的數(shù)組。
數(shù)組我們已經(jīng)知道整形數(shù)組,字符數(shù)組。
int arr1[5]; char arr2[6];
?那指針數(shù)組是怎樣的?
int* arr3[5];//是什么?
arr3是一個(gè)數(shù)組,有五個(gè)元素,每個(gè)元素是一個(gè)整形指針。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-418060.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-418060.html
到了這里,關(guān)于史上最強(qiáng)C語(yǔ)言教程----指針(初階)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!