c语言学习笔记(11)宏定义使用与分析
来源:互联网 发布:固态硬盘克隆软件 编辑:程序博客网 时间:2024/06/05 23:46
摘要:总结了宏常量的用法,宏表达式及其与函数的优势对比,宏常量和表达式作用域,最后使用内置宏给出了一种编写使用日志宏的方法。
一、宏常量
1.#define宏常量可以出现在代码的任何地方。
2.#define从本行开始,之后的代码都可以使用这个宏常量。
3.#define宏常量可以使用接续符来定义比较长的常量。
例如以下定义,编译器是不会报错的:
#definePATH3 d:\fedora\c\
myfile.c
等价于:
#definePATH3 d:\fedora\c\myfile.c
二、宏定义表达式
1.#define宏定义表达式有函数调用的假象,但是却不是函数。
2.#define可以比函数更加强大。
3.#define会比函数更加容易出错。
下面给出一个例子,可以解释以上一和二所做的总结,例子如下:
<span style="font-size:18px;">#include <stdio.h></span>
<span style="font-size:18px;">#define MAX 99#define MIN 1 #define SUM(a,b) ((a)+(b))//这里有没有括号在下面的使用有区别#define DIM(a) (sizeof(a)/sizeof(*a))#define MESSAGE_1 "This is aexample!\n"#define MESSAGE_2 This is a examp\le! //这里使用接续符,编译的时候不会报错,在预处理的时候也会被作为宏定义被去掉 /*fun1中宏定义了SUB,在main函数里面并没有调用fun1,但是我们任然可以使用fun1*//*说明#define的作用域是不受限制的,要限制范围,就使用#undef,如下面的的fun2所示*//*如果不注释掉#undef那一行,在主函数里面就会报错,说未定义CAL*/int fun1(void){ #defineSUB(c,d) (c)-(d)//这里没有括号,下面SUB*SUB结果就会出现错误 return0; } int fun2(void){ #defineCAL(m,n) (m)*(n)// #undefCAL return0; } int main(void){ printf("%d\n",SUM(MAX,MIN)); printf("%d\n",SUB(2,1)); printf("%d\n",SUB(2,1)*SUB(2,1)); printf("%d\n",SUM(1,2)*SUM(1,2)); printf("%d\n",CAL(3,4)); printf("mesage1: %s\n",MESSAGE_1); return0; }</span>
#gcc –E example.c –o example.i
得到的example.i结果如下:
<span style="font-size:18px;"># 2 "example.c" 2# 16 "example.c"int fun1(void){ return 0; } int fun2(void){ return 0;} int main(void){ printf("%d\n",((99)+(1))); printf("%d\n",(2)-(1)); printf("%d\n",(2)-(1)*(2)-(1)); printf("%d\n",((1)+(2))*((1)+(2))); printf("%d\n",(3)*(4)); printf("mesage 1: %s\n","Thisis a example!\n"); return 0;}</span>
可以看到宏定义已经被全部展开,注释部分也去掉了。
#gcc example.c –o example
运行结果如下:
三、宏定义表达式和函数的优势对比
1.宏表达式在预编译期间被处理,编译器并不知道定义的宏,因为到编译的时候已经被完全替换了。
2.宏表达式用“实参”完全替代形参,不进行任何的计算。
3.宏表达式没有任何的调用开销,因为他不是调用,在使用的时候已经被替换了。
4.宏表达式中不能出现递归定义。
关于上面的规则,下面的例子可以说明一部分,例子如下:
#include<stdio.h> #define CAL(a) a*CAL(a-1)#define DIM(a) (sizeof(a)/sizeof(*a)) int main(void){ int a[]={1,2,3,4,5}; int dim(int array[]) { return sizeof(array)/sizeof(*array); } // printf("%d\n",CAL(10));注释掉是因为会报错 printf("%d\n",dim(a));//这里打印出来是1,因为函数在传值的时候,进来的不是数组,而是指向数组的指针 printf("%d\n",DIM(a)); //宏表达式就可以出色的完成计算大小的任务 return 0;}
#gcc –E example2.c –o example2.i
#gcc–S example2.i –o example2.s
example2.s得到的结果如下:
.file "example2.c" .text .type dim.1674, @functiondim.1674: pushl %ebp movl %esp, %ebp movl $1, %eax popl %ebp ret .size dim.1674, .-dim.1674 .section .rodata.LC0: .string "%d\n" .text.globl main .type main, @functionmain: pushl %ebp movl %esp, %ebp andl $-16, %esp subl $48, %esp movl $1, 28(%esp) movl $2, 32(%esp) movl $3, 36(%esp) movl $4, 40(%esp) movl $5, 44(%esp) leal 28(%esp), %eax movl %eax, (%esp) call dim.1674 movl $.LC0, %edx movl %eax, 4(%esp) movl %edx, (%esp) call printf movl $.LC0, %eax movl $5, 4(%esp) movl %eax, (%esp) call printf movl $0, %eax leave ret .size main, .-main .ident "GCC: (GNU) 4.5.1 20100924 (Red Hat4.5.1-4)" .section .note.GNU-stack,"",@progbits
#gcc example2.c –o example
运行结果如下:
可以看到得到的结果是不一样的,但是宏定义表达式使我们需要的结果。
再说说,为什么宏定义表达式不能使用递归调用,函数如下:
<span style="font-size:18px;">#include <stdio.h> #define CAL(a) a*CAL(a-1) int main(void){ printf("%d\n",CAL(10));注释掉是因为会报错,原因可以看example2.i文件 return 0;}</span>
#gcc –E example3.c –o example3.i
#gcc–S example3.i –o example3.s
可以看到展开以后是这样的:
#2 "example2.c" 2
int main(void)
{
printf("%d\n",10*CAL(10 -1));
return 0;
}
这里我们并没有定义CAL,是因为递归展开之后是完全不做计算的展开。
在看看.s文件,
.file "example2.c" .section .rodata.LC0: .string "%d\n" .text.globl main .type main, @functionmain: pushl %ebp movl %esp, %ebp andl $-16, %esp subl $16, %esp movl $9, (%esp) call CAL movl %eax, %edx movl %edx, %eax sall $2, %eax addl %edx, %eax addl %eax, %eax movl %eax, %edx movl $.LC0, %eax movl %edx, 4(%esp) movl %eax, (%esp) call printf movl $0, %eax leave ret .size main, .-main .ident "GCC: (GNU) 4.5.1 20100924 (Red Hat4.5.1-4)" .section .note.GNU-stack,"",@progbits
红色字体部分是说要调用CAL,我们并没有定义一个CAL函数显然会出现未定义的错误。
四、定义日志宏
首先,内置的宏是可以被用来直接使用的,如下:
我们可以只用这些来定义日志宏,帮助我们调试程序,我认为原因在于,如果你使用函数,存在一个调用开销,这样打印出来的信息,例如行,会是你调用的子函数的信息,而宏是直接替换展开,这样就不存在调用,就是原汁原味的原来的信息,说白了宏定义的核心思想就是“合理的替换”。例子如下:
#include<stdio.h>#include <time.h> #define LOG(s) do{ \ time_tt; \ structtm* ti; \ time(&t); \ ti=localtime(&t); \ printf("%s[%s:%d] %s",asctime(ti),__FILE__,__LINE__,s); \ }while(0) int fun(void){ LOG("enterthe fun...\n"); printf("wedo nothing!\n"); LOG("exitthe fun...!\n"); return0;} int main(){ LOG("enterthe main...\n"); fun(); LOG("exitthe main...\n"); return0; }
编译运行后的结果如下:
这篇帖子就总结到这里吧,如有不正确的地方还请指出,大家共同进步!- c语言学习笔记(11)宏定义使用与分析
- 【C语言学习】16__宏定义与使用
- 【C语言复习(八)】宏定义的使用与分析
- 宏定义-C语言基础学习笔记
- c语言学习笔记之宏定义
- 学习笔记---C语言中全局变量,变量,常量的定义与使用
- C语言的宏定义与使用
- c语言学习笔记(12)条件编译使用分析
- 专题3-2.宏定义与使用分析( 国嵌C语言视频)
- C语言数组与指针定义分析
- C语言学习笔记(八) -宏定义 Macro Definition
- C语言学习笔记3-进制与内存分析
- C语言再学习 -- 声明与定义
- C语言中的全局变量定义与使用
- C语言宏定义讲解(C和指针 笔记)
- C语言学习笔记(六)宏定义,typedef,static和extern
- (五)JNI学习之C语言基础,typedef与宏定义
- C语言学习笔记:13_变量和函数的声明与定义
- HDU 1719 找规律
- java篇 【15】抽象类与接口的应用 《==重要
- cf#307-C - GukiZ hates Boxes-二分+贪心
- virtual
- dijkstra
- c语言学习笔记(11)宏定义使用与分析
- mysql闪退解决
- 浏览器问题-FireFox-Adobe Flash 插件已崩溃解决方法
- Excel导入CSV文件中文乱码
- How To Add The Remi, EPEL And RPMFusion Repositories On CentOS 7
- jQuery的deferred对象详解
- poj 2404 Jogging Trails 状压dp
- UE4的AI c++代码初始化
- UE4的MaterialInstance作用