C++【基础语法(part 3)】

前言

在前文我们讲解了C++的诞生与历史,顺便讲解一些C++的小语法,本文会继续讲解C++的基础语法知识。

1.inline(内联函数)

  1. inline是C++新加入的关键字,用inline修饰的函数叫做内联函数,编译时C++编译器会在调用的地方将函数展开,这样每次调用内联函数就不需要建立新的栈帧,就可以提高效率
#include<iostream>
using namespace std;

inline int Add(int& a, int& b)
{
	int ret = a + b;
	return ret;
}

int Sub(int& a, int& b)
{
	int ret = a - b;
	return ret;
}
int main()
{
	int a = 10;
	int b = 5;
	// 可以通过汇编观察程序是否展开

	// 有call Add语句就是没有展开,没有就是展开了

	int ret = Add(a, b);
	cout << ret << endl;

	int tmp = Sub(a, b);
	cout << tmp << endl;

	return 0;
}

只要在有call一个地址就是没有展开。
在这里插入图片描述
我们看到不管是有inline修饰的函数,还是没有inline修饰的函数,他们都有在call一个地址(也就是没有展开函数),那不就和inline会展开函数的定义相悖吗。

其实是因为我使用的是VS编译器,VS编译器在debug版本下默认是不展开inline函数的,这样是为了方便调试,如果我们在VS中想要展开,那么就要设置一下两个地方。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

修改了这些之后,我们就可以看到inline的展开了

在这里插入图片描述

  1. 但是我们想要知道的是,inline对于编译器而言只是一个建议,也就是说,即使你在函数前加了inline,编译器也有可能不展开,不同编译器关于inline在什么情况下展开各不相同,因为C++标准并没有规定,所以inline适合的场景是使用频繁的短小函数,对于递归函数、代码比较多的函数,即使加上了inline,编译器也不会展开。

为什么要这样做呢,因为是防止有些程序员不靠谱,在每个函数前都加上inline,如果都展开,那么这个代码量就太大了。

假设我有一个一百行代码的函数a,并且我在函数前加上了inline,这时,我调用了1万次a,如果编译器在每次调用a的时候就展开,那么这个工程编译处理下来,至少要执行 100 ∗ 10000 100*10000 10010000(一百万)条可执行程序,这样效率就会下降。

如果不展开,那么编译处理下来,就只要执行 10000 + 100 10000+100 10000+100(call一百次)条可执行程序,比起前面的一万行,这时不展开的效率就会高很多。

//该函数在编译时就不会展开了
inline int Add(int& a, int& b)
{
	int ret = a + b;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	return ret;
}

int main()
{
	int a = 10;
	int b = 5;
	// 可以通过汇编观察程序是否展开

	// 有call Add语句就是没有展开,没有就是展开了

	//inline修饰的函数
	int ret = Add(a, b);
	//cout << ret << endl;

	return 0;
}

在这里插入图片描述

  1. C++设计inline的目的是为了替代C的宏函数,虽然C语言的宏函数也会在预处理的时候替换展开,但是宏函数实现很复杂很容易出错,并且不好调试。

  2. inline不建议声明和定义分离到两个文件,分离会导致链接错误;因为inline被展开,就没有了函数地址,那么链接时就会报错。

//Func.h
#pragma once
#include<iostream>
using namespace std;
inline int Add(int& a, int& b);

//Func.cpp
#include"Func.h"

int Add(int& a, int& b)
{
	int ret = a + b;
	return ret;
}

//test.cpp
#include"Func.h"
int main()
{
	int a = 10;
	int b = 5;
	int ret = Add(a, b);
}

在这里插入图片描述

2.nullptr

在C语言表述一个函数为空函数是使用NULL,但NULL其实是一个宏函数,在C头文件(stddef.h)中,可以看到如下代码:

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

这段代码的意思是,如果是在C语言环境下,那么NULL就会被定义成((void) * 0),如果是在C++环境下,那么就直接将NULL定义为0。无论采取何种定义,在使用空值的指针时,都不可避免的遇到一些问题,例如我想通过f(NULL)来调用指针版的f(int*)函数,但由于NULL被定义成了0,那么就会调用f(int x),这样就会于程序的目的相悖

#include<iostream>
#include<stdlib.h>
using namespace std;
void F(int x)
{
	cout << "void F(int x)" << endl;
}

void F(int* x)
{
	cout << "void F(int* x)" << endl;
}

int main()
{
	F(0);

	F(NULL);

	return 0;
}

在这里插入图片描述
并且我们调用f((void*)NULL)时也会报错,因为C++检查的更严格,C++环境下,void*不会自动转换成对于的类型*,如果要转换,就必须使用强制类型转换(C语言会自动转换)。
在这里插入图片描述

既然NULL在定义上已经不是一个指针了,那么我们就需要一个真正意义上的空指针。

那么C++11就引入了nullptr,nullptr是一个特殊的关键字,是一种特殊类型的字面量,他可以转换成任意类型的指针类型。使用nullptr定义空指针也可以避免类型转换的问题,因为nullptr只能被隐式转换为指针类型,不能被上转换成其他类型。

#include<iostream>
#include<stdlib.h>
using namespace std;
void F(int x)
{
	cout << "void F(int x)" << endl;
}

void F(int* x)
{
	cout << "void F(int* x)" << endl;
}

int main()
{
	F(0);

	F(NULL);

	//F((void*) NULL);
	F(nullptr);

	int* a = nullptr;
	double* b = nullptr;

	//int x = nullptr;
	return 0;
}

在这里插入图片描述
在这里插入图片描述

结语

本文进一步讲解了c++基础知识,讲解了关键词inline的使用,如何在VS下看到以及他的目的和角色;讲解了nullptr的诞生原因以及使用。

最后感谢您能阅读完此片文章,如果有任何建议或纠正欢迎在评论区留言,也可以前往我的主页看更多好文哦(点击此处跳转到主页)。
如果您认为这篇文章对您有所收获,点一个小小的赞就是我创作的巨大动力,谢谢!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值