系列文章目录
前言
蓝色问号代表个人理解 绿色代表来源 红色问号代表尚有疑问
为什么要读标准?因为全面、权威,所有答案都在标准里面!
哪些人适合浏览本系列文章?不清楚C语言程序的组成,以及每个组成部分的详细内容
持续更新,码字不易,求点赞收藏
1、预处理指令
预处理指令中预处理标记之间(从引入#预处理标记之后到终止新行字符之前)出现的唯一空白字符是空格和水平制表符(包括在翻译阶段3中替换注释的空格)

2、条件并入
- “#if 常量表达式 新行 可选的程序组”
- “#elif 常量表达式 新行 可选的程序组”
- “#ifdef 标识符 新行 可选的程序组”
- “#ifndef 标识符 新行 任选的程序组”
- “defined 标识符”、“defined (标识符)”、 “!defined 标识符”、"!defined (标识符)"也可替换前述常量表达式
#include <bits/stdc++.h>
using namespace std;
#define a 1 //成功输出b的值
#if defined(a)
#define b 100
#endif
int main()
{
cout << b;
return 0;
}
3、源文件并入
#include指示应标识一个可被实现处理的头文件或源文件
- #include<头文件名序列>新行,在实现定义的位置搜索序列相同的文件
- #include"文件名字符序列"新行,搜索方式是实现定义的,搜索失败按<>方式处理
- #include 预处理token 新行,预处理token是和""还是<>组合是实现定义的
- 在分隔符内的外部源文件名:一个或多个字母,后跟句点(.)和单个字母
//test.h
int b = 100;
//test1.cpp
#include <bits/stdc++.h>
#define a "test.h"
#include a
using namespace std;
extern int b;
int main()
{
cout << b; //成功输出100
return 0;
}
4、宏替换
-
#define 标识符 替换表 新行
-
#define 标识符 左括号 可选的标识符表 右括号 替换表 新行
-
#undef
-
当且仅当两个替换列表中的预处理标记具有相同的数目、顺序、拼写和空白类符分隔(其中所有空白类符分隔被视为相同)时,两个替换列表是相同的
-
不用左括号的宏即类似对象的宏,可再重新定义,只要第二个定义是类似对象的定义,且替换表相同
-
使用左括号的宏即类似函数的宏,可再重新定义,只要第二个定义是类似函数的定义,且形参数目和拼写相同
-
类似函数的宏调用的实参数目应与形参数目一致,且应存在一个终止该调用的预处理单词 ‘)’,且其中的形参标识符应该使唯一声明的
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define RAND (rand() % 10) //宏只是文本替换
#define ADD(a, b) (a + b) //带参数的宏
#define N 100
#define COUT printf("%d", N) //不能递归调用宏
int main(int argc,char *argv[]){
srand((unsigned) time(NULL));
for(int i = 0; i < 10; i++ )
printf("%d\n", RAND);
COUT;
printf("\n%d", ADD(5, 6));
return 0;
}
4
3
9
2
7
7
1
6
8
7 //随机函数正常工作
100
11
4.1、实参替换
#算符
对于类似函数的宏,替换列表中的每个#预处理标记后面都应跟随一个参数,将实参字符替换到形参的相对位置
#include <bits/stdc++.h>
using namespace std;
#define b(c,a) c#a
int main()
{
int buf = 100; //输出
cout << 1 << endl; //1
b(cout << ,buf << endl); // buf << endl
cout << endl << 2 << endl; //2
return 0;
}
##算符
##预处理标记不得出现在任何形式宏定义的替换列表的开头或结尾,将实参含类型替换到形参
#include <bits/stdc++.h>
using namespace std;
#define b(c,a) c##a
int main()
{
int buf = 100; //输出
cout << 1 << endl; //1
cout << b(100, 200); //111311
cout << endl << 2 << endl; //2
return 0;
}
4.2、重新扫描
代替完替换表的所有形参后,对结果所得预处理token和余下的token再重新扫描,若结果所得的预处理token中发现要替换的宏名,则不替换它,而且任何嵌套的替换遇到所要替换的宏名也不替换,这也是为什么宏名不可递归
4.3、宏定义作用域
直至遇到#undef或翻译单位末尾为止
5、行控制
- #line 数字序列 新行,设置行号
- #line 数字序列"字符串字面值" 新行,设置行号,并将源文件的假定名称更改为字符串文字的内容
- #line 预处理token 新行,此形式也允许,但宏替换后需与前两种形式的一种匹配
#include <bits/stdc++.h>
using namespace std;
#line 100
int main()
{
cout << __LINE__ << endl; //103
cout << __LINE__; //104
return 0;
}
6、其他指令
出错处理指令
#error 可选的预处理token 新行 ,产生一个诊断消息,该信息包含指定的token序列
编译指令
#pragma 可选的预处理token 新行, 使实现按一种实现定义的方式动作,实现不能识别的编译指令将被忽略
空指令
#新行, 无作用
7、预定义的宏名
-
_LINE_当前源行的行号(十进制常量)
-
_FILE_假设的源文件名(字符串字面值)
-
_DATE_翻译源文件的日期(格式为Mmm dd yyyy的字符串字面值,其中月份的名称与asctime函数生成的名称相同,如果值小于10,则dd的第一个字符为空格字符)如果翻译日期不可用,则应提供实施定义的有效日期
-
_TIME_源文件的翻译时间(格式为hh:mm:ss的字符串字面值,与asctime函数生成的时间相同)。如果翻译时间不可用,则应提供实施定义的有效时间
-
_STDC_十进制常数1,用以指示是一个符合标准的实现
-
除_LINE_和_FILE_外,预定义宏值不变
-
所有宏名不可再定义,且以一个下划线开始后跟大写字母或第二个下划线
以上纯属个人观点,欢迎大佬批评指正
https://blog.youkuaiyun.com/init33/article/details/121258745 ↩︎
https://blog.youkuaiyun.com/init33/article/details/121318734 ↩︎
https://blog.youkuaiyun.com/init33/article/details/121319873 ↩︎
https://blog.youkuaiyun.com/init33/article/details/121323883 ↩︎
https://blog.youkuaiyun.com/init33/article/details/121323943 ↩︎
https://blog.youkuaiyun.com/init33/article/details/121323932 ↩︎
https://blog.youkuaiyun.com/init33/article/details/121323958 ↩︎
https://editor.youkuaiyun.com/md/?articleId=121323994 ↩︎
本文详细介绍了C语言的预处理指令,包括预处理指令的种类、条件并入、源文件并入、宏替换及其作用域、行控制以及其他指令。重点讲解了宏替换的实参替换、##运算符和#define的作用域。适合初学者了解C语言预处理的全面知识。
1778

被折叠的 条评论
为什么被折叠?



