【C++】内联函数(inline)与宏定义(#define)使用说明及区别
详细分析C++中内联函数(inline)与宏定义(#define)使用方法和注意事项以及两者的异同点。
目录
一、内联函数 inline
在函数名前添加关键字inline
,该函数就被声明为内联函数。每当程序中出现对该函数的调用时,C++编译器会将函数体中的代码插入到调用该函数的代码处,同时使用实参代替形参,从而使程序运行时不再进行函数调用。
引入内联函数主要是为了消除调用函数时的系统开销,以提高代码运行速度。
说明:
- 内联函数在第一次被调用之前必须进行完整的定义
- 在内联函数体内一般不能含有复杂的控制语句,如
for
语句和switch
语句等 - 类中的成员函数默认是内联函数(要求成员函数没有循环和递归,当其中存在循环的递归的时候,编译器会将其默认为一个普通函数处理)
- 使用内联函数是一种空间换时间的措施,若内联函数较长,较复杂且调用较为频繁时不建议使用
- 使用内联函数替代宏定义,能消除宏定义的不安全性(宏定义不检查函数参数,返回值)
示例:
#include <iostream>
using namespace std;
/*inline*/
inline double CircleArea(double r) //内联函数
{
double PI = 3.14;
return PI * r * r;
}
int main()
{
for (int i = 1; i <= 5; i++)
cout << "r = " << i << " area = " << CircleArea(i) << endl;
return 0;
}
二、宏定义 #define
#define
命令是C++中的宏定义,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。
1. 无参宏定义
//#define <宏名> <字符串>
#define PI 3.1415926
示例:
#include <iostream>
using namespace std;
/*#define <宏名> <字符串>*/
#define NUM 1+1 //宏定义
int main()
{
int Sum = NUM * NUM;
cout << "Sum = " << Sum << endl;
return Sum;
}
上述代码中,宏NUM
代表的字符串是1+1,我们在理解main函数时,容易产生的误解是先求出NUM
为1+1=2,然后在程序中计算Sum
时再使用乘法,即NUM
* NUM
= 2 * 2 = 4,然而Sum
的实际结果为3。
这是因为宏展开是在预处理阶段完成的,这个阶段把替换文本只是看作一个字符串,并不会有任何的计算发生,在展开时是在宏NUM
出现的地方,并且只是简单地使用字符串2+2来代替NUM
,并不会添加任何符号,所以对该程序展开后的计算结果是Sum
= 1+1*1+1,计算后Sum
= 3,这就是宏替换的实质。
如何使计算结果Sum
= 4:如下列代码所示,只需要将宏定义改为#define NUM (1+1)
#include <iostream>
using namespace std;
#define NUM (1+1) //宏定义
int main()
{
int Sum = NUM * NUM;
cout << "Sum = " << Sum << endl;
return Sum;
}
2. 有参宏定义
//#define <宏名> (<参数表>) <宏体>
#define AREA(i) i*i
说明: 需要在宏体中的每个参数加上括号,并在整个宏体上再加一个括号
#include <iostream>
using namespace std;
#define AREA(i) i*i //宏定义
int main()
{
int Sum = AREA(2+2);
cout << "Sum = " << Sum << endl;
return Sum;
}
上述程序中,宏AREA
参数i
为2+2,Sum
所得的结果“理应”为4 * 4=16,但该程序的实际结果为8。在这个程序中,2+2为AREA(i)
宏中的参数,应该将2+2替换宏定义中的i
,即为2 + 2 * 2 + 2 = 8。那如果按照前文中的解决办法,将2+2括起来,即把宏体中的i
括起来,是否可行?#define AREA(i) (i)*(i)
,对于AREA(2+2)
,替换为(2+2) * (2+2) = 16,确实可以解决。
但是对于AREA(2+2)/AREA(2+2)
又会如何?是否 = 1呢?这个宏替换后会变为 (2+2) * (2+2) / (2+2) * (2+2)即4 * 4 / 4 * 4,结果为16。解决方法为在整个宏体上再加一个括号,即#define AREA(i) ((i)*(i))
。
#include <iostream>
using namespace std;
#define AREA(i) ((i)*(i)) //宏定义
int main()
{
int Sum = AREA(2+2)/AREA(2+2);
cout << "Sum = " << Sum << endl;
return Sum;
}
3. 多行宏定义函数
示例1: 无return
#include <iostream>
using namespace std;
#define PI 3.14
#define AREA(r,Sum)\ //Sum在宏内部赋值
if(r >= 0)\
{\
Sum = PI * r * r;\
}
int main()
{
double Sum;
int r = 2;
AREA(r, Sum);
cout << "Sum = " << Sum << endl;
return Sum;
}
//结果
Sum = 12.56
示例2: 有return
#include <iostream>
using namespace std;
#define PI 3.14
#define ZERO 0
#define AREA(r)\
if (r >= ZERO)\
{\
return PI*(r)*(r);\
}\
else\
{\
return -1;\
}
double calcArea(double r)
{
AREA(r);
}
int main() {
double Sum = calcArea(2);
cout << "Sum = " << Sum << endl;
return 0;
}
//结果
Sum = 12.56
4. 宏中的#和##
在C++中,# 和 ## 是两个预处理操作符,通常用于宏定义中。
#
:字符串化操作符。在宏定义中,# 可以将参数转换为字符串形式。当 # 跟在参数前面时,它会将参数转换为一个字符串常量,其形式为双引号括起来的参数文本。例如:
#define STR(x) #x
std::cout << STR(hello); // 输出 "hello"
##
:连接操作符。在宏定义中,## 可以将两个标记连接成一个标记。例如:
#define CONCAT(x, y) x##y
int xy = CONCAT(10, 20); // 实际上被展开为 int xy = 1020;
注意,#
和 ##
这两个操作符只能用于宏定义中,在编译器进行预处理时会将它们的作用应用到宏的参数或者标记上,生成相应的文本。使用这两个操作符时需要注意它们的使用场景和语法规则,以避免意外的错误。
5. 常用预定义宏
说明: 这些C++编译器内置的预定义宏不能被取消定义(#undef
)也不能被编程人员重新定义。
-
__DATE__
代表日期,形式为Mmm dd yyyy 的字符串常量 -
__FILE__
表示当前源文件名,类型为字符串常量 -
__TIME__
代表时间,hh:mm:ss 形式的字符串型常量 -
__func__
和__FUNCTION__
都代表当前函数的函数名,类型为字符串常量 -
__LINE__
代表当前程序行的行号,类型为十进制整数常量
#include <iostream>
using namespace std;
int main()
{
const char* date = __DATE__;
const char* file = __FILE__;
const char* time = __TIME__;
const char* func = __func__;
const char* function = __FUNCTION__;
int line = __LINE__;
cout << "date: " << date << endl;
cout << "file: " << file << endl;
cout << "time: " << time << endl;
cout << "func: " << func << endl;
cout << "function: " << function << endl;
cout << "line: " << line << endl;
return 0;
}
//结果
date: Apr 28 2023
file: c:\users\ADiao\source\repos\c++\main.cpp
time: 17:10:00
func: main
function: main
line: 11
三、内联函数与宏定义的区别
内联函数和宏定义都可以用来实现代码的快速执行,但它们有以下几点区别:
- 参数类型安全性: 内联函数比宏定义更加类型安全。内联函数会对参数进行类型检查,而宏定义不会。这意味着,使用内联函数可以避免一些潜在的类型错误。
- 编译器优化: 内联函数是在编译期间展开的,因此它可以进行更多的编译器优化。而宏定义则是在预处理器展开,不能进行编译器优化。因此,使用内联函数通常可以获得更好的性能。
- 调试: 内联函数比宏定义更容易进行调试。因为内联函数是实际函数的一份副本,可以通过调试器跟踪到内联函数的执行过程。而宏定义则无法通过调试器进行调试。
- 名称空间: 内联函数位于名称空间中,而宏定义不属于任何名称空间。这意味着,内联函数可以避免名称冲突问题,而宏定义可能会导致名称冲突。
- 大小和可读性: 内联函数比宏定义更易于阅读和维护。宏定义的代码通常比较冗长,而内联函数则可以使用常规的C++语法编写,更加简洁易懂。另外,内联函数可以利用C++的函数重载和模板等特性,提高代码的可读性和可维护性。
总结: 虽然内联函数和宏定义都可以用来实现代码的快速执行,但内联函数通常比宏定义更优秀,因为它更加类型安全、可读性更强、容易调试和进行编译器优化。
如果这篇文章对你有所帮助,渴望获得你的一个点赞!
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)