免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 6707 | 回复: 11

[编译]如何处理C的预处理? [复制链接]

论坛徽章:
0
发表于 2006-02-20 11:37 |显示全部楼层
C的预处理应该怎样和词法分析和语法分析协作呢?这里面有许多东西需要考虑.
似乎预处理应该介于词法和语法之间.但是这样一来函数宏的处理比较麻烦,因为它有语法.如果作为语法处理,显然违背了宏的初衷(替代并返回到输入中重新进行词法分析)。

例如

  1. #define max(x,y) ((x)>=(y)?(x):(y))
  2. x=max(max(a+b,c+d),e+f);
  3. 被预处理成
  4. x=((((a+b)>=(c+d)?(a+b):(c+d)))>=(e+f)?(((a+b)>=(c+d)?(a+b):(c+d))):(e+f));
复制代码

通过对bc3.1的cpp程序的试验,似乎只是寻找找到配对的()和[],
例如
max(int a () [],1)
cpp也能预处理

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
发表于 2006-02-20 11:39 |显示全部楼层
语法分析和词法分析 有专门的工具.LEX,YACC. 你可以找相关的资料看一下.

至于此问题.为何非要预处理 而不封装成函数?

论坛徽章:
0
发表于 2006-02-20 12:10 |显示全部楼层

回复 1楼 qtdszws 的帖子

>> 函数宏的处理比较麻烦,因为它有语法.如果作为语法处理
预处理就是简单的宏替换, #指令解析等, 处理完毕的结果才作为词法分析的输入文件, 完全不涉及词法、语法分析的操作啊。

论坛徽章:
0
发表于 2006-02-20 15:12 |显示全部楼层
你认为先进行预处理,然后把结果传给词法分析器?这个思路也很好。
但是我认为这样的结构也很好:语法分析器调用词法分析器,而词法分析器遇到一个'#'而又是新行的话就调用预处理器;而当词法分析解析出一个ID时,先传给预处理器进行预处理。

论坛徽章:
0
发表于 2006-02-20 15:32 |显示全部楼层
#define a "mmmm"
char *s="a";
这里的"a"肯定不会被扩展为""mmmm"",因为"a"是一个单词.
因此这样的结构是合理的。

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
发表于 2006-02-20 21:50 |显示全部楼层
原帖由 qtdszws 于 2006-2-20 15:12 发表
你认为先进行预处理,然后把结果传给词法分析器?这个思路也很好。
但是我认为这样的结构也很好:语法分析器调用词法分析器,而词法分析器遇到一个'#'而又是新行的话就调用预处理器;而当词法分析解析出一个ID时, ...

按照你这个思路,
预处理器得被调用多少次呀。

论坛徽章:
0
发表于 2006-02-20 22:58 |显示全部楼层
原帖由 qtdszws 于 2006-2-20 15:12 发表
你认为先进行预处理,然后把结果传给词法分析器?这个思路也很好。
但是我认为这样的结构也很好:语法分析器调用词法分析器,而词法分析器遇到一个'#'而又是新行的话就调用预处理器;而当词法分析解析出一个ID时, ...

这个思路明显很不好,而且实际中预处理器也不是这样工作的

论坛徽章:
0
发表于 2006-02-22 19:12 |显示全部楼层
根据我的经验:
C的宏处理完全是词法分析的范畴,gcc调用预处理器cpp将所有包含文件和源文件进行预处理之后生成一个.i的中间文件,再将这个中间文件传递给cc1进行编译.
可以使用-v选项观察gcc编译过程.
另外,预处理并不仅仅处理宏展开.
关于宏的展开可以参考gcc源代码:
下面的代码来自gcc-1.35的cccp.c

  1. /* Structure allocated for every #define.  For a simple replacement
  2.    such as
  3.            #define foo bar ,
  4.    nargs = -1, the `pattern' list is null, and the expansion is just
  5.    the replacement text.  Nargs = 0 means a functionlike macro with no args,
  6.    e.g.,
  7.        #define getchar() getc (stdin) .
  8.    When there are args, the expansion is the replacement text with the
  9.    args squashed out, and the reflist is a list describing how to
  10.    build the output from the input: e.g., "3 chars, then the 1st arg,
  11.    then 9 chars, then the 3rd arg, then 0 chars, then the 2nd arg".
  12.    The chars here come from the expansion.  Whatever is left of the
  13.    expansion after the last arg-occurrence is copied after that arg.
  14.    Note that the reflist can be arbitrarily long---
  15.    its length depends on the number of times the arguments appear in
  16.    the replacement text, not how many args there are.  Example:
  17.    #define f(x) x+x+x+x+x+x+x would have replacement text "++++++" and
  18.    pattern list
  19.      { (0, 1), (1, 1), (1, 1), ..., (1, 1), NULL }
  20.    where (x, y) means (nchars, argno). */
  21. /* 对每个#define 分配的结构. 对于简单的替换例如:
  22.         #define foo bar ,
  23.     nargs = -1 , `样式' 列表(见结构体定义中的pattern list) 为空,
  24.     expansion ( 见结构体中的expansion 定义) 仅仅是替换文本.
  25.     Nargs=0 意味着一个不带参数的函数类似的宏定义, 例如:
  26.             #define getchar() getc(stdin).
  27.     当存在参数的时候, expansion 是不带任何参数的替换文本,
  28.     并且reflist 是一个描述如何从输入构建输出的列表:
  29.     例如: " 三个字符, 然后第一个参数, 之后久个字符,
  30.     之后第三个参数, 之后零个字符, 之后第二个参数."
  31.     这里的字符来自expansion.
  32.     Whatever is left of the
  33.    expansion after the last arg-occurrence is copied after that arg.
  34.    注意: reflist 可以是任意长度:
  35.    它的长度依赖于参数在替换文本中出现的次数,
  36.    而不是到底有多少参数, 例如:
  37.    #define f(x) x+x+x+x+x+x+x 将会有替换文本"++++++" 和样式列表:
  38.    { (0, 1), (1, 1), (1, 1), ..., (1, 1), NULL }
  39.    这里的(x,y) 的意义是(nchars,argno)  即(字符数, 参数序号)
  40. */
  41. typedef struct definition DEFINITION;
  42. struct definition {
  43.   int nargs;
  44.   int length;                        /* length of expansion string */ /* expansion string 的长度*/
  45.   U_CHAR *expansion; /* expansion string*/
  46.   struct reflist {
  47.     struct reflist *next;
  48.     char stringify;                /* nonzero if this arg was preceded by a  # operator. */
  49.                                         /* 非零, 如果这个参数之前有# 操作符*/                                  
  50.     char raw_before;                /* Nonzero if a ## operator before arg. */ /* 非零如果一个## 在这个操作符前*/
  51.     char raw_after;                /* Nonzero if a ## operator after arg. */ /*  非零如果一个## 在这个操作符后*/
  52.     int nchars;                        /* Number of literal chars to copy before this arg occurrence.  */
  53.                                         /* 在这个参数出现之前需要复制的字符数*/
  54.     int argno;                        /* Number of arg to substitute (origin-0) */
  55.                                         /* 需要替换的参数的序号(初始为0) */
  56.   } *pattern; /* pattern list */ /* 样式列表 */
复制代码

[ 本帖最后由 crspo 于 2006-2-22 19:15 编辑 ]

论坛徽章:
0
发表于 2006-02-23 10:12 |显示全部楼层
原帖由 crspo 于 2006-2-22 19:12 发表
根据我的经验:
C的宏处理完全是词法分析的范畴,gcc调用预处理器cpp将所有包含文件和源文件进行预处理之后生成一个.i的中间文件,再将这个中间文件传递给cc1进行编译.
可以使用-v选项观察gcc编译过程.
另外,预处 ...


可是我用gcc -v根本看不到对cpp的调用, 直接就cc1了, 为什么呢? 请教~

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
发表于 2006-02-23 12:24 |显示全部楼层
你没看他的是gcc-1.35,
你的是gcc?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP