由“源代码”到“可执行文件”的过程包括四个步骤:预编译编译汇编链接。所以,首先就应该清楚的首要问题就是:预编译只是对程序的文本起作用,换句话说就是,预编译阶段仅仅对源代码的单词进行变换,而不是对程序中的变量、函数等。

预编译指令的基本知识不作详细介绍,只稍作汇总,重点是后面的我能想到的 使用时的注意事项。

1. 基本内容

预编译指令基本分类如下

类别

指令

预定义符号 __FILE__、__LINE__、__DATE__、__TIME__、__STDC__
#define
文件包含 #include
条件编译 #if、#elif、#else、#ifdef、#ifndef、#endif

还有一些指令,名称和功能如下表:

指令 功能
# 空指令
#undef 移除一个空定义
#error 停止编译,并生成错误信息
#line 修改__LINE__和__FILE__的值
#progma 允许编译器提供额外功能

在定义宏的时候,有两个运算符

运算符 功能
# 将宏参数转换为字符串
## 将多个符号连接成一个标识符

2. 宏定义

1. 一般在宏定义的结尾不加分号。

我们在使用的时候,要加上分号,像我们平时写语句一样。

2. 注意加括号。

在有参数的空定义中,如果含有数值运算,那么就要在“宏整体”和“宏参数”两端都要加上括号。 
        如:#define max(a, b) ((a)+(b));

3. 注意空格。

在有参数的宏定义中,注意“宏名称”和“参数列表”之间不能有空格。 
        如:#define max (a, b) ((a)+(b));  在"max”和”(a, b)”之间不能有空格。

4. 不要使用有副作用的参数区调用宏。

常见的有副作用的参数有:a++,getchar()等。 
        如:宏定义为#define max (a, b) ((a)+(b));  那么使用max(i++, j++)调用该宏,会造成 i 或 j 中的一个值增加2,而不是我们期望的 1。

5. 可以使用编译器选项 添加宏 和 移除宏。

我使用的是gcc,添加宏的指令是”-D”,移除宏的指令是”-U”。

6. 宏参数替换的时候,不会替换字符串中的字符。

即不会替换双引号之间的字符,其他的都会被替换,包括单引号之间的。

7. 可以使用#将 宏参数的值 转化为字符串。

直接使用#,是将宏参数的名称转化为字符串。利用下面的技巧(增加一个过渡宏),可以将“宏参数的值”转化为字符串(当宏参数有值时,这时的宏参数常常也是一个宏)。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define NUMBER ten   /* 宏名称为NUMBER,宏的值为ten */
  4. #define Str(x) #x
  5. #define XStr(x) Str(x)  /* 增加的一个 过渡宏 */
  6. int main(){
  7. printf("Str(NUMBER) == %s  /n", Str(NUMBER));
  8. printf("XStr(NUMBER) == %s  /n", XStr(NUMBER));
  9. system("pause");
  10. return EXIT_SUCCESS;
  11. }

输出结果为:

  1. Str(NUMBER) == NUMBER
  2. XStr(NUMBER) == ten

8. 使用##运算符来实现标识符连接。

不过,不建议使用操作符##来连接标识符,因为这个容易是程序可读性大大降低。

3. 文件包含

1. 要将头文件的定义在保护条件中。

目的是为了防止重复包含头文件。如果你查看过gcc或者其他编译器的源代码,你一定对这个非常熟悉。

例如,你要编写一个头文件,myheader.h,那么你的头文件的内容形式应该为:(定义一个_MYHEADER宏)

  1. #ifndef _MYHEADER
  2. #define _MYHEADER 1
  3. /* 中间是你的头文件内容   */
  4. #endif  /* _MYHEADER */

2. 注意windows系统和Unix系统的路径符号不同。

可以再#include中指定路径来包含文件,例如 #include “../head.h”。但是注意,windows中使用反斜线”/”作为路径分隔符,而Unix系统使用的是斜线”/”

3. 可以使用 编译器选项 来设置搜索路径。

我使用的gcc,使用的-Idir选项,例如: -I"D:/Dev-Cpp/include"。

4. 条件编译

1. #ifdef等价于#if defined(),#ifndef等价于#if !defined()。

2. 在#if中可以使用逻辑操作符(&&、||、!)。在#ifdef 中是不可以使用的,这也是#if的优越点。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define A 1
  4. #define B 0
  5. int main(){
  6. #if defined( A ) && defined( B )
  7. printf("test logic operation in #if  /n");  /* 如果上面的逻辑判断成立,那么将打印出一句话;如果不成立,那么就不会打印这句话 */
  8. #endif
  9. system("pause");
  10. return EXIT_SUCCESS;
  11. }

运行结果:

  1. test logic operation in #if

3. sizeof(int)在预编译阶段是不会被求值的。

只要知道“预编译阶段”在真正的“编译阶段”之前,就很容易理解了。预编译阶段只是对组成源代码中的字符进行作用,从某种意义上来说,它有时甚至不知道它的操作对象是什么,它只是按照既定的规则执行替换。

sizeof(int),无论是sizeof的解析,还是类型的解析,都是在“编译阶段”才开始的,编译阶段知道它的操作对象是什么。

下面的代码是错误

  1. #if sizeof(int) == 2
  2. printf("precompile sizeof(int)");
  3. #endif

5. 额外注意

把一个预处理指令写成多行的形式,要使用符号”/”,并且在该符号后面应紧跟换行符。而非预处理指令代码行不需要使用该符号,直接换行即可。 原因:编译阶段会自动忽略空白符,而预编译阶段不会。

C语言的预编译的更多相关文章

  1. 聊聊C语言的预编译指令include

    "include"相信大家不会陌生,在我们写代码时,开头总会来一句"include XXX".include是干嘛用的,很多教材都提到了,因此这里不会再详细解释 ...

  2. javaScript语言的预编译与运行

    JS代码执行的过程: 1.预编译 ---- 事先对js代码做一个预处理 2.代码运行---开始执行JS代码. JS编程: 1.加载DOM的最好在/BODY之前 2.与DOM渲染无关的放在Head里面 ...

  3. C语言的预编译,程序员必须懂的知识!【预编译指令】【预编译过程】

    由“源代码”到“可执行文件”的过程包括四个步骤:预编译.编译.汇编.链接.所以,首先就应该清楚的首要问题就是:预编译只是对程序的文本起作用,换句话说就是,预编译阶段仅仅对源代码的单词进行变换,而不是对 ...

  4. (十八)C语言之预编译命令、宏

  5. iOS中的预编译指令的初步探究

    目录 文件包含 #include #include_next #import 宏定义 #define #undef 条件编译 #if #else #endif #if define #ifdef #i ...

  6. C语言的傻瓜式随笔(二):全局变量、预编译、goto

    函数的作用:可以实现代码的重用. 函数只需要定义1次,那么函数中的代码就可以随意的调用.       -某不知出处的基本概念 学而时习之,如有误笔,请指正 一.goto跳转语句 goto在C语言的作用 ...

  7. 浅谈css的预编译---less语言

    正如各位所知道的一样,css是一门标记性语言,语法相对简单,对使用者的要求也比较低 .不过可乐不知道友友们有没有发现,在使用css的时候需要书写大量看似没有逻辑的代码,不方便维护及扩展,不利于复用,尤 ...

  8. c语言中条件编译相关的预编译指令

    一. 内容概述 本文主要介绍c语言中条件编译相关的预编译指令,包括#define.#undef.#ifdef.#ifndef.#if.#elif.#else.#endif.defined. 二.条件编 ...

  9. Java程序员的现代RPC指南(Windows版预编译好的Protoc支持C++,Java,Python三种最常用的语言,Thrift则支持几乎主流的各种语言)

    Java程序员的现代RPC指南 1.前言 1.1 RPC框架简介 最早接触RPC还是初学Java时,直接用Socket API传东西好麻烦.于是发现了JDK直接支持的RMI,然后就用得不亦乐乎,各种大 ...

随机推荐

  1. Mobx使用详解

    Mobx是一个功能强大,上手非常容易的状态管理工具.就连redux的作者也曾经向大家推荐过它,在不少情况下你的确可以使用Mobx来替代掉redux. 本教程旨在介绍其用法及概念,并重点介绍其与Reac ...

  2. Luogu P2756 [网络流24题]飞行员配对方案问题_二分图匹配

    二分图模板题 我用的是匈牙利 其实最大流也可以做 #include<iostream> #include<cstdio> #include<cstdlib> #in ...

  3. 讨论过后而引发对EF 6.x和EF Core查询缓存的思考

    前言 最近将RabbitMQ正式封装引入到.NET Core 2.0项目当中,之前从未接触过是个高大上的东东跟着老大学习中,其中收获不少,本打算再看看RabbitMQ有时间写写,回来后和何镇汐大哥探讨 ...

  4. SSA-一种适合中小型企业的新型服务架构

    写在前面 好久好久没写了,最近刚换了工作,花了几天的时候熟悉了项目,接着就是功能的完善,随后就是对新项目的基础架构搭建. 看过Po主博客的都知道,Po主一直致力于推广.Net Core在微服务架构上的 ...

  5. java中equal方法总结

    场景:本周在完成一个公司业务功能时,在判断是否为代叫单时调用了equal方法: PublishOrderType.HELP_ORDER.equals(valetOrderExtraInfoDO.get ...

  6. Azure AI 服务之语音识别

    笔者在前文<Azure AI 服务之文本翻译>中简单介绍了 Azure 认知服务中的文本翻译 API,通过这些简单的 REST API 调用就可以轻松地进行机器翻译.如果能在程序中简单的集 ...

  7. Bootstrap3 排版-缩略语

    当鼠标悬停在缩写和缩写词上时就会显示完整内容,Bootstrap 实现了对 HTML 的 <abbr> 元素的增强样式.缩略语元素带有 title 属性,外观表现为带有较浅的虚线框,鼠标移 ...

  8. 深入Java虚拟机(1)——Java体系结构

    Java体系结构 Java体系结构包括四个独立但相关的技术: 1.Java程序设计语言 2.Java class文件格式 3.Java应用编程接口(API) 4.Java虚拟机 当编写并运行一个Jav ...

  9. Tomcat如何实现Comet

    Comet模式是一种服务器端推技术,它的核心思想提供一种能让当服务器端往客户端发送数据的方式.Comet模式为什么会出现?刚开始人们在客户端通过不断自动刷新整个页面来更新数据,后来觉得体验不好又使用了 ...

  10. Ubuntu使用dpkg安装软件依赖问题解决 ubuntu-tweak ubuntu 16.04 LTS 系统清理

    Ubuntu使用dpkg安装软件依赖问题解决 这里以在ubuntu 16.04安装Ubuntu Tweak为例进行说明,通常安装包依赖问题都可以用这种方法解决: sudo apt-get instal ...