文档章节

C语言宏定义

andrew810810
 andrew810810
发布于 2016/12/12 15:53
字数 1570
阅读 23
收藏 0
点赞 0
评论 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
博文 127
码字总数 117648
作品 0
朝阳
C语言编程学习—宏定义的一些使用技巧

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

小辰带你看世界 ⋅ 03/24 ⋅ 0

从G_BEGIN_DECLS和 G_END_DECLS说起

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

ShualLiu ⋅ 2016/12/21 ⋅ 0

C语言嵌入式系统编程修炼之性能优化

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

jus3ve ⋅ 2017/12/31 ⋅ 0

c语言编译预处理和条件编译执行过程的理解

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

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

GNU C和ANSI C函数库的区别

glibc和libc都是Linux下的C函数库, libc是Linux下的ANSI C的函数库; glibc是Linux下的GUN C的函数库; ANSI C和GNU C有什么区别呢? ANSI C是基本的C语言函数库,包含了C语言最基本的库函数...

新年 ⋅ 2014/12/19 ⋅ 0

不可小看的技术——C语言编程中的“宏”

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

小辰带你看世界 ⋅ 03/25 ⋅ 0

C语言强大的宏定义

宏就是预定义的意思, 关键字 #define 如定义 #define PI 3.14 C语言的宏是在编译之前就替换了对应的宏定义 如: #define PI 3.14 int main(){ printf(PI); return 0; } // PI在编译之前就会...

MQ_7 ⋅ 2014/07/16 ⋅ 0

C语言嵌入系统编程修炼-性能优化

本文来自:智趣网-C/C++语言编程技术交流论坛http://www.bczh.net 使用宏定义 在C语言中,宏是产生内嵌代码的唯一方法。对于嵌入式系统而言,为了能达到性能要求,宏是一种很好的代替函数的方...

魁4 ⋅ 2012/03/12 ⋅ 0

十六、预处理指令2-条件编译

说明:这个C语言专题,是学习iOS开发的前奏。也为了让有面向对象语言开发经验的程序员,能够快速上手C语言。如果你还没有编程经验,或者对C语言、iOS开发不感兴趣,请忽略 上一篇已经介绍了预...

长平狐 ⋅ 2013/03/28 ⋅ 0

c++中define用法

#define 将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本 该命令有两种格式:一种是简单的宏定义,另一种是带参数的宏定义 #define <宏名>  <字符串> #de...

memristor ⋅ 2014/05/04 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

JAVA RMI

什么是JAVA RMI Java RMI (Remote Method Invocation) 远程方法调用,能够让客户端像使用本地调用一样调用服务端 Java 虚拟机中的对象方法。RMI 是面向对象语言领域对 RPC (Remote Proced...

saulc ⋅ 4分钟前 ⋅ 0

Linux系统工程狮养成记

如今的社会,随着时代的发展,出现了很多职业,像电子类,计算机类的专业,出现了各种各样的工程师,有算法工程师,java工程师,前端工程师,后台工程师,Linux工程师,运维工程师等等,不同...

linux-tao ⋅ 15分钟前 ⋅ 0

进入编辑模式 vim命令模式 vim实践

1.

oschina130111 ⋅ 15分钟前 ⋅ 0

mysql用户管理、常用sql语句、mysql数据库备份恢复

1. mysql用户管理 mysql默认有一个root超级管理员账户,实际工作环境中不可能每个人都用此root权限,防止误操作、误删除,可以给单独的用户进行授权。 Mysql创建用户以及授权: grant all on...

laoba ⋅ 15分钟前 ⋅ 0

类型后面三个点(String...)和数组(String[])的区别

类型后面三个点(String…),是从Java 5开始,Java语言对方法参数支持一种新写法,叫可变长度参数列表,其语法就是类型后跟…,表示此处接受的参数为0到多个Object类型的对象,或者是一个Obj...

流氓兔- ⋅ 21分钟前 ⋅ 0

JEPLUS表格组件之表格合并——JEPLUS软件快速开发平台

JEPLUS表格组件之表格合并 我们在列表配置时会遇见这样的一种情况,需要对个人的数据进行统一化,对一些数据进行归类,这样展示出来美观又直观,在这篇笔记中我来给大家介绍下如何配置出来专...

JEPLUS ⋅ 22分钟前 ⋅ 0

golang 并发中全局唯一操作

package main// go 携程共享 数据// 加锁解锁操作// 同步锁import ("sync""fmt")// 创建Once结构var once = sync.Once{}func computed(data *int, lock *sync.Mut...

304158 ⋅ 23分钟前 ⋅ 0

Mobx入门之二:asynchronous actions

这一节主要看mobx怎么实现asynchronous actions 1 要实现的demo功能 输入地名,查询天气,利用openweathermap api 2 思想 observable观察数据:location地点、temperature温度 observer响应式...

pengqinmm ⋅ 25分钟前 ⋅ 0

【2018.0620学习笔记】【linux高级知识 13.4-13.6】

13.4 mysql用户管理 13.5 常用sql语句 13.6 mysql数据库备份恢复

lgsxp ⋅ 47分钟前 ⋅ 0

Java强弱引用示例

package jdk;import java.lang.ref.PhantomReference;import java.lang.ref.ReferenceQueue;import java.lang.ref.SoftReference;import java.lang.ref.WeakReference;public ......

月下狼 ⋅ 52分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部