C++ C++;如何保护自己免受stdio.h宏的攻击

C++ C++;如何保护自己免受stdio.h宏的攻击,c++,macros,C++,Macros,我花了很长时间试图弄明白为什么以下内容无法编译: enum IPC_RC {OK, EOF, ERROR, NEW }; 错误消息只说了一些大意是它不希望看到开括号的内容。直到我尝试在更现代的编译器上编译它,我才了解到: /usr/include/stdio.h:201:13: note: expanded from macro 'EOF' #define EOF (-1) 所以我终于被一个宏烧死了!:) 我的代码不包含(我不包含任何带有.h后缀的内容),但很明显,我包含的内容导致了

我花了很长时间试图弄明白为什么以下内容无法编译:

enum IPC_RC {OK, EOF, ERROR, NEW };
错误消息只说了一些大意是它不希望看到开括号的内容。直到我尝试在更现代的编译器上编译它,我才了解到:

/usr/include/stdio.h:201:13: note: expanded from macro 'EOF'
#define EOF     (-1)
所以我终于被一个宏烧死了!:)


我的代码不包含(我不包含任何带有.h后缀的内容),但很明显,我包含的内容导致了包含。有没有任何方法(名称空间?)来保护我自己,而不去追查它到底包含在哪里

对于您描述的问题,我不知道一个令人满意的解决方案,但我只想分享一种处理这种情况的方法。你必须时不时地使用一些特别令人讨厌的标题,这些标题重新定义了英语的一大部分。我想到了
Python.h
的X11头。我最后做的是(通常是在我注意到破损后)将第三方标题包在我自己的标题中,并处理其中的丑陋之处

例如,在使用Ruby解释器的项目中,我通常不包括
Ruby.h
目录,而是包括一个
ourruby.h
文件,该文件如下所示:

#ifndef RUBY_OURRUBY_H
#define RUBY_OURRUBY_H

// In Ruby 1.9.1, win32.h includes window.h and then redefines some macros
// which causes warnings. We don't care about those (we cannot fix them).
#ifdef _MSC_VER
#  pragma warning(push)
#  pragma warning(disable:4005)
#endif
#include <ruby.h>
#ifdef _MSC_VER
#  pragma warning(pop)
#endif

// In Ruby 1.8.7.330, win32.h defines various macros which break other code
#ifdef read
#  undef read
#endif
#ifdef close
#  undef close
#endif
#ifdef unlink
#  undef unlink
#endif
// ...

#endif // !defined(RUBY_OURRUBY_H)
\ifndef RUBY\u OURRUBY\u H
#定义RUBY\u OURRUBY\u H
//在Ruby 1.9.1中,win32.h包含window.h,然后重新定义一些宏
//这会引起警告。我们不关心这些(我们无法修复它们)。
#ifdef硕士学位
#pragma警告(推送)
#杂注警告(禁用:4005)
#恩迪夫
#包括
#ifdef硕士学位
#布拉格警告(pop)
#恩迪夫
//在Ruby 1.8.7.330中,win32.h定义了各种宏,这些宏会破坏其他代码
#ifdef读取
#未定义读取
#恩迪夫
#ifdef关闭
#未定义关闭
#恩迪夫
#ifdef取消链接
#取消链接
#恩迪夫
// ...
#endif/!已定义(RUBY_OURRUBY_H)

这样,我就不必记住某些头不是完全干净的名称空间。

名称空间将不是解决方案,因为宏会忽略它们

所以你有两个选择:

  • 自己摆脱这些宏:

    #ifdef EOF
    #undef EOF
    #endif
    
  • 在枚举值中使用前缀:

    enum IPC_RC
    {
        IPC_OK,
        IPC_EOF,
        IPC_ERROR,
        IPC_NEW
    };
    

我只是想用一个不同的名字,但归根结底,你可以“取消定义”。@chris我不会“取消定义”它。不过,我会使用不同的名称,因为宏基本上是一种文本替换。@遗憾的是,在处理宏时并不容易。宏在C/C++中是一种非常原始的机制。和其他全局标识符一样,没有名称空间。这就是为什么强大的命名约定有助于您的项目。例如,枚举数的某些前缀,或不使用所有大写字母(通常宏都是大写字母)。。。