![](/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.