文档章节

C语言宏定义

andrew810810
 andrew810810
发布于 2016/12/12 15:53
字数 1570
阅读 24
收藏 0

参见:

https://gcc.gnu.org/onlinedocs/cpp/index.html

在宏定义中,#和##

#表示将其后的内容转换为字符串,##表示将它前后两个TOKEN连接为一个

3.4 Stringification

Sometimes you may want to convert a macro argument into a string constant. Parameters are not replaced inside string constants, but you can use the ‘#’ preprocessing operator instead. When a macro parameter is used with a leading ‘#’, the preprocessor replaces it with the literal text of the actual argument, converted to a string constant. Unlike normal parameter replacement, the argument is not macro-expanded first. This is called stringification.

There is no way to combine an argument with surrounding text and stringify it all together. Instead, you can write a series of adjacent string constants and stringified arguments. The preprocessor will replace the stringified arguments with string constants. The C compiler will then combine all the adjacent string constants into one long string.

Here is an example of a macro definition that uses stringification:

     #define WARN_IF(EXP) \
     do { if (EXP) \
             fprintf (stderr, "Warning: " #EXP "\n"); } \
     while (0)
     WARN_IF (x == 0);
          ==> do { if (x == 0)
                fprintf (stderr, "Warning: " "x == 0" "\n"); } while (0);
The argument for EXP is substituted once, as-is, into the if statement, and once, stringified, into the argument to fprintf. If x were a macro, it would be expanded in the if statement, but not in the string.

The do and while (0) are a kludge to make it possible to write WARN_IF (arg);, which the resemblance of WARN_IF to a function would make C programmers want to do; see Swallowing the Semicolon.

Stringification in C involves more than putting double-quote characters around the fragment. The preprocessor backslash-escapes the quotes surrounding embedded string constants, and all backslashes within string and character constants, in order to get a valid C string constant with the proper contents. Thus, stringifying p = "foo\n"; results in "p = \"foo\\n\";". However, backslashes that are not inside string or character constants are not duplicated: ‘\n’ by itself stringifies to "\n".

All leading and trailing whitespace in text being stringified is ignored. Any sequence of whitespace in the middle of the text is converted to a single space in the stringified result. Comments are replaced by whitespace long before stringification happens, so they never appear in stringified text.

There is no way to convert a macro argument into a character constant.

If you want to stringify the result of expansion of a macro argument, you have to use two levels of macros.

     #define xstr(s) str(s)
     #define str(s) #s
     #define foo 4
     str (foo)
          ==> "foo"
     xstr (foo)
          ==> xstr (4)
          ==> str (4)
          ==> "4"
s is stringified when it is used in str, so it is 

 

3.5 Concatenation

It is often useful to merge two tokens into one while expanding macros. This is called token pasting or token concatenation. The ‘##’ preprocessing operator performs token pasting. When a macro is expanded, the two tokens on either side of each ‘##’ operator are combined into a single token, which then replaces the ‘##’ and the two original tokens in the macro expansion. Usually both will be identifiers, or one will be an identifier and the other a preprocessing number. When pasted, they make a longer identifier. This isn't the only valid case. It is also possible to concatenate two numbers (or a number and a name, such as 1.5 and e3) into a number. Also, multi-character operators such as += can be formed by token pasting.

However, two tokens that don't together form a valid token cannot be pasted together. For example, you cannot concatenate x with + in either order. If you try, the preprocessor issues a warning and emits the two tokens. Whether it puts white space between the tokens is undefined. It is common to find unnecessary uses of ‘##’ in complex macros. If you get this warning, it is likely that you can simply remove the ‘##’.

Both the tokens combined by ‘##’ could come from the macro body, but you could just as well write them as one token in the first place. Token pasting is most useful when one or both of the tokens comes from a macro argument. If either of the tokens next to an ‘##’ is a parameter name, it is replaced by its actual argument before ‘##’ executes. As with stringification, the actual argument is not macro-expanded first. If the argument is empty, that ‘##’ has no effect.

Keep in mind that the C preprocessor converts comments to whitespace before macros are even considered. Therefore, you cannot create a comment by concatenating ‘/’ and ‘*’. You can put as much whitespace between ‘##’ and its operands as you like, including comments, and you can put comments in arguments that will be concatenated. However, it is an error if ‘##’ appears at either end of a macro body.

Consider a C program that interprets named commands. There probably needs to be a table of commands, perhaps an array of structures declared as follows:

     struct command
     {
       char *name;
       void (*function) (void);
     };
     
     struct command commands[] =
     {
       { "quit", quit_command },
       { "help", help_command },
       ...
     };
It would be cleaner not to have to give each command name twice, once in the string constant and once in the function name. A macro which takes the name of a command as an argument can make this unnecessary. The string constant can be created with stringification, and the function name by concatenating the argument with ‘_command’. Here is how it is done:

     #define COMMAND(NAME)  { #NAME, NAME ## _command }
     
     struct command commands[] =
     {
       COMMAND (quit),
       COMMAND (help),
       ...
     };

 

注释:

##表示将它前后两个TOKEN连接为一个,以下三种结果为:

1. #define YNAME(n) y_##n_Y, 则YNAME(1),表示变量y_n_Y

2. #define YNAME(n) y_n##_Y, 则YNAME(1),表示变量y_n_Y

3. #define YNAME(n) y_##n##_Y, 则YNAME(1),表示变量y_1_Y

 

宏定义何时替换

例如#define VAL 352*288,当c语言函数调用VAL时,是使用352*288,还是101376哪?

我们知道GCC编译源代码生成最终可执行的二进制程序,GCC后台隐含执行了四个阶段步骤。
GCC编译C源码有四个步骤:预处理-----> 编译 ----> 汇编 ----> 链接
现在我们就用GCC的命令选项来逐个剖析GCC过程。

#include <stdio.h>

#define VAL 352*288

int main(void)
{

  printf("val:%d\n", VAL);
  return 1;
}


1)预处理(Pre-processing)
在该阶段,编译器将C源代码中的包含的头文件如stdio.h编译进来,用户可以使用gcc的选项”-E”进行查看。
用法:#gcc -E test_macro.c -o test_macro.i
作用:将test_macro.c预处理输出test_macro.i文件。

vim test_macro.i


int main(void)
{
    printf("val:%d\n", 352*288);

    return 0;
}

注释:VAL被替换为352*288

2)编译阶段(Compiling)
第二步进行的是编译阶段,在这个阶段中,Gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,Gcc把代码翻译成汇编语言。用户可以使用”-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。
选项 -S
用法:[root]# gcc -S test_macro.i -o test_macro.s
作用:将预处理输出文件 test_macro .i汇编成 test_macro .s文件。

vim test_macro .s

        .file   "test_macro.c"
        .section        .rodata
.LC0:
        .string "val:%d\n"
        .text
.globl main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16 
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    $.LC0, %eax
        movl    $101376, %esi
        movq    %rax, %rdi
        movl    $0, %eax
        call    printf
        movl    $0, %eax
        leave
        .cfi_def_cfa 7, 8
        ret 
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-16)"
        .section        .note.GNU-stack,"",@progbits

注释:VAL的值为101376,即为352*288的结果

3)汇编阶段(Assembling)
汇编阶段是把编译阶段生成的”.s”文件转成二进制目标代码.
选项 -c
用法:[root]# gcc -c test_macro .s -o test_macro .o
作用:将汇编输出文件 test_macro .s编译输出 test_macro .o文件。

 

4)链接阶段(Link)
在成功编译之后,就进入了链接阶段。
无选项链接
用法:[root]# gcc test_macro.o -o test_macro.exe
作用:将编译输出文件 test_macro .o链接成最终可执行文件 test_macro .exe。

 

注释:

宏定义的表达式在编译阶段进行计算。

© 著作权归作者所有

共有 人打赏支持
andrew810810
粉丝 3
博文 141
码字总数 117648
作品 0
朝阳
C语言编程学习—宏定义的一些使用技巧

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界
03/24
0
0
C语言嵌入式系统编程修炼之性能优化

这是我13年前创作和发表在互联网上的文章,这么多年过去了,这篇文章仍然在到处传播。现在贴回Linuxer公众号。 全文目录: C语言嵌入式系统编程修炼之道——背景篇 C语言嵌入式系统编程修炼之...

jus3ve
2017/12/31
0
0
从G_BEGIN_DECLS和 G_END_DECLS说起

今天开始正式研究gedit的源码,第一次看c/c++的源码,有点吃力啊,坚持不懈,慢慢来。 在gedit.c文件中遇到了GBEGINDECLS和GENDDECLS,平常搞C语言的东西,我个人几乎没这样用过(怕是太菜了...

ShualLiu
2016/12/21
86
0
跟涛哥一起学嵌入式 第04集:一道面试题,测出你的C语言功底

大家好,我是涛哥,欢迎阅读《跟涛哥一起学嵌入式》第04集,今天聊聊面试题。 嵌入式C语言面试题中,大家经常会看到宏定义的考题。比如:定义一个宏,求两个数中的最大数。别小看这个考题,虽...

宅学部落
07/03
0
0
c语言编译预处理和条件编译执行过程的理解

  在C语言的程序中可包括各种以符号#开头的编译指令,这些指令称为预处理命令。预处理命令属于C语言编译器,而不是C语言的组成部分。通过预处理命令可扩展C语言程序设计的环境。 一.预处理...

文艺小青年
2017/10/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

oracle 安装 PL/SQL Developer连接64位Oracle免安装配置

PL/SQL Developer连接64位Oracle 在64位系统上安装64位的Oracle数据库,但是没有对应的64位PL/SQL Developer,此时就不能使用PL/SQL Developer来进行直接连接的,所以要想实现连接还得需要其...

PeakFang-BOK
9分钟前
1
0
裁员寒冬袭来,30岁还在CRUD的Java程序员,拿什么安身立命?

就在近日,智联招聘公布的数据更是侧面印证了很多公司“瘦身”的事实:“2018年第二季度,小微企业用人需求较第一季度平均下降26.6%”。 裁员大潮正滚滚向前,席卷各行各业! 你做好失业的准...

Java填坑之路
12分钟前
0
0
第一章:什么是SpringCloud

第一章:什么是SpringCloud 何为微服务 在了解 SpringCloud之前,我们先来大致了解下 微服务这个概念吧。 传统单体架构 单体架构在小微企业比较常见,典型代表就是一个应用、一个数据库、一个...

DemonsI
17分钟前
4
0
环境搭建之八-- node.js

1.node.js官网下载64位二进制压缩包 node-v8.12.0-linux-x64.tar.xz 2.解压文件 2.1 xz格式文件为 tar格式 xz -d node-v8.12.0-linux-x64.tar.xz 此时文件已经转变为 node-v8.12.0-linux-x64...

imbiao
21分钟前
1
0
JVM调优浅谈

1.数据类型 java虚拟机中,数据类型可以分为两类:基本类型和引用类型。 基本类型的变量保存原始值,即:它代表的值就是数值本身,而引用类型的变量保存引用值。 “引用值”代表了某个对象的...

xtof
25分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部