2013-05-15 31 views
2

我碰到一些代碼來了一天,這是類似於以下(下面已被過度簡化爲簡潔起見):是否在宏擴展之前處理#include指令,而不管它們在文件中的位置?

的config.h

#ifndef __CONFIG__ 
#define __CONFIG__ 

#define DEVELOPMENT_BLD  _TRUE_ 

#if (DEVELOPMENT_BLD == _TRUE_) 
    #define FILE_EXT ".dev" 
#else 
    #define FILE_EXT ".bin" 
#endif 

#define PROJECT_STRING "my_project" 
#define FILE_NAME  PROJECT_STRING FILE_EXT 

/* Common include files */ 
#include "my_defs.h" 

#endif /* __CONFIG__ */ 

my_defs。^h

#ifndef __MY_DEFS__ 
#define __MY_DEFS__ 

#define _TRUE_  1 

#endif /* __MY_DEFS__ */ 

該項目一直編譯沒有任何問題,但自從我做了一些細微的變化(和實際的項目是老鼠她大)我決定在它上面運行Lint。當我做,我收到以下錯誤:

Warning 553: Undefined preprocessor variable '_TRUE_', assumed 0

然後我不知道爲什麼編譯器沒聽懂_TRUE_是在其中宏的第一次使用後,包括my_defs.h定義。所以我編譯它在一個不同的編譯器具有相同的結果 - 成功編譯,沒有警告和FILE_NAME正確評估,無論我如何設置DEVELOPMENT_BLD(使用_TRUE_!_TRUE_)。這裏是我的兩個編譯器設置:

ArmCC -c -cpu Cortex-M3 -g -O0 --apcs=interwork -I "..\ARM\CMSIS\Include" -I "..\ARM\INC\NXP\LPC17xx" -o "file.o" --omf_browse "file.crf" --depend "file.d" "file.c"

mingw32-gcc.exe -pedantic -Wall -g -c D:\dev\practice\header_question\main.c -o obj\Debug\main.o

我決定進行一個簡單的測試,看是否FILE_NAME值由預處理程序進行適當的評估。我也想看看DEVELOPMENT_BLD的實際價值。我跑以下代碼兩次:

的main.c

#include "config.h" 
#include <stdio.h> 
#include <stdlib.h> 

int main() 
{ 
    printf("FILE_NAME:%s, WHAT_IS_TRUE:%d", FILE_NAME,DEVELOPMENT_BLD); 
    return 0; 
} 

我第一次對這個結果中使用的值#define DEVELOPMENT_BLD _TRUE_

FILE_NAME:my_project.dev, WHAT_IS_TRUE:1

我使用的第二時間值#define DEVELOPMENT_BLD !_TRUE_帶此結果:

FILE_NAME:my_project.bin, WHAT_IS_TRUE:0

我的第一個念頭是,也許_TRUE_是被定義在別處 - 所以只是要確定我註釋掉#include "my_defs.h"。然後我就開始收到一個編譯器錯誤:

error: '_TRUE_' undeclared (first use in this function)

所有這一切導致了我的問題。在宏擴展之前,預處理器是否需要對#include語句進行評估,或者我是否幸運?

回答

3

C預處理器在指令遇到它們時作用於指令。在這種情況下,警告是正確的;當您使用#if DEVELOPMENT_BUILD == _TRUE_時,_TRUE_的有效值爲零。但是,由於#define DEVELOPMENT_BUILD _TRUE_定義,預處理器正在評估#if 0 == 0,這是正確的。但是,如果您指定了#define DEVELOPMENT_BUILD _FALSE_,那麼您將得到相同的結果,因爲_FALSE_也將隱含地爲0,因此再次測試將爲#if 0 == 0(其也評估爲真)。如果預處理器在#if條件下完成了對錶達式的求值,則會留下標識符,它們被隱式假定爲0.

請注意,以下劃線和大寫字母或其他下劃線開頭的名稱是爲任何由實施使用。您正在使用諸如_TRUE___CONFIG__等名稱選擇非常薄的冰面。 (只是因爲系統頭文件使用這樣的名稱並不是一個很好的理由 - 事實上恰恰相反,系統頭文件被小心地保留在保留給您使用的名稱空間之外;您應該避開名稱空間爲系統保留。)

+0

林特警告對我來說非常有意義。不過,我很好奇爲什麼這兩個編譯器都沒有提供任何警告。他們不僅編譯正確,而且正確地評估了_TRUE_。 –

+3

因爲沒有什麼值得警告的;該標準說「你應該解釋未定義的標識符(在預處理器條件中),就好像它們被定義爲0」一樣,因此編譯器可以毫無怨言地做到這一點。 –

+1

當然,在包含'my_defs.h'標頭後,'_TRUE_'的含義會發生變化,但條件不變。如果'#if DEVELOPMENT_BUILD == _TRUE_'在'#include「my_defs.h」'後面遇到,那麼它擴展爲'#if 1 == 1'(而不是'#if 0 == 0),但是結尾結果仍然如此。宏的值在使用時確定,而不是在定義時確定(因此,在擴展宏'DEVELOPMENT_BUILD'之前,初始'#define DEVELOPMENT_BUILD _TRUE_'的RHS保持爲'_TRUE_',然後執行'_TRUE_'它的當前值 –

相關問題