C - 宏观矛盾
C - Macro contradiction
我刚开始学习 C,我不确定一切是如何工作的。有些例子对我来说看起来很奇怪,我无法预测它会打印什么。我在 GCC 中编程,我的意思是它是我的编译器,所以我测试的每个代码都在 GCC 中。
我理解指针等所有东西是如何工作的,而且你也知道有宏。宏很奇怪,我发现了一个我没有解释的例子。例如看这段代码特定代码:
#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;
}
它应该打印什么?好吧,我虽然很容易猜到,但现在我发现这并不容易。结果和我想的不一样
这是我的推理:我们首先定义 A
为 1
然后如果 A
为 0
然后定义 B
为 1
。因此,如果定义了 B
,则意味着 A == 0
,否则 A == 1
。但是,令人惊讶的是它打印:
B is defined, which means that A == 0.
Value of A is 1
什么?它只是打印了一个矛盾。我不确定这在 GCC 中是否有些奇怪(因为我没有 visual studio 或其他编译器来测试它)。但是,我 99% 确定我以某种方式误解了宏。我尝试使用变量而不是宏,它工作正常。
这是个问题,还是我只是误解了宏的工作原理?请记住,我是 C 初学者,所以放轻松 :)。
提前致谢。
在翻译非预处理标记之前处理预处理指令。特别是,预处理指令总是在编译时进行评估,而不是在运行时进行评估(参见 C 标准,例如 this draft)。
如果一个token是预处理token定义如下(其他都是非预处理token):
6.10 Preprocessing directives
(2) A preprocessing directive consists of a sequence of preprocessing
tokens that begins with a # preprocessing token that (at the start of
translation phase 4) is either the first character in the source file
(optionally after white space containing no new-line characters) or
that follows white space containing at least one new-line character
所以 if(A == 0)
是非预处理标记(因为相应的行不以 #
开头,可选地前面有一些 space 个字符),而 #define B 1
是一个预处理标记。
根据翻译阶段,预处理标记在翻译时先于其他标记评估:
5.1.1.2 Translation phases
(4) Preprocessing directives are executed, macro invocations are
expanded, and _Pragma unary operator expressions are executed.
(7) ... The resulting tokens are syntactically and semantically
analyzed and translated as a translation unit.
所以 B
将在 if(A==0)
被翻译之前定义,特别是在 if(A==0)
被执行之前。因此,B
将始终在您的程序中定义。
在实际编译代码之前,宏是预处理器的一部分。编译器将搜索所有预处理器指令的代码并相应地修改代码。因此,当编译器到达 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);
希望这对您有所帮助,祝您编码愉快!
#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");
我刚开始学习 C,我不确定一切是如何工作的。有些例子对我来说看起来很奇怪,我无法预测它会打印什么。我在 GCC 中编程,我的意思是它是我的编译器,所以我测试的每个代码都在 GCC 中。
我理解指针等所有东西是如何工作的,而且你也知道有宏。宏很奇怪,我发现了一个我没有解释的例子。例如看这段代码特定代码:
#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;
}
它应该打印什么?好吧,我虽然很容易猜到,但现在我发现这并不容易。结果和我想的不一样
这是我的推理:我们首先定义 A
为 1
然后如果 A
为 0
然后定义 B
为 1
。因此,如果定义了 B
,则意味着 A == 0
,否则 A == 1
。但是,令人惊讶的是它打印:
B is defined, which means that A == 0.
Value of A is 1
什么?它只是打印了一个矛盾。我不确定这在 GCC 中是否有些奇怪(因为我没有 visual studio 或其他编译器来测试它)。但是,我 99% 确定我以某种方式误解了宏。我尝试使用变量而不是宏,它工作正常。
这是个问题,还是我只是误解了宏的工作原理?请记住,我是 C 初学者,所以放轻松 :)。
提前致谢。
在翻译非预处理标记之前处理预处理指令。特别是,预处理指令总是在编译时进行评估,而不是在运行时进行评估(参见 C 标准,例如 this draft)。 如果一个token是预处理token定义如下(其他都是非预处理token):
6.10 Preprocessing directives
(2) A preprocessing directive consists of a sequence of preprocessing tokens that begins with a # preprocessing token that (at the start of translation phase 4) is either the first character in the source file (optionally after white space containing no new-line characters) or that follows white space containing at least one new-line character
所以 if(A == 0)
是非预处理标记(因为相应的行不以 #
开头,可选地前面有一些 space 个字符),而 #define B 1
是一个预处理标记。
根据翻译阶段,预处理标记在翻译时先于其他标记评估:
5.1.1.2 Translation phases
(4) Preprocessing directives are executed, macro invocations are expanded, and _Pragma unary operator expressions are executed.
(7) ... The resulting tokens are syntactically and semantically analyzed and translated as a translation unit.
所以 B
将在 if(A==0)
被翻译之前定义,特别是在 if(A==0)
被执行之前。因此,B
将始终在您的程序中定义。
在实际编译代码之前,宏是预处理器的一部分。编译器将搜索所有预处理器指令的代码并相应地修改代码。因此,当编译器到达 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);
希望这对您有所帮助,祝您编码愉快!
#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");