老生常談C語(yǔ)言中指針的使用
前提
指針,是C語(yǔ)言中的一個(gè)重要概念及其特點(diǎn),也是掌握C語(yǔ)言比較困難的部分。指針也就是內(nèi)存地址,指針變量是用來(lái)存放內(nèi)存地址的變量,在同一CPU構(gòu)架下,不同類型的指針變量所占用的存儲(chǔ)單元長(zhǎng)度是相同的,而存放數(shù)據(jù)的變量因數(shù)據(jù)的類型不同,所占用的存儲(chǔ)空間長(zhǎng)度也不同。有了指針以后,不僅可以對(duì)數(shù)據(jù)本身,也可以對(duì)存儲(chǔ)數(shù)據(jù)的變量地址進(jìn)行操作
指針描述了數(shù)據(jù)在內(nèi)存中的位置,標(biāo)示了一個(gè)占據(jù)存儲(chǔ)空間的實(shí)體,在這一段空間起始位置的相對(duì)距離值。在 C/C++語(yǔ)言中,指針一般被認(rèn)為是指針變量,指針變量的內(nèi)容存儲(chǔ)的是其指向的對(duì)象的首地址,指向的對(duì)象可以是變量(指針變量也是變量),數(shù)組,函數(shù)等占據(jù)存儲(chǔ)空間的實(shí)體。
一.指針基礎(chǔ)
1.1 變量指針
//首先我們聲明一個(gè)變量
int a = 10; //聲明一個(gè)指針并指向該變量的地址
int* b = &a; printf("%d\n", *b);
運(yùn)行結(jié)果:10
1.2 數(shù)據(jù)指針
//首先我們聲明一個(gè)數(shù)組變量
int c[10] = { 0,1,2,3,4,5,6,7,8,9 };
printf("c的地址:%p\n", c);
printf("c[0]的地址:%p\n", &c[0]);
運(yùn)行結(jié)果:兩個(gè)一樣
Why? 數(shù)組本質(zhì)為內(nèi)存空間上連續(xù)的空間,而作為數(shù)組的地址,其實(shí)為首元素的地址,而對(duì)于數(shù)組,數(shù)組名稱其實(shí)就是個(gè)指針(重點(diǎn))然后我們創(chuàng)建數(shù)組指針
int* d=c
printf("d[9]的值為:%d\n",*(d+9));
//另一種寫法printf("d[9]的值為:%d\n",d[9]);
運(yùn)行結(jié)果:9
1.3 指針的本質(zhì)
通過(guò)1.0和1.1的指示我們突然發(fā)現(xiàn),整數(shù)的指針和整數(shù)數(shù)組的指針居然是同個(gè)類型!
事實(shí)上確實(shí)是一個(gè)東西,指針為指向變量地址的類型,所以對(duì)于指針來(lái)說(shuō)是不需要類型的,但是為了程序規(guī)范性,增加了類型說(shuō)法
下面使用無(wú)類型指針輸出a的值(10)
void* p = &a;
printf("void指針的值:%d\n", *(int*)p);
//對(duì)比b指針變量
printf("這是b的值:%p\n", b);
printf("這是p的值:%p\n", p);

上述結(jié)果可以完全表明,指針變量?jī)?chǔ)存的是地址,而且是單一變量的地址
接下來(lái)可以解釋數(shù)組和數(shù)為啥指針就是同一個(gè)類型了
回到1.2所述 單個(gè)數(shù)占了1個(gè)空間(這里不提占用內(nèi)存),而數(shù)組占了n個(gè)空間,而指針指向的都是首地址,而對(duì)于單個(gè)數(shù)來(lái)說(shuō)
首地址就是數(shù)所在的地址,而對(duì)于數(shù)組來(lái)說(shuō),首地址就是頭元素所在的地址
1.2解釋了數(shù)組地址就是數(shù)組名本身,所以數(shù)組指針就是數(shù)組本身了,所以有下面這種寫法
printf("直接使用數(shù)組名獲取索引3的元素:%d\n", c[3]);
printf("使用指向c的指針獲取索引3的元素:%d\n", d[3]);

但是數(shù)組和指針數(shù)組還是有一定的區(qū)別
1.4 指針數(shù)組
/如果我們把指針看作一個(gè)變量,那么類比整數(shù)類型,我們一定可以聲明一個(gè)指向指針的指針
int g = 50;
int* h = &g;
int** i = &h;
//輸出一下
printf("h的值:%p\n", h);
printf("i的值:%p\n", *i);
//實(shí)驗(yàn)證明這是可行的,那我們嘗試做一個(gè)數(shù)組
int j[5] = { 9,8,7,6,5 };
int* k[5] = { &j[0],&j[1],&j[2],&j[3],&j[4] };
int** l = k;
printf("k[1]的值:%p,對(duì)應(yīng)的整數(shù)的值:%d\n", k[1], *k[1]);
printf("l獲取k1的值:%p,對(duì)應(yīng)的整數(shù)的值:%d\n", *(l + 1), **(l + 1));

這就是指針數(shù)組,把指針當(dāng)作一個(gè)變量看,指針也可以做數(shù)組
1.5 指針的移動(dòng)
對(duì)于數(shù)組指針獲取元素,上面展示了可以通過(guò)"變量名[索引]"的方式獲取,當(dāng)然也可以通過(guò)移動(dòng)指針位置來(lái)獲取
int* m = (int*)malloc(10 * sizeof(int)); for (int i = 0; i < 10; ++i) *(m++) = i;
指針移動(dòng)到了分配內(nèi)存的最后一塊
“變量名[索引]“的方式表明了是以當(dāng)前指針為數(shù)組頭,偏移索引個(gè)單位為方法獲取元素的方式,索引獲取第8個(gè)元素
錯(cuò)誤寫法 printf(”%d”,m[8]);
正確寫法
printf("%d\n", *(m - 2));

所以釋放的時(shí)候要釋放頭指針,所以要回到頭指針
for (int i = 0; i < 10; ++i) (--m); free(m);
所以實(shí)際操作的情況下盡量不要去移動(dòng)指針
int* n = (int*)malloc(10 * sizeof(int));
for (int i = 0; i < 10; ++i)
*(n + i) = i;
printf("%d\n", *(n + 8));
free(n);

1.5 Scanf函數(shù)的解釋
scanf函數(shù)為獲取變量地址函數(shù)
首先我們看一下scanf函數(shù)的定義
scanf函數(shù)有一個(gè)格式符參數(shù)和一個(gè)可變參數(shù)
可變參數(shù)類型為指針!?。?!
char o;
第一種直接寫法
scanf("%c", &o);
printf(“輸出的字符:%c\n”, o);
同理
用指針獲取
char* q = &o;
scanf("%c", q);
printf("通過(guò)指針獲取輸出的字符:%c\n", *q);

上面我們說(shuō)了,指針可以代表數(shù)組,而c語(yǔ)言對(duì)字符串的定義就是字符數(shù)組
char r[] = { "Hello" };
scanf("%s", r);
printf("字符串的值:%s\n", r);
這樣我們就可以解釋為什么對(duì)于獲取字符串的時(shí)候,不用寫&的原因了,因?yàn)樽址麛?shù)組本來(lái)就是指針!!
二.指針的進(jìn)階玩法
2.1 二維指針
由名字可得,二維指針就是指針的指針,指針可以代表數(shù)組,自然二維指針就可以代表二維數(shù)組
int u[4][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6},{4,5,6,7} };
int** v = u;
二維數(shù)組在內(nèi)存空間上也是線性排列的,就如下順序
1 2 3 4 2 3 4 5 3 4 5 6 4 5 6 7
所以二維數(shù)組獲取元素也可以靠指針移動(dòng)
printf("u[1][2]的值:%d\n", *(v + 6));
也可以動(dòng)態(tài)內(nèi)存分配實(shí)現(xiàn)
int** w = (int**)malloc(4 * sizeof(int)); //分配行內(nèi)存
for (int i = 0; i < 4; ++i)
w[i] = (int*)malloc(4 * sizeof(int)); //分配列內(nèi)存
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j)
w[i][j] = (j + 1) + i;
printf("w[1][2]的值:%d\n", w[1][2]);
free(w); //記得釋放
2.2 結(jié)構(gòu)體指針
先聲明一個(gè)結(jié)構(gòu)體
struct people
{
char* name;
int age;
}SPeople;
//方便表示替換掉標(biāo)識(shí)符
typedef struct people People;
People dr;
dr.name = "aaa";
dr.age = 18;
printf("dr的名字是:%s,年齡是:%d\n", wusihao.name, wusihao.age);
People* lp = &wusihao; //注意屬性獲取的符號(hào)"->"
lp->name = "sss";
lp->age = 19;
printf("lp的名字是:%s,年齡是:%d\n", lp->name, lp->age);
當(dāng)然也可以動(dòng)態(tài)內(nèi)存分配使用
People* lps = (People*)malloc(1 * sizeof(People));
lps->name = "xxx";
lps->age = 20;
printf("lps的名字是:%s,年齡是:%d\n", lps->name, lps->age);
結(jié)語(yǔ)
指針定義后,在不用時(shí)最好指向NULL,比如
int* s = &a;
printf("%p\n", s);
s = NULL;
因?yàn)榫退汜尫诺魞?nèi)存,但是指針指向的內(nèi)存地址依舊可用(獲取處于沉睡狀態(tài)),所以最好不要去動(dòng)它,不然很容易導(dǎo)致內(nèi)存泄露
動(dòng)態(tài)聲明的指針一定要釋放,free函數(shù)釋放
動(dòng)態(tài)分配內(nèi)存最好要檢查是否成功分配到
int* t = (int*)malloc(10 * sizeof(int));
if (t == NULL)
{
//分配錯(cuò)誤
system("pause");
return 0;
}
else
{
//你的代碼
system("pause");
free(t);
}
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
OpenCV + MFC實(shí)現(xiàn)簡(jiǎn)單人臉識(shí)別
這篇文章主要為大家詳細(xì)介紹了OpenCV + MFC實(shí)現(xiàn)簡(jiǎn)單人臉識(shí)別,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08
C語(yǔ)言實(shí)現(xiàn)的一個(gè)萬(wàn)年歷小程序
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)的一個(gè)萬(wàn)年歷小程序,具有一定的參考價(jià)值,做C語(yǔ)言日期計(jì)算的朋友可以參考下2014-07-07
基于Matlab實(shí)現(xiàn)中國(guó)象棋的示例代碼
中國(guó)象棋是起源于中國(guó)的一種棋,屬于二人對(duì)抗性游戲的一種,在中國(guó)有著悠久的歷史。由于用具簡(jiǎn)單,趣味性強(qiáng),成為流行極為廣泛的棋藝活動(dòng)。本文將利用Matlab實(shí)現(xiàn)這一游戲,需要的可以參考一下2022-02-02

