接下來創(chuàng)建一個變量
a
:int a = 4
,int
類型占四個字節(jié),所以a
在內(nèi)存中占4個內(nèi)存單元:
可以用&
對a
進行取地址:&a
,a
是一個整形變量,占用4個字節(jié),每個字節(jié)都有自己的地址,這里取出的地址是第一個字節(jié)的地址(較小的地址)
所以這里&a
取出值的是0x0012ff44
通過一下代碼就能得出指針變量的大小
#includeint main()
{printf("%d\n", sizeof(char *));
printf("%d\n", sizeof(short *));
printf("%d\n", sizeof(int *));
printf("%d\n", sizeof(double *));
return 0;
}
結(jié)論:指針大小在32位平臺是4個字節(jié),64位平臺是8個字節(jié)
以上結(jié)論是根據(jù)程序結(jié)果得到的,接下來我們也可以通過物理層面去得到指針變量大?。?/p>
對于32位機器,它有著32根地址線,每跟線在尋址的時候會產(chǎn)生高電壓和低電壓也就是1或者0,有著32根地址線,也就是地址有著32歌比特位,1字節(jié)等于8比特,所以32位機器中地址占4個字節(jié)
這里我們可以繼續(xù)算下去:32根地址線,就有2的32次方個地址,2^32Byte == 2^32/1024KB ==2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB
,所以在32位機器中,可以給4G的空間進行編址。
這里我們就明白:
總結(jié):
指針有許多類型,如:int*
,short*
,char*
,float*
……
但是從前面得知指針的類型都是4或8字節(jié)大小,它們不會存在像如int
和char
不同類型有著不同占用空間的問題,既然指針類型大小一樣,那么為什么不把指針設(shè)置成一個同一類型的指針并且可以接收不同的類型變量的地址呢?不同的指針類型都有什么意義呢?
首先看一下代碼:
#includeint main()
{int a = 0x11223344;
int* pa = &a;
*pa = 0;
return 0;
}
調(diào)試后進入內(nèi)存窗口,找到
a
的地址后可以看見在內(nèi)存中a
中的存儲的值為:44332211
,這樣“倒著存放”的原因是在Visual studio這個IDE中采取小端存儲,如不懂大小端存儲,👉可點擊跳轉(zhuǎn)
然后繼續(xù)運行下一行代碼,會發(fā)現(xiàn)*pa = 0
使內(nèi)存中4個字節(jié)內(nèi)容變?yōu)?
接著再看一個代碼:
#includeint main()
{int a = 0x11223344;
char* pa = &a;
*pa = 0;
return 0;
這個代碼與上面代碼不同的一點是這次
pa
指針變量是char
類型的,上一個代碼中的類型為int
類型的
還是調(diào)試后進入內(nèi)存窗口,找到a
的地址后可以看見在內(nèi)存中a
中的存儲的值為:44332211
接著運行,會發(fā)現(xiàn),內(nèi)存中只有
44
變?yōu)榱?code>00,也就是只有一個字節(jié)的內(nèi)容發(fā)生了改變
這兩個代碼比較得出結(jié)論:
int*
類型的解引用會對4個字節(jié)內(nèi)容進行操作char*
類型的解引用會對1個字節(jié)內(nèi)容進行操作float*
類型的解引用會對4個字節(jié)內(nèi)容進行操作double
類型的解引用會對8個字節(jié)內(nèi)容進行操作所以,我們可以通過不同的指針變量按需求對不同字節(jié)大小內(nèi)容進行操作
觀看一下代碼:
int main()
{int a = 0x11223344;
int* pa = &a;
char* pc = &a;
printf("%p\n", pa);
printf("%p\n", pa+1);
printf("%p\n", pc);
printf("%p\n", pc+1);
return 0;
}
得出結(jié)果:
可以看出,前兩個地址大小差4,后兩個地址大小差2pa
的類型為int*
,pa+1
的地址增加了4,pc
的類型為char*
,pc+1
的地址增加了1
得出結(jié)論:
野指針概念:野指針就是指針指向的位置是不可知的(隨機的、不正確的、沒有明確限制的)
野指針成因
- 指針未初始化
#includeint main()
{int *p;
*p = 20;
return 0;
}
這里的指針p
在定義的時候沒有初始化,默認為隨機值,也就是指針指向位置不可知。
以隨機值為地址,解引用時需要找到那個地址處,是很危險的
此程序在編譯器里不會被編譯成功,會警告:使用了未被初始化的內(nèi)存p
- 指針越界訪問
#includeint main()
{int arr[10] = {0};
int *p = arr;
int i = 0;
for(i=0; i<=11; i++)
{*(p++) = i;
}
return 0;
}
在arr
數(shù)組內(nèi)有10個元素,循環(huán)時,到下標為10的時候,指針指針指向的范圍超出數(shù)組arr的范圍時,p就是野指針
3.指針指向的空間釋放
int* test()
{int a = 10;
return &a;
}
int main()
{int* p = test();
printf("%d\n", *p);
return 0;
}
在test
函數(shù)中,返回int
類型a
的地址,在主函數(shù)中用p
接受這個地址,但是當test
運行完return
語句后,在test
函數(shù)中對于變量開辟的空間就會歸還給系統(tǒng),此時&a
已經(jīng)不存在了,所以p
為野指針
p = NULL
if(p!=NULL)
指針加減整數(shù)在前面指針類型意義里提到,這里不多說
這里看一個代碼:
int main()
{int values[5];
int * vp;
for(vp = &values[0]; vp< &values[5];)
{*vp++ = 0;
}
}
這個代碼很簡單,就是通過指針,逐步把數(shù)組中元素值賦值為0
這里有一點值得注意的:
*vp++ = 0;
,++
優(yōu)先級比*
高,又由于++
是后置,先用再加加。
vp
的初始值為&values[0]
,所以才會從values[0]
開始賦值,接著++
,再對下一個位置賦值。若改為前置++,則會先加加,再賦值,就會倒置
values[0]
位置處未被賦值。
指針-指針,前提是:兩個指針要指向同一塊空間,得到的結(jié)果的絕對值是兩個指針之間元素個數(shù)
int main()
{int arr[10] = {0 };
printf("%d", &arr[9] - &arr[0]);
return 0;
}
輸出結(jié)果:
這里我們可以利用指針減指針來模擬strlen
函數(shù)
假設(shè)有一個字符串abcdefghi
,在內(nèi)存中實際存儲的是a b c d e f g h i \0
,所以只要找到首元素的地址和\0
的地址,相減就能得到字符串的長度
代碼如下:
int my_strlen(char* s)
{char* begin = s;
while (*s!='\0')
{s++;
}
return s - begin;
}
int main()
{char arr[] = "abcdefghi";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
以數(shù)組為例,數(shù)組首元素地址是小于數(shù)組除首元素其他元素地址
但是標準規(guī)定:
允許指向數(shù)組元素的指針與指向數(shù)組最后一個元素后面的那個內(nèi)存位置的指針比較,但是不允許與
指向第一個元素之前的那個內(nèi)存位置的指針進行比較。
數(shù)組尾部的地址不是最后一個元素的地址,而是下標為數(shù)組長度處的地址
如上圖:可以用&arr[6]
和指針vpp
與數(shù)組中其他地址進行比較,但是不可以用指針vp
進行比較
通俗來講就是:可以用數(shù)組后邊的地址比較,但是不可以用數(shù)組前面的地址比較。
指針與數(shù)組的關(guān)系主要在這篇文章里👉數(shù)組與地址,數(shù)組名到底是什么?
我們觀察以下代碼
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10 };
//用數(shù)組下標遍歷數(shù)組元素
for (int i = 0; i< 10; i++)
{printf("%d ", arr[i]);
}
printf("\n");
//利用指針遍歷數(shù)組元素
int* p = arr;
for (int i = 0; i< 10; i++)
{printf("%d ", *(p + i));
}
}
因為數(shù)組名就是數(shù)組元素首地址,所以在這個程序里arr
與p
是完全相同的。
同時這里可以發(fā)現(xiàn)用數(shù)組下標遍歷數(shù)組和利用指針遍歷數(shù)組都可以遍歷,所以arr[i]
和*(p+i)
是等價的
也就是有一個指針p
,p[i]<=>*(p+i)
并且我們還可以推理:因為加法有交換律,所以*(p+i)
和*(i+p)
肯定是是等價的,通過*(i+p)
又可以推出i[p]
所以p[i]
是可以寫成i[p]
的,只是因為i[p]
這種寫法太過于逆天,所以不常見。
指針變量也是變量,是變量就有地址,那指針變量的地址存放在哪里?
這就是二級指針
。
int main()
{int a = 10;
int* pa = &a; //一級指針
int** ppa = &pa; //二級指針
return 0;
}
上面代碼中的ppa
就是二級指針
二級指針有兩個*
,怎么理解呢?
先看一級指針 ,
int* pa = &a
,一級指針中的那個*
就表示pa
是一個指針,剩下的那個int
表示指針pa
指向一個int
類型變量
二級指針也同理int** ppa = &pa
,離ppa
近的那個*
表示ppa
是一個指針變量,剩下的int*
表示ppa
這個指針指向int*
類型
二級指針的解引用也和一級指針一樣:**ppa
指針的內(nèi)容還有很多
還有 字符指針 ~ 指針數(shù)組 ~ 數(shù)組指針 ~ 函數(shù)指針 ~~~~ 等等
~Coming soon~
🎸🎸🎸
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧