预处理详解(三)—— 命令行定义 + 条件编译 + 文件包含

命令行定义

许多C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。
例如:当我们根据同一个源文件要编译出不同的一个程序的不同版本的时候,这个特性便起到了作用。(假定某个程序中声明了一个某长度的数组,但是一个机器的内存有限,我们需要一个很小的数组,但是另外一个机器的内存很大,我们需要一个较大的数组。)

#include <stdio.h>
int main()
{
	int array[ARRAY_SIZE];
	int i = 0;
	for (i = 0; i< ARRAY_SIZE; i++)
	{
		array[i] = i;
	}
	for (i = 0; i< ARRAY_SIZE; i++)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
	return 0;
}

可以看到,代码中没有明确定义数组的大小。在编译这种代码时,我们需要使用命令行对数组的大小进行定义。

例如,在Linux环境下,编译指令如下:

gcc -D programe.c ARRAY_SIZE = 10

经过该编译指令后,便可以打印出0到9的数字。

条件编译

条件编译,即满足条件就参与编译,不满足条件就不参与编译。

在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。

常见的条件编译指令有以下几种:
1.单分支的条件编译

#if 表达式
	//待定代码
#endif

如果#if后面的表达式为真,则“待定代码”的内容将参与编译,否则“待定代码”的内容不参与编译。

2.多分支的条件编译

#if 表达式
	//待定代码1
#elif 表达式
	//待定代码2
#elif 表达式
	//待定代码3
#else 表达式
	//待定代码4
#endif

多分支的条件编译类似于if-else语句,“待定代码1,2,3,4”之中只会有一段代码参与编译。

3.判断是否被定义

//第一种的正面
#if defined(表达式)
	//待定代码
#endif

//第一种的反面
#if !defined(表达式)
	//待定代码
#endif

如果“表达式”被#define定义过,则“第一种的正面”的“待定代码”将参与编译,否则不参与编译。“第一种的反面”的执行机制与“第一种的正面”恰好相反。

//第二种的正面
#ifdef 表达式
	//待定代码
#endif

//第二种的反面
#ifndef 表达式
	//待定代码
#endif

如果“表达式”被#define定义过,则“第二种的正面”的“待定代码”将参与编译,否则不参与编译。“第二种的反面”的执行机制与“第二种的正面”恰好相反。

4.嵌套指令

#include <stdio.h>
#define MIN 10
int main()
{
#if !defined(MAX)
#ifdef MIN
	printf("hello\n");
#else
	printf("world\n");
#endif
#endif
	return 0;
}

这里条件编译指令的嵌套类似于if-else语句的嵌套,博友们可以类比理解。

注意:未满足条件编译指令的代码,在预处理阶段将被编译器自动删除,不参与后面的代码编译过程。
例如,以下代码:

#include <stdio.h>
int main()
{
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d\n", i);
#if 0
		printf("hello world!\n");
#endif
	}
	return 0;
}

因为#if后面的表达式为假,语句 #if 0 和 #endif 之间的代码将不参与编译,所以在预处理阶段过后,编译器编译的代码是:

//#include <stdio.h>
//预处理阶段头文件也被包含了
int main()
{
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d\n", i);
	}
	return 0;
}

所以,代码运行后只会打印0到9的数字。

文件包含

我们知道,#include指令可以使被包含的文件参与编译,在预处理阶段,就会进行文件的包含。
例如:

#include <stdio.h>

在预处理阶段,编译器会先删除该指令,并用stdio.h文件中的内容进行替换。

但是,文件的包含有两种:

#include <stdio.h>
#include "stdio.h"

一种是用尖括号将要包含的文件括起来,另一种是用双引号将要包含的文件引起来。这两种方法,在某些情况下似乎都可行,那么这两种方法到底有什么区别呢?

< >:如果使用尖括号的方式对头文件进行包含,那么当代码运行到预处理阶段,将对头文件进行包含时,编译器会自动去自己的安装路径下查找库目录,若库目录中含有该头文件,则将其进行包含,若库目录下不存在该头文件,则提示编译错误。

" ":如果使用双引号的方式对头文件进行包含,那么当代码运行到预处理阶段,将对头文件进行包含时,编译器会首先去正在编译的源文件目录下进行查找,若没有找到目标头文件,则再去库目录下进行查找,若两处都没有找到目标头文件,则提示编译错误。

这样看来,当我们要包含的头文件是库函数的头文件的时候,我们使用尖括号或者双引号都可以,但是当我们要包含的头文件是自定义的头文件时,我们只能用双引号进行头文件的包含。

但是如果我们明明知道自己要包含的头文件是库函数的头文件,那我们就没有必要使用双引号去包含,因为那样会降低代码的效率。所以说,为了提高代码执行效率:
< >:一般用于包含C语言提供的库函数的头文件。
" ":一般用于包含自定义的头文件。

关于头文件,还有一点值得注意的是,当我们使用#include来包含头文件时,如果我们重复包含同一个头文件,那么在预处理阶段就会重复包含该头文件的内容,会大大加长代码量,导致代码冗余

避免该问题的发生,有以下两种方法:
方法一:

#ifndef __ADD_H__
#define __ADD_H__

//头文件内容

#endif

当第一次包含该头文件时,会用#define定义符号__ADD_H__,当第二次重复包含该头文件时,因为__ADD_H__已经被定义过,就无法再次包含该头文件的内容了。

方法二:

#pragma once

//头文件内容

只需在头文件开头加上这句代码,那么该头文件就只会被包含一次。

  • 13
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

2021dragon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值