详解C语言#define预处理宏定义
#define介绍:
C语言里可以用#define定义一个标识符来表示一个常量。特点是:定义的标识符不占内存,只是一个临时的符号,预编译后这个符号就不存在了,也不做类型定义。预编译又叫预处理。预编译就是编译前的处理。这个操作是在正式编译之前由系统自动完成的。
#define又称宏定义,标识符为所定义的宏名,简称宏。标识符的命名规则和变量的命名规则是一样的。#define的功能是将标识符定义为其后的常量,一经定义,程序中就可以直接用标识符来表示这个常量,也就是文本替换。变量名表示的是一个变量,但宏名表示的是一个常量,可以给变量赋值,但绝不能给常量赋值。
宏定义最大的好处是方便程序的修改。使用宏定义可以用宏代替一个在程序中经常使用的常量。这样,当需要改变这个常量的值时,就不需要对整个程序一个一个进行修改,只需修改宏定义中的常量就行了。且当常量比较长时,使用宏就可以用较短的有意义的标识符来代替它,这样编程的时候就会更方便,不容易出错。因此,宏定义的优点就是方便和易于维护。
#define宏定义无参的一般形式为:#define 标识符 常量
注意最后没有分号,因为宏不是语句,结尾不用加分号,否则会被替换进进程中。还有一点就是宏名最好用大写字母加下划线组成,以此来区分变量名。
来看一个#define宏定义无参的例子:
#include<stdio.h> #define PI 3.1415926//标识符或宏名叫PI 常量是个浮点型 作用是圆周率 #define R 2//标识符或宏名叫R 常量是个整型 作用是圆的半径 #define PRINT "半经为2的圆 面积=%lf\n"//标识符或宏名叫PRINT 常量是个字符串 作用是代替了printf()函数的第一个参数 int main() { printf(PRINT,PI*R*R);//在这里PRINT被替换成"半经为2的圆 面积=%lf" PI被替换成3.1415926 R被替换成2 printf("半经为2的圆 面积=%lf\n",3.1415926*2*2);//这句是上面一句代码替换后的代码 return 0; }
#define宏定义有参的一般形式为:#define 标识符(参数表) 表达式
带参数的宏定义,宏名中不能有空格,宏名与形参表之间也不能有空格,而形参表中形参之间可以有空格。
来看一个#define宏定义有参的例子:
#include<stdio.h> #define SQUARE(x) x*x//标识符或宏名叫SQUARE 表达式是x*x 作用是算x的平方 int main() { printf("%d %d\n",SQUARE(3),SQUARE(4));//SQUARE(3)被替换成3*3 SQUARE(4)被替换成4*4 printf("%d %d\n",3*3,4*4);//这句是上面一句代码替换后的代码 return 0; }
我门来稍微改动下代码:
#include<stdio.h> #define SQUARE(x) x*x//标识符或宏名叫SQUARE 表达式是x*x 作用是算x的平方 int main() { printf("%d %d\n",SQUARE(2+1),SQUARE(3+1)); return 0; }
这里只是把传的参数3被改成了2+1,4被改成了3+1,可能有些朋友会说2+1=3,3+1=4,答案不就和刚才一样嘛。其实不是,因为#define宏定义只是简单的文本替换,那应该被替换成什么呢。SQUARE(2+1)和SQUARE(3+1)分别替换成2+1*2+1和3+1*3+1, 那么最终答案自然是5和7了。那么如何杜绝这个问题呢?
很简单,只要在传参时多加一层小括号:
#include<stdio.h> #define SQUARE(x) x*x//标识符或宏名叫SQUARE 表达式是x*x 作用是算x的平方 int main() { printf("%d %d\n",SQUARE((2+1)),SQUARE((3+1)));//SQUARE((2+1))被替换成(2+1)*(2+1) SQUARE((3+1))被替换成(3+1)*(3+1) printf("%d %d\n",(2+1)*(2+1),(3+1)*(3+1));//这句是上面一句代码替换后的代码 return 0; }
如果觉得这样太繁杂,麻烦了,那么直接在宏定义的表达式那里的每个参数都加上小括号:
#include<stdio.h> #define SQUARE(x) (x)*(x)//标识符或宏名叫SQUARE 表达式是x*x 作用是算x的平方 int main() { printf("%d %d\n",SQUARE(2+1),SQUARE(3+1));//SQUARE((2+1))被替换成(2+1)*(2+1) SQUARE((3+1))被替换成(3+1)*(3+1) printf("%d %d\n",(2+1)*(2+1),(3+1)*(3+1));//这句是上面一句代码替换后的代码 return 0; }
我们又来稍微改动下代码:
#include<stdio.h> #define SQUARE(x) (x)*(x)//标识符或宏名叫SQUARE 表达式是x*x 作用是算x的平方 int main() { printf("%d %d\n",9/SQUARE(3),16/SQUARE(4)); return 0; }
这里在传参数之前加了个除法,那么3*3后是9,再被9除,等于1,4*4后是16,再被16除,等于1,那么预想的最终答案就是1和1了,其实也不是。再来一次文本替换,9/SQUARE(3)和16/SQUARE(4)分别替换成9/(3)*(3)和16/(4)*(4), 那么最终答案自然是9和16了。那么又如何杜绝这个问题呢?
也很简单,只要在传参时多加一层小括号:
#include<stdio.h> #define SQUARE(x) (x)*(x)//标识符或宏名叫SQUARE 表达式是x*x 作用是算x的平方 int main() { printf("%d %d\n",9/(SQUARE(3)),16/(SQUARE(4)));//9/(SQUARE(3))和16/(SQUARE(4))分别替换成9/((3)*(3))和16/((4)*(4)) printf("%d %d\n",9/((3)*(3)),16/((4)*(4)));//这句是上面一句代码替换后的代码 return 0; }
如果依然觉得这样太繁杂,麻烦了,那么直接在宏定义的表达式那里的整个表达式都加上小括号:
#include<stdio.h> #define SQUARE(x) ((x)*(x))//标识符或宏名叫SQUARE 表达式是x*x 作用是算x的平方 int main() { printf("%d %d\n",9/SQUARE(3),16/SQUARE(4));//9/(SQUARE(3))和16/(SQUARE(4))分别替换成9/((3)*(3))和16/((4)*(4)) printf("%d %d\n",9/((3)*(3)),16/((4)*(4)));//这句是上面一句代码替换后的代码 return 0; }
表达式也可以写多个语句:
#include<stdio.h> #define AB(a,b) a=i+5,b=j+3 int main() { int i=3,j=5,m=0,n=0; AB(m,n);//AB(m,n)被替换成m=i+5,n=j+3 printf("%d %d\n",m,n); return 0; }
#运算符:
#运算符的作用就是将#后边的宏参数进行字符串的操作,也就是将#后边的参数两边加上一对双引号使其成为字符串。例如param是一个宏的形参,则替换文本中的#param被系统转化为"param",这个转换过程即为字符串化。如下代码:
#include<stdio.h> #define TEST(param) #param//标识符或宏名叫TEST 表达式是#param 作用是把param参数转换为字符串 int main() { printf("%s\n",TEST(换行前\n第一次换行\n第二次换行));//TEST(换行前\n第一次换行\n第二次换行)被替换成"换行前\n第一次换行\n第二次换行" printf("换行前\n第一次换行\n第二次换行\n");//这句是上面一句代码替换后的代码 return 0; }
##运算符:
##运算符也可以用在替换文本中,它的作用起到粘合的作用,即将两个宏参数连接为一个数。如下代码:
#include<stdio.h> #define TEST(param1,param2) (param1##param2)//标识符或宏名叫TEST 表达式是(param1##param2) 作用是把param1参数和param2参数和连接为一个数 int main() { printf("%d\n",TEST(12,34));//TEST(12,34)被替换成(1234) printf("%d\n",(1234));//这句是上面一句代码替换后的代码 return 0; }
可变宏...和__VA_ARGS__:
可变宏...和__VA_ARGS__的作用主要是为了方便管理软件中的打印信息。在写代码或DEBUG时通常需要将一些重要参数打印出来,但在软件发行的时候不希望有这些打印,这时就用到可变参数宏了。如下代码:
#include<stdio.h> #define PRINT(...) printf(__VA_ARGS__)//标识符或宏名叫PRINT 表达式是printf(__VA_ARGS__) __VA_ARGS__被用在替换文本中,来表示省略号...代表了什么 int main() { PRINT("hello\n");//PRINT("hello\n")被替换成printf("hello\n") printf("hello\n");//这句是上面一句代码替换后的代码 return 0; }
在宏定义中,形参列表的最后一个参数为省略号...,而__VA_ARGS__被用在替换文本中,来表示省略号...代表了什么。
开发项目中常用的宏定义:
防止头文件被重复包含:
#ifndef COMDEF_H #define COMDEF_H //头文件的内容 #endif
得到一个制定地址上的一个字节或字:
#define MEM_B(X) (*((byte*)(x))) #define MEM_W(X) (*((word*)(x)))
求最大值与最小值:
#define MAX(x,y) ((x)>(y)?(x):(y)) #define MIN(x,y) ((x)<(y)?(x):(y))
得到一个结构体中field所占用的字节数:
#define FSIZ(type,field) sizeof(((type*)0)->field)
得到一个field在结构体中的偏移量:
#define FPOS(type,field)\((dword)&(((type*)0)->field)
按照LSB格式把两个字节转化为一个word:
#define FLIPW(ray) (((word)(ray)[0]*256)+(ray)[1])
按照LSB格式将一个WORD转化为两个字节:
#define FLOPW(ray,val) (ray)[0]=((val)/256);(ray)[1]=((val)&0xFF)
得到一个变量的地址:
#define B_PTR(var) ((byte*)(void*)&(var)) #define W_PTR(var) ((word*)(void*)&(var))
得到一个字的高位与低位字节:
#define WORD_LO(xxx) ((byte)((word)(xxx)&255)) #define WORD_HI(xxx) ((byte)((word)(xxx)>>8))
用宏得到一个数组所含的元素个数:
#define ARR_SIZE(a) (sizeof(a)/sizeof((a)[0]))
到此这篇关于详解C语言#define预处理宏定义 的文章就介绍到这了,更多相关C语言#define预处理宏定义 内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!
相关文章
- 这篇文章主要为大家详细介绍了C语言实现放烟花的程序,有音乐播放,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-02-23
- 本篇文章主要介绍C语言中char的知识,并附有代码实例,以便大家在学习的时候更好的理解,有需要的可以看一下...2020-04-25
- 这篇文章主要介绍了详解如何将c语言文件打包成exe可执行程序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-25
- free函数是释放之前某一次malloc函数申请的空间,而且只是释放空间,并不改变指针的值。下面我们就来详细探讨下...2020-04-25
- 这篇文章主要介绍了C语言中计算正弦的相关函数总结,包括正弦和双曲线正弦以及反正弦的函数,需要的朋友可以参考下...2020-04-25
详解C语言中的rename()函数和remove()函数的使用方法
这篇文章主要介绍了详解C语言中的rename()函数和remove()函数的使用方法,是C语言入门学习中的基础知识,需要的朋友可以参考下...2020-04-25- 这篇文章主要介绍了C语言中求和、计算平均值、方差和标准差的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-12-10
- 本篇文章主要讲解C语言 基本语法,这里提供简单的示例和代码来详细讲解C语言的基本语法,开始学习C语言的朋友可以看一下,希望能够给你带来帮助...2021-09-18
- 这篇文章主要介绍了C语言中send()函数和sendto()函数的使用方法,是C语言入门学习中的基础知识,需要的朋友可以参考下...2020-04-25
- 今天小编就为大家分享一篇C语言实现从文件读入一个3*3数组,并计算每行的平均值,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-04-25
- 这篇文章主要介绍了使用C语言操作文件的基本函数整理,包括创建和打开以及关闭文件的操作方法,需要的朋友可以参考下...2020-04-25
- 这篇文章主要介绍了C语言中memcpy 函数的用法详解的相关资料,需要的朋友可以参考下...2020-04-25
- 这篇文章主要介绍了C语言中查找字符在字符串中出现的位置的方法,分别是strchr()函数和strrchr()函数的使用,需要的朋友可以参考下...2020-04-25
- 很多同学在学习c语言的时候是不是会碰到a++和++a都有甚么作用啊。今天我们就来探讨下...2020-04-25
- 下面小编就为大家带来一篇C语言实现时间戳转日期的算法(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-04-25
- 这篇文章主要对C语言中const关键字的用法进行了详细的分析介绍,需要的朋友可以参考下...2020-04-25
- 这篇文章主要介绍了C语言之整数划分问题(递归法)实例代码的相关资料,需要的朋友可以参考下...2020-04-25
- 本文给大家简单介绍下c实现linux下的数据库备份的方法和具体的源码,十分的实用,有需要的小伙伴可以参考下。...2020-04-25
C语言正则表达式详解 regcomp() regexec() regfree()用法详解
C语言处理正则表达式常用的函数有regcomp()、regexec()、regfree()和regerror(),这里就为大家介绍一下,需要的朋友可以参考一下啊...2020-04-25- 这篇文章主要介绍了c语言实现找最大值最小值位置查找,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-04