你有沒有想過,C語言一些簡單的語法規(guī)則,可以做出很巧妙的方法。
舉個例子,C語言的數(shù)組長度是不允許是負(fù)數(shù)的,當(dāng)然常識中數(shù)組長度為負(fù)數(shù)好像也沒什么意義。
例如int arr[1];是對的,而int arr[-1];是錯的。
這個規(guī)則很簡單,也很容易理解,當(dāng)然也不會引人關(guān)注。
但是呢,我說可以用這個語法規(guī)則做C語言assert斷言錯誤檢查,你信么?優(yōu)秀的程序員,一般都是想盡一切辦法將程序的錯誤盡可能地被攔截在運(yùn)行之前,即編譯階段和預(yù)編譯階段的,而不是流出到運(yùn)行階段,更不是發(fā)生在用戶手里的產(chǎn)品中。對于預(yù)編譯階段的錯誤攔截,比較簡單,通過#if和#error等預(yù)編譯指令就可以做到,例如1. FreeRTOS中的Priority檢查,用戶必須將優(yōu)先級定義大于或等于1,否則就報錯
這是因為,程序設(shè)計里如果遇到configMAX_PRIORITIES < 1的情況,可能會導(dǎo)致更嚴(yán)重的錯誤。
所以這種在預(yù)編譯階段攔截了這個錯誤,是很有作用的2. FreeRTOS中的運(yùn)行時狀態(tài)獲取函數(shù),必須依賴于configUSE_TRACE_FACILITY的定義
void vTaskGetRunTimeStats( char *pcWriteBuffer )
{
TaskStatus_t *pxTaskStatusArray;
UBaseType_t uxArraySize, x;
uint32_t ulTotalTime, ulStatsAsPercentage;
{
}
因為,這個函數(shù)很依賴其他其他的函數(shù)實現(xiàn)或者資源定義,那么這個configUSE_TRACE_FACILITY管理了這些
3. 版本檢查,如果你設(shè)計一個比較通用的功能,可以用到很多項目中去,為了方便管理和迭代更新,就要對這個功能模塊做版本定義。那么不同版本之間就存在差異,可能存在不兼容的情況,此時就可以用預(yù)編譯指令做這種兼容的檢查
通過預(yù)編譯指令來檢查錯誤是很有限的,因為預(yù)編譯指令能檢查的是立即數(shù)和一些邏輯關(guān)系。于是,我們還要考慮編譯階段的錯誤檢查,即對在編譯階段產(chǎn)生的結(jié)果做檢查。例如,unsigned long或者void*的長度檢查即sizeof(unsignedlong)或sizeof(void*)的值,就必須在編譯階段由于unsigned long和void*的長度在不同芯片架構(gòu)上可能存在差異,如果你的程序依賴這個類型的長度,那必須要檢查其長度是否為你設(shè)計的那樣。像這種#if sizeof(unsigned long) == 4是不正確的做法,因為預(yù)編譯無法計算sizeof(unsigned long)那么有沒有一種情況能確保sizeof(unsigned long)的值是4才不出錯呢?也許你會想到一個叫assert的東西,例如FreeRTOS里面定義的
既可以這樣使用configASSERT(sizeof(unsigned long) == 4),如果sizeof(unsignedlong) == 4,那么程序是“靜悄悄”的,當(dāng)做啥事都沒發(fā)生,但是如果sizeof(unsigned long) == 8,那么程序就會進(jìn)入for( ;; );無法自拔。但是這種做法是運(yùn)行階段的,必須要在程序已經(jīng)集成好并讓其在實際環(huán)境中運(yùn)行才能發(fā)現(xiàn)這種錯誤,排查起來成本還是有點(diǎn)高。那么有沒有一種辦法可以讓其扼殺在編譯階段呢?答案是有的,像C++11和C11就支持這種assert了,名叫STATIC_ASSERT,需要包含頭文件assert.h。但是,如果我還沒用到C11,也想用這種STATIC_ASSERT呢,有沒有辦法自己實現(xiàn)一個?答案也是可以的,需要想個竅門,例如從數(shù)組的長度入手。像int arr[1];這種定義是沒有問題的,但int arr[-1];卻是會引起編譯器報錯的,我們就可以基于這種東西做文章了,即將數(shù)組長度換成檢查條件COND,即如果COND為TRUE就不報錯,為FALSE就報錯通過一頓反復(fù)嘗試,搞成這樣或許就可以了
int arr[(!!(COND))*2-1];
這里(!!(COND))*2-1沒有一個符號是多余的,簡單解釋下:1.(COND),這里加了一層括號,防止COND是個比較復(fù)雜的表達(dá)式,可能引發(fā)未知的優(yōu)先級問題;2.(!!(COND)),這里有兩個感嘆號,即邏輯取反再取反。也許你會覺得有點(diǎn)奇怪,!!TRUE不就是TURE嗎,!!FALSE也是FALSE啊,雙層取反是不是有點(diǎn)多余。那你就要認(rèn)真思考下TRUE和FALSE的定義了,C語言中的FALSE是0,而非0是TRUE,這個非0就有很多發(fā)揮空間了,例如整數(shù)100,也是TRUE,但是!!100就會變成1,這里的(!!(COND))就是讓其結(jié)果變成0或者1,而不是其他數(shù)。3.通過2的解釋,就很容易理解(!!(COND))*2-1這可以保證這個結(jié)果是1或者-1,而不存在其他數(shù)值。因為,我們的目的要的是int arr[1];或者int arr[-1];即可。
定義一個數(shù)組用在程序中間好像用起來沒那么友好,可以換成
typedef int arr[(!!(COND))*2-1];
為了還能看到多一點(diǎn)點(diǎn)錯誤信息,還可以將其定義成宏,并帶多一個參數(shù),這就成了這樣
defineSTATIC_ASSERT(COND,MSG)typedefchar static_assertion_##MSG[(!!(COND))*2-1]
在這個基礎(chǔ)上再搞定其他的,就可以這樣了
// token pasting madness:
接著,判斷sizeof(unsigned long) 是否為4,就可以這樣
STATIC_ASSERT(sizeof(unsignedlong)==4,unsigned_long_size_is_not_4_error);
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。
舉報投訴
-
C語言
+關(guān)注
關(guān)注
183文章
7644瀏覽量
145635 -
C++
+關(guān)注
關(guān)注
22文章
2124瀏覽量
77138 -
編譯
+關(guān)注
關(guān)注
0文章
694瀏覽量
35175
原文標(biāo)題:C語言開發(fā)如何將錯誤扼殺在編譯階段
文章出處:【微信號:embedded_sw,微信公眾號:嵌入式軟件實戰(zhàn)派】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
熱點(diǎn)推薦
Rust語言中錯誤處理的機(jī)制
在Rust語言中,錯誤處理是一項非常重要的任務(wù)。由于Rust語言采用靜態(tài)類型檢查,在編譯時就能發(fā)現(xiàn)很多潛在的錯誤,這使得程序員能夠更加自信和
如何將高級C語言編譯成機(jī)器碼
器各個階段做得事情,這里不做詳細(xì)介紹,感興趣的粉絲可以自己找資料學(xué)習(xí)。C語言的編譯器有很多種,在我們芯片行業(yè),主要有GCC和LLVM。下面框圖簡單的描述了一個CPU
發(fā)表于 06-01 16:53
C語言編譯過程中的錯誤分析
語言的最大特點(diǎn)是:功能強(qiáng)、使用方便靈活。C編譯的程序?qū)φZ法檢查并不象其它高級語言那么嚴(yán)格,這就給編程人員留下“靈活的余地”,但還是由于這個靈活給程序的調(diào)試
發(fā)表于 09-11 11:43
?1537次閱讀
C語言編程時常犯的18種錯誤
C語言的最大特點(diǎn)是:功能強(qiáng)、使用方便靈活。C編譯的程序?qū)φZ法檢查并不象其它高級語言那么嚴(yán)格,這就給編程人員留下“靈活的余地”,但還是由于這個
如何將C源代碼從MPLAB C18編譯器移植到MPLAB XC8C編譯器的詳細(xì)概述
本文檔介紹了針對PIC18 MCU的MPLAB? C編譯器(以前的說法,本文檔稱為MPLAB C18)與MPLAB XC8 C編譯器間的差異
發(fā)表于 06-07 09:28
?30次下載
C語言編譯器中有哪些錯誤信息詳細(xì)中英文對比
本文檔的主要內(nèi)容詳細(xì)介紹的是C語言編譯器中有哪些錯誤信息詳細(xì)中英文對比詳細(xì)資料免費(fèi)下載。
發(fā)表于 11-09 17:43
?13次下載
VScode編譯器如何配置C/C++編譯環(huán)境
昨天有伙伴私信我,為什么我用C語言寫的hello world幾行代碼,在編譯器里面報錯了呢?
c語言代碼錯誤怎么找
,它們通常是由于C語言編寫規(guī)則被打破所引起的。實際上,C編譯器會提供非常詳細(xì)的錯誤消息,告訴我們在哪個地方發(fā)生了
C語言關(guān)鍵字分別發(fā)生在哪個階段
以下C語言關(guān)鍵字,分別發(fā)生在哪個階段? 第一個,define。 首先得糾正一下,define 并不是C語言里面的關(guān)鍵字,即使加了井號,也不是
C語言開發(fā)如何將錯誤扼殺在編譯階段
評論