指針,是C語言中的一個重要概念及其特點,也是掌握C語言比較困難的部分。指針也就是內(nèi)存地址,指針變量是用來存放內(nèi)存地址的變量。
本質(zhì)還是一個變量,指針提供了一種對存儲位置的動態(tài)訪問手段,(相對于普通變量而言,普通變量只能訪問自己所占的存儲位置)
內(nèi)存地址對齊,是計算機在內(nèi)存中的數(shù)據(jù)排列、訪問數(shù)據(jù)的方式,包含了基本數(shù)據(jù)對齊和結(jié)構(gòu)體數(shù)據(jù)對齊的兩種相互獨立又相互關(guān)聯(lián)的部分。
現(xiàn)代計算機在內(nèi)存中讀寫數(shù)據(jù)是按字節(jié)塊進行操作,理論上任意類型的變量訪問可以從任何地址開始,但是計算機系統(tǒng)對任意數(shù)據(jù)類型在內(nèi)存中存放位置有限,它會要求這些數(shù)據(jù)的首地址的值為K(4位或者8位)的整數(shù)倍。
如何踩坑的?
在一份十分優(yōu)秀的代碼中,指針的使用率占比很高,因為指針能讓代碼實現(xiàn)變得更自由、更高效和更方便等諸多優(yōu)點,可對于不十分熟悉指針的朋友來說,用起來也許就是災(zāi)難(常見的就是程序跑飛)
因此,通過指針的使用率大概就能判斷一個人的編程能力水平
請看下面的代碼,運行結(jié)果是怎么樣的呢?
//?假設(shè)數(shù)組首地址為?0x00004000,符合內(nèi)存對齊:4的倍數(shù)
static?unsigned?char?sg_arrBuf[100];
int?main()
{
memset(sg_arrBuf,?0,?sizeof(sg_arrBuf));
//?地址為?0x00004000
uint8_t?*pucVal?=?(uint8_t?*)&sg_arrBuf[0];
//?地址為?0x00004001
uint16_t?*puiVal?=?(uint16_t?*)&sg_arrBuf[1];
*pucVal?=?20;???//?HEX:?0x14
*puiVal?=?2000;?//?HEX:?0x07d0
printf("HEX:?");
for?(int?i?=?0;?i?<?3;?i++)
{
printf("%02x?",?sg_arrBuf[i]);
}
printf("n");
return?0;
}
很多朋友期望的結(jié)果如下(小端模式):
HEX:?14?d0?07
事實真的一定如此嗎?
不一定!
也許部分朋友在自己電腦上打開 VS 復(fù)制粘貼運行了,編譯后運行,結(jié)果還真和上面一樣!??!你這不是在忽悠人嗎?。?!
那有試過在 MCU 上跑過嗎?是不是程序跑飛了?
為什么?
當計算機讀取或?qū)懭雰?nèi)存地址時,它將以字(word)大小的塊進行存儲。數(shù)據(jù)對齊意味著將數(shù)據(jù)放在等于字長的倍數(shù)的內(nèi)存偏移處,正是由于這種CPU處理內(nèi)存的方式從而提高了系統(tǒng)的性能。大多數(shù)CPU只能訪問內(nèi)存對齊的地址。
意味著部分系統(tǒng)架構(gòu)體系對于未對齊的地址進行訪問時會發(fā)生異常,如果嘗試去訪問內(nèi)存未對齊的變量進行操作會導(dǎo)致總線錯誤。它只支持對齊訪問。
比如我們訪問一個 4 字節(jié) (Double Word) 型的變量時,如果這個變量的起始地址是能被 4 整除的話,我們說這種訪問是雙字節(jié)對齊的。如果訪問一個 2 字節(jié) ( Word ) 變量,當起始地址能被 2 整除時是對齊的。訪問字節(jié) ( Byte ) 型變量,總是對齊的。
根據(jù)這個就能很快鎖定問題原因了,那就是程序運行到這個位置就導(dǎo)致總線錯誤,從而跑飛了。
//?地址為?0x00004001,未對齊
*puiVal?=?2000;?//?HEX:?0x07d0
預(yù)防及解決措施
關(guān)于上述的寫法,有些朋友可能在電腦端實現(xiàn)了某個功能,電腦測試沒有任何問題,但是一旦移植到 MCU 上就不行,那么抓緊檢查一下是不是這個問題呢。
因此,為了確保我們的代碼有很高的移植性和穩(wěn)定性,那么一定要預(yù)防這種情況,可以通過采用訪問字節(jié) ( Byte ) 型變量,也可以使用memcpy的方式處理這種操作就能避免這種問題。
//?假設(shè)數(shù)組首地址為?0x00004000,符合內(nèi)存對齊: 4的倍數(shù)
static?unsigned?char?sg_arrBuf[100];
int?main()
{
memset(sg_arrBuf,?0,?sizeof(sg_arrBuf));
uint8_t?ucVal?=?20;
uint16_t?uiVal?=?2000;
memcpy(&sg_arrBuf[0],?&ucVal,?1);
memcpy(&sg_arrBuf[1],?&uiVal,?2);
printf("HEX:?");
for?(int?i?=?0;?i?<?3;?i++)
{
printf("%02x?",?sg_arrBuf[i]);
}
printf("n");
return?0;
}