指針、結構體、聯合體的安全規范
發布時間:2007/8/30 0:00:00 訪問次數:948
來源:單片機及嵌入式系統應用 作者:清華大學 張樂平 邵貝貝
指針賦予了C編程最大的靈活性;結構體使得C程序整齊而緊湊;聯合體在某些要求注重效率的場合有精彩的表現,這三個要素是C語言的精華。
然而,精華并不意味著完美,C語言在賦予程序員足夠靈活性的同時,也給了程序員很多犯錯誤的機會。所以有必要關注指針、結構體和聯合體的實現細節,從而保障程序的安全性。
在此.第一部分介紹《MISRA—C:2004》中與指針相關的部分規則,第二部分講解結構體和聯合體的操作規范。下文中凡是未加特殊說明的都是強制(required)規則,個別推薦(advisory)規則加了“推薦”標示。
1 指針的安全規范
《MISRA—C:2004》關于指針的規范主要分為三個部分:指針的類型轉換規則、指針運算的規則和指針的有效性規則。
1.1 指針的類型轉換
指針類型轉換是個高風險的操作,所以應該盡量避免進行這個操作。MISRA—C對其中可能造成嚴重錯誤的情況作了嚴格的限定,選擇其中兩條作簡要分析。
規則11.4(推薦):指向不同數據類型的指針之間不能相互轉換。
思考如下程序:
uint8_t*pl;
uint32)_t*p2;
p2=(uint32_t*)pl;
/*注:uint8_t表示8位無符號整型,uint3_t表示32位無符號整型。*/
程序員希望將從p1單元開始的4個字節組成一個32付的整型來參與運算。
如果CPU允許各種數據對象存放在任意的存儲單元,則以上轉換沒有問題。但某些CPU對某種(些)數據類型加強了對齊限制,要求這些數據對象占用一定的地址空間,比如某些字節尋址的CPU會要求32位(4字節)整型存放在4的整倍數地址上。在這個前提下.思考程序中的指針轉換:假設pl一開始指向的是0x00O3單元(對uint8_t型的整型沒有對齊要求),則執行最后一行強制轉換后,p2到底指向哪個單元就無法預料了。
規則1 1.5:指針轉換過程中不允許丟失指針的const、volatile屬性。按如下定義指針:
uIntl6一tx;
uint16_t*const cpi=&x; /*const指針*/
uintl6_t*const *pcpi;/*指向const指針的指針*/
const uintl6_t* *ppci;/*指向const整型指針的指針*/
uIntl6_t**ppi ;
const uint16_t *pci;/*指向const整型的指針*/
volatik uint16_t*pvi;/*指向volatile整型的指針*/
uintl6_t*pi;
則以下指針轉換是允許的:
pl=cpi;
以下指針轉換是不允許的:
pi=(umtl6_t*)pci;
pi=(uintl6_t*)pvil
ppi=(uintl6_t* *)pcpi;
ppi=(uintl6_I**)ppci+
以上非法指針類型轉換將會丟失const或者volatile類型。丟失const屬性,將有可能導致在對只讀內容進行寫操作時,編譯器不會發出警告,編譯器將不對具有volatile屬性的變量作優化;丟失volatile屬性,編譯器的優化可能導致程序員預先設計的硬件時序操作失效,這樣的錯誤很難發現。關于const和volatile關鍵字的詳細作用,讀者可參考ISOC獲取更多信息。
1.2 指針的運算
ISOC標準中,對指向數組成員的指針運算(包括算術運算、比較等)做了規范定義,除此以外的指針運算屬于未定義(undefined)范圍,具體實現有賴于具體編譯器,其安全性無法得到保障,MISRA—C中對指針運算的合法范圍做了如下限定。
規則17.1:只有指向數組的指針才允許進行算術運算①。
規則17 2:只有指向同一個數組的兩個指針才允許相減 ②。
規則17 3:只有指向同一個數組的兩個指針才允許用>,>=,<,<=等關系運算符進行比較。
為了盡最大可能減少直接進行指針運算帶來的隱患,尤其是程序動態運行時可能發生的數組越界等問題,MISRA—C對指針運算作了更為嚴格的規定。規則17 4:只允許用數組索引做指針運算。按如下方式定義數組和指針:
uint8_ta[10];
uint8_t*p;
則*(p+5)=O是不允許的.而p[5]=O則是允許的,盡管就這段程序而言,二者等價。
以下給出一段程序,讀者可參照相應程序行的注釋,細細品味上述規則的含義。
void my_fn(uInt*_t*p1.uint8_t p2[]){
①其實此處的算術運算僅限定于指針加減某個整數.比如ppoint=point一5.ppoint++等。0兩個指針可指向不同的散組成員。
uint8_t index=0;
uint8_t*p3
uint8_t*p4;
*pl=O;
p1++;/*不允許,pl不是指向數組的指針*/
p1=p1+5;/*不允許,pl不是指向數組的指針*/
pl[5]=O;&
來源:單片機及嵌入式系統應用 作者:清華大學 張樂平 邵貝貝
指針賦予了C編程最大的靈活性;結構體使得C程序整齊而緊湊;聯合體在某些要求注重效率的場合有精彩的表現,這三個要素是C語言的精華。
然而,精華并不意味著完美,C語言在賦予程序員足夠靈活性的同時,也給了程序員很多犯錯誤的機會。所以有必要關注指針、結構體和聯合體的實現細節,從而保障程序的安全性。
在此.第一部分介紹《MISRA—C:2004》中與指針相關的部分規則,第二部分講解結構體和聯合體的操作規范。下文中凡是未加特殊說明的都是強制(required)規則,個別推薦(advisory)規則加了“推薦”標示。
1 指針的安全規范
《MISRA—C:2004》關于指針的規范主要分為三個部分:指針的類型轉換規則、指針運算的規則和指針的有效性規則。
1.1 指針的類型轉換
指針類型轉換是個高風險的操作,所以應該盡量避免進行這個操作。MISRA—C對其中可能造成嚴重錯誤的情況作了嚴格的限定,選擇其中兩條作簡要分析。
規則11.4(推薦):指向不同數據類型的指針之間不能相互轉換。
思考如下程序:
uint8_t*pl;
uint32)_t*p2;
p2=(uint32_t*)pl;
/*注:uint8_t表示8位無符號整型,uint3_t表示32位無符號整型。*/
程序員希望將從p1單元開始的4個字節組成一個32付的整型來參與運算。
如果CPU允許各種數據對象存放在任意的存儲單元,則以上轉換沒有問題。但某些CPU對某種(些)數據類型加強了對齊限制,要求這些數據對象占用一定的地址空間,比如某些字節尋址的CPU會要求32位(4字節)整型存放在4的整倍數地址上。在這個前提下.思考程序中的指針轉換:假設pl一開始指向的是0x00O3單元(對uint8_t型的整型沒有對齊要求),則執行最后一行強制轉換后,p2到底指向哪個單元就無法預料了。
規則1 1.5:指針轉換過程中不允許丟失指針的const、volatile屬性。按如下定義指針:
uIntl6一tx;
uint16_t*const cpi=&x; /*const指針*/
uintl6_t*const *pcpi;/*指向const指針的指針*/
const uintl6_t* *ppci;/*指向const整型指針的指針*/
uIntl6_t**ppi ;
const uint16_t *pci;/*指向const整型的指針*/
volatik uint16_t*pvi;/*指向volatile整型的指針*/
uintl6_t*pi;
則以下指針轉換是允許的:
pl=cpi;
以下指針轉換是不允許的:
pi=(umtl6_t*)pci;
pi=(uintl6_t*)pvil
ppi=(uintl6_t* *)pcpi;
ppi=(uintl6_I**)ppci+
以上非法指針類型轉換將會丟失const或者volatile類型。丟失const屬性,將有可能導致在對只讀內容進行寫操作時,編譯器不會發出警告,編譯器將不對具有volatile屬性的變量作優化;丟失volatile屬性,編譯器的優化可能導致程序員預先設計的硬件時序操作失效,這樣的錯誤很難發現。關于const和volatile關鍵字的詳細作用,讀者可參考ISOC獲取更多信息。
1.2 指針的運算
ISOC標準中,對指向數組成員的指針運算(包括算術運算、比較等)做了規范定義,除此以外的指針運算屬于未定義(undefined)范圍,具體實現有賴于具體編譯器,其安全性無法得到保障,MISRA—C中對指針運算的合法范圍做了如下限定。
規則17.1:只有指向數組的指針才允許進行算術運算①。
規則17 2:只有指向同一個數組的兩個指針才允許相減 ②。
規則17 3:只有指向同一個數組的兩個指針才允許用>,>=,<,<=等關系運算符進行比較。
為了盡最大可能減少直接進行指針運算帶來的隱患,尤其是程序動態運行時可能發生的數組越界等問題,MISRA—C對指針運算作了更為嚴格的規定。規則17 4:只允許用數組索引做指針運算。按如下方式定義數組和指針:
uint8_ta[10];
uint8_t*p;
則*(p+5)=O是不允許的.而p[5]=O則是允許的,盡管就這段程序而言,二者等價。
以下給出一段程序,讀者可參照相應程序行的注釋,細細品味上述規則的含義。
void my_fn(uInt*_t*p1.uint8_t p2[]){
①其實此處的算術運算僅限定于指針加減某個整數.比如ppoint=point一5.ppoint++等。0兩個指針可指向不同的散組成員。
uint8_t index=0;
uint8_t*p3
uint8_t*p4;
*pl=O;
p1++;/*不允許,pl不是指向數組的指針*/
p1=p1+5;/*不允許,pl不是指向數組的指針*/
pl[5]=O;&