目录
1. 知识引入
在学C语言的宏时,我们了解了宏函数的概念与用法。我们也知道宏函数有一些易错点,稍不注意就可能出错!你若过不相信的话,就别看下面的代码自己写一个 a + b 的宏函数。
#define ADD(a, b) ((a) + (b))
int main()
{
cout << ADD(10, 20) << endl;
return 0;
}
不知道UU们写对了吗?为什么说宏函数容易写错呢?我们知道红的特性就是替换,这种特性会使我们遇到一些难以避免的错误,我们来看看这些常见的错误:
不加外面的括号替换之后由于运算符的优先级问题,导致结果出错!
#define ADD(a, b) (a) + (b)
int main()
{
cout << ADD(10, 20) * 2 << endl; //使用宏函数
cout << (10) + (20) * 2 << endl; //宏函数替换之后
return 0;
}
下面的代码同样也存由于运算符的优先级产生的问题:加法的优先级更高,导致结果出错。
#define ADD(a, b) (a + b)
int main()
{
int a = 1, b = 2;
cout << ADD(a | b, a & b) << endl; //使用宏函数
cout << (a | b + a & b) << endl; //宏函数替换之后
return 0;
}
那你可能会说了:那我每次都想正确的写法那样,多加几个括号不就能避免这种错误啦!很遗憾因为宏的替换特性,有些错误难以避免,观察如下代码:
我们尝试利用一个宏函数求解 b 的平方,但是我们用 ++b 来使用该函数,替换之后 ++b 会被执行两次,显然最终的结果并不是我们想要的。
#define Square(a) ((a) * (a))
int main()
{
int b = 2;
cout << Square(++b) << endl; //使用宏函数
cout << ((++b) * (++b)) << endl; //宏函数替换之后
return 0;
}
但是呢宏还是有优点的,下面我们就来看看宏的优缺点:
C++ 是既想要宏的优点,又不想要宏的缺点。因此C++引入了内联函数。
2. 知识点讲解
2.1 内联函数的使用
用法很简单,我们只要在一个函数返回值的前面加上 inline 关键字即可。像这样:
inline int Add(int x, int y)
{
return x + y;
}
int main()
{
cout << Add(2, 3) << endl;
return 0;
}
2.2 内联函数的特性
再来看看内联函数的概念吧:
这里的在调用内联函数的地方展开,指的是:内联函数经过编译之后,会直接在调用处转化成汇编代码,而不是通过 call 指令,跳转到函数的实际地址处进行函数汇编代码的执行。
在VS2019的debug模式下,默认是不会对程序进行优化的,我们看不到内联函数的展开,但是在release模式下又不支持调试,因此我们需要进行相关的设置才能看到内联函数的展开。
在项目---->属性中更改下面的设置即可
我们通过调试下面的代码,然后查看汇编代码之后发现 Add 函数的调用并不是 call 函数地址而是直接在函数调用的地方展开,变成了汇编代码。
2.2 内联函数的注意点
通过上面的调试过程我们直到内联函数是直接在调用处展开的,因此内联函数并没有函数地址。也就是说,内联函数不会出现在符号表中,也就意味着内联函数不可使用函数定义与函数声明分开的写法。
为什么会报错呢?UU们不妨结合编译,链接的相关知识想一想。
Add函数是内联函数,在调用Add函数的地方,编译器会在调用的地方展开。但是在调用Add函数的源文件中,只有Add函数的声明。 编译器无法做到在调用的地方展开,于是编译器会尝试通过 call 指令去调用函数,但是内联函数不会在符号表中出现,就会找不到函数的地址。因此会出现链接错误。
因此我们得出一个重要的结论:内联函数的声明与定义不可分离。
那应该怎么解决这个问题呢?很简单,直接在头文件里面写内联函数的定义即可。
3. 面试题