• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > 详解C语言编程中预处理器的用法

详解C语言编程中预处理器的用法

作者: 字体:[增加 减小] 来源:互联网 时间:2017-05-28

通过本文主要向大家介绍了c语言指针详解,c语言题库及详解答案,c语言关键字详解,c语言链表详解,c语言32关键字详解等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

预处理最大的标志便是大写,虽然这不是标准,但请你在使用的时候大写,为了自己,也为了后人。

预处理器在一般看来,用得最多的还是宏,这里总结一下预处理器的用法。

  • #include <stdio.h>
  • #define MACRO_OF_MINE
  • #ifdef MACRO_OF_MINE
  • #else
  • #endif

上述五个预处理是最常看见的,第一个代表着包含一个头文件,可以理解为没有它很多功能都无法使用,例如C语言并没有把输入输入纳入标准当中,而是使用库函数来提供,所以只有包含了stdio.h这个头文件,我们才能使用那些输入输出函数。 #define则是使用频率第二高的预处理机制,广泛用在常量的定义,只不过它和const声明的常量有所所区别:

#define MAR_VA 100
const int Con_va = 100;
...
/*定义两个数组*/
...
for(int i = 0;i < 10;++i)
{
  mar_arr[i] = MAR_VA;
  con_arr[i] = Con_va;
}
</div>

区别1,定义上MAR_VA可以用于数组维数,而Con_va则不行
区别2,在使用时,MAR_VA的原理是在文中找到所有使用本身的地方,用值替代,也就是说Con_va将只有一分真迹,而MAR_VA则会有n份真迹(n为使用的次数) 剩下三个则是在保护头文件中使用颇多。
几个比较实用的用于调试的宏,由C语言自带

  • __LINE__和__FILE__ 用于显示当前行号和当前文件名
  • __DATE__和__TIME__ 用于显示当前的日期和时间
  • __func__(C99) 用于显示当前所在外层函数的名字

上述所说的五种宏直接当成值来使用即可。

__STDC__

如果你想检验你现在使用的编译器是否遵循ISO标准,用它,如果是他的值为1。

printf("%d\n", __STDC__);
</div>

输出: 1

如果你想进一步确定编译器使用的标准版本是C99还是C89可以使用__STDC__VERSION__,C99(199901)

printf("%d\n", __STDC_VERSION__);
</div>

输出: 199901

对于#define

预处理器一般只对同一行定义有效,但如果加上反斜杠,也能一直读取下去

 #define err(flag) \
   if(flag) \
    printf("Correctly")
</div>

可以看出来,并没有在末尾添加;,并不是因为宏不需要,而是因为,我们总是将宏近似当成函数在使用,而函数调用之后总是需要以;结尾,为了不造成混乱,于是在宏定义中我们默认不添加;,而在代码源文件中使用,防止定义混乱。

预处理同样能够带来一些便利

  #define SWAP1(a, b) (a += b, b = a - b, a -= b)
  #define SWAP2(x, y) {x ^= y; y ^= x; x ^= y}
</div>

引用之前的例子,交换两数的宏写法可以有效避免函数开销,由于其是直接在调用处展开代码块,故其比拟直接嵌入的代码。但,偶尔还是会出现一些不和谐的错误,对于初学者来说:

 int v1 = 10;
 int v2 = 20;
 SWAP1(v1, v2);
 SWAP2(v1, v2);//报错
</div>

对于上述代码块的情况,为什么SWAP2报错?对于一般的初学者来说,经常忽略诸如, goto do...while等少见关键字用法,故很少见SWAP1的写法,大多集中于SWAP2的类似错误,错就错在{}代表的是一个代码块,不需要使用;来进行结尾,这便是宏最容易出错的地方 宏只是简单的将代码展开,而不会做任何处理 对于此,即便是老手也常有失足,有一种应用于单片机等地方的C语言写法可以在此借鉴用于保护代码:

 #define SWAP3(x ,y) do{ \
     x ^= y; y ^= x; x ^= y; \
     }while(0)
</div>

如此便能在代码中安全使用花括号内的代码了,并且如之前所约定的那样,让宏的使用看起来像函数。

但正所谓,假的总是假的,即使宏多么像函数,它依旧不是函数,如果真的把它当成函数,你会在某些时候错的摸不着头脑,还是一个经典的例子,比较大小:

 #define CMP(x, y) (x > y ? x : y)
 ...
 int x = 100, y = 200;
 int result = CMP(x, y++);
 printf("x = %d, y = %d, result = %d\n", x, y, result);
</div>

执行这部分代码,会输出什么呢? 答案是,不知道!至少result的值我们无法确定,我们将代码展开得到

 int result = (x > y++ ? x : y++);
</div>

看起来似乎就是y递增两次,最后result肯定是200。真是如此?C语言标准对于一个确定的程序语句中,一个对象只能被修改一次,超过一次那么结果是未定的,由编译器决定,除了三目操作符?:外,还有&&, ||或是,之中,或者函数参数调用,switch控制表达式,for里的控制语句 由此可看出,宏的使用也是有风险的,所以虽然宏强大,但是依旧不能滥用。

对于宏而言,前面说过,它只是进行简单的展开,这有时候也会带来一些问题:

 #define MULTI(x, y) (x * y)
 ...
 int x = 100, y = 200;
 int result = MULTI(x+y, y);
</div>

看出来问题了吧?展开之后会变成: int result = x+y * y; 完全违背了当初我们设计时的想法,一个比较好的修改方法是对每个参数加上括号: #define MULTI(x, y) ((x) * (y))如此,展开以后:

 int result = ((x+y) * (y));
</div>

这样能在很大程度上解决一部分问题。

如果对自己的宏十分自信,可以嵌套宏,即一个表达式中使用宏作为宏的参数,但是宏只展开这一级的宏,对于多级宏另有办法展开

 int result = MULTI(MULTI(x, y), y);
</div>

展开成:

int result = ((((x) * (y))) * (y));
</div>

对宏的应用

由于我们并不明白,在某些情况下宏是否被定义了,(NULL宏是一个例外,它可以被重复定义),所以我们可以使用一些预处理保护机制来防止错误发生

  •   #ifndef MY_MACRO
  •   #define MY_MACRO 10000
  •   #endif

如果定义了MY_MACRO那就不执行下面的语句,如果没定义那就执行。

在宏的使用中有两个有用的操作符,姑且叫它操作符#, ##

对于# 我们可以认为#操作符的作用是将宏参数转化为字符串。

  #define HCMP(x, y) printf(#x" is equal to" #y" ? %d\n", (x) == (y))
  ...
  int x = 100, y = 200;
  HCMP(x, y);
</div>

展开以后

 

  printf("x is equal to y ? %d\n", (100) == (200));
</div>

注:可以自行添加编译器选项,来查看宏展开之后的代码,具体可以查询GCC的展开选项,这里不再详述。特别是在多层宏的嵌套使用情况下,但是我不太推荐,故不做多介绍。

能说的就是如何正确的处理一些嵌套使用,之所以不愿意多说也不愿意多用,是因为C预处理器就是一个奇葩
举一个典型的例子,__LINE__ 和 __FILE__的使用。

  /* 下方会说到的 # 预处理指示器,这里先用,实在看不懂,可以自己动手尝试 */
  #define WHERE_AM_I #__LINE__ " lines in " __FILE__
  ...
  fputs(WHERE_AM_I, stderr);
</div>

这样能工作吗?如果能我还讲干嘛。

  /* 常理上这应该能工作,但是编译器非说这错那错的 */
  /* 好在有前人踏过了坑,为我们留下了解决方案 */
  #define DEPAKEGE(X) #X
  #define PAKEGE(X) DEPAKEGE(X)
  #define WHERE_AM_I PAKEGE(__LINE__) " lines in " __FILE__
  ...
  fputs(WHERE_AM_I, stderr);
</div>

不要问我为什么,因为我也不知道C预处理器的真正工作机制是什么。

第一次看见这种解决方案是在 Windows 核心编程 中,这本书现在还能给我许多帮助,虽然已经渐渐淡出了书架

总结起来,即将宏参数放于#操作符之后便由预处理器自动转换为字符串常量,转义也由预处理器自动完成,而不需要我们自行添加转义符号。

对于##
它实现的是将本操作符两边的参数合并成为一个完整的标记,但需要注意的是,由于预处理器只负责展开,所以程序员必须自己保证这种标记的合法性,这里涉及到一些写法问题,都列出来

   #define MERGE(x, y) have_define_ ## (x + y)
   #define MERGE(x, y) have_define_##(x + y)
   ...
   result = MERGE(1, 3);
</div>

这里首先说明,上述写

分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • C语言中条件编译详解
  • C语言中强制地址跳转详解
  • 详解C 语言项目中.h文件和.c文件的关系
  • C语言中 值传递和指针传递实例详解
  • 详解C语言中的函数、数组与指针
  • C语言 坐标移动详解及实例代码
  • 详解C语言中的字符串拼接(堆与栈)
  • C语言 经典题目螺旋矩阵 实例详解
  • C语言 文件操作解析详解及实例代码
  • C语言 冒泡排序算法详解及实例

相关文章

  • 2017-05-28如何在C语言的宏中使用类型关键字
  • 2017-05-28C++中的类模板详解及示例
  • 2017-05-28解析四则表达式的编译过程及生成汇编代码
  • 2017-05-28简单谈谈C++ 中指针与引用
  • 2017-05-28详解C语言中的错误报告errno与其相关应用方法
  • 2017-05-28类成员函数的重载、覆盖与隐藏之间的区别总结
  • 2017-05-28C++通过自定义函数求一元二次方程的根
  • 2017-05-28C++空类默认函数详细解析
  • 2017-05-28深入理解c++中char*与wchar_t*与string以及wstring之间的相互转换
  • 2017-05-28DSP中浮点转定点运算--举例及编程中的心得

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • 深入理解二叉树的非递归遍历
    • C++实现将一个字符串中的字符替换成另一个字符串的方法
    • VC6.0如何创建以及调用动态链接库实例详解
    • 位运算实现十进制转换为二进制
    • 解决C++中事件不响应的方法详解
    • 简单的汉诺塔问题解法代码
    • MFC中动态创建控件以及事件响应实现方法
    • 使用boost读取XML文件详细介绍
    • 将字符串str1复制为字符串str2的三种解决方法
    • C++中的异或运算符^的使用方法

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有