你必须知道的C语言预处理的问题详解
C语言预处理器执行宏替换、条件编译和文件包含。通常采用以“#”为行首的提示。下面是C语言预处理的应用场合:
1.三字母词(Trigraph Sequences)
C源程序的字符集被包含在7位的ASCII字符集中,但是它是ISO 646-1983 Invariant Code Set的超集。为了让程序可以在缩减集(reduced set)中呈现出来,下面的三字母词会被替换成相应的单字符.
三字母词 | 单字符 |
??= | # |
??/ | \ |
??' | ^ |
??( | [ |
??) | ] |
??! | | |
??< | { |
??> | } |
??- | ~ |
替换发生在任何其他处理之前。
例如:如果你尝试打印字符串"what??!"
printf("what??!\n");
会得到字符串"what|"。
如果你这样注释代码,结果会让你意外:
// Will the next line be executed?????????????/
a++;
a++并不会执行。前提是你知道\的作用。
注意:由于编译器对ANSI C的支持不一样,有些编译器会把三字母词当普通字符处理,你需要给编译选项加上“-trigraphs”
2.行拼接
以反斜杠"\"结尾的行会把该行和下一行拼接成一行(预处理器做的工作就是把该反斜杠'"\"和接着的换行字符'\n'删除)。['\'称为续行符]
例如你可以这样写
/\
* is a legal comment. *\
/
3.宏定义和展开
a)简单宏替换
简单宏替换使程序中能用一个标识符来表示一个单词串,指令形式为:
#define 标识符 单词串
标识符(称为宏名)被定义为后面的单词串;单词串(简称串)是任意以换行结束的用于替换程序中该标识符的正文。如果串太长需要写成多行,则除了最后一行外每一行末尾都要有一个续行符(即添加一个“\”后回车)。
注意:字符串常数中出现的与宏名相同的字符串不在替换之列。例如:
#define YES 1
printf("YES"); // 输出 YES,而不是1
b)带参数的宏替换
预处理指令的形式为:
#define 标识符(标识符,标识符,...,标识符) 单词串
“标识符(标识符,标识符,...,标识符)”是被定义的宏,()外面的标识符称为宏名,()中的标识符是宏的形式参数;宏名与其后的()之间不能有空白符。
例如:
#define max(a,b) ((a)>(b)? (a): (b))
♦操作符#和##
操作符#把其后的串变成双引号包围的串;
操作符##把两个标志符拼在一起,形成一个新的标识符
#define str(expr) #expr
#define cat(x,y) x ## y
int ab=12;
printf(str(hello world!)); // 会被替换成 printf("hello world!");
printf("ab=%d\n", cat(a,b)); // 会被替换成 printf("ab=%d\n", ab); 输出 ab=12
♦宏替换时的顺序
#include <stdio.h>
#define f(a,b) a##b
#define g(a) #a
#define h(a) g(a)
int main()
{
printf("%s\n", h(f(1,2)));
printf("%s\n", g(f(1,2)));
return 0;
}
输出结果是12和f(1,2)。为什么会这样呢,宏的解开不像函数,由里到外。
(1)在""内的宏名或宏参数名不被替换
(2)宏替换顺序:一个带参数的宏内部调用另一个宏,参数也是一个宏,则先替换外层的宏,再替换外层宏的参数,最后替换内层宏。
知道这些规则对于出现上面的结果就不难理解了。
温馨提示:在写带参数的宏替换指令时,推荐的做法时将单词串中的每一个参数都用()括起来,整个表达式也要用()括起来;否则,替换结果可能不是你想要的,例如:
#define sqr(x) x * x
// 如果程序中的宏的引用形式为
sqr(3.0+1.0); // 经预处理后会被替换为 3.0 + 1.0 * 3.0 + 1.0
结果与你的原意(3.0+1.0)*(3.0+1.0)不等价
c)取消宏定义
#undef 标识符
会使宏名标识符失去定义。如果#undef 一个没有定义过的标识符 也不会引发错误。
4.文件包含
#include <filename> // 引用标准库的头文件(编译器将从标准库目录开始搜索)
#include "filename" // 引用非标准库的头文件(编译器将从用户的工作目录开始搜索)
#include 标识符 // 标识符是由#define 定义的<filename>或"filename"的宏名
5.条件编译
条件编译指令格式如下:
if-line 正文
[#elif 常量表达式 正文]
...
[#else 正文]
#endif
if-line为下面中的任意一种形式:
(1)#if 常量表达式
(2)#ifdef 标识符
(3)#ifndef 标识符
♦defined操作符用来判断标识符是否定义过。形式如下:
defined identifier
或
defined (identifier)
下面的
#ifdef identifier
#ifndef identifier
等价于
#if defined identifier
#if ! defined identifier
6.行控制
行控制指令有下列两种形式
(1)#line n "filename"
(2)#line n
行控制预处理功能为其他产生C源程序的预处理程序(例如数据库系统中的宿主C预编译程序)在跟踪被处理程序(例如被宿主C预编译程序处理的扩展名为.pc的预编译源程序)的行号时提供方便,便于最终用户的源程序查错和该错。它会使编译器相信n(十进制正整数)为下一个源程序行的行号,“filename”会被当作当前文件名。
7.生成错误
#error error_messageopt
让编译器输出错误信息error_message
8.Pragmas
#pragma token-sequenceopt
#pragma是编译程序实现时定义的指令,它允许由此向编译程序传入各种指令。例如,一个编译程序可能具有支持跟踪程序执行的选项,此时可以用#pragma语句选择该功能。编译程序忽略其不支持的#pragma选项。#pragma提高C源程序对编译程序的可移植性。
9.空指令
形如
#
没有任何作用
10.预定义宏
C语言规范了5个固有的预定义宏,他们分别是
__LINE__ 当前源程序的行号
__FILE__ 正在编译的程序的文件名
__DATE__ 编译的日期字符串,形如"Mmm dd yyyy"
__TIME__ 编译的时间字符串,形如"hh:mm:ss"
__STDC__ 如果__STDC__的内容是十进制常数1,则表示编译程序的实现符合标准C
相关文章
- 这篇文章主要为大家详细介绍了C语言实现放烟花的程序,有音乐播放,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-02-23
- 本篇文章主要介绍C语言中char的知识,并附有代码实例,以便大家在学习的时候更好的理解,有需要的可以看一下...2020-04-25
- 这篇文章主要介绍了详解如何将c语言文件打包成exe可执行程序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-25
- free函数是释放之前某一次malloc函数申请的空间,而且只是释放空间,并不改变指针的值。下面我们就来详细探讨下...2020-04-25
- 这篇文章主要介绍了C语言中计算正弦的相关函数总结,包括正弦和双曲线正弦以及反正弦的函数,需要的朋友可以参考下...2020-04-25
详解C语言中的rename()函数和remove()函数的使用方法
这篇文章主要介绍了详解C语言中的rename()函数和remove()函数的使用方法,是C语言入门学习中的基础知识,需要的朋友可以参考下...2020-04-25- 这篇文章主要介绍了C语言中求和、计算平均值、方差和标准差的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-12-10
- 本篇文章主要讲解C语言 基本语法,这里提供简单的示例和代码来详细讲解C语言的基本语法,开始学习C语言的朋友可以看一下,希望能够给你带来帮助...2021-09-18
- 这篇文章主要介绍了C语言中send()函数和sendto()函数的使用方法,是C语言入门学习中的基础知识,需要的朋友可以参考下...2020-04-25
- 今天小编就为大家分享一篇C语言实现从文件读入一个3*3数组,并计算每行的平均值,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-04-25
- 这篇文章主要介绍了使用C语言操作文件的基本函数整理,包括创建和打开以及关闭文件的操作方法,需要的朋友可以参考下...2020-04-25
- 这篇文章主要介绍了C语言中memcpy 函数的用法详解的相关资料,需要的朋友可以参考下...2020-04-25
- 这篇文章主要介绍了C语言中查找字符在字符串中出现的位置的方法,分别是strchr()函数和strrchr()函数的使用,需要的朋友可以参考下...2020-04-25
- 很多同学在学习c语言的时候是不是会碰到a++和++a都有甚么作用啊。今天我们就来探讨下...2020-04-25
- 下面小编就为大家带来一篇C语言实现时间戳转日期的算法(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-04-25
- 这篇文章主要对C语言中const关键字的用法进行了详细的分析介绍,需要的朋友可以参考下...2020-04-25
- 这篇文章主要介绍了C语言之整数划分问题(递归法)实例代码的相关资料,需要的朋友可以参考下...2020-04-25
- 本文给大家简单介绍下c实现linux下的数据库备份的方法和具体的源码,十分的实用,有需要的小伙伴可以参考下。...2020-04-25
C语言正则表达式详解 regcomp() regexec() regfree()用法详解
C语言处理正则表达式常用的函数有regcomp()、regexec()、regfree()和regerror(),这里就为大家介绍一下,需要的朋友可以参考一下啊...2020-04-25- 这篇文章主要介绍了c语言实现找最大值最小值位置查找,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-04