《C++ 新经典》 编译预处理


本章内容概述

本文意在为笔者阅读《C++ 新经典》一书第八章——编译预处理时,记录笔记所写,在阅读书本的过程中,加入自己查到的资料与理解,配合书中代码,深刻理解书中知识。

从C语言源文件到一个可执行文件,期间需经过许多步骤,一般包括预处理、编译、汇编、链接四个步骤。在预编译过程中,主要处理源代码中的预处理指令,比如引入头文件(#include),去除注释,处理所有的条件编译指令(#ifdef, #ifndef, #else, #elif, #endif),宏的替换(#define),添加行号,保留所有的编译器指令。本章主要讲述C语言提供的编译预处理功能,主要有三个:宏定义,文件包含,条件编译。其中,宏定义可以一定程度上简化代码,提高代码可移植性;文件包含可以减少重复代码,避免重复编写;条件编译可以提高编译效率,使得代码可以在不同平台下运行。


一、宏定义

宏定义,是一种常见的预处理指令,简单理解,就是用一种“标识符”来代替一串特定字符,其中,“标识符”叫做宏名。在预处理过程中,预处理器会将源程序中作用域下所有的宏名替换为相应的特定字符。

常见的宏定义有两种,不带参数的宏定义和带参数的宏定义。

1.不带参数的宏定义

无参数宏定义,完成最基本的文本替换,此处的文本替换意在说明,宏定义本身并不具备逻辑分析能力,只进行最原始的文本替换,在使用时需要考虑到被替换带程序中的情况,定义形式如下:

#define 宏名 被替换字符串

简单实用无参宏定义,计算圆面积:

#define PI 3.1415
#include<iostream>
using namespace std;

//返回半径为 r 的圆的面积
double area(int r)
{	return PI * r * r;	}

int main()
{
	int r = 10;
	cout << area(r) << endl;
	return 0;
}

需要注意的是,在替换过程中,宏不关注语法正确性,无论语法正确与否,只做最基础的文本替换,因此,也无需在结尾加分号,否则也会被替换进入源程序。例如,在计算圆面积时,PI 替换后直接参与运算,也没有变量类型的概念。

宏的作用域,在本文件和包含本文件的文件中,从定义到结束宏语句,默认为到程序结束,但是字符串中如果有宏名,则不会被替换。

2.带参数的宏定义

带参数的宏定义在替换时,除了进行文本替换,还会进行参数替换,代码如下:

#define PI 3.1415
#define S(r) PI*r*r
#include<iostream>
using namespace std;

int main()
{
	int r = 10;
	cout << S(r) << endl;
	return 0;
}

在进行参数替换时,宏依然只进行文本替换,无逻辑规范,对此进行测试,代码如下:

#define PI 3.1415
#define S(r) PI*r*r
#include<iostream>
using namespace std;

int main()
{
	int r = 10;
	cout << S(5+5) << endl;
	return 0;
}

本意仍是计算半径为10的圆的面积,但是输出却并不符合预期,因为在替换过程中,宏只做最基本的文本替换,替换后的表达式变成了:

cout << PI * 5 + 5 * 5 + 5 << endl;

为避免这样的情况,可以在定义宏时加括号解决,代码如下:

#define PI 3.1415
#define S(r) PI*(r)*(r)
#include<iostream>
using namespace std;

int main()
{
	int r = 10;
	cout << S(5+5) << endl;
	return 0;
}

这样输出结果便符合预期了。但是笔者浅薄的认为,非必要情况下,常量和函数调用基本可以满足对宏的需求,或者将所需常量和函数封装为类,使用时未必会比宏麻烦。

二、文件包含

文件包含是常用的预处理功能,通过包含系统文件可以方便使用已有库函数,包含头文件有利于简化代码结构,组织代码管理。

预处理器对文件包含的处理为,直接将被包含的文件复制粘贴至对应文件,需要注意的是,文件可以嵌套包含,也就容易导致定义在头文件中的一些变量可能被重复定义,需要进行防卫式声明,避免重复包含,代码如下:

#ifndef __COMPLEX__
#define __CEMPLEX__

//头文件内容
...

#endif

在包含头文件时,有两种情况:

#include<iostream> //表示被包含的文件在系统库中
#include"hello.h" //表示被包含文件优先在当前目录下

三、条件编译

在不同的条件下,使用到的代码可能有所不同,为提高编译效率,可以使用条件编译。例如,要求代码能够在不同平台下运行时,就可以通过条件编译,从而不修改代码也能满足要求,代码如下:

#define __CEMPLEX__
//...
#ifdef __COMPLEX__
	//代码段1
#else
	//程序段2
#endif

本章总结

本章主要讨论了C语言为预处理阶段提供的三种预处理功能,以及预处理器对其作出的处理,希望对读者有所帮助。

最后,我是Alkaid#3529,一个追求不断进步的学生,期待你的关注!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Alkaid3529

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

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

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

打赏作者

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

抵扣说明:

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

余额充值