【C语言】15-预处理指令1-宏定义
预处理指令简介
一、不带参数的宏定义
1.一般形式
#define 宏名 字符串
比如#define ABC 10
右边的字符串也可以省略,比如#define ABC
2.作用
它的作用是在编译预处理时,将源程序中所有"宏名"替换成右边的"字符串",常用来定义常量。
接下来写个程序根据圆的半径计算周长

- 1 #include <stdio.h>
- 2
- 3 // 源程序中所有的宏名PI在编译预处理的时候都会被3.14所代替
- 4 #define PI 3.14
- 5
- 6 // 根据圆的半径计radius算周长
- 7 float girth(float radius) {
- 8 return 2 * PI *radius;
- 9 }
- 10
- 11 int main ()
- 12 {
- 13 float g = girth(2);
- 14
- 15 printf("周长为:%f", g);
- 16 return 0;
- 17 }

在第4行定义了一个叫PI的宏,在编译预处理之后,第8行中的2 * PI *radius就会变成2 * 3.14 * radius。
输出结果:
3.使用习惯与注意
1> 宏名一般用大写字母,以便与变量名区别开来,但用小写也没有语法错误
2> 对程序中用双引号扩起来的字符串内的字符,不进行宏的替换操作。比如:

- 1 #define R 10
- 2 int main ()
- 3 {
- 4 char *s = "Radio";
- 5 return 0;
- 6 }

在第1行定义了一个叫R的宏,但是第4行中"Radio"里面的'R'并不会被替换成10
3> 在编译预处理用字符串替换宏名时,不作语法检查,只是简单的字符串替换。只有在编译的时候才对已经展开宏名的源程序进行语法检查

- 1 #define I 100
- 2 int main ()
- 3 {
- 4 int i[3] = I;
- 5 return 0;
- 6 }

在做编译预处理的时候,不管语法对不对,第4行的I都会被替换为100。不过在编译的时候就会报第4行的错。

- 1 #define PI 3.14
- 2 /*
- 3 .
- 4 .
- 5 .
- 6 .
- 7 */
- 8 #undef PI

PI这个宏在第1行到第8行之间是有效的,第8行后就无效了
- #define R 3.0
- #define PI 3.14
- #define L 2*PI*R
- #define S PI*R*R
二、带参数的宏定义
1.一般形式
#define 宏名(参数列表) 字符串
2.作用
在编译预处理时,将源程序中所有宏名替换成字符串,并且将 字符串中的参数 用 宏名右边参数列表 中的参数替换

- 1 #include <stdio.h>
- 2
- 3 #define average(a, b) (a+b)/2
- 4
- 5 int main ()
- 6 {
- 7 int a = average(10, 4);
- 8
- 9 printf("平均值:%d", a);
- 10 return 0;
- 11 }

第3行中定义了一个带有2个参数的宏average,第7行其实会被替换成:int a = (10 + 4)/2;,输出结果为:。是不是感觉这个宏有点像函数呢?
3.使用注意
1> 宏名和参数列表之间不能有空格,否则空格后面的所有字符串都作为替换的字符串

- 1 #define average (a, b) (a+b)/2
- 2
- 3 int main ()
- 4 {
- 5 int a = average(10, 4);
- 6 return 0;
- 7 }

注意第1行的宏定义,宏名average跟(a, b)之间是有空格的,于是,第5行就变成了这样:
- int a = (a, b) (a+b)/2(10, 4);
这个肯定是编译不通过的
2> 带参数的宏在展开时,只作简单的字符和参数的替换,不进行任何计算操作。所以在定义宏时,一般用一个小括号括住字符串的参数。
下面定义一个宏D(a),作用是返回a的2倍数值:
- 如果定义宏的时候不用小括号括住参数

- 1 #include <stdio.h>
- 2
- 3 #define D(a) 2*a
- 4
- 5 int main ()
- 6 {
- 7 int b = D(3+4);
- 8
- 9 printf("%d", b);
- 10 return 0;
- 11 }

第7行将被替换成int b = 2*3+4;,输出结果:
- 如果定义宏的时候用小括号括住参数,把上面的第3行改成:
- #define D(a) 2*(a)
注意右边的a是有括号的,第7行将被替换成int b = 2*(3+4);,输出结果:
3> 计算结果最好也用括号括起来
下面定义一个宏P(a),作用是返回a的平方:
- 如果不用小括号括住计算结果

- 1 #include <stdio.h>
- 2
- 3 #define Pow(a) (a) * (a)
- 4
- 5 int main(int argc, const char * argv[]) {
- 6 int b = Pow(10) / Pow(2);
- 7
- 8 printf("%d", b);
- 9 return 0;
- 10 }

注意第3行,没有用小括号扩住计算结果,只是括住了参数而已。第6行代码被替换为:
- int b = (10) * (10) / (2) * (2);
简化之后:int b = 10 * (10 / 2) * 2;,最后变量b为:
- 如果用小括号括住计算结果
将上面的第3行代码改为:
- #define Pow(a) ( (a) * (a) )
那么第6行被替换为:
- int b = ( (10) * (10) ) / ( (2) * (2) );
简化之后:int b = (10 * 10) / (2 * 2);,最后输出结果:。这个才是我们想要的结果。
也就意味着前面的#define average(a, b) (a+b)/2应该写成#define average(a, b) (((a)+(b))/2)
5.与函数的区别
从整个使用过程可以发现,带参数的宏定义,在源程序中出现的形式与函数很像。但是两者是有本质区别的:
1> 宏定义不涉及存储空间的分配、参数类型匹配、参数传递、返回值问题
2> 函数调用在程序运行时执行,而宏替换只在编译预处理阶段进行。所以带参数的宏比函数具有更高的执行效率
【C语言】15-预处理指令1-宏定义的更多相关文章
- VC中预处理指令与宏定义详解
刚接触到MFC编程的人往往会被MFC 向导生成的各种宏定义和预处理指令所吓倒,但是预处理和宏定义又是C语言的一个强大工具.使用它们可以进行简单的源代码控制,版本控制,预警或者完成一些特殊的功能. 一个 ...
- c语言的预处理指令分3种 1> 宏定义 2> 条件编译 3> 文件包含
宏简介 1.C语言在对源程序进行编译之前,会先对一些特殊的预处理指令作解释(比如之前使用的#include文件包含指令),产生一个新的源程序(这个过程称为编译预处理),之后再进行通常的编译 所有的预处 ...
- C语言 预处理二(宏定义--#define)
//#define 宏定义(宏定义一般大写) //知识点一-->#define的作用域:从#define开始,从上往下,如果遇到#undef就到#undef处结束,如果没有就是作用于当前整个文件 ...
- C语言的本质(19)——预处理之一:宏定义
我们在写代码时已多次使用过以"#"号开头的预处理命令.如包含命令#include,宏定义命令#define等.在源程序中这些命令都放在函数之外,而且一般都放在源文件的前面,它们称为 ...
- 从头开始-06.C语言中预处理指令
预处理指令 不带参数的宏定义: 格式: #define 宏名 值 作用:提高代码的可读性 在程序编译前把所有出现宏名标示的位置都替换为定义宏的时候,宏名后面的值 带参数的宏定义 格式 #define ...
- C语言的头文件和宏定义详解
原文链接:https://blog.csdn.net/abc_12366/article/details/79155540
- Linux C编程学习之C语言简介---预处理、宏、文件包含……
C的简介 C语言的结构极其紧凑,C语言是一种模块化的编程语言,整个程序可以分割为几个相对独立的功能模块,模块之间的相互调用和数据传递是非常方便的 C语言的表达能力十分强大.C语言兼顾了高级语言和汇编语 ...
- C语言-1.结构体,2.枚举,3.typedef,4.预处理指令的概念,5.条件编译
1. 结构体数组 定义:由若干个相同类型的结构体变量组成的有序的集合. 定义格式: 1) 定义结构体的同时定义结构体数组 struct Car{ int lunzi; int speed; }cars ...
- C语言include预处理命令与多文件编译
#include预处理命令几乎使我们在第一次接触C的时候就会碰到的预处理命令,可我现在还不怎么清楚,这次争取一次搞懂. 一.#include预处理指令的基本使用 预处理指令可以将别处的源代码内容插入到 ...
- 宏定义中的##操作符和... and _ _VA_ARGS_ _
1.Preprocessor Glue: The ## Operator 预处理连接符:##操作符 Like the # operator, the ## operator can be used i ...
随机推荐
- swiper组件实现向上翻页时缩小
var mySwiper = new Swiper ('.swiper-container', { direction: 'vertical', loop: true, // 如果需要前进后退按钮 n ...
- Datasets for Data Mining and Data Science
https://github.com/mattbane/RecommenderSystem http://grouplens.org/datasets/movielens/ KDDCUP-2012官网 ...
- 【转】velocity 显示List和Map方法
一.遍历个map类型 1.先看后台java程序Java代码 Map<String,String> paramValues=new HashMap<String, String ...
- 28. 字符串的全排列之第2篇[string permutation with repeating chars]
[本文链接] http://www.cnblogs.com/hellogiser/p/string-permutation-with-repeating-chars.html [题目] 输入一个字符串 ...
- Android 中的Resource
Android与ios相比,各种各样Resource算个独特之处.详情请参见官网Resource Types Resource有许多种,常见的有图像资源,布局资源,等等.每一种资源的位置都是固定的,这 ...
- 将文件放到Android模拟器的SD卡
1.打开DDMS页面2.打开File Explorer页,如果没有,在Window –> Show View –>File Explorer3.一般就在mnt –> sdcard中4 ...
- Java for LeetCode 198 House Robber
You are a professional robber planning to rob houses along a street. Each house has a certain amount ...
- iOS国际化(Xcode5)
如何将你的app内的语言可以根据系统语言切换而切换呢?这是本篇所要解决的问题.废话先不说,上软硬件环境: 硬件:Macbook Pro 软件:Xcode 5.1 代码:https://github.c ...
- JavaScript当离开页面时可以进行的操作
当JavaScript离开页面时可以进行的操作 window.onbeforeunload = function() { var email = document.getElementById(&qu ...
- 理解Fragment生命周期
官网帮助文档链接: http://developer.android.com/guide/components/fragments.html 主要看两张图,和跑代码 一,Fragment的生命周 二 ...