引言:變量的內(nèi)存地址:先前我們了解到,C程序中變量的值都是存儲在計(jì)算機(jī)內(nèi)存中特定的存儲單元中。而內(nèi)存中的每個(gè)單元都有唯一的地址。1.1理解指針是什么?🔻
1.指針是內(nèi)存中一個(gè)最小的單元編號,也就是地址。
2.口語中的指針,指的是指針變量,是用來存放變量的地址。
我們可以進(jìn)一步將指針理解為“內(nèi)存”
創(chuàng)新互聯(lián)從2013年成立,先為道外等服務(wù)建站,道外等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為道外企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。圖片描述
1.2指針變量😁如何獲得變量的地址呢?通過&(取地址操作符)取出變量的其實(shí)地址,然后放在一個(gè)變量中,這個(gè)變量就是指針變量。 通過指針變量間接存取它指向的變量的訪問方式稱為間接尋址。
#includeint main()
{
int a = 5;//在內(nèi)存中開辟一塊空間
int* pa = &a;//取出變量a的地址,存放到pa這個(gè)變量中。pa就是指針變量。
printf("%p\n",&a);//需要用到取地址操作符
printf("%p\n", pa);
? ? //打印地址格式用%p,表示輸出變量的地址值
? ? //地址值時(shí)用一個(gè)十六進(jìn)制(以16為基數(shù))的無符號整數(shù)表示。
return 0;
}
圖片描述
//如何定義兩個(gè)相同基類型的指針變量呢?
int*pa,*pb;
//注意不可以用int*pa,pb。這表示定義了可以指向整型數(shù)據(jù)的指針變量pa和整型變量pb。
總結(jié):?
1. 指針變量--->用來存放地址的變量。(存放在指針中的值都被當(dāng)成地址來處理)
2.那么最小的一個(gè)單元有多大呢?---- 一個(gè)字節(jié)
3.深刻剖析內(nèi)存單元如何進(jìn)行編址?
經(jīng)過仔細(xì)的計(jì)算和權(quán)衡我們發(fā)現(xiàn)一個(gè)字節(jié)給一個(gè)對應(yīng)的地址是比較合適的。
對于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
這里就有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ī)器,如果給64根地址線,那能編址多大空間,自己計(jì)算。
也就是說:
1.在32位的機(jī)器上,地址是32個(gè)0或者1組成二進(jìn)制序列,那地址就得用4個(gè)字節(jié)的空間來存儲,所以
一個(gè)指針變量的大小就應(yīng)該是4個(gè)字節(jié)。
2.那如果在64位機(jī)器上,如果有64個(gè)地址線,那一個(gè)指針變量的大小是8個(gè)字節(jié),才能存放一個(gè)地
址
總結(jié):?
1.指針是用來存放地址的,地址是唯一標(biāo)識一塊地址空間的。
2.在32位平臺上,指針的大小占4個(gè)字節(jié);在64位平臺上,指針的大小占8個(gè)字節(jié)。
接下來我們觀察一下指針變量在內(nèi)存中的存儲情況
int main()
{
int num = 0;//地址編號為:0x00effcf0,存放的值為00000000
int* pa = #//地址編號為:0x00EFFCE4,存放的值為0x00effcf0
char* pc = (char*)#//地址編號為:0x00EFFCD8,存放的值為0x00effcf0
return 0;
}
注意:
本例第5行:指針變量只能指向同一基類型的變量,否則會引起warning。所以本例使用了強(qiáng)制類型轉(zhuǎn)換。
通過上述代碼和圖片解釋,我們發(fā)現(xiàn):🌜2.指針和指針類型🌛
指向某變量的指針變量,雖然指針變量中存放的是變量的地址值,二者在數(shù)值上相等,但在概念上變量的指針并不等同于變量的地址。變量的地址是一個(gè)常量,不能對其進(jìn)行賦值。而變量的指針是一個(gè)變量,其值是可以改變的。
我們都知道,變量有不同的類型,如整型、浮點(diǎn)型等等。那么指針也有對應(yīng)的指針類型。
int num=10;
int* p=#//pa是一個(gè)指針變量,它指向一個(gè)整型變量
變量p是一個(gè)指針變量,用來存放num的地址。因?yàn)閚um的類型是整型,那么指針存放的數(shù)據(jù)類型是整型,對應(yīng)p是一個(gè)整型指針,同時(shí)需要在int和p之間加一個(gè)*確保它是一個(gè)指針變量。2.1指針類型:
指針類型定義的方式:類型關(guān)鍵字+*+指針變量名
char* p=NULL;(存放char類型變量的地址)
short* p=NULL;(存放short類型變量的地址)
long* p=NULL;(存放long類型變量的地址)
int* p=NULL;(存放int類型變量的地址)
float* p=NULL;(存放float類型變量的地址)
double* p=NULL;(存放double類型變量的地址)
介紹一下 指針運(yùn)算符, 也稱 間接尋址運(yùn)算符 或 解引用運(yùn)算符:(*)間接尋址運(yùn)算符*用來訪問指針變量指向變量的值。 運(yùn)算時(shí),要求指針已經(jīng)被正確初始化或者已經(jīng)指向內(nèi)存中某個(gè)確定的存儲單元,防止野指針的出現(xiàn)。2.2指針+-整數(shù)
int main()
{
int num = 10;
int* pn = #
char* pc = (char*)#
printf("&num=%p\n", &num);
printf("pn=%p\n", pn);
printf("pn+1=%p\n",pn+1);
printf("pc=%p\n",pc);
printf("pc+1=%p\n",pc+1);
return 0;
}
內(nèi)存中的地址是由十六進(jìn)制顯示的:pn是整型指針,pn+1與pn相差4個(gè)字節(jié);而pc是字符型指針,pc+1與pc相差1個(gè)字節(jié)
總結(jié):指針的類型決定了指針向前或者向后走一步是多大距離
2.3指針的解引用int main()
{
int num = 10;
int* pn = #
char* pc = (char*)#
printf("%d\n", *pn);
printf("%d\n", *pc);
return 0;
}
這樣看對指針的解引用不深刻,我們換一種方法:
int main()
{
int n = 0x11223344;
char* pc = (char*)&n;
int* pn = &n;
*pc = 0; //重點(diǎn)在調(diào)試的過程中觀察內(nèi)存的變化。
*pn = 0; //重點(diǎn)在調(diào)試的過程中觀察內(nèi)存的變化。
return 0;
}
//接下來我們通過調(diào)試觀察在內(nèi)存中的變化
通過對pc進(jìn)行解引用操作,按ctrl+F10進(jìn)行逐語句調(diào)試過程,按F10到25行,再按一次調(diào)試25行代碼跳到26行,可以觀察25行程序運(yùn)行的情況:觀察內(nèi)存變化發(fā)現(xiàn)內(nèi)存由0x11223344變成了0x11223300。*pc對pc進(jìn)行解引用操作并賦值為0,因?yàn)閜c是Char類型指針,訪問一個(gè)字節(jié)的內(nèi)存空間,所以將內(nèi)存地址的低位44改成了00。
當(dāng)我們再按F10的時(shí)候,語句調(diào)試第26行,跳到27行,觀察第26行程序的運(yùn)行:因?yàn)閜n是整型指針,對pn解引用操作會對內(nèi)存訪問四個(gè)字節(jié)的內(nèi)存空間,將*pn賦值為0,也就是將0x11223344變成了0x00000000。
總結(jié):💗
指針的類型決定了指針解引用操作對內(nèi)存能訪問多大的權(quán)限(能操作幾個(gè)字節(jié))
比如: char* 的指針解引用就只能訪問一個(gè)字節(jié),而 int* 的指針的解引用就能訪問四個(gè)字節(jié)。
同樣,我們可以通過解引用修改變量的值,看下面程序運(yùn)行結(jié)果: 📄
int main()
{
int num = 0;
int* pa = #//定義指針變量的同時(shí)對其初始化
*pa = 20;//修改指針變量pa所指向的變量的值
? ? //引用指針?biāo)赶虻淖兞康闹?,也稱為指針的解引用。
printf("%d\n", num);
return 0;
}
那么:既然*pa和變量num的值一樣,我們原本可以很容易的將變量a的值打印出來,為什么還要舍近求遠(yuǎn)地使用指針變量來得到a的值呢?通過”標(biāo)題4展現(xiàn)指針的強(qiáng)大功能“😍
👺3.野指針👺概念:
?野指針就是指指向的內(nèi)存是不可知的。(隨機(jī)的、不正確的、沒有明確限制的)
3.1野指針的成因😺 3.1.1指針的未初始化int main()
{
? ? int*p;//此時(shí)指針變量p指向的內(nèi)存是不可知的
? ? *p=20;//對指針進(jìn)行解引用修改值可能會對內(nèi)存發(fā)生不可逆的影響
? ? return 0;
}
3.1.2指針的越界訪問int main()
{
int arr[10] = { 0 };
int* p=&arr;//數(shù)組的第一個(gè)元素的首地址
int i = 0;
for (i = 0; i< 11; i++)
{
*(p++) = i;//先對p賦值,后p++(詳看++的運(yùn)算規(guī)則)
? ? ? ? //指針指向的范圍超出了數(shù)組的范圍,此時(shí)p就是野指針
}
for (i = 0; i< 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
3.1.3指針指向的空間釋放與動(dòng)態(tài)內(nèi)存相關(guān)內(nèi)容,后續(xù)發(fā)布文章講解3.2如何規(guī)避野指針
1.指針初始化
2.防止指針越界的情況發(fā)生
3.指針指向空間釋放即使置NULL
4.避免返回局部變量的指針
5.指針使用之前檢查有效性
int main()
{
int* p = NULL;
int num = 10;
p = #
if (p != NULL)
{
*p = 10;
}
printf("%d\n", num);
? ? //10
}
恪守準(zhǔn)則:🌜4.指針運(yùn)算🌛 4.1指針+-整數(shù)😇
1.永遠(yuǎn)清楚每個(gè)指針指向了哪里,指針必須只想一塊有意義的內(nèi)存;
2.永遠(yuǎn)清楚每個(gè)指針指向的對象的內(nèi)容是什么;
3.永遠(yuǎn)不要使用未初始化的指針變量。
#define N_VALUES 5
float values[N_VALUES];//浮點(diǎn)型數(shù)組
float* vp;//定義一個(gè)浮點(diǎn)型指針
//指針+-整數(shù);指針的關(guān)系運(yùn)算
int main()
{
for (vp = &values[0]; vp< &values[N_VALUES];)
{
*vp++ = 0;//數(shù)組從下標(biāo)為0的元素開始賦值為0;
}
return 0;
}
4.2指針-指針😇//函數(shù)功能:模擬實(shí)現(xiàn)strlen
//函數(shù)形參:字符型指針
//函數(shù)返回值:整型
int my_strlen(char* ch)
{
char* p = ch;
//數(shù)組名是數(shù)組首地址
while (*p!='\0')
{
p++;
}
return p - ch;
//指針-指針:實(shí)現(xiàn)指針之間的距離(相差的字節(jié)數(shù))
}
int main()
{
char ch[20] = { 0 };
fgets(ch, sizeof(ch), stdin);
printf("%d\n", my_strlen(ch));
return 0;
}
4.3指針的關(guān)系運(yùn)算😇#define N_VALUES 5
float values[N_VALUES];//浮點(diǎn)型數(shù)組
float* vp;//定義一個(gè)浮點(diǎn)型指針
//指針+-整數(shù);指針的關(guān)系運(yùn)算
int main()
{
for(vp = &values[N_VALUES]; vp >&values[0];)//vp指向下標(biāo)為5的內(nèi)存空間
? ? {
? ? ? ? *--vp = 0;//從下標(biāo)為4的位置開始
? ? }
return 0;
}
當(dāng)我們對代碼進(jìn)行簡化時(shí):
#define N_VALUES 5
float values[N_VALUES];//浮點(diǎn)型數(shù)組
float* vp;//定義一個(gè)浮點(diǎn)型指針
//指針+-整數(shù);指針的關(guān)系運(yùn)算
int main()
{? ?
? ? for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
? ? {
? ? ? ? *vp = 0;
? ? ? ? //當(dāng)vp=&values[0]時(shí),*vp=0,此時(shí)會進(jìn)行vp--,導(dǎo)致指針指向第一個(gè)元素之前的內(nèi)存空間,應(yīng)避免這樣? ? ? ? ? //的問題
? ? }
return 0;
}
實(shí)際在絕大部分的編譯器上是可以順利完成任務(wù)的,然而我們還是應(yīng)該避免這樣寫,因?yàn)闃?biāo)準(zhǔn)并不保證
它可行
?標(biāo)準(zhǔn)規(guī)定?
允許指向數(shù)組元素的指針與 指向數(shù)組最后一個(gè)元素后面的那個(gè)內(nèi)存位置的指針比較,但是 不允許與4.4按值調(diào)用和模擬按引用調(diào)用 ?引例1?修改實(shí)參的值
指向第一個(gè)元素之前的那個(gè)內(nèi)存位置的指針進(jìn)行比較。
void Change(int num)
{
num = 20;
}
int main()
{
int num = 10;
printf("%d\n", num);
Change(num);
printf("%d\n", num);
return 0;
}
程序在函數(shù)Change中改變了num的值,但是再次輸出實(shí)參的值卻發(fā)生沒有任何變化,說明函數(shù)的形參值的改變并未影響到實(shí)參值的改變。這是因?yàn)樾螀⑹菍?shí)參的一份臨時(shí)拷貝,通過按值調(diào)用不能在被調(diào)函數(shù)中改變其調(diào)用語句中的實(shí)參值。這時(shí)我們就要用到指針這個(gè)秘密武器了。指針變量的一個(gè)重要作用就是用作函數(shù)參數(shù),由于傳給被調(diào)函數(shù)的這個(gè)值不是變量的值,而是變量的地址,通過向被調(diào)函數(shù)傳遞變量的地址可以在被調(diào)函數(shù)中改變主調(diào)函數(shù)中變量的值,模擬C++中的按引用調(diào)用。
void Change(int* num)
{
*num = 20;
}
int main()
{
int num = 10;
printf("%d\n", num);
Change(&num);
printf("%d\n", num);
return 0;
}
實(shí)際上是通過實(shí)參的地址對實(shí)參進(jìn)行修改
?引例2?實(shí)現(xiàn)兩個(gè)整數(shù)的交換void Swap(int*a,int*b)//形參是兩個(gè)整型指針
{
int tmp = 0;//交換兩個(gè)數(shù)的值
tmp = *a;
*a = *b;
*b = tmp;
}
int main()
{
int a = 10;
int b = 20;
Swap(&a, &b);//傳址
printf("%d\n%d\n", a, b);
return 0;
}
如果修改為Swap(a,b),void Swap(int a,int b)結(jié)果是什么呢?原理和引例1相同,我就不過多解釋啦😁
🌜5.指針和數(shù)組🌛先看一個(gè)例子:
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr);
printf("%p\n", &arr[0]);
return 0;
}
結(jié)論:數(shù)組名表示的是數(shù)組首元素的地址
//也就是說我們可以這樣寫:
int arr[10]={0};
int*pa=&arr;//pa存放的是數(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]);
? ? for(i=0; ip+%d = %p\n", i, &arr[i], i, p+i);
? ? }
? ? return 0;
}
進(jìn)一步可以表示為以下這種方式:
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.二級指針🌛指針變量也是變量,那么指針變量的地址存放在哪里呢?-----二級指針
int main()
{
int a = 10;
int* pa = &a;
printf("對pa進(jìn)行解引用=%d\n", *pa);
printf("變量a的地址=%p\n", &a);
printf("變量a的地址存儲在pa中=%p\n", pa);
int** ppa = &pa;
printf("指針變量pa的地址=%p\n", &pa);
printf("對ppa解引用得到pa的值=%p\n", *ppa);
printf("對ppa解引用得到pa再解引用得到a=%d\n", **ppa);
printf("變量pa的地址存儲在ppa中=%p\n",ppa);
**ppa = 30;
//**ppa相當(dāng)于*pa,*pa相當(dāng)于a
printf("a=%d\n", a);
return 0;
}
程序運(yùn)行結(jié)果如上
🌜7.指針數(shù)組🌛指針數(shù)組是指針還是數(shù)組呢?答案是數(shù)組,是存放指針變量的數(shù)組。
我們知道存在整型數(shù)組、浮點(diǎn)型數(shù)組、字符數(shù)組,那指針數(shù)組是什么樣子的呢?
int* arr[5]={0};
如圖片所示
簡單介紹即可,進(jìn)階版跟后續(xù)筆記😭
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧
當(dāng)前文章:修仙之指針初階-創(chuàng)新互聯(lián)
網(wǎng)頁URL:http://chinadenli.net/article28/cdghcp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站策劃、手機(jī)網(wǎng)站建設(shè)、網(wǎng)站收錄、網(wǎng)站建設(shè)、網(wǎng)站內(nèi)鏈、外貿(mào)建站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容