IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言笔记——预处理器 -> 正文阅读

[C++知识库]C语言笔记——预处理器

预处理指令

大多数的预处理指令都属于3中类型之一
宏定义。#define指令定义一个宏,#under指令删除一个宏定义
文件包含。#include 指令导致一个指令文件的内容被包含到程序中。
条件编译。#if、#ifdef、#ifndef、#elif、#else和#endif指令可以根据预处理器可以测试的条件来确定是将一段文本块包含到程序中还是将其排除在程序之外。剩下的#error、#line、#pragma指令是更特殊的指令,较少用到。
指令都以#开始。#符号不需要在一行的行首,只要它在之前只有空白字符就行。在#后是指令名,接着是指令所需要的其他信息。
在指令的符号之间可以插入任意数量的空格或水平制表符。例如,下面的指令是合法的:

#      define     N    100

指令总是在第一个换行符处结束,除非明确地指明要延续。如果想在下一行延续指令,我们必须在当前行的末尾使用\字符。例如,下面的指令定义了一个宏来表示硬盘的容量,按字节来计算:

#define DISK_CAPACITY(SIDES *            	\
                      TRACKS_PER_SIDE *  	\
                      SECTORS_PER_TRAACK *	\
                      BYTES_PER_SECTOE)

指令可以出现在程序中的任何地方。但我们通常将#define和#include指令放在文件的开始,其他指令则放在后面 ,甚至可以放在函数定义的中间。
注释可以与指令放在同一行。实际上,在宏定义的后面加一个注释来解释宏的含义是一种比较好的习惯:

#define FREEZING_PT    32.0f   /*  freezing point of water  */

宏定义

简单的宏(C标准中称为对象式宏)的定义有如下格式:
[#define指令 (简单的宏)]

#define 标识符  替换列表

替换列表是一系列的预处理记号,宏的替换列表可以包括标识符、关键字、数值常量、字符串字面量、操作符和排列。

不要在宏定义中放置任何额外的符号,否则它们会被作为替换列表的一部分。例如:

#define N = 100  /* wrong */
#define N 100;  /* wrong */

使用#define来为常量命名有很多的优点:
1.程序会更易读
2.程序会更易于修改
3.可以帮助避免前后不一致或键盘输入错诶
4.可以对C语法做小的修改
5.对类型重命名
6.控制条件编译
顺便提一下,宏定义的替换列表为空是合法的。

带参数的宏

带参数的宏的定义格式如下:
[#define指令 (带参数的宏)]

#define  标识符(x1,x2,x3......xn) 替换列表

在宏的名字和左括号之间必须没有空格。如果有空格,预处理器就会认为是在定义一个简单的宏,其中(x1,x2,x3…xn)为替换列表的一部分。

宏参数没有类型检查
当一个函数被调用时,编译器会检查每一个参数来确定它们是否是正确的类型,打。如果不是,要么将参数转换成正确的类型,要么由编译器产生一条出错的消息。预处理器不会检查宏参数的类型,也不会进行类型转换。

宏可能不止一次地计算它的参数
函数对它的参数只会计算一次,而宏可能会计算两次甚至更多次。如果参数有副作用,多次计算参数的值可能会产生不可预知的结果。举一个例子:

n=MAX(i++,j);

下面是这条语句在预处理之后的结果:

n=((i++)>(j)?(i++):j);

如果i大于j,那么i可能会被(错误地)增加两次,同时n可能赋予错误的值。
由于多次计算宏的参数而导致错误可能非常难于发现,因为宏调用和函数调用看起来是一样的。更糟糕的是,这类宏可能在大多数情况下可以正常工作,仅在特定参数有副作用时失效。为了自我保护,最好避免使用带副作用的参数。

带参数的宏不仅适用于模拟函数调用,还经常用作需要重复书写的代码段模式。例如:

#define PRINTF_INT(n)   printf("%d\n",n)

一旦定义了PRINTF_INT,预处理器就会将这行PRINTF_INT(i/j)转换成

printf("%d\n",i/j);

对于在一个宏定义中哪里要加圆括号有两条规则要遵守。首先,如果宏的替换列表中有运算符,那么始终要将替换列表放在圆括号中:

#define TWO_PI   (2*3.14159)

其次,如果宏有参数,每个参数每次在替换列表中出现时都要放在圆括号中:

#define SCALE(x)     ((x)*10)

为了展示为替换列表添加圆括号的重要性,下面据一些例子:

#define  TWO_PI    2*3.14159  /*  需要给替换列表加圆括号  */

在预处理时,语句 conversion_factor = 360/TWO_PI 变为

conversion_factor = 360/2*3.14159;

除法会在乘法之前执行,产生的结果并不是期望的结果。

当参数有宏时,仅给替换列表添加圆括号是不够的。参数的每一次出现都要添加圆括号。例如:

#define SCALE(x)     (x*10) /* 需要给x添加括号 */

在预处理的过程中,语句 j=SCALE(i+1) 变为 j=(i+1*10);
由于乘法的优先级比加法高,这条语句等价于 j = i+10;当然我们希望的是
j=(i+1)*10;

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-23 10:32:07  更:2021-07-23 10:33:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/28 14:55:47-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码