预处理器指令if-elses是否正常运行?

时间:2013-12-26 07:28:16

标签: c++ obfuscation preprocessor-directive

来自How To Write Unmaintainable Code(结构的缩进矿):

  

我听说过预处理器最富有想象力的用途之一是在代码准备编译之前要求通过CPP五次。通过巧妙地使用define和ifdef,混淆的主人可以使头文件根据它们包含的次数声明不同的东西。当一个标题包含在另一个标题中时,这变得特别有趣。这是一个特别狡猾的例子:

#ifndef DONE
    #ifdef TWICE
        // put stuff here to declare 3rd time around
        void g(char* str);
        #define DONE
    #else // TWICE
        #ifdef ONCE
            // put stuff here to declare 2nd time around
            void g(void* str);
            #define TWICE
        #else // ONCE
            // put stuff here to declare 1st time around
            void g(std::string str);
            #define ONCE
        #endif // ONCE
    #endif // TWICE
#endif // DONE

鉴于此代码,我希望这种行为:

  1. #ifndef DONE评估为true,进入该区块
  2. #ifdef TWICE评估为false
  3. 分支到并输入#else // TWICE
  4. #ifdef ONCE评估为false
  5. 分支到并输入#else // ONCE
  6. #define ONCE
  7. #endif三次
  8. 为什么预处理器会进行多次“传递”?预处理程序指令中的if-elses是否表现得不像标准的if-else控制结构?在预处理器if语句中调用函数是否会导致完全重新评估?

2 个答案:

答案 0 :(得分:7)

引用您引用的内容:

  

混淆大师可以使头文件声明不同的东西   取决于他们被包括多少次

此处作者假设您多次包含此文件。换句话说,您可能有一个看起来像这样的文件:

#include "that.file.in.your.question.h"
#include "that.file.in.your.question.h"
#include "that.file.in.your.question.h"

第一次,流程如您所述,导致ONCE被定义。第二次,因为现在定义了ONCE,它需要一个不同的分支(定义{{​​1}}的分支)。第三次,因为定义了TWICE,它需要一个不同的分支(定义{{​​1}}的分支)。最后,第四次,以及之后的任何时间,因为定义了TWICE,所以跳过了整个事情。这是当然除非其他一些预处理程序指令取消定义DONE

答案 1 :(得分:1)

预处理器一次完成所有工作。

Roedy Green的含义是,如果在同一个编译单元中多次包含此头文件将执行不同的操作。比如,文件名为header.h,然后,如果编译单元调用该文件四次,则每次调用都会产生不同的结果:

// Here, nothing is defined
#include "header.h"
// Here, ONCE is defined
#include "header.h"
// Here, TWICE is defined
#include "header.h"
// Here, DONE is defined
#include "header.h"

将扩展为:

void g(std::string str); // first invocation
void g(void* str);       // second invocation
void g(char* str);       // third invocation
                         // fourth invocation

这种设备在某些情况下可能会派上用场,但如果没有正确记录,它也会在某种程度上模糊程序员的意图。