宏定义与宏使用分析
来源:互联网 发布:元朝摔头胎 知乎 编辑:程序博客网 时间:2024/05/16 15:04
编译预处理
专题三:编译预处理。包括以下章节:
- 编译过程简介
- 宏定义与宏使用分析
- 条件编译使用分析
- #error和#line
- #pragma预处理分析
- #和##运算符使用解析
宏定义常量
- #define定义的宏常量可以出现在代码的任何地方
- 从定义宏开始,之后的代码都可以使用这个宏常量
以下定义的宏定义是否正确?
2-1.c
#define ERROR -1#define PI 3.1415926#define PATH_1 "D:\Dep\C\ts.txt"#define PATH_2 D:\Dep\C\ts.txt //预编译器直接替换//以下定义:后边的\是接续符,相当于D:\DepCts.txt#define PATH_2 D:\Dep\C\ ts.txt int main(){ return 0;}
执行命令:gcc -E 2-1.c -o 2-1.i
2-1.i
# 1 "2-1.c"# 1 "<built-in>"# 1 "<命令行>"# 1 "2-1.c"# 9 "2-1.c"//直接删除了宏定义,说明编译器认为定义的宏都是正确的int main(){ return 0;}
宏定义表达式
- #define表达式给人一种函数调用的假象,其实她不是
- #define表达式可以比函数更强大
- #define表达式比函数更容易出错
2-2.c
#define SUM(a,b) (a)+(b)#define MIN(a,b) ((a)<(b) ? (a) : (b))#define DIM(a) (sizeof(a)/sizeof(*a))int sum(int a, int b){ return a+b;//或者(a)+(b)或者((a)+(b)),结果不变}int min(int a, int b){ return ((a)<(b) ? (a) : (b));//传入参数时已经计算完毕,即i++=1,b=2,不带入函数体内计算}int dim(int a[]){ return (sizeof(a)/sizeof(*a));}int main(){ //我们要的结果是6,而结果却是5 //宏定义应该定义为((a)+(b)) printf("%d\n", SUM(1,2)*SUM(1,2)); int i = 1; int j = 5; //我们预想的结果是1(1<5,返回i++),结果却是2 //预编译器直接替换printf("%d\n", ((i++)<(j) ? (i++) : (j))); //i++执行了两次,所以i++=2 //使用宏定义时应该避免在宏参数中写表达式 printf("%d\n", MIN(i++, j)); int a[] = {1, 2, 3, 4, 5}; //打印结果为5,正确 printf("%d\n", DIM(a)); //调用函数,打印结果为1,错误 //函数参数退化为指针(int* a)。a为unsigned int类型。所以sizeof(a)/sizeof(*a)=1 //所以有时候,函数无法代替宏表达式 printf("%d\n", dim(a)); return 0;}
实例分析2-1:宏对于函数的优势
2-3.c
#include <stdio.h>#include <malloc.h>#define MALLOC(type, x) (type*)malloc(sizeof(type)*x)#define FOREVER() while(1)#define BEGIN {#define END }#define FOREACH(i, m) for(i=0; i<m; i++)int main(){ int array[] = {1, 2, 3, 4, 5}; int x = 0; int*p = MALLOC(int, 5); FOREACH(x, 5) BEGIN p[x] = array[x]; END FOREACH(x, 5) BEGIN printf("%d\n", p[x]); END FOREVER(); free(p); printf("Last printf...\n"); return 0;}
执行命令:gcc -E 2-3.c -o 2-3.i
2-3.i
# 1 "2-3.c"# 1 "<built-in>"# 1 "<命令行>"# 1 "2-3.c"# 1 "/usr/include/stdio.h" 1 3 4# 28 "/usr/include/stdio.h" 3 4# 1 "/usr/include/features.h" 1 3 4# 324 "/usr/include/features.h" 3 4# 1 "/usr/include/x86_64-linux-gnu/bits/predefs.h" 1 3 4# 325 "/usr/include/features.h" 2 3 4# 357 "/usr/include/features.h" 3 4# 1 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 1 3 4# 378 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 3 4# 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4# 379 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4# 358 "/usr/include/features.h" 2 3 4# 389 "/usr/include/features.h" 3 4# 1 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 1 3 4/*......此处省略n行(展开的<stdio.h>和<malloc.h>)......*/# 3 "2-3.c" 2# 11 "2-3.c"int main(){ int array[] = {1, 2, 3, 4, 5}; int x = 0; //预编译后宏定义直接替换 //宏定义强于函数1:宏定义可以传递类型参数(int),而函数只能传递形参(int a) int*p = (int*)malloc(sizeof(int)*5); //宏定义强于函数2:宏定义可以扩展c语言的关键字或者说用法 for(x=0; x<5; x++) { p[x] = array[x]; } for(x=0; x<5; x++) { printf("%d\n", p[x]); } while(1); free(p); printf("Last printf...\n"); return 0;}
宏表达式与函数的对比
- 宏表达式在预编译期间被处理,编译器不知道宏表达式
- 宏表示式用“实参”完全替代形参,不进行任何计算
- 宏表达式没有任何“调用”开销(在预编译期直接替换)
- 宏表达式中不能出现递归定义
- #define FAC(n) ((n>0) ? (FAC(n-1)+1) : 0)
2-4.c
#include<stdio.h>#define FAC(n) ((n>0) ? (FAC(n-1)+1) : 0)int main(){ int j = FAC(100); //编译器报错却没有标明出错的行号 //而汇编器报错,是不会标明出错行号的 //如果发现编译报错却没有发现出错行号,可以一步一步编译(预编译->编译->汇编),来查错 printf("%d\n", j); return 0;}
结果:
2-4.i
"2-4.c"# 1 "<built-in>"# 1 "<命令行>"# 1 "2-4.c"/*......此处省略n行(展开的<stdio.h>)......*/# 940 "/usr/include/stdio.h" 3 4# 2 "2-4.c" 2int main(){//预编译后直接替换宏表达式,会出现一个没有定义的FAC int j = ((100>0) ? (FAC(100 -1)+1) : 0); printf("%d\n", j); return 0;}
2-4.s
.file "2-4.c" .section .rodata.LC0: .string "%d\n" .text .globl main .type main, @functionmain:.LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movl $99, %edi movl $0, %eax call FAC //调用FAC函数,发现没有定义FAC函数 addl $1, %eax movl %eax, -4(%rbp) movl $.LC0, %eax movl -4(%rbp), %edx movl %edx, %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: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3" .section .note.GNU-stack,"",@progbits
使用#undef
- 宏定义的常量或表达式是否有作用域?
#include<stdio.h>int f1(int a, int b){ #define MIN(a,b) ((a)<(b) ? a :b) return MIN(a,b);}int f2(int a, int b, int c){ return MIN(MIN(a,b),c);}int main(){ printf("%d\n", f1(1,2)); //编译没有出错,且结果正确 //所以宏定义可以出现在代码的任何地方,而且其以下都可以使用她 printf("%d\n", f2(1,2,3)); return 0;}
可以使用#undef来限制宏的作用域
#include<stdio.h>int f1(int a, int b){ #define MIN(a,b) ((a)<(b) ? a :b) return MIN(a,b); //加上#undef //在return加上#undef是可行的,因为在预编译期进行的 #undef }int f2(int a, int b, int c){ return MIN(MIN(a,b),c);}int main(){ printf("%d\n", f1(1,2)); //编译出错 //所以宏定义可以扩展c语言,定义局部函数。(标准c语言中没有局部函数的概念) printf("%d\n", f2(1,2,3)); return 0;}
强大的内置宏
手把手教你写代码:定义日志宏
2-6.c
#include<stdio.h>#include<time.h>#define LOG(s) do{ \ time_t t; \ struct tm* ti; \ time(&t); \ ti = localtime(&t); \ printf("%s[%s:%d] %s\n", asctime(ti), __FILE__, __LINE__, s); \}while(0) //#define f1 (x) ((x)-1) 错误的写法//以下这种写法没有错(0_0)#define f2( a, b ) ((a)-(b))void f(){ LOG("ENTER f()..."); LOG("EXIT f()...");}int main(){ //日志宏不能用函数代替,因为行号不会变化。(每次调用都显示日志函数的行号) LOG("Enter mian()..."); f(); LOG("Exit main()...");// printf("%d\n", f1(6)); //调用正确(=^_^=),打印正确 printf("%d", f2( 1, 2 )); return 0;}
2-6.i
# 1 "2-6.c"# 1 "<built-in>"# 1 "<命令行>"# 1 "2-6.c"/*......此处省略n行(展开的<stdio.h>和<time.h>)......*/# 417 "/usr/include/time.h" 3 4# 3 "2-6.c" 2# 16 "2-6.c"void f(){ do{ time_t t; struct tm* ti; time(&t); ti = localtime(&t); printf("%s[%s:%d] %s\n", asctime(ti), "2-6.c", 18, "ENTER f()..."); }while(0); do{ time_t t; struct tm* ti; time(&t); ti = localtime(&t); printf("%s[%s:%d] %s\n", asctime(ti), "2-6.c", 19, "EXIT f()..."); }while(0);}int main(){ do{ time_t t; struct tm* ti; time(&t); ti = localtime(&t); printf("%s[%s:%d] %s\n", asctime(ti), "2-6.c", 24, "Enter mian()..."); }while(0); f(); printf("%d", ((1)-(2))); do{ time_t t; struct tm* ti; time(&t); ti = localtime(&t); printf("%s[%s:%d] %s\n", asctime(ti), "2-6.c", 31, "Exit main()..."); }while(0); return 0;}
结果:
阅读全文
0 0
- 宏定义与使用分析
- 宏定义与使用分析
- 宏定义与宏使用分析
- 宏定义 使用分析
- 宏定义及使用分析
- 3.1、宏定义和使用分析
- 专题3-2.宏定义与使用分析( 国嵌C语言视频)
- 【C语言复习(八)】宏定义的使用与分析
- c语言学习笔记(11)宏定义使用与分析
- 主题三 编译过程介绍---- 16.宏定义与使用分析
- iOS宏定义的使用与规范
- iOS 宏定义的使用与规范
- iOS宏定义的使用与规范
- C语言的宏定义与使用
- UNSTUFF_BITS宏定义分析
- __range_ok 宏定义分析
- container_of宏定义分析
- offsetof宏定义分析
- OpenCL、OpenGL和DirectX三者的区别
- 泡泡机器人原创专栏-Cartographer】Cartographer理论及实现浅析
- 字符串应用-密码截取
- 江西电力职业技术学院
- Twisted入门教程(11)
- 宏定义与宏使用分析
- vue实现切换class(与style的绑定)
- java读取resouces目录下文件
- 扫描,生成二维码
- Twisted入门教程(12)
- 逢えば恋する乙女ら
- 算法之暴力求解
- springmvc结合jquery.form.js异步提交表单上传文件
- iOS代码规范