当前位置:  编程技术>c/c++/嵌入式

你必须知道的C语言预处理的问题详解

    来源: 互联网  发布时间:2014-10-13

    本文导语:    C语言预处理器执行宏替换、条件编译和文件包含。通常采用以“#”为行首的提示。下面是C语言预处理的应用场合:   1.三字母词(Trigraph Sequences)   C源程序的字符集被包含在7位的ASCII字符集中,但是它是ISO 646-1983 I...

  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=%dn", cat(a,b));        // 会被替换成 printf("ab=%dn", ab);  输出 ab=12


  ♦宏替换时的顺序  
代码如下:

#include
 #define f(a,b)  a##b
 #define g(a)   #a
 #define h(a)   g(a)

 int main()
 {
         printf("%sn", h(f(1,2)));
         printf("%sn", 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      // 引用标准库的头文件(编译器将从标准库目录开始搜索)
#include "filename"       // 引用非标准库的头文件(编译器将从用户的工作目录开始搜索)
#include 标识符            // 标识符是由#define 定义的或"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


    
 
 
 
本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • 用JAVA语言怎样知道一个目录下的文件数目?
  • 我是一个linux新手,不知道如何在linux进行C语言的编写和使用?
  • 我想在Linux下开发,不知道用什么工具、什么开发语言??
  • 请教高手:C语言怎么知道一个进程是否存在(Linux下)
  • linux下用c语言如何在不知道文件名的情况下读去一个目录里的所有文件?
  • 我想开发一个基于BS结构的办公自动化程序,不知道用哪种语言合适,请大虾指教!
  • 我想在linux下开发软件,但不知道使用什么语言好,和使用什么开发工具
  • 怎样才能知道UNIX 下C语言的ERRNO都代表什么意思?到哪里去查,别外怎样在程序中截获这个errno我好做相应的处理。
  • 如何知道在linux下面如何知道mysql服务是用哪个端口啊
  • 那位知道怎样能知道 linux C/C++文件它所依赖的文件
  • 哪位大哥知道?我怎样知道LINUX redhat 7.2下声音设备由哪个应用程序占用?
  • 我做了个简单的留言板,不知道如何知道留言者的ip地址?
  • 只知道一个命令, 不知道其进程,问如何停止它?
  • red hat linux 9.0安装问题,不知道有人知道吗?
  • 知道进程的ID,有什么函数知道这个进程还活不活?
  • 我插入USB设备,知道是1-1:1.0下面,如何知道ttyUSB?
  • UI小问题,可我就是不知道怎么实现,或许您知道……
  • 我在rh8下装了wine,可是我不知道装到哪里了,那位知道默认路径?
  • 请问如何在不知道对方IP,仅知道MAC地址的情况下和对方通信呢?
  • 已知一个进程的PID,但不知道这个进程是否停止,如何能知道系统中是否有相应的进程在运行。
  • 不知道这个问题是否有人知道!
  • 在不知道表结构的前提下,怎样把该表的一条记录打印出来?(即不知道各字段类型时,用哪个方法?)
  • SUN OS5.8怎么没有make ,我想装APACHE2.0不知道怎么下手,原来的APACHE1.3有不知道怎么卸载
  • 要在arm开发板上实现DHCP+方式拨号上网,谁能给点下手的思路,知道不知道都给点意见吧?
  • 可信计算你知道嘛?红旗的可信计算你知道嘛?
  • 我装了jbuilder5,知道sn,但没有注册,每次启动都要跳出个jbuilder licence,要你注册,烦得很,那位大虾知道注册码,千万告诉小弟我,谢了
  • 我现在只有debian kernel 2.6,我把disc1的iso格式释放到了我的硬盘上面,我不知道里面有什么软件,也不知道怎样安装这些软件,莫展一筹
  • 一JSP网站,统一指定一个errorPage页面,统一处理异常,在指定的errorPage页面中,我想知道具体是哪个页面出错的,即想知道出错页面的具


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3