简介C/C++预处理器的一些工作

多么令人愉快的一个问题啊

就在被带到编译器那里之前,预处理器都会对你的源代码瞧上一瞧, 做一些格式化的工作,并执行任何你在源代码里面留给它来执行的指令.

像什么?

好吧,预处理器的指令就被叫做预处理器指令,而他们都以一个#开头.

像 #include 这样?

正确.

每一个被预处理器遇到的 # 命令都会导致在某种方式上对源代码的修改. 让我们来简单的研究研究它们,然后我们就会之后这背后都是怎么运转的了.

#include

包含其他库、类、接口等的头文件。预处理器实际上就只是把整个头文件复制到你的源代码里面 (是的,这就是包含防御之所以是件好事的原因了).
#define

谁会不喜欢宏呢! 预处理器会把所有定义的实体替换成被定义的代码. 定义会一直持续直到发现这个定义的 #undef 指令.
#ifdef

条件行为告诉预处理器包含在遇到声明的条件成立的条件块中的代码. 你可以就像if-else语句一样使用它们,从这里面选择: #ifdef, #ifndef, #if, #else, 以及 #elif, 而你总是要使用一个 #endif 作为结束。

#error #warning

用来向用户发送消息。预处理器会在 #error 处, 而不会在 #warning 处停下来. 两种情况下他都会发送他在指令背后(的括号里面)发现的字符串, 发送到屏幕作为输出,因此它是一种确保针对你的平台一切OK的手动方式.
#line

用来在你遇到编译错误时修改显示的错误行号和文件名. 例如,加入你需要查看一个来自编译的中间文件的源文件(可能是自动生成的).
#pragma

其它由编译器解释的特殊指令。你的编译器文档会告诉你指令是怎么用的,而你不要假定他们在全世界都通用哦.

#assert #unassert

这些在老程序里面总是特别受欢迎的 (好吧,只要我也曾经为这样一个程序工作过), 但是它们在现在已经过时了。强烈建议不使用它们,这意味着不要把他们放到新的代码里面
预定义宏

有许多可以利用的预定义宏:

__FILE__ 给出一个字符串的文件名
__LINE__  给出当前的行号(整型)
__DATE__ 当前编译日期的字符串
__TIME__ 当前编译时间的字符串
__STDC__ 同编译器相关的,但常常被定义成1,以声明同ISO C标准兼容.
__cplusplus 在编译一个C++程序是总是会被定义

特别是开头两个在调试时真的非常有用。只要拿出它们俩,不用你自己编写文件和行处理类,就能神奇的让你获得丰富的信息输出.

你的编译器可能还支持其它的宏,例如,你这从 这里 获得(面向GCC)的整个宏清单.
那么当你运行预处理器时实际会发生什么呢?

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, %d\n", 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 了 .

神奇吧!

其实它只是在编译的时候, 把你的源代码文件复制一份, 当作临时文件, 然后把里面的预处理指令替换掉. 用完后就把这个临时文件删了. 所以一般情况下我们不知道这个文件的存在.

(0)

相关推荐

  • 深入理解C预处理器

    C 预处理器不是编译器的组成部分,是编译过程中一个单独的步骤.C预处理器只是一个文本替换工具,它会指示编译器在实际编译之前完成所需的预处理. 所有的预处理器命令都是以井号(#)开头.它必须是第一个非空字符,为了增强可读性,预处理器指令应从第一列开始. 下表包含所有重要的预处理器指令: 指令 描述 #define 定义宏 #include 包含一个源代码文件 #undef 取消已定义的宏 #ifdef 如果宏已经定义,则返回真 #ifndef 如果宏没有定义,则返回真 #if 如果给定条件为真,则

  • 常用C/C++预处理指令详解

    预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查.预处理命令以符号"#"开头. 常用的预处理指令包括: 宏定义:#define 文件包含:#include 条件编译:#if.#elif.#ifndef.#ifdef.#endif.#undef 错误信息指令:#error #line指令 布局控制:#pragma 宏定义 宏定义又称为宏代换.宏替换,简称"宏".宏替换只作替换,不做计算,不做表达式求解.宏定义分带参数的宏定义和不带参数的宏

  • 详解C语言编程中预处理器的用法

    预处理最大的标志便是大写,虽然这不是标准,但请你在使用的时候大写,为了自己,也为了后人. 预处理器在一般看来,用得最多的还是宏,这里总结一下预处理器的用法. #include <stdio.h> #define MACRO_OF_MINE #ifdef MACRO_OF_MINE #else #endif 上述五个预处理是最常看见的,第一个代表着包含一个头文件,可以理解为没有它很多功能都无法使用,例如C语言并没有把输入输入纳入标准当中,而是使用库函数来提供,所以只有包含了stdio.h这个头文

  • C#预处理器指令的用法实例分析

    本文实例讲述了C#预处理器指令的用法.分享给大家供大家参考.具体用法分析如下: C#预处理器指令是在编译时调用的.预处理器指令(preprocessor directive)告诉C#编译器要编译哪些代码,并指出如何处理特定的错误和警告.C#预处理器指令还可以告诉C#编辑器有关代码组织的信息. 1. 定义符号和取消符号定义的预处理指令#define 和 #undef 预处理指令都以#号开头并位于行首前面可以出现空格符. 复制代码 代码如下: #define DEBUG #define ISSAY

  • 你必须知道的C语言预处理的问题详解

    C语言预处理器执行宏替换.条件编译和文件包含.通常采用以"#"为行首的提示.下面是C语言预处理的应用场合: 1.三字母词(Trigraph Sequences) C源程序的字符集被包含在7位的ASCII字符集中,但是它是ISO 646-1983 Invariant Code Set的超集.为了让程序可以在缩减集(reduced set)中呈现出来,下面的三字母词会被替换成相应的单字符. 三字母词 单字符 ??= # ??/ \ ??' ^ ??( [ ??) ] ??! | ??<

  • C#中的预处理器指令详解

    目录 1. #define 和 #undef 2. #if.#elif.#else 和#endif 3. #warning 和 #error 4. #region 和#endregion 5. #line 6. #pragma C#中有许多名为"预处理器指令"的命令.这些命令从来不会转化为可执行代码中的命令,但会影响编译过程的各个方面. 例如,使用预处理器指令可以禁止编译器编译代码的某一部分.如果计划发布两个版本的代码,即基本版本和拥有更多功能的企业版本,就可以使用这些预处理器指令.在

  • 简介C/C++预处理器的一些工作

    多么令人愉快的一个问题啊 就在被带到编译器那里之前,预处理器都会对你的源代码瞧上一瞧, 做一些格式化的工作,并执行任何你在源代码里面留给它来执行的指令. 像什么? 好吧,预处理器的指令就被叫做预处理器指令,而他们都以一个#开头. 像 #include 这样? 正确. 每一个被预处理器遇到的 # 命令都会导致在某种方式上对源代码的修改. 让我们来简单的研究研究它们,然后我们就会之后这背后都是怎么运转的了. #include 包含其他库.类.接口等的头文件.预处理器实际上就只是把整个头文件复制到你的

  • 浅谈c++ 预处理器

    预处理器是一些指令,指示编译器在实际编译之前所需完成的预处理. 所有的预处理器指令都是以井号(#)开头,只有空格字符可以出现在预处理指令之前.预处理指令不是 C++ 语句,所以它们不会以分号(;)结尾. 我们已经看到,之前所有的实例中都有 #include 指令.这个宏用于把头文件包含到源文件中. C++ 还支持很多预处理指令,比如 #include.#define.#if.#else.#line 等,让我们一起看看这些重要指令. #define 预处理 #define 预处理指令用于创建符号常

  • C/C++ 活动预处理器详解

    预处理器简介 预处理器不是编译器的组成部分,他是编一过程中的一步,发生在编译之前.我们把C预处理器(C Preprocessor)简称为CPP.预处理的作用就是在代码被编译前对代码做某些替换. 指令规则 预处理指令的写法都是以#开头,#必须是该行第一个非空白字符,#和关键字之间允许存在任意个数的空白字符,接着是指令所需要的其他信息,整行够成了一条预处理指令.预处理指令总是在第一个换行符结束,除非明确的指明指令要继续.预处理指令可以出现在文件的任何地方.通常我们将#define和#include指

  • 前端构建 Less入门(CSS预处理器)

    Less 是一门 CSS 预处理语言,它扩充了 CSS 语言,增加了诸如变量.混合(mixin).函数等功能,让 CSS 更易维护.方便制作主题.扩充. Less 可以运行在 Node.浏览器和 Rhino 平台上.网上有很多第三方工具帮助你编译 Less 源码. 一.前言 说到前端构建怎能缺少CSS预处理器呢!其实CSS的预处理器有很多啦,比较出名的有Scss.Sass.Stylus和Less.(最近还听说出现了Autoprefixer等CSS后处理器,可参考@一丝的PPT) 众多CSS预处理

  • C#中变量、常量、枚举、预处理器指令知多少

    一.变量 C#共有其中变量类型有:静态变量.实类变量.数组元素.数值参数.引用参数.输出参数和局部变量 先定义一个简单的类来说明,如下: public class VariableDefine { private static uint variableUInt; public static uint VariableUInt { get => variableUInt; set => variableUInt = value; } string VariableStr; public Var

  • Webpack 之 babel-loader文件预处理器详解

    loader官方解释是文件预处理器,通俗点说就是webpack在处理静态文件的时候,需要使用 loader 来加载各种文件,比如: html文件需要使用html-loader ,css 需要使用css-loader . style-loader 等等. 今天我们来认识的是 babel-loader,用来处理ES6语法,将其编译为浏览器可以执行的js语法. 安装 我们需要用到 babel-loader babel-core babel-preset 配合版本: webpack 3.x | babe

随机推荐