C - 宏观矛盾

时间:2017-06-05 21:17:59

标签: c gcc macros c99 iso

我刚开始学习C而且我不确定一切是如何运作的。有些例子对我来说看起来很奇怪,我无法预测它会打印什么。我在GCC编程,我的意思是它是我的编译器,所以我测试的每个代码都在GCC中。

我理解指针等所有内容的工作原理,但也知道有宏。宏非常奇怪,我发现了一个我没有解释的例子。例如,请查看此代码特定代码:

s_client s_server s_time

它应该打印什么?好吧,我虽然很容易猜到,但现在我发现它并不容易。结果不是我想的那样。

这是我的推理:我们首先将s_server定义为#include <stdio.h> int main(void){ #define A 1 if(A == 0){ #define B 1 } #ifdef B printf("B is defined, which means that A == 0.\n"); #elif printf("B isn't defined, which means that A == 1.\n"); #endif printf("Value of A is %d\n", A); return 0; } ,然后A1,然后将A定义为0 }。因此,如果定义B,则表示1和其他B。但是,令人惊讶的是它打印出来:

  

B被定义,这意味着A == 0   A的值是1

什么?它只是印出了一个矛盾。我不确定这在GCC中是否有些奇怪(因为我没有可视化工作室或其他编译器来测试它)。但是,我99%肯定我不知何故误解了宏。我尝试使用变量而不是宏,它可以正常工作。

这是一个问题,还是我只是误解了宏是如何工作的?请记住,我是C初学者,所以请放轻松:)。

提前谢谢。

3 个答案:

答案 0 :(得分:4)

在实际编译代码之前,宏是预处理器的一部分。编译器将在代码中搜索所有预处理程序指令并相应地修改代码。因此,当编译器到达if(A == 0) {#define B 1}时,它已经扩展到if(0 == 1) { }

宏可以轻松配置代码的一个方面或将幻数替换为有意义的定义。例如:

#define DO_HOUR_CALCULATION

#define NUMBER_OF_SECONDS_PER_MINUTE (60)
#define NUMBER_OF_SECONDS_PER_HOUR (NUMBER_OF_SECONDS_PER_MINUTE * 60)
...
#ifdef DO_HOUR_CALCULATION
int hours = time / NUMBER_OF_SECONDS_PER_HOUR;
int minutes = (time % NUMBER_OF_SECONDS_PER_HOUR) / NUMBER_OF_SECONDS_PER_MINUTE;
#endif // defined(DO_TIME_CALCULATION)

另一个用途是简化重复或可配置的任务:

#define DEBUG

#ifdef DEBUG
#define DPRINTF(fmt, args...) printf(fmt, ## args)
#else
#define DPRINTF(args...)   do {} while (0)
#endif
...
ret = read(fd, p_buffer, buf_size);
DPRINTF("read(fd) returned %d\n", ret);

希望这有助于您和快乐的编码!

答案 1 :(得分:2)

在转换非预处理标记之前处理预处理指令。特别是,预处理指令总是在编译时评估,而不是在运行时(参见C标准,例如this draft)。 如果令牌是预处理令牌,则定义如下(所有其他令牌都是非预处理令牌):

  

6.10预处理指令

     

(2)预处理指令由一系列预处理组成   以#预处理标记开头的标记(在...开头)   转换阶段4)是源文件中的第一个字符   (可选地,在不包含换行符的空格之后)或   在包含至少一个换行符

的空格之后

所以if(A == 0)是非预处理标记(因为相应的行不以#开头,可选地前面有一些空格字符),而#define B 1是预处理标记。 根据翻译阶段,预处理令牌在翻译时在其他令牌之前进行评估:

  

5.1.1.2翻译阶段

     

(4)执行预处理指令,宏调用   展开,并执行_Pragma一元运算符表达式。

     

(7)......由此产生的标记在语法和语义上都是如此   分析并翻译为翻译单位。

因此B将在if(A==0)翻译之前定义,特别是在if(A==0)执行之前。因此,B将始终在您的程序中定义。

答案 2 :(得分:1)

#define B 1在运行时不会发生。它发生在编译时。所以#define B 1是编译的一部分,无论A == 0的结果如何,都是运行时比较

  if(A == 0){
    #define B 1
  }

以下代码

  #ifdef B
    printf("B is defined, which means that A == 0.\n");
  #elif
    printf("B isn't defined, which means that A == 1.\n");
  #endif

相同
  printf("B is defined, which means that A == 0.\n");