关于C语言中的预处理器的简单笔记

简介:

    在将源代码提交给编译器之前,C语言预处理器将对源代码做出一定修正。预处理器命令有很多如最常用的#include#define命令.

预处理命令都是以#开头,一般放在代码的最左侧,通常定义的宏全部都是用大写。

条件编译

下面这一系列的指令都可以用来决定代码的哪个部分应该被编译,这些指令包括:#if,#elif,#else,#ifdef,#else,#ifdef以及#ifndef。以#if开头的语句块必须以#endif结尾。

可以使用条件编译来注释代码,例如


 
 
  1. #if 0 
  2.  
  3. /* comment ... 
  4.  
  5. */ 
  6.  
  7.   
  8.  
  9. // code 
  10.  
  11.   
  12.  
  13. /* comment */ 
  14.  
  15. #endif 

#if 表达式,如果表达式为假,那么这部分代码就不会被编译,为真才参与编译。虽然你会说使用块注释也是可行的,但是可惜的是块注视不支持嵌套,在某些环境下选择#if 0确实是一个更好的选择。测试代码:

 

 
  1. #include<stdio.h> 
  2.  
  3. int main() 
  4.  
  5.  
  6. #if 0 
  7.  
  8.       int a=10; 
  9.  
  10. #endif 
  11.  
  12.   
  13.  
  14. #if 1 
  15.  
  16.       int a=11; 
  17.  
  18. #endif 
  19.  
  20.       printf("a=%d\n",a); 
  21.  

可以好似用#ifndef来避免头文件被重复编译:例如下面这段代码:

 
 
 
 
  1. #ifndef _FILE_NAME_H_ 
  2. #define _FILE_NAME_H_ 
  3.   
  4. /* code */ 
  5.   
  6. #endif // #ifndef _FILE_NAME_H_ 

同样的技术可以用来定义一些常量,如:

 
 
 
 
  1. #ifndef NULL 
  2. #define NULL (void *)0 
  3. #endif // #ifndef NULL 

 

宏定义

使用简单的宏就不多说了,对于比较复杂的宏,常见的是使用类似于函数定义的宏,使用这种宏要特别注意宏的副作用。例如下面这段代码:

#define MULT(x, y) x * y

int z = MULT(3 + 2, 4 + 2);

一般情况,要对所有的实参添加括号,同时对宏体整体添加括号。

下面考察一种更加复杂的情况,交换两个数的宏定义可以写为:

 
 
 
 
  1. #define SWAP(a, b)  a ^= b; b ^= a; a ^= b;  
  2. int x = 10; 
  3. int y = 5; 
  4.   
  5. // works OK 
  6. SWAP(x, y); 
  7.   
  8. // What happens now? 
  9. if(x < 0) 
  10.     SWAP(x, y); 

 

对于第if语句会出现逻辑错误,因为这个if语句只执行第一个异或操作。嗯,或许你已经想到了,加上大括号,像下面这样:

 
 
 
 
  1. #define SWAP(a, b)  {a ^= b; b ^= a; a ^= b;}  

然而,还是对于第二个if语句还是不正确,因为预处理器替换之后就出现了‘};’语法错误。这里有一个技巧,正确的写法如下:

 
 
 
 
  1. #define SWAP(a, b)  do { a ^= b; b ^= a; a ^= b; } while ( 0 ) 

这下就可以了,这里没有给实参加括号的原因是因为我们只是针对变量的交换,不考虑那些表达式交换。

 

使用\分割宏体

 

如果宏体比较复杂的话,我们就需要使用\来分割宏体,示例代码如下所示:

 
 
 
 
  1. #define SWAP(a, b)  {                   \ 
  2.                         a ^= b;         \ 
  3.                         b ^= a;         \  
  4.                         a ^= b;         \ 
  5.                     }  

 

表达式合并

有时候在宏体中我们需要连接两个参数的名字这时候可以使用##

 
 
 
 
  1. struct command 
  2. char *name; 
  3. void (*function) (); 
  4. }; 
  5. struct command commands[] = 
  6. "quit", quit_command}, 
  7. "help", help_command}, 
  8. {"run",run_help}, 
  9. {"open",open_help} 
  10. }; 

struct中显然每个名字都重复了一遍,通过使用##可以减少重复,方便维护:

 
 
 
 
  1. #define COMMAND(NAME) { #NAME, NAME ## _command } 
  2. struct command commands[] = 
  3. COMMAND (quit), 
  4. COMMAND (help), 
  5. COMMAND (run), 
  6. COMMAND (open) 
  7. }; 

符号字符串化

有时候我们经常需要将传入到宏中的符号或者叫做参数转换为文本形式,这样对于打印这些符号的时候比较方便,在写一些调试模块的时候,经常会遇到这样的需求,实现这个过程的语法很简单,只需要在符号前面添加一个‘#’就可以了。例如下面这段代码:

 
 
 
 
  1. #define PRINT_TOKEN(token) printf(#token " is %d", token) 
  2. PRINT_TOKEN(foo); 

PRINT_TOKEN(foo);将被扩展 printf("<foo>" " is %d" <foo>)

新版本的C语言支持将多个使用双引号括起来的字符串练成一个整体的字符串。

下面为测试代码:

 

 
  1. #include<stdio.h> 
  2.  
  3. #define PRINT_TOKEN(token) printf(#token " is %d\n",token); 
  4.  
  5. int main() 
  6.  
  7.  
  8.       int x=10,y=20; 
  9.  
  10.       PRINT_TOKEN(x+y); 
  11.  

 


本文转自hipercomer 51CTO博客,原文链接:http://blog.51cto.com/hipercomer/790155


相关文章
|
19天前
|
存储 人工智能 算法
【C语言】自学终极笔记
【C语言】自学终极笔记
84 0
|
7月前
|
编译器 C语言 C++
25 C语言 - 预处理器
25 C语言 - 预处理器
18 0
|
19天前
|
C语言
C语言(指针详解)重点笔记:指针易错点,都是精华
C语言(指针详解)重点笔记:指针易错点,都是精华
22 0
|
19天前
|
程序员 编译器 C语言
【C 言专栏】C 语言中的预处理器指令
【5月更文挑战第6天】C 语言的预处理器指令在编译前起作用,提供代码灵活性。常见指令包括:`#define`(定义常量和宏)、`#include`(包含文件)、`#if` 等条件编译指令,以及`#pragma`(编译器特定指示)。合理使用能提升代码可读性和可维护性,但过度使用可能导致复杂性增加。注意其文本替换性质及顺序处理,避免头文件重复包含。预处理器在实际应用中用于实现不同功能和配置,是成为优秀 C 语言程序员的关键技能之一。
【C 言专栏】C 语言中的预处理器指令
|
19天前
|
NoSQL Redis C语言
|
11月前
|
存储 编译器 C语言
C语言笔记第03章:数组(四)
C语言笔记第03章:数组
78 0
|
11月前
|
编译器 C语言 索引
C语言笔记第03章:数组(一)
C语言笔记第03章:数组
86 0
|
19天前
|
前端开发 C语言
前端知识笔记(四十)———用C语言实现计算器功能
前端知识笔记(四十)———用C语言实现计算器功能
47 0
|
7月前
|
C语言
C语言初阶 牛客网刷题笔记(将持续更新..)
C语言初阶 牛客网刷题笔记(将持续更新..)
C语言初阶 牛客网刷题笔记(将持续更新..)
|
7月前
|
C语言
C语言编程陷阱:预处理器 陷阱
预处理器 不能忽视宏定义中的空格: #include &lt;stdio.h&gt;
21 1