![](/img/trans.png)
[英]in C or C++, how can I prevent previous #define in a header file from affecting another header file later included?
[英]How can a #define from another headder file be used if the headderfile is not included?
我目前正在使用 FreeRTOS,並注意到一些我以前從未遇到過的事情。
文件“projdefs.h”使用文件“FreeRTOSConfig.h”中的定義,但不包含“FreeRTOSConfig.h”而不包含它。 “projdefs.h”不包含與此相關的任何其他文件。
這怎么可能?
案例如下圖:
//projdefs.h
#ifndef pdMS_TO_TICKS
#define pdMS_TO_TICKS( xTimeInMs ) ( ( TickType_t ) ( ( ( TickType_t ) ( xTimeInMs ) * ( TickType_t ) configTICK_RATE_HZ ) / ( TickType_t ) 1000U ) )
#endif
//FreeRTOSConfig.h
#ifndef configTICK_RATE_HZ
#define configTICK_RATE_HZ (1000)
#endif
我試圖用谷歌搜索我的答案,但沒有任何結果。
如果不包含頭文件,如何使用來自另一個頭文件的
#define
?
候選人的答案。
盡管projdefs.h
沒有直接包含FreeRTOSConfig.h
, projdefs.h
確實包含了包含FreeRTOSConfig.h
的 some.h 文件。 或者可能projdefs.h
確實包含 some.h 文件,其中包含 some.h 文件,其中包含FreeRTOSConfig.h
等(OP 斷言:“projdefs.h”不包含與此相關的任何其他文件。)
projdefs.h
本身或直接(或間接)包含定義configTICK_RATE_HZ
的內容,並且包含FreeRTOSConfig.h
的斷言是不正確的。
configTICK_RATE_HZ
由編譯器定義。
設計提示:
查找define
、對象、函數或常量的聲明/定義位置通常很麻煩。
我發現在"same_prefix.h"
中為它們使用一個共同的prefix_
來緩解這個問題。
如果不包含頭文件,如何使用來自另一個頭文件的#define?
宏定義來自以下來源之一:
C 編譯器不會在未經請求的情況下處理源文件,因此僅當編譯器有理由讀取header.h時,才會從header.h處理宏定義。 通常,這個原因是編譯器處理了一個#include
指令,該指令將它定向到標頭。 標頭也可以直接命名為要編譯的文件,但這會將其內容放入自己的翻譯單元中,因此它可能不是您所觀察到的。 一些編譯器還提供了一種通過命令行指定標頭的方法,例如GCC 的-include option ,這可能符合您對“不包含”的定義。 或不。
可能是您通過模糊的路徑包含了有問題的標頭,例如在多層#include
指令之后,或者依賴於先前已經處理過的#include
指令,或者來自帶有宏的#include
-生成的標題名稱,或通過符號鏈接支持的不同名稱。
還可以看到具有相同名稱和替換文本的宏,該宏將通過包含由任何一個產生的一個標頭來提供
並且這些都沒有被很好地描述為來自未包含的標頭,但它們可能會給人這樣的印象。
至於...
文件“projdefs.h”使用文件“FreeRTOSConfig.h”中的定義,但不包含“FreeRTOSConfig.h”而不包含它。 “projdefs.h”不包含與此相關的任何其他文件。
... projdefs.h使用它未定義的符號這一事實,既不直接也不通過包含任何其他文件,使我假設它根本不是一個獨立的標頭。 這取決於僅在任何一個點上被#include
d
pdMS_TO_TICKS
已經定義,或者configTICK_RATE_HZ
已經定義,符號TickType_t
被定義為類型指示符或擴展為類型指示符的宏在FreeRTOS.h的開頭,您會注意到:
/* Application specific configuration options. */
#include "FreeRTOSConfig.h"
/* Basic FreeRTOS definitions. */
#include "projdefs.h"
此外,內核本身不使用pdMS_TO_TICKS()
宏。 FreeRTOS 內核始終使用節拍。 該宏僅供用戶(我們,應用程序開發人員)使用。
已經發布了一些很好的答案,但只是添加有關預處理器和頭文件的一些詳細信息:
頭文件中的所有內容都會擴展到包含它的 each.c 文件中。 正式的 ac 文件和它包含的 all.h 文件構成了一個翻譯單元。 So.h 文件實際上並沒有自己的生命,它們的內容總是被預處理器“粘”到 .c 文件中。
這可能會導致各種奇怪或意外的副作用。 例如,您的FreeRTOSConfig.h
將能夠訪問projdefs.h
中的所有內容,前提是兩者都包含在同一個.c 文件中並且首先包含FreeRTOSConfig.h
。 但是,依賴於此是非常糟糕的做法,因為它會強制頭文件的用戶以特定順序包含它們。 相反,每個 .h 文件應該包含它需要的每個 other.h 文件。
這就是所謂的“header guards” #ifndef MYHEADER_H
#define MYHEADER_H
... #endif
進來的地方。因為如果你沒有在每個 .h 文件中都有這些,你的 .c 文件可能會以多個聲明 /擴展標題后對同一事物的定義。 這也是為什么在頭文件中定義變量或函數是有問題的——用戶最終可能會得到多個定義。
一些好的/慣用做法的小清單:
extern
但全局變量也是不好的做法。static inline
的手動優化可能是可以接受的,但也是最后的手段。文件“projdefs.h”使用文件“FreeRTOSConfig.h”中的定義,但不包含“FreeRTOSConfig.h”而不包含它。 “projdefs.h”不包含與此相關的任何其他文件。
這是可能的,因為您最終以包含這兩個文件的編譯單元結束。 如果您不包含其中之一,未定義的宏將自行展開。
這在歷史上曾在文件<stdio.h>
中用於某些被重新定義為宏的函數,以避免調用函數。 例如,如果您執行#include <stdio.h>
宏putchar()
被定義為:
#define putchar(c) fputc(c, stdout)
因此,如果您包含標頭,對putchar()
的調用將直接轉換為對fputc()
的調用,而如果您不包含它,則執行映射的函數將鏈接到可執行文件中。
#include <stdio.h>
int putchar(int c)
{
return fputc(c, stdout);
}
而你沒有注意到其中的區別。
這仍然用於幾個使用默認屏幕的 ncurses 庫函數,以防您使用縮寫形式。 這會減少函數調用的開銷。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.