关于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


相关文章
|
7月前
|
存储 人工智能 算法
【C语言】自学终极笔记
【C语言】自学终极笔记
110 0
|
编译器 C语言 C++
25 C语言 - 预处理器
25 C语言 - 预处理器
44 0
|
27天前
|
程序员 编译器 C语言
C语言中的预处理器指令,涵盖其基本概念、常见指令(如`#define`、`#include`、条件编译指令等)、使用技巧及注意事项
本文深入解析C语言中的预处理器指令,涵盖其基本概念、常见指令(如`#define`、`#include`、条件编译指令等)、使用技巧及注意事项,并通过实际案例分析,展示预处理器指令在代码编写与处理中的重要性和灵活性。
61 2
|
4月前
|
测试技术 C语言 C++
【C语言刷题训练——6】鹏哥C语言刷题训练营笔记,含代码讲解改进
【C语言刷题训练——6】鹏哥C语言刷题训练营笔记,含代码讲解改进
|
4月前
|
存储 C语言
【C语言】鹏哥C语言刷题训练营——第5节内容笔记(含代码全面分析和改进,讲解)
【C语言】鹏哥C语言刷题训练营——第5节内容笔记(含代码全面分析和改进,讲解)
|
6月前
|
C语言
|
6月前
|
C语言
|
7月前
|
程序员 编译器 C语言
【C 言专栏】C 语言中的预处理器指令
【5月更文挑战第6天】C 语言的预处理器指令在编译前起作用,提供代码灵活性。常见指令包括:`#define`(定义常量和宏)、`#include`(包含文件)、`#if` 等条件编译指令,以及`#pragma`(编译器特定指示)。合理使用能提升代码可读性和可维护性,但过度使用可能导致复杂性增加。注意其文本替换性质及顺序处理,避免头文件重复包含。预处理器在实际应用中用于实现不同功能和配置,是成为优秀 C 语言程序员的关键技能之一。
102 0
【C 言专栏】C 语言中的预处理器指令
|
7月前
|
C语言
C语言(指针详解)重点笔记:指针易错点,都是精华
C语言(指针详解)重点笔记:指针易错点,都是精华
86 0
|
7月前
|
NoSQL Redis C语言