C++初阶 内联函数和nullptr

目录

    • 内联函数
    • nullptr

内联函数

C++初阶 内联函数和nullptr_第1张图片

inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率

展开是指不用调用函数,把函数功能直接放到这实现

  • 在release模式下,查看编译器生成的汇编代码中是否存在call Add
  • 在debug模式下,需要对编译器进行设置,否则不会展开(因为debug模式下,编译器默认不会对代码进行优化,以下给出vs2022的设置方式)
    C++初阶 内联函数和nullptr_第2张图片
    C++初阶 内联函数和nullptr_第3张图片
  • inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。
  • inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性

下图这种函数规模大的情况下编译器就会忽略inline特性
C++初阶 内联函数和nullptr_第4张图片

内联函数可能会使目标文件变大:
C++初阶 内联函数和nullptr_第5张图片

假设是傻傻的函数功能直接复制了函数中代码,但是一般不会函数功能直接复制过来

100个地方调用就是100个地方展开就是100*100行
200行是因为,调用只需要一行指令就可以,call跳过去执行指令然后回来,只要call跳过去就要建立栈帧,调用的指令加上被调用函数返回地址,分配栈空间的指令

  • inline不能声明和定义出现在两个文件,不然会出现链接错误
    编译器认为内联函数在用的地方就展开了,就不会把相关指令、函数地址放进符号表。
Func.h文件中
inline void f(int i);

void fx();
Func.cpp文件中
void f(int i)
{
	cout <<"f(int i)"<< i << endl;
}

void fx()
{
	// 既有声明,也有定义,是内联直接展开
	//f(1);
}
test.cpp文件中
在main函数中
f(1);//会出现问题
fx();//不会出现问题

直接调用内联函数找不到地址,但是可以间接调用,说明 f 这个函数是在的,可以间接调用是因为Func.cpp文件中既有f声明也有定义,在fx函数中直接把f展开了。

解决方案:可以把内联函数的声明和定义都放到.h文件中

Func.h文件中
inline void f(int i)
{
	cout << "f(int i)" << i << endl;
}

void fx();

我们以前学习时,假如Func.h被两个cpp文件包含,这时在test.cpp和func.cpp两边都有fx()的定义,就重定义,发生链接冲突了。
但是对于内联函数 f 不会有这种重定义的情况,因为内联函数不生成地址进入符号表,在调用的地方就展开了。

C++替换宏避坑
inline替换宏函数
const enum替换宏常量

nullptr

NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:

#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif

条件编译,如果是c++定义的__cplusplus宏,那么#define NULL 0,else就是没有定义这个宏,那么#define NULL ((void *)0) 比如c语言。

在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)0。

void f(int i)
{
	cout << "f(int)" << endl;
}

void f(int* p)
{
	cout << "f(int*)" << endl;
}
int main()
{
	f(0);//第一个
	f(NULL);//第一个
	
	f(nullptr);//第二个
	f((int*)NULL);//第二个

	return 0;
}

在c++中构成函数重载, f(0);和f(NULL);都会匹配到第一个f(int)
这是因为c++的一个坑,把NULL定义成了0,为了不影响其他使用者,就不改了,用新关键字nullptr来解决,可以认为值是0但是类型是void*了。

  1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。
  2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。

你可能感兴趣的:(C++初阶,c++)