使用以前未定义的宏时不会出现编译器错误

No compiler error when macros that weren't previously defined are used

请问为什么下面的代码编译器不报错? 这是 flash.h 文件:

#ifndef _FLASH_H_
#define _FLASH_H_
#define BANK_A    0
#define BANK_B    1
#define BANK_C    3
#define FLASH_IS_BUSY         (FCTL3 & BUSY)//FCTL3 and BUSY are defined in msp430f5438a.
#endif

这是 main.c 文件:

#include "flash.h"
#include <msp430f5438a.h>
void main(void)
{
   while(1)
   {
      ;
   }
}

问题是我不明白编译器为什么不在这一行报错:

#define FLASH_IS_BUSY         (FCTL3 & BUSY)

因为(据我所知)编译器无法知道FCTL3和BUSY是什么意思。这两个宏在 msp430f5438a.h 中定义如下:

#define FCTL3  (*((unsigned char*)0x0144u))
#define BUSY   0x01  

但是 flash.h 包含在 msp430f5438a.h 之前 编译器如何解析这些符号:FCTL3 和 BUSY?

这里要注意的是,#define在预处理阶段作为文本替换,它们与变量声明或定义不同。

在您的 header 文件中,您只定义了 FLASH_IS_BUSY 宏,但在您的代码中,您没有使用它。甚至,如果您使用过它,在 main() 之前,header #include <msp430f5438a.h> 就在那里,这使得 FCTL3BUSY 的定义对您可用代码,如果使用的话。

TL;DRFLASH_IS_BUSY定义在header文件中不需要FCTL3BUSY 已经被定义。例如,您可以使用 -D 选项和 gcc 提供这些 MACRO 值,它们根本不需要出现在代码中。

也就是说,main()的推荐签名是int main(void)

您没有使用FLASH_IS_BUSY,所以预处理器不会替换不正确的语法。

顺便说一下,在较旧的 C 标准(和较新的 C++ 标准)中编写 while(1); 时要小心,因为您的程序的行为是未定义的。

Is while(1); undefined behavior in C?

限制您问题的特定主题(预处理器替换),其中:

#define FLASH_IS_BUSY (FCTL3 & BUSY)

您没有以任何方式使用 FCTL3BUSY。您只是在指示编译器执行文本替换。它不需要知道任何关于它们的信息。

然而,当您像这样在代码中使用它时:

int flags = FLASH_IS_BUSY;

它将执行第一次替换:

int flags = (FCTL3 & BUSY);

替换循环将继续,直到有东西要替换(或达到硬阈值)。如果你忘记包含 msp430f5438a.h 那么上面的代码将不会简单地编译(因为 FCTL3BUSY 是未知的),但是如果你包含正确的头文件(无论顺序如何:msp430f5438a.h 首先或 flash.h 首先)它们将被预处理器替换为它们的实际值,您将拥有正确的代码:

int flags = ((*((unsigned char*)0x0144u)) & 0x01);

现在预处理器完成,编译器(启用优化时)将(可能!!!)直接用文字值替换 flags

备注

  • 您的 main() 签名不标准。它应该(对于托管环境)为 int main(void)int main(int argc, char* argv[])。另见 What is the proper declaration of main?. Note that if your compiler allows void main(void) signature then it's not wrong but it's just non-standard. However Lunding highlighted that in freestanding environments main() function, according to C standard, signature is completely implementation-defined. See his enlightening post: Why is the type of the main function in C and C++ left to the user to define?
  • 我知道 while(1) 只是说明性的(它不是您的 真实的 代码)但它可能是早期 C 规范中的 UB。我不会在这里重复这个(长)讨论然后请参考 Are compilers allowed to eliminate infinite loops? 进一步研究。
  • u 后缀不是必需的,在 C 中整数文字总是非负的,那么 u 只是多余的。然而,某些特定准则(例如在关键系统中,请参阅 MISRA)可能 要求 明确(向读者)常量旨在成为非负整数。
  • 您的空循环可能会被简单地优化掉。编译器可以自由选择不为 ; 生成任何代码。 Various workarounds exists 如果你想让它便携。