004 C++基础篇

前言

大家好,本文将会为您带来内联函数,auto关键字,基于范围的for循环,指针空值nullptr相关知识

一、内联函数

什么是内联函数


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

内联函数的运用

inline int Add(int a, int b)
{
       return a + b;
}
int main()
{
       int ret = 0;
       ret = Add(1, 2);
       return 0;
}
在观察有无inline的差异时,我们首先应做以下配置(在Debug模式下)

鼠标右键点击项目
在这里插入图片描述
点击属性
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

以下是程序加上inline与不加inline的反汇编(待程序调试起来,鼠标右键点击反汇编)

不加inline
在这里插入图片描述

加inline
在这里插入图片描述

我们可以观察到不加inline会有call语句去调用函数,而加inline后call语句则被替换成了函数体(mov,add,mov)


inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用
缺陷:会使目标空间变大,优势:少了调用开销,提高程序运行效率

内联函数的局限性


1、内联机制用于规模较小,流程直接、频繁调用的函数。很多编译器都不支持内联递归函数,当一个规模较大的函数可能将不会在调用点内联地展开
2、inline不建议声名与定义分离
以下程序如果调用f(),则会出现错误

在这里插入图片描述

func.cpp
#include "Func.h"	
void f(int i)
{
	cout << i << endl;
}
void fx()
{
	f(1);  //在此文件里有f的声名和定义,不用call f()地址,直接展开
}

func.h
#include <iostream>
using namespace std;
inline void f(int i);

main.cpp
int main()
{
	error:f(1);//此语句虽然是内联,但是展不开,因为.h文件里只有声名
	//只能去链接,但是内联函数不生成指令进入符号表。符号表里没有该函数的地址,如果声名与定义分离,就会出现链接错误
	fx();//如果调用fx,便可以间接展开f的内联,原因是在func.cpp中,f函数的声名(头文件)和定义都存在
	return 0;
}


二、auto关键字( C++11 )

在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量 在C++11及之后的版本中,auto关键字被重新定义为类型推断的工具。使用auto可以让编译器根据变量的初始化表达式来推断其数据类型,从而省略了显式指定变量类型的过程。

注意:用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&

int main()
{
	int x = 1;
	auto& a = x; 
	auto b = 1.1;
	auto c = 'c';
	auto d = &a;
	auto* e = &a;
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;
	cout << d << endl;
	cout << e << endl;
	return 0;
}

auto不能推导的场景

1、当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

void TestAuto()
{
    auto a = 1, b = 2; 
    auto c = 3, d = 4.0;  // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}


2、auto不能作为函数的参数

void TestAuto(auto a)
{}
// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导


3、auto不能直接用来声明数组

void TestAuto()
{
    int a[] = {1,2,3};
    auto b[] = {456};
}

三、基于范围的for循环( C++11 )

如果要遍历一个数组,对于程序员来说,说明循环的范围是没有太大必要的,因此C++11中引入了基于范围的for循环。

由两部分组成:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围

int main()
{
	int array[] = { 1,2,3,4,5,6,7,8,9 };
	for (auto a : array)
	{
		cout << a << " ";
	}
	return 0;
}

在这里插入图片描述

四、指针空值nullptr( C++11 )

在C++98中,我们通常使用NULL来表示空指针 NULL实际上是一个宏,在传统的C头文件(stddef.h)中,如下:
#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif

可以看到,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦
NULL是一个宏定义,其值为0,因此可以被隐式地转换为整型。例如在重载函数时,如果有一个函数接受指针参数,另一个函数接受整型参数,传入NULL时可能会导致调用错误的函数
在C++11中,引入了nullptr关键字,用于表示空指针常量。nullptr是一个字面量,它的类型是nullptr_t。nullptr的引入主要是为了解决NULL的一些问题和不明确性。

注意:
1、 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。
2、 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
3、 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。

小结

如果本文存在遗漏或错误的地方,还请您能够指出,祝您天天开心啦!
posted @ 2023-08-06 11:33  Fan_558  阅读(3)  评论(0编辑  收藏  举报  来源