#error的作用是什么?
#error ?指令讓預處理器發出一條錯誤信息,并且會中斷編譯過程。下面我們從Linux代碼中抽取出來一小段代碼并做修改得到示例代碼:
這段示例代碼很簡單,當RX_BUF_IDX宏的值不為0~3時,在預處理階段就會通過 #error ?指令輸出一條錯誤提示信息:
"Invalid configuration for 8139_RXBUF_IDX" 下面編譯看一看結果:
位操作的基本使用
給一個32bit數據的位置1,怎么用宏來實現?
#define SET_BIT(x, bit) (x |= (1 << bit)) /* 置位第bit位 */
?
?
?
隱式轉換規則
如下代碼的輸出結果是?為什么?
#include? 程序輸出結果為:int main(void) { unsigned int a = 6; int b = -20; if (a + b > 6) printf("a+b大于6 "); else printf("a+b小于6 "); return 0; }
a+b大于6? 原因是因為編譯器會將有符號數b轉換成為一個無符號數,即此處 a+b 等價于 a+(unsigned int)b 。 該程序運行在32bit環境下,b的值為 0xFFFFFFFF-20+1 = 4294967276 ,即a+b將遠遠大于6。 C 語言按照一定的規則來進行此類運算的轉換,這種規則稱為 正常算術轉換 ,轉換的順序為:
double>float>unsigned long>long>unsigned int>int? 即操作數類型排在后面的與操作數類型排在前面的進行運算時,排在后面的類型將 隱式轉換 為排在前面的類型。
typedef與define的區別
(1)#define之后不帶分號,typedef之后帶分號。 (2)#define可以使用其他類型說明符對宏類型名進行擴展,而 typedef 不能這樣做。如:
#define INT1 int unsigned INT1 n; //沒問題 typedef int INT2; unsigned INT2 n; //有問題? INT1可以使用類型說明符unsigned進行擴展,而INT2不能使用unsigned進行擴展。 (3)在連續定義幾個變量的時候,typedef 能夠保證定義的所有變量均為同一類型,而 #define 則無法保證。如:
#define PINT1 int*; P_INT1 p1,p2; //即int *p1,p2; typedet int* PINT2; P_INT2 p1,p2; //p1、p2 類型相同? PINT1定義的p1與p2類型不同,即p1為指向整形的指針變量,p2為整形變量;PINT2定義的p1與p2類型相同,即都是指向 int 類型的指針。
寫一個MAX宏
#define MAX(x,y) ((x) > (y) ? (x) : (y))? 使用括號把參數括起來可以解決了運算符優先級帶來的問題。這樣的MAX宏基本可以滿足日常使用,但是還有更嚴謹的高級寫法。 感興趣的可參考文章: https://www.zhaixue.cc/c-arm/c-arm-express.html
死循環
嵌入式系統中經常要用到無限循環,你怎么樣用C編寫死循環呢? (1)while
while(1) { }? (2)for
for(;;) { }? (3)goto
Loop: … goto Loop;?static的作用
在C語言中,關鍵字static有三個明顯的作用: 1、在函數體修飾變量 一個被聲明為靜態的變量在這一函數被調用過程中維持其值不變。 2、 在模塊內(但在函數體外)修飾變量 一個被聲明為靜態的變量可以被模塊內所用函數訪問,但不能被模塊外其它函數訪問。它是一個本地的全局變量。 3、在模塊內修飾函數 一個被聲明為靜態的函數只可被這一模塊內的其它函數調用。那就是,這個函數被限制在聲明它的模塊的本地范圍內使用。
const的作用
下面的聲明都是什么意思:
?
const int a; int const a; const int *a; int * const a; int const * a const;前兩個的作用是一樣,a是一個常整型數。
第三個意味著a是一個指向常整型數的指針(也就是,整型數是不可修改的,但指針可以)。
第四個意思a是一個指向整型數的常指針(也就是說,指針指向的整型數是可以修改的,但指針是不可修改的)。
最后一個意味著a是一個指向常整型數的常指針(也就是說,指針指向的整型數是不可修改的,同時指針也是不可修改的)。
volatile的作用
以下內容來自百度百科: 一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器里的備份。下面是volatile變量的幾個例子: 1). 并行設備的硬件寄存器(如:狀態寄存器) 2). 一個中斷服務子程序中會訪問到的非自動變量(Non-automatic variables) 3). 多線程應用中被幾個任務共享的變量 回答不出這個問題的人是不會被雇傭的。我認為這是區分C程序員和嵌入式系統程序員的最基本的問題。嵌入式系統程序員經常同硬件、中斷、RTOS等等打交道,所用這些都要求volatile變量。不懂得volatile內容將會帶來災難。 假設被面試者正確地回答了這是問題(嗯,懷疑這否會是這樣),我將稍微深究一下,看一下這家伙是不是直正懂得volatile完全的重要性。 1). 一個參數既可以是const還可以是volatile嗎?解釋為什么。 2). 一個指針可以是volatile 嗎?解釋為什么。 3). 下面的函數有什么錯誤:
?
int square(volatile int *ptr) { return *ptr * *ptr; }? 下面是答案: 1). 是的。一個例子是只讀的狀態寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。 2). 是的。盡管這并不很常見。一個例子是當一個中服務子程序修該一個指向一個buffer的指針時。 3). 這段代碼的有個惡作劇。這段代碼的目的是用來返指針 *ptr 指向值的平方,但是,由于 *ptr 指向一個volatile型參數,編譯器將產生類似下面的代碼:
int square(volatile int* &ptr)//這里參數應該申明為引用,不然函數體里只會使用副本,外部沒法更改 { int a,b; a = *ptr; b = *ptr; return a*b; }? 由于*ptr的值可能在兩次取值語句之間發生改變,因此a和b可能是不同的。結果,這段代碼可能返回的不是你所期望的平方值!正確的代碼如下:
long square(volatile int*ptr) { int a; a = *ptr; return a*a; }?變量定義
用變量a給出下面的定義:
a)一個整型數
b) 一個指向整型數的指針
c) 一個指向指針的的指針,它指向的指針是指向一個整型數
d) 一個有10個整型數的數組
e) 一個有10個指針的數組,該指針是指向一個整型數的:
f) 一個指向有10個整型數數組的指針
g) 一個指向函數的指針,該函數有一個整型參數并返回一個整型數
h) 一個有10個函數指針的數組,該指針指向一個函數,該函數有一個整型參數并返回一個整型數
a) int a; b) int *a; c) int **a; d) int a[10]; e) int *a[10]; f) int ( *a)[10]; g) int ( *a)(int); h) int ( *a[10])(int);
中斷函數
中斷是嵌入式系統中重要的組成部分,這導致了很多編譯開發商提供一種擴展—讓標準C支持中斷。具代表事實是,產生了一個新的關鍵字 __ interrupt 。 下面的代碼就使用了 __ interrupt 關鍵字去定義了一個中斷服務子程序(ISR),請評論一下這段代碼的。
__interrupt double compute_area (double radius) { double area = PI * radius * radius; printf(" Area = %f", area); return area; }? 1). ISR 不能返回一個值。 2). ISR 不能傳遞參數。 3). 在許多的處理器/編譯器中,浮點一般都是不可重入的。有些處理器/編譯器需要讓額處的寄存器入棧,有些處理器/編譯器就是不允許在ISR中做浮點運算。此外,ISR應該是短而有效率的,在ISR中做浮點運算是不明智的。 4). 與第三點一脈相承,printf()經常有重入和性能上的問題。
編輯:黃飛
?
評論
查看更多