C语言 预处理详解(#include#define#if,特殊预定宏)

Posted 流楚丶格念

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言 预处理详解(#include#define#if,特殊预定宏)相关的知识,希望对你有一定的参考价值。

预处理

基本概念

C语言对源程序处理的四个步骤:预处理、编译、汇编、链接

预处理是在程序源代码被编译之前,由预处理器(Preprocessor)对程序源代码进行的处理。

这个过程并不对程序的源代码语法进行解析,但它会把源代码分割或处理成为特定的符号为下一步的编译做准备工作。

文件包含指令(#include)

文件包含处理

文件包含处理”是指一个源文件可以将另外一个文件的全部内容包含进来。C语言提供了#include命令用来实现“文件包含”的操作。

例如下图简单示例:

#incude<>和#include""区别

区别:

  • “” 表示系统先在file1.c所在的当前目录找file1.h,如果找不到,再按系统指定的目录检索。
  • < > 表示系统直接按系统指定的目录检索。

注意:

  1. #include <>常用于包含库函数的头文件;
  2. #include ""常用于包含自定义的头文件;
  3. 理论上#include可以包含任意格式的文件(.c .h等) ,但一般用于头文件的包含(.h);

宏定义(#define)

无参数的宏定义(宏常量)

如果在程序中大量使用到了100这个值,那么为了方便管理,我们可以将其定义为:

const int num = 100;

但是如果我们使用num定义一个数组,在不支持c99标准的编译器上是不支持的,因为num不是一个编译器常量,如果想得到了一个编译器常量,那么可以使用:

#define num 100

在编译预处理时,将程序中在该语句以后出现的所有的num都用100代替。这种方法使用户能以一个简单的名字代替一个长的字符串,在预编译时将宏名替换成字符串的过程称为“宏展开”。

宏定义,只在宏定义的文件中起作用。

#define PI 3.1415
void test(){
	double r = 10.0;
	double s = PI * r * r;
	printf("s = %lf\\n", s);
}

带参数的宏定义(宏函数)

在项目中,经常把一些短小而又频繁使用的函数写成宏函数,这是由于宏函数没有普通函数参数压栈、跳转、返回等的开销,可以调高程序的效率

宏通过使用参数,可以创建外形和作用都与函数类似地类函数宏(function-like macro)。宏的参数也用圆括号括起来。

#define SUM(x,y) (( x )+( y ))
void test(){
	//仅仅只是做文本替换 下例替换为 int ret = ((10)+(20));
	//不进行计算
	int ret = SUM(10, 20);
	printf("ret:%d\\n",ret);
}

注意:

  1. 宏的名字中不能有空格,但是在替换的字符串中可以有空格。ANSI C允许在参数列表中使用空格;
  2. 用括号括住每一个参数,并括住宏的整体定义
  3. 用大写字母表示宏的函数名
  4. 如果打算宏代替函数来加快程序运行速度,需要用多次才能得以体现。假如在程序中只使用一次宏对程序的运行时间没有太大提高

宏定义几点说明:

  1. 宏名一般用大写,以便于与变量区别;
  2. 宏定义可以是常数、表达式等;
  3. 宏定义不作语法检查,只有在编译被宏展开后的源程序才会报错
  4. 宏定义不是C语言,不在行末加分号
  5. 宏名有效范围为从定义到本源文件结束;
  6. 可以用#undef命令终止宏定义的作用域;
  7. 在宏定义中,可以引用已定义的宏名;

条件编译(#if)

基本概念

一般情况下,C语言源程序中的每一行代码都要参加编译。但有时候出于对程序代码优化的考虑,希望只对其中一部分内容进行编译,此时就需要在程序中加上条件,让编译器只对满足条件的代码进行编译,将不满足条件的代码舍弃,这就是条件编译

条件编译格式

在C语言中,若要对程序中的代码段有条件地进行编译,就要用到条件编译命令,条件编译主要有如下几种格式:

if格式

#if 表达式
     语句序列①
[#else
     语句序列②]
#endif

功能:当表达式的值为真时,编译语句序列①,否则编译语句序列②。其中,#else和语句序列②可有可无。

ifdef格式

#ifdef     标识符
     语句序列①
[#else
     语句序列②]
#endif

功能:当标识符已被定义时(用#define定义),编译语句序列①,否则编译语句序列②。其中#else和语句序列②可有可无。

ifndef格式

#ifndef     标识符
     语句序列①
[#else
     语句序列②]
#endif

功能:该格式功能与ifdef相反。

用法

防止头文件被重复包含引用:

#ifndef _SOMEFILE_H
#define _SOMEFILE_H

//需要声明的变量、函数
//宏定义
//结构体

#endif

注意:这里的_SOMEFILE_H不是固定的,下面我们举个栗子。

栗子
设有3个源文件如下图所示,其中存在着对同一个源文件重复包含的问题。请修改程序,不要删除代码,利用条件编译避免重复包含,使得每个源文件都能通过编译。

分析:
源文件a.c中的main()函数要调用b.c中的subl()函数,也要调用c.c中的sub2()函数,所以,文件a.c包含了b.c和c.c。源文件b.c中的sub3()函数要调用c.c中的 sub2()函数,所以b.c也包含c.c。这样一来就造成了重复包含的问题,即文件a.c包含了两次c.c,a.c的内容相当于是这样的:

其中c.c的内容出现了两遍,就有了两次sub2()函数的定义,导致编译错误。
要解决这个问题,需要用条件编译,将c.c的内容放在条件编译控制之下,即将c.c的内容修改为

//c.c
#ifndef FILE_C
#define FILE_C
void sub()
{
  ...
}
#endif

这样就可以避免c.c的内容被重复包含。

一些特殊的预定宏

C编译器,提供了几个特殊形式的预定义宏,在实际编程中可以直接使用,很方便。

//	__FILE__			宏所在文件的源文件名 
//	__LINE__			宏所在行的行号
//	__DATE__			代码编译的日期
//	__TIME__			代码编译的时间

void test()
{
	printf("%s\\n", __FILE__);
	printf("%d\\n", __LINE__);
	printf("%s\\n", __DATE__);
	printf("%s\\n", __TIME__);
}

运行结果为:

以上是关于C语言 预处理详解(#include#define#if,特殊预定宏)的主要内容,如果未能解决你的问题,请参考以下文章

c语言基础——预处理详解

C语言#define预处理详解

C语言-预处理(#define#if...)

C语言-预处理(#define#if...)

C语言中程序的编译(预处理操作)+链接详解(详细介绍程序预编译过程)

C语言 预处理二(宏定义--#define)