【问题标题】:Do preprocessor directive if-elses behave normally?预处理器指令 if-elses 是否正常运行?
【发布时间】:2014-01-13 21:05:20
【问题描述】:

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

我听说过的预处理器最有想象力的用途之一是在代码准备好编译之前需要通过 CPP 五次。通过巧妙地使用定义和 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三遍

为什么预处理器会多次“通过”?预处理器指令中的 if-else 是否不像标准 if-else 控制结构?在预处理器 if 语句中调用函数会导致完全重新评估吗?

【问题讨论】:

  • 如果我在我正在编辑的头文件中看到该代码,我会射杀编写它的人,而且世界上没有陪审团会定我有罪。
  • 这是一本关于混淆的讽刺指南,你期待什么? :)
  • 说真的,这就像迪士尼的邪恶。 :)
  • 至少他们有 cmets

标签: c++ obfuscation preprocessor-directive


【解决方案1】:

引用你引用的内容:

混淆大师可以让头文件声明不同的东西 取决于它们被包含的次数

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

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

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

【讨论】:

  • 多次包含同一个文件的想法并没有让我完全接受。不过这是有道理的。
  • @trojansdestroy One of the most imaginative uses of the preprocessor I have heard of was requiring five passes through CPP before the code was ready to compile. 我不认为这句话是指代码示例。然后他说This becomes especially interesting when one header is included in another header.。因此,他希望整个结构出现两次,一次是针对包含它的标头,第二次是针对包含该标头的时间。
  • 多重包含可以通过包含此标头的其他标头显式完成或间接完成?
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 2017-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-23
    • 1970-01-01
    • 2012-06-08
    • 1970-01-01
    相关资源
    最近更新 更多