在c语言中我们写好代码之后在vc++或者vs中运行就是预处理、编译、链接、然后就是生成.c文件。预处理(宏替换、去注释、头文件展开、条件编译),编译(翻译成汇编语言),汇编(翻译成二进制),最后链接(其实就是链接函数,比如pritnf函数,我们自己不用写,而是直接调用库函数这时候就是链接库函数使我们的printf能正常使用或者我们自己定义的函数自己就要本地自己调用自己的函数等等)。
今天我就写一些关于宏的一些知识有些地方不好希望大家能提出来我会一一改正。
首先我先在linux下写一段代码直接解释预处理中的宏替换,去注释,头文件展开,条件编译自身知识储备有限暂时还不会请大家原谅。
这是源代码,下面就是经过预处理之后的代码
经过在linux下预处理后生成.I文件后我们可以看到源代码中的头文件中的#include<stdio.i>展开了/usr/include/stdio.h , 而且在main函数中的M被替换成了10,还有源代码中的注释被删除了,这就是预处理
中的宏替换、去注释和头文件展开。
在宏中还有一些预定义的符号如:
__FILE__ //进行编译的源文件
__LINE__ // 文件当前的行号
__DATE__ // 文件被编译的日期
__TIME__ // 文件被编译的时间
在此我写一个非常简单的宏:
从这个中断可以看出__FILE__这个文件是存在d盘的test文件下,LINE他是在程序的第8行,
__DATE__时期是在2016年11月3号刚好是我编写程序的日期,还有他是在20点35分10秒的时候完成的当预处理器搜索#define 定义的符号时,字符串常量的内容并不进行检查。你如果想把宏参数插入字符串常量
中,可以使两种技巧
1.邻近字符串连接特性。这个特性可以使我们很容易把一个字符串分成几段。
经过预编译处理后:
可以看出来这时的#define的内容都已经被替换printf中("the value is :""%d""\n", 10);在这个printf中我认为此时宏可以起到连接作用,下来让我们看看运行的结果:
而它的输出就是 the value is :10。
2.使用预处理器将一个个宏参数转换为一个字符串(#math这种结构被处理器翻译成'math')。
预编译处理后:
这是我们可以看见#VALUE经过预编译后就直接成了M,所以在预编译期间#带宏定义的名字就此时就是取这个字符的表面意思其实就是这个字符。结果和我们预期的一样
##结构则执行一种不同的任务。它把位于它两边的符号连接成一个符号。作为用途之 ,它允许宏定义从分离的文本创
段标识符。
在这个代码中我们可以看到CAT这个宏它的作用就是用双#号实现连接两个字符经过预处理后:
我们可以看到此时的printf中aa和bb成为了一个字符aabb。
宏和函数的:
宏可以非常频繁地用于执行简单的计算,比如在两个表达式中寻找其中较大(或较小)的一个:
#define MAX( a, b ) ( (a) > (b) ? (a) : (b) )
为什么不用函数来完成这个任务呢?有两个原因:
1. 于调用和从函数返回的代码很可能比实际执行这个小型计算工作的代价更大,所以使用宏比使用函数在程序的规模和速度更胜胜筹。
2.但是更重要的是,函数的参数必须申明为一种特定的类型,所以它只能在类型合适的表达式上使用。
反之,上面的宏可以用于整形、长整形、单浮点型、双浮点型以及其他任何可以使 >操作符比较值大小的类型。
换句话说: 宏是类型无关的。
和函数相比,使用宏的不利之处在于每次使用宏时,一份宏定义代码的拷贝将插入到程序中,除非宏非常短,否则使用宏可能会大幅度增加程序的长度。
还有 些任务根本无法使用函数实现。让我们仔细观察下面的代码:
#define MALLOC( n, type) \
( ( type *) malloc ( (n) * sizeof( type ) ) )
int *pi = MALLOC( 25, int );
这个宏的第二个参数是一种类型,它无法作为函数参数进行传递。
这个宏替换完成之后:
int *pi = ( ( int *) malloc ( (25) * sizeof( int ) ) );
以上就是我对宏的一些了解有些地方可能理解的有点狭义或者有些地方有一些错误希望各位一一指出。