C语言-宏定义与使用分析
1、C语言中的宏定义
#define是预处理器处理的单元实体之—
#define定义的宏可以出现在程序的任意位置
#define定义之后的代码都可以使用这个宏
2、定义宏常量
#define定义的宏常量可以直接使用
#define定义的宏常量本质为字面量
下面的宏常量定义正确吗?
#define ERROR -1
#define PATHl "D:\test\test.c"
#define PATH2 D:\test\test.c
#define PATH3 D:\test\
test.c
3、实例分析
宏定义分析 21-1.c
#define ERROR -1
#define PATH1 "D:\test\test.c"
#define PATH2 D:\test\test.c
#define PATH3 D:\test\
test.c
int main()
{
int err = ERROR;
char* p1 = PATH1;
char* p2 = PATH2;
char* p3 = PATH3;
}
预处理器不会进行语法检查只是简单的文本替换,即这些宏定义都正确!!但编译器会报错!!
4、宏定义表达式
#define表达式的使用类似函数调用
#define表达式可以比函数更强大
#define表达式比函数更容易出错
下面的宏表达式定义正确吗?
#define _SUM_(a, b) (a) + (b)
#define _MIN_(a, b) ((a) < (b) ? (a) : (b))
#define _DIM_(a) sizeof(a)/sizeof(*a)
5、实例分析
宏表达式分析 21-2.c
#include <stdio.h>
#define _SUM_(a, b) (a) + (b)
#define _MIN_(a, b) ((a) < (b) ? (a) : (b))
#define _DIM_(a) sizeof(a)/sizeof(*a)
int main()
{
int a = ;
int b = ;
int c[] = {};
int s1 = _SUM_(a, b);
int s2 = _SUM_(a, b) * _SUM_(a, b);
int m = _MIN_(a++, b);
int d = _DIM_(c);
printf("s1 = %d\n", s1);
printf("s2 = %d\n", s2);
printf("m = %d\n", m);
printf("d = %d\n", d);
return ;
}
6、宏表达式与函数的对比
宏表达式被预处理器处理,编译器不知道宏表达式的存在
宏表达式用“实参“完全替代形参,不进行任何运算
宏表达式没有任何的“调用”开销
宏表达式中不能出现递归定义
#define _SUM_(n) ((n > 0) ? (_SUM_(n-1) + n): 0) //error int s = _SUM_();
7、有趣的问题
宏定义的常量或表达式是否有作用域限制?
下面的程序合法吗?
void def()
{
#define PI 3.1415926
#define AREA(r) r*r*PI
}
double area(double r)
{
return AREA(r);
}
8、实例分析
宏的作用域分析 21-3.c
#include <stdio.h>
void def()
{
#define PI 3.1415926
#define AREA(r) r * r * PI
}
double area(double r)
{
return AREA(r);
}
int main()
{
double r = area();
printf("PI = %f\n", PI);
printf("d = 5; a = %f\n", r);
return ;
}
9、强大的内置宏
__LINE__ 表示当前行号,整型值
__FILE__ 表示当前文件名,字符串类型
__DATE__ 编译的日期,字符串类型
__TIME__ 编译的时间,字符串类型
__STDC__ 预定义的宏
10、实例分析
宏使用综合示例 21-4.c
#include <stdio.h>
#include <malloc.h>
#define MALLOC(type, x) (type*)malloc(sizeof(type)*x)
#define FREE(p) (free(p), p=NULL)
#define LOG(s) printf("[%s] {%s:%d} %s \n", __DATE__, __FILE__, __LINE__, s)
#define FOREACH(i, m) for(i=0; i<m; i++)
#define BEGIN {
#define END }
int main()
{
int x = ;
int* p = MALLOC(int, );
LOG("Begin to run main code...");
FOREACH(x, )
BEGIN
p[x] = x;
END
FOREACH(x, )
BEGIN
printf("%d\n", p[x]);
END
FREE(p);
LOG("End");
return ;
}
11、小结
预处理器直接对宏进行文本替换
宏使用时的参数不会进行求值和运算
预处理器不会对宏定义进行语法检查
宏定义时出现的语法错误只能被编译器检测
宏定义的效率高于函数调用
宏的使用会带来—定的副作用
C语言-宏定义与使用分析的更多相关文章
- C 语言宏定义
C 语言宏定义1.例子如下: #define PRINT_STR(s) printf("%s",s.c_str()) string str = "abcd"; ...
- 将C语言宏定义数值转换成字符串!
将C语言宏定义转换成字符串! 摘自:https://blog.csdn.net/happen23/article/details/50602667 2016年01月28日 19:15:47 六个九十度 ...
- C语言 宏定义之可变参数
可变参数宏定义 C99编译器标准允许你可以定义可变参数宏(variadic macros),这样你就可以使用拥有可以变化的参数表的宏.可变参数宏就像下面这个样子: #define dbgprint(. ...
- c语言宏定义
一. #define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能理解该命令的本质,总是在此处产生一些困惑,在编程时 ...
- C语言宏定义#define用法
#define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能 理解该命令的本质,总是在此处产生一些困惑,在编程时误用 ...
- C 语言宏定义函数编写时 do-while 的妙用和一些注意事项
在 C 语言中,我们都知道可以用宏定义来编写函数,一般称为宏函数.如果一个宏函数比较复杂,那么在编写这样的宏函数是有一定技巧和注意事项的.文章给出一些我认为值得关注的地方,以及一些注意事项(个人建议) ...
- C语言宏定义时#(井号)和##(双井号)的用法1
#在英语里面叫做 pound 在C语言的宏定义中,一个#表示字符串化:两个#代表concatenate 举例如下: #include <iostream> void quit_comman ...
- C语言宏定义时#(井号)和##(双井号)的用法
C语言中如何使用宏C(和C++)中的宏(Macro)属于编译器预处理的范畴,属于编译期概念(而非运行期概念).下面对常遇到的宏的使用问题做了简单总结. 关于#和## 在C语言的宏中,#的功能是将其后面 ...
- C语言宏定义相关
写好C语言,漂亮的宏定义很重要,使用宏定义可以防止出错,提高可移植性,可读性,方便性 等等.下面列举一些成熟软件中常用得宏定义......1,防止一个头文件被重复包含#ifndef COMDEF_H# ...
随机推荐
- Swift Playgrounds for mac基础知识介绍
Swift Playgrounds是一款适用于iPad和Mac的革命性应用程序,它使Swift学习变得互动而有趣.它不需要编码知识,因此非常适合刚开始的学生.使用Swift解决难题,以掌握基本知识.S ...
- 完成:vue-styled-components
基础 https://es6.ruanyifeng.com/#docs/string#模板字符串 注释:模板字符串返回字符串类型 如果${...}中的值不是字符串,将按照一般的规则转为字符串.比如,大 ...
- Picture POJ - 1177 线段树+离散化+扫描线 求交叉图像周长
参考 https://www.cnblogs.com/null00/archive/2012/04/22/2464876.html #include <stdio.h> #include ...
- MySQL int(1) 与 int(3) 的区别
1.占用的字节都一样,只是容纳字符个数有区别. int(1): 一个字符. int(3): 三个字符. 2.另外要注意的是,无符号整数,自动左侧0填充宽度. int(3),插入值5,会变成005存储. ...
- Pikachu-URL重定向
不安全的url跳转 不安全的url跳转问题可能发生在一切执行了url地址跳转的地方.如果后端采用了前端传进来的(可能是用户传参,或者之前预埋在前端页面的url地址)参数作为了跳转的目的地,而又没有做判 ...
- @RequestBody 和 @RequestParam(“test”) 的区别与联系
@RequestBody @RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的):GET方式无请求体,所以使用@RequestBody接收数据时,前端不能使用 ...
- CF1300E Water Balance
题目链接 problem 给出一个长度为n的序列,每次可以选择一个区间\([l,r]\)并将区间\([l,r]\)内的数字全部变为这些数字的平均数.该操作可以进行任意多次. 求出进行任意次操作后可以得 ...
- C语言递归之二叉树的最小深度
题目描述 给定一个二叉树,找出其最小深度. 最小深度是从根节点到最近叶子节点的最短路径上的节点数量. 说明: 叶子节点是指没有子节点的节点. 示例 输入:[3,9,20,null,null,15,7] ...
- vue组件之间传值
父组件向子组件传值 父组件可以在引用子组件时,通过属性绑定(v-bind:)的形式,把数据传递给子组件.在子组件的props中定义后即可使用数据 <div id="app"& ...
- spark.streaming.kafka.maxRatePerPartition的理解
spark.streaming.kafka.maxRatePerPartition设定对目标topic每个partition每秒钟拉取的数据条数. 假设此项设为1,批次间隔为10s,目标topic只有 ...