简介C/C++预处理器的一些工作
多么令人愉快的一个问题啊 就在被带到编译器那里之前,预处理器都会对你的源代码瞧上一瞧,做一些格式化的工作,并执行任何你在源代码里面留给它来执行的指令. 像什么? 好吧,预处理器的指令就被叫做预处理器指令,而他们都以一个#开头. 像 #include 这样? 正确. 每一个被预处理器遇到的 # 命令都会导致在某种方式上对源代码的修改. 让我们来简单的研究研究它们,然后我们就会之后这背后都是怎么运转的了. #include 包含其他库、类、接口等的头文件。预处理器实际上就只是把整个头文件复制到你的源代码里面 (是的,这就是包含防御之所以是件好事的原因了). 谁会不喜欢宏呢! 预处理器会把所有定义的实体替换成被定义的代码. 定义会一直持续直到发现这个定义的 #undef 指令. 条件行为告诉预处理器包含在遇到声明的条件成立的条件块中的代码. 你可以就像if-else语句一样使用它们,从这里面选择: #ifdef,#ifndef,#if,#else,以及 #elif,而你总是要使用一个 #endif 作为结束。 #error #warning 用来向用户发送消息。预处理器会在 #error 处,而不会在 #warning 处停下来. 两种情况下他都会发送他在指令背后(的括号里面)发现的字符串,发送到屏幕作为输出,因此它是一种确保针对你的平台一切OK的手动方式. 用来在你遇到编译错误时修改显示的错误行号和文件名. 例如,加入你需要查看一个来自编译的中间文件的源文件(可能是自动生成的). 其它由编译器解释的特殊指令。你的编译器文档会告诉你指令是怎么用的,而你不要假定他们在全世界都通用哦. #assert #unassert 这些在老程序里面总是特别受欢迎的 (好吧,只要我也曾经为这样一个程序工作过),但是它们在现在已经过时了。强烈建议不使用它们,这意味着不要把他们放到新的代码里面 有许多可以利用的预定义宏: __FILE__ 给出一个字符串的文件名 特别是开头两个在调试时真的非常有用。只要拿出它们俩,不用你自己编写文件和行处理类,就能神奇的让你获得丰富的信息输出.
1. 替换所有的三字母组合,我会在将来的一篇文章中谈论到他,因为尽管他只是一个历史上的特性(而且你也要在GCC中对它进行切换),它仍让是很有趣的. 2. 将并列的源代码分成多行. 3. 移除所有的注释并用一个空格替换. 4. 处理(我们上面讲到的)的预处理器指令。对于 #include,他会在新文件上递归执行1 - 3步 :-) 5. 处理转义序列. 6. 把文件发送给编译器 如果你想看看预处理之后你的文件会是什么样子 (谁不想呢?),你可以向 gcc 传入 -E 选项. 这将会想stdout标准输出发送预处理过的源代码,并且没有编译和连接就直接终止gcc命令的执行。
g++ -E myfile.cpp 你也可以使用这个参数: -save-temps 编译的后会有一份临时文件。 拿下面这个简单的程序说吧: #include <stdio.h> #define ONE 1 #define TWO 2 int main() { printf("%d,%dn",ONE,TWO); return 0; } 用下面这行命令编译 g++ hello.cpp -save-temps 编译完后,会在文件夹中生成两个文件: hello.s 和 hello.ii hello.s 里面是汇编代码, 而 hello.ii 则是预处理过后的源代码。 用文本编辑器打开 hello.ii,你会发现多出许多代码. 那是因为 #include 指令把 stdio 头文件的代码加进去了。 如果你把滚动条拉到最底下,就会发现,printf 那一行的宏定义 ONE 和 TWO 已经被预处理器替换成 1 和 2 了 . 神奇吧! 其实它只是在编译的时候,把你的源代码文件复制一份,当作临时文件,然后把里面的预处理指令替换掉. 用完后就把这个临时文件删了. 所以一般情况下我们不知道这个文件的存在. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |