C语言中的预处理

1、 宏定义

预处理命令可以改变程序设计环境,提高编程效率,它们并不是 C 语言本身的组成部分,不能直接对 它们进行编译,必须在对程序进行编译之前,先对程序中这些特殊的命令进行“预处理” 。经过预处理后,程序就不再包括预处理命令了,最后再由编译程序对预处理之后的源程序进行编译处理,得到可供执行的 目标代码。C 语言提供的预处理功能有三种,分别为宏定义、文件包含和条件编译,下面将对它们进行简 单介绍。 宏定义 在 C 语言源程序中允许用一个标识符来表示一个字符串,称为“宏” ,被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的宏名,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。 宏定义是由源程序中的宏定义命令完成的,宏代换是由预处理程序自动完成的。在 C 语言中,宏分为 有参数和无参数两种。无参宏的宏名后不带参数,其定义的一般形式为: #define 标识符 字符串; 其中“#”表示这是一条预处理命令(在 C 语言中凡是以“#”开头的均为预处理命令)“define”为宏定义命令,“标识符”为所定义的宏名, “字符串”可以是常数、表达式、格式串等。符号常量的定义就是一种无参宏定义。 此外,常常对程序中反复使用的表达式进行宏定义。例如:

#define M (y*y+3*y);

它的作用是指定标识符 M 来代替表达式(y*y+3*y)。 在编写源程序时, 所有的(y*y+3*y)都可由 M 代替, 而对源程序进行编译时,将先由预处理程序进行宏代换,即用(y*y+3*y)表达式去置换所有的宏名 M,然后 再进行编译。 C语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。对于带 参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。 带参宏定义的一般形式为: #define 宏名(形参表) 字符串; 在字符串中含有各个形参。 带参宏调用的一般形式为: 宏名(实参表); 例如:

#define M(y) y*y+3*y

/*宏定义*/

......

k=M(5);

/*宏调用*/

...... 在上面的宏调用时,用实参 5 去代替形参 y,经预处理宏展开后的语句为: k=5*5+3*5; 程序 2.26 给出了一个宏定义和调用的完整实例。 定义一个名为 MAX 的带参数的宏,可以通过它来选出参数 a、b 中的较大值:test26.c。

#include

#define MAX(a,b) (a>b)?a:b

/*带参数的宏定义*/

main()

{

int x,y,max;

printf("input two numbers: ");

scanf("%d %d",&x,&y);

max=MAX(x,y);

printf("max=%d\n",max);

/*宏调用*/

}

程序运行结果如下(□表示空格,↙表示回车) :

input two numbers: 2009□2010↙

max=2010

可以看到,宏替换相当于实现了一个函数调用的功能,而事实上,与函数调用相比,宏调用更能提高 C 程序的执行效率。

2、文件包含

文件包含 文件包含是 C 预处理程序的另一个重要功能,文件包含命令行的一般形式为: #include "文件名" 或者 #include <文件名> 文件包含命令的功能是把指定的文件插入该命令行位置取代该命令行,从而把指定的文件和当前的源 程序文件连成一个源文件。 在程序设计中,文件包含是很有用的。一个大的程序可以分为多个模块,由多个程序员分别编程,有 些公用的符号常量或宏定义等可单独组成一个文件,在其他文件的开头用包含命令包含该文件即可使用。 这样,可避免在每个文件开头都去书写那些公用量,从而节省时间,并减少出错。 这里对 C 语言的文件包含命令进行以下几点说明: (1)包含命令中的文件名可以用双引号引起来,也可以用尖括号引起来。例如以下写法都是允许的:

#include "stdio.h"

#include <stdio.h>

但是这两种形式是有区别的:使用尖括号表示在包含文件目录中去查找(包含目录是由系统的环境变 量进行设置的,一般为系统头文件的默认存放目录,比如 Linux 系统在/usr/include 目录下),而不在源文件的存放目录中查找; 使用双引号则表示首先在当前的源文件目录中查找, 若未找到才到包含目录中去查找。 用户编程时可根据自己文件所在的目录来选择某一种命令形式。 (2)一个 include 命令只能指定一个被包含文件,若有多个文件要包含,则需用多个 include 命令。 (3)文件包含允许嵌套,即在一个被包含的文件中又可以包含另一个文件。

3、条件编译

条件编译 预处理程序提供了条件编译的功能,可以按不同的条件去编译不同的程序部分,因而产生不同 的目标代码文件,这对于程序的移植和调试是很有用的。条件编译可分为三种形式。 第一种形式如下: #ifdef 标识符 程序段 1 #else 程序段 2 #endif 它的功能是如果标识符已被 #define 命令定义过则对程序段 1 进行编译;否则对程序段 2 进行编译。 如果没有程序段 2(为空),本格式中的#else 可以没有,即可以写为: #ifdef 标识符 程序段 #endif 第二种形式如下: #ifndef 标识符 程序段 1 #else 程序段 2 #endif 与第一种形式的区别是将“ifdef”改为“ifndef”。它的功能是如果标识符未被#define 命令定义过则对程 序段 1 进行编译,否则对程序段 2 进行编译。这与第一种形式的功能正好相反。 第三种形式如下: #if 常量表达式 程序段 1 #else 程序段 2 #endif 它的功能是如果常量表达式的值为真(非 0),则对程序段 1 进行编译,否则对程序段 2 进行编译。 因此可以使程序在不同的条件下完成不同的功能。

4、 其他与处理命令

#error 等其他常用预处理命令 除了上面介绍的之外,C 语言还有#erroe、#line、#pragma 等其他常用的预处理命令,在很多 C 语言的 程序中也是经常可见的。下面向读者简单介绍一下它们。 1. #error #error 指令强制编译程序停止编译,它主要用于程序调试。#error 指令的一般形式是: #error error-message 注意,宏串 error-message 不用双引号引起来。遇到#error 指令时,错误信息被显示,可能同时还显示 编译程序作者预先定义的其他内容。 2. #line #line 指令改变__LINE__和__FILE__的内容。__LINE__和__FILE__都是编译程序中预定义的标识符。 __FILE__的内容是当前被编译源文件的文件名。 #line标识符__LINE__的内容是当前被编译代码行的行号,其一般形式是: #line number “filename” 其中,number 是正整数并变成__LINE__的新值; 可选的“filename”是合法文件标识符并变成__FILE__的新值。#line 主要用于调试和特殊应用。 3. #pragma #pragma 是编译程序实现时定义的指令,它允许由此向编译程序传入各种指令。例如,一个编译程序可能具有支持跟踪程序执行的选项,此时可以用#pragma 语句选择该功能,编译程序忽略其不支持的#pragma 选项。使用#pragma 预处理命令可提高 C 源程序对编译程序的可移植性。

原文发布于微信公众号 - 编程范(dotcpp)

原文发表时间:2015-12-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏jianhuicode

什么是编译错误,运行时错误及逻辑错误?

程序的错误主要分成三种: 编译链接错误(语法错误); 编译链接错误又分成编译错和链接错。 编译错就是普通意义上的语法错,编译器进行语法检查不通过,也就是程...

367100
来自专栏编程微刊

如何把传统写法改成框架形式 es6http://www.expressjs.com.cn/

23820
来自专栏PHP在线

WordPress的可拓展性初探(二)

作者:西瓜玩偶(racnil070512 at hotmail dot com) 上一篇文章介绍了如何设计数据库,从而达到可拓展性的目的。下面的篇幅将介绍在P...

342120
来自专栏程序员同行者

Linux三剑客之sed

13920
来自专栏大内老A

.NET Core的文件系统[3]:由PhysicalFileProvider构建的物理文件系统

ASP.NET Core应用中使用得最多的还是具体的物理文件,比如配置文件、View文件以及网页上的静态文件,物理文件系统的抽象通过PhysicalFilePr...

22390
来自专栏MasiMaro 的技术博文

为什么C语言会有头文件

前段时间一个刚转到C语言的同事问我,为什么C会多一个头文件,而不是像Java和Python那样所有的代码都在源文件中。我当时回答的是C是静态语言很多东西都是需要...

35650
来自专栏java达人

基本线程同步(一) 同步方法

在这个指南中,我们将学习在Java中如何使用一个最基本的同步方法,即使用 synchronized关键字来控制并发访问方法。只有一个执行线程将会...

42090
来自专栏深度学习之tensorflow实战篇

pycharm 或者其他Python IDE不支持中文编码的解决方案

Python的自带编辑器IDLE或者Python Shell在默认情况下都不支持中文编码,若在脚本程序中出现中文,则会出现一定的错误。 原因: 因为Python...

33140
来自专栏知识分享

16-GPRS(Air202)关于多个文件中的变量调用和定时器

大家有没有看这篇 https://www.cnblogs.com/yangfengwu/p/8965054.html

11720
来自专栏Astropeak

Python:怎样用线程将任务并行化?

23670

扫码关注云+社区

领取腾讯云代金券