0

我试图绕过它但放弃了。您能否解释以下内容,摘自 Mike Banahan 的 C 书(第 7.3.7 节条件编译)。尽管付出了很多努力,但我无法掌握“组成的令牌序列......”之后的部分。你能简单地说一下吗

1)“定义的”前缀究竟是什么,以及

2) 名称甚至 C 关键字减少到零(不要忘记对 sizeof 的混淆引用)怎么办?

用一小段代码进行解释将非常有帮助。谢谢你。

#if 和 #elif 构造将单个整数常量表达式作为其参数。预处理器整型常量表达式与其他整型常量表达式相同,只是它们不能包含强制转换运算符。构成常量表达式的记号序列会进行宏替换,但以定义为前缀的名称不会被扩展。在此上下文中,如果当前定义了NAME,则表达式定义的 NAME定义的 ( NAME )计算结果为 1 ,否则计算结果为0。表达式中的任何其他标识符,包括那些是 C 关键字的标识符替换为值 0。然后计算表达式。甚至替换关键字意味着不能在这些表达式中使用 sizeof 来获得您通常期望的结果。

4

2 回答 2

4

这是指启用或禁用条件编译。它讨论了使用整数表达式和预处理器宏。

假设您希望仅在调试时启用某些代码。你可以这样做:

#define DEBUGGING 1

#if DEBUGGING
    printf( "I'm debugging\n" );
#endif

void function( void ) {
   if ( DEBUGGING ) {
       printf( "I'm debugging\n" );
   }
}

该代码将起作用,即您将得到两个打印的输出语句,即“我正在调试”。

原因是因为在第一个块 ( #if DEBUGGING) 中,宏DEBUGGING被转换为 1(它被定义为表示的值),然后它被评估为整数表达式。换句话说,预处理器将代码简化为:

#if 1
    printf( "I'm debugging\n" );
#endif

void function( void ) {
   if ( 1 ) {
       printf( "I'm debugging\n" );
   }
}

但是现在考虑根本#DEBUGGING没有定义宏的情况。

在这种情况下,预处理器会将代码简化为:

//#define DEBUGGING 1   <-- commented out

#if 0  // <--- DEBUGGING is not a macro-word here, so it is converted to 0
    printf( "I'm debugging\n" );
#endif

void function( void ) {
   if ( DEBUGGING ) { // <---- look what happened here! *Not* in "#if", so NOT preprocessed to zero!
       printf( "I'm debugging\n" );
   }
}

由于 C 语言的定义方式,#if测试会将它不能识别为宏的任何单词(例如 int、sizeof、myVariableName、SOME_OTHER_UNDEFINED_THING)减少为 0,并且不会给出错误。第一个 printf 语句不会被编译。

但是,由于不再定义宏,因此使用先前版本代码中的宏的第二条语句将不会DEBUGGING被替换。在这种情况下,在预处理器运行之后,编译器将抱怨它无法识别的名称,因为它会从字面上看到这个词DEBUGGING并且不知道如何理解它(它不是变量名或关键字等) .

这是你引用的段落的第一件事。

它提到的第二件事是#if语句会将其参数简化为单个整数表达式的结果。这意味着您可以编译如下所示的代码:

#define VERSION 2

#if VERSION > 1
    printf( "this code only runs on v2+\n" );
#endif

预处理器首先将单词 VERSION 替换为它所代表的东西:

#if 2 > 1
...

然后预处理器将该行计算为整数表达式。由于 2 大于 1(真),因此表达式/行简化为:

#if 1
    printf( "this code only runs on v2+\n" );
#endif

关于sizeof(以及任何其他关键字或非宏词),在#if语句中,预处理器无法识别的所有名称都将转换为零。这样它就可以处理不存在的宏的条件编译,如上面我注释掉时所示DEBUGGING

因此,这段代码看起来像是您可能想要做的事情:

#if sizeof(int) > 2
    printf( "This machine has 4+ byte integers\n" );
#endif

将减少到这个:

#if 0(0) > 2  // sizeof and int are non-macro-words so they get replaced with 0

这是一个无效的表达式,所以编译器会报错。因此,您不能sizeof在预处理器宏中使用(或任何其他关键字或变量名)。

如果您通过将 C 预处理器与 C 编译器分开来考虑它,这是有道理的。预处理器不知道常规的 C 关键字,这不是它的工作。它只是解析它关心的标记(#if等),然后将后处理的 C 文件交给编译器。

于 2014-09-28T18:31:02.143 回答
2

这里的“定义”是指预处理器指令#define

一段源代码可能包括#define以下内容:

#define UNITTEST

然后,你可以写

#ifdef UNITTEST
  runtest();
#endif

至少有两种方法可以为预处理器定义符号。第一个是#define在文件中包含 a ,如上所述。第二个是包含-D<symbol>来自gcc命令行的指令,如-DUNITTEST. 在这种情况下,符号UNITTEST将在整个编译过程中定义。

一般来说,最好不要#define在源文件中没有符号,然后#undef删除符号。如果没有关闭#undef,符号将保持定义,项目将难以调试(或者更糟糕的是,可能存在名称冲突)。因此,例如,在源文件中,

#define ABBV 
...
#undef ABBV

在运行单元测试时,然后指示构建系统 ( make, SCons) 应用该-D指令并统一编码源文件以在此符号上分支。

请注意,#define可以以您引用的文本的方式定义符号(如果定义,则UNITTEST解析为1),也可以定义带有参数的 marco。规范的宏是

#define SQR(x) ((x) * (x))
int a(1);
int b(SQR(a));

宏在使用后仍然是一种很好的做法#undef,或者将“全局”宏放在自己的文件中以便于识别。所以,鉴于我SQR上面写的,我会跟着

#undef SQR

参考您关于条件编译的具体问题,我-D在构建测试、开发或生产时在编译器命令行上使用指令(通常很多)。然后我在源代码中测试这些符号。而且,对于#define源代码中的 s,我定义本地宏(例如SQR宏)并取消定义它们,或者将全局宏写入单个core_macros.h文件。

于 2014-09-28T18:12:46.080 回答