今天我们一起来学习和了解一个比较简单但是很重要的一个知识点吧!“宏”的相关使用。在介绍之前先给大家介绍一些基本概念:宏定义的基本形式为:#define A B,其中A部分称之为“宏名”,B部分称之为“宏体”,“宏”分为两种——“对象式宏”与“函数式宏”,现在就这两个方面分别给大家进行介绍!
一、“对象式宏”的定义和使用:
-
“对象宏”是什么?
我们经常所说的“宏”也是指“对象式宏”,其定义的一个常见的格式为#define NAME 12。也就意味在NAME就代表着数字12,我们用printf输出函数输出NAME就可以在屏幕中得到数字12。
-
“对象式宏”的使用需要注意什么?
对象式宏的使用使用比较简单,但是要注意一个很重要的点,“对象式宏”是一个常量,不可以被改变,因此#define后面所定义的字母也被称之为#define所定义的标识符常量。我们来做一下这个测试:(注:这个代码不能运行的哦!)
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#define NAME 12
int main()
{
NAME = 10;
printf("%d", NAME);
return 0;
}
我们编译一下,看一下它所给出的错误信息 :
这里我们通过#define定义了NAME这个量,编译器在编译的时候认为这是一个不可以被改变的常量。由此可以总结得到由“#define所定义的量”是“#define所定义的标识符常量”,既然是常量就不能够被改变,因此#define之后的量一般都会进行大写标注,以此来区分它和普通的变量。这是需要大家特别注意的一个方面。
-
“对象式宏”的使用会带来哪些便捷?
“对象式宏”的使用会带来哪些编程上的便捷呢?一个比较大的便捷就是能够很便捷的对数组的长度进行修改。就比如之前我们有定义了一个NAME,它是12,现在我还定义了一个int类型的数组:int arr[NAME];那这个数组长度是12哦!我现在想改变它怎么办呢?诶,#define NAME *** 我们修改一下***是不是就可以了呢,就是这样的。
二、“函数式宏”的定义和使用
-
“函数式宏”是什么?
以下是一个比较简单的用于实现比较大小的“函数式宏”:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#define MAX(x,y) x>y?x:y
int main()
{
int a, b;
scanf("%d %d", &a, &b);
printf("MAX=%d", MAX(a,b));
return 0;
}
“函数式宏”也是被#define所定义的,而且大家发现了没有,它和函数有一个非常相似的方面,它一般也是有参数的,那么它和我们的函数有什么的不同呢?首先一个非常明显的区别就在于,“函数式宏”对于它的参数是没有具体指代其类型的,而函数却指代了它的类型。
-
“函数式宏”与函数的一些区别在哪里?
其实最初这个“函数式宏”的出现也正是因为“宏体”的这一优于函数的特征。话说有一天,公司的老板给公司的两个程序员出了一个问题,要求他们写一个程序,专门用于计算各种数的平方。A程序员的思路是这样的:数有很多类型,至少有着整数和小数这两种类型,于是他写出了这样的一个程序:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int sqr_int(int a)
{
return a * a;
}
double sqr_double(double a)
{
return a * a;
}
int main()
{
int a;
double b;
printf("输入一个整数: ");
scanf("%d", &a);
printf("%d", sqr_int(a));
printf("\n");
printf("请输入一个小数: ");
scanf("%lf", &b);
printf("%f", sqr_double(b));
return 0;
}
运行结果:
这个程序仅仅也考虑了int和double类型的数据类型,但是我们知道除了最基本的两个类型,还有long,long long等等其他的类型,那么我们都去为每个类型单独去写一个函数吗?即便这样写出了代码也是不太理想,因为存在着大量的功能相同,而且名称相似的函数了。那么我们再来看一下B程序员所写的代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#define sqr(x) (x)*(x)
int main()
{
int a;
double b;
printf("输入一个整数:");
scanf("%d", &a);
printf("%d", sqr(a));
printf("\n");
printf("输入一个小数:");
scanf("%lf", &b);
printf("%f", sqr(b));
return 0;
}
运行效果:
显然代码的结构上优于A程序员所写的代码!
既然“函数式宏”的使用这么方便,那是不是就把函数的“饭碗”给抢了呢?实则不然,我们来看一道题目,请分析给出以下程序的运行结果:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#define F(x) 2.84+x
#define w(y) printf("%d",(int)(y))
#define P(y) w(y)
int main()
{
int x = 2;
P(F(5) * x);
return 0;
}
这个代码的运行结果是多少呢?诶!你是不是和大部分人一样给出了15这么一个结果呢?我们来运行一下,看一下结果到底是多少:
发现了没有,为什么给出是12的结果呢?为什么不是5+2.84=7.84,然后7.84*2=15.64,强制类型转换之后不应该是15吗,为什么,why?实际上我想告诉大家的是,无论是“对象宏”亦或是“函数式宏”,他们都只是代码的简单替换,使用“宏体”来替换“宏名”,这是“函数式宏”和函数最大的不同,你只有真正理解了这个,你才能正确区分“函数式宏”与函数的使用。
所以实际上我们的编译器在运行上述程序时,它是这么做的,直接将我们的原先“宏体”中的“2.84+x”中x替换为5,然后它输出的实际上是(int)(2.84+5*2)的结果,这个结果是多少?12嘛!那么我们想得到15的结果应该对代码进行怎样的修改呢?我们实际上只要加一些括号就可以了:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#define F(x) (2.84+x)
#define w(y) printf("%d",(int)(y))
#define P(y) w(y)
int main()
{
int x = 2;
P(F(5) * x);
return 0;
}
我们仅仅是在第一个#define F(x) 2.84+x那里加了一个括号,将2.84+x用括号括了起来,它会不会给出(int)(2.84+5)*2的结果,也就是15呢?我们来看运行结果:
所以确实在使用“对象式宏”与“函数式宏”的时候,我们的编译器是直接将它们的内容给复刻到我们的代码当中,这也就不难理解前面“通过#define定义的量是常量”的原因了。
三、最后的总结:
通过前面的讨论和研究,我们大致可以就这个知识点总结得出以下内容:
- “宏”是简单的字符串替换,是没有类型参数的替换。而函数参数的传递是有数据类型的。
- “宏”是在编译之前进行的,先用宏体替换宏名,再进行编译。而函数是在程序执行之时被调 用的。因此宏的运行时间一般比函数要短。
这就是给大家介绍的全部内容了,你get到了吗!下一篇博客我们将用这些所学的知识来完成C语言游戏三子棋,嘻嘻谢谢大家!!!