【C++】内联函数、auto关键字、基于范围的for循环、指针空值nullptr

这篇具有很好参考价值的文章主要介绍了【C++】内联函数、auto关键字、基于范围的for循环、指针空值nullptr。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【C++】内联函数、auto关键字、基于范围的for循环、指针空值nullptr,C++,c++,开发语言,c语言,算法

👀樊梓慕:个人主页

 🎥个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C++》

🌝每一个不曾起舞的日子,都是对生命的辜负


目录

前言

1.内联函数

1.1概念

1.2特性

2.auto关键字

2.1类型别名思考

2.2auto简介

2.3auto的使用细则

2.4auto不能使用的场景

3.基于范围的for循环

3.1范围for的用法

3.2范围for的使用条件

4.指针空值nullptr


前言

本篇文章是进入类和对象学习的前一课,也是最后一些与大家交代的C++入门知识,大家可以收藏下方便记忆。


欢迎大家📂收藏📂以便未来做题时可以快速找到思路,巧妙的方法可以事半功倍。

=========================================================================文章来源地址https://www.toymoban.com/news/detail-728461.html

GITEE相关代码:🌟fanfei_c的仓库🌟

=========================================================================


1.内联函数

1.1概念

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

观察一些普通函数的汇编代码:

【C++】内联函数、auto关键字、基于范围的for循环、指针空值nullptr,C++,c++,开发语言,c语言,算法

那我们学习过函数栈帧的同学肯定非常熟悉,这里去call到add的地址。

那内联函数存在的意义就是省略掉这个过程,给程序运行提速。

如果在上述函数前增加inline关键字将其改成内联函数,在编译期间编译器会用函数体替换函数的调用。


🎦我们来看看如何查看这一过程🎦

查看方式:

  • 在release模式下,查看编译器生成的汇编代码中是否存在call Add。
  • 在debug模式下,需要对编译器进行设置,否则不会展开(因为debug模式下,编译器默认不会对代码进行优化,以下给出vs2013的设置方式)。

【C++】内联函数、auto关键字、基于范围的for循环、指针空值nullptr,C++,c++,开发语言,c语言,算法

【C++】内联函数、auto关键字、基于范围的for循环、指针空值nullptr,C++,c++,开发语言,c语言,算法


1.2特性

1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。
2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。


下图为《C++prime》第五版关于inline的建议:

【C++】内联函数、auto关键字、基于范围的for循环、指针空值nullptr,C++,c++,开发语言,c语言,算法


3. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。

比如:

// F.h
#include <iostream>
using namespace std;
inline void f(int i);
// F.cpp
#include "F.h"
void f(int i)
{
	cout << i << endl;
}
// main.cpp
#include "F.h"
int main()
{
	f(10);
	return 0;
}
// 链接错误:main.obj : error LNK2019: 无法解析的外部符号 "void __cdecl f(int)" (?
//f@@YAXH@Z),该符号在函数 _main 中被引用

🎁【面试题】🎁


宏的优缺点?
优点:
1.增强代码的复用性。
2.提高性能。
缺点:
1.不方便调试宏。(因为预编译阶段进行了替换)
2.导致代码可读性差,可维护性差,容易误用。
3.没有类型安全的检查 。


C++有哪些技术替代宏?
1. 常量定义 换用const enum
2. 短小函数定义 换用内联函数


2.auto关键字

2.1类型别名思考

随着程序越来越复杂,程序中用到的类型也越来越复杂,经常体现在:

  • 类型难于拼写
  • 含义不明确导致容易出错
#include <string>
#include <map>
int main()
{
	std::map<std::string, std::string> m{ { "apple", "苹果" }, { "orange", "橙子" },
	{"pear","梨"} };
	std::map<std::string, std::string>::iterator it = m.begin();
	while (it != m.end())
	{
		//....
	}
	return 0;
}

std::map<std::string, std::string>::iterator 是一个类型,但是该类型太长了,特别容易写错。聪明的同学可能已经想到:可以通过typedef给类型取别名,比如:

#include <string>
#include <map>
typedef std::map<std::string, std::string> Map;
int main()
{
	Map m{ { "apple", "苹果" },{ "orange", "橙子" }, {"pear","梨"} };
	Map::iterator it = m.begin();
	while (it != m.end())
	{
		//....
	}
	return 0;
}

使用typedef给类型取别名确实可以简化代码,但是typedef有会遇到新的难题:

typedef char* pstring;
int main()
{
	const pstring p1; // 编译成功还是失败?
	const pstring* p2; // 编译成功还是失败?
	return 0;
}

 分析:

  • const pstring p1;:这行代码定义了一个名为 p1 的常量指针,指向 char 类型的数据。这意味着 p1 是一个常量指针,指向的数据是不能修改的。编译器会发出警告,因为 p1 是一个常量指针,但是没有指定它指向的数据是一个常量。
  • const pstring* p2;:这行代码定义了一个名为 p2 的指针,指向 pstring 类型的常量指针。这意味着 p2 是一个指向常量指针的指针。编译器不会发出警告,因为 p2 是一个指针,只是指向 pstring 类型的常量指针。

需要注意的是,虽然编译器可能会警告或报错,但实际的编译结果可能会因编译器的不同而有所差异。这是由于不同的编译器对于 const 修饰符的处理方式可能有所不同。

总之,根据代码的定义和使用情况,编译结果可能会有警告或错误,具体取决于编译器的实现。建议在使用typedef时,确保定义和使用的一致性,并遵循编译器的警告和错误提示。

即在编程时,常常需要把表达式的值赋值给变量,这就要求在声明变量的时候清楚地知道表达式的类型。然而有时候要做到这点并非那么容易,因此C++11给auto赋予了新的含义。


2.2auto简介

在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,但遗憾的是一直没有人去使用它,大家可思考下为什么?
C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

int TestAuto()
{
	return 10;
}
int main()
{
	int a = 10;
	auto b = a;
	auto c = 'a';
	auto d = TestAuto();
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;
	//auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化
	return 0;
}

注意:使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。 


2.3auto的使用细则

1.auto与指针和引用结合起来使用
用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&。

比如:

int main()
{
	int x = 10;
	auto a = &x;
	auto* b = &x;
	auto& c = x;
	cout << typeid(a).name() << endl;
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	*a = 20;
	*b = 30;
	c = 40;
	return 0;
}

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

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

2.4auto不能使用的场景

1.auto不能作为函数的参数,返回值。

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

2. auto不能直接用来声明数组。

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

3. 为了避免与C++98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法。
4. auto在实际中最常见的优势用法就是跟以后会讲到的C++11提供的新式for循环,还有lambda表达式等进行配合使用。

5.auto真正的意义:定义对象时,类型较长,用auto比较方便。


3.基于范围的for循环

3.1范围for的用法

在C++98中如果要遍历一个数组,可以按照以下方式进行:

void TestFor()
{
	int array[] = { 1, 2, 3, 4, 5 };
	for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
		array[i] *= 2;
	for (int* p = array; p < array + sizeof(array) / sizeof(array[0]); ++p)
		cout << *p << endl;
}

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。

因此C++11中引入了基于范围的for循环。

for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。 

void TestFor()
{
	int array[] = { 1, 2, 3, 4, 5 };
	for (auto& e : array)//想要修改数组中的值,加引用&,这里不能用指针!
		e *= 2;
    //依次取数组中数据赋给e,自动++,自动判断结束
	for (auto e : array)//可用实际类型,但推荐使用auto
		cout << e << " ";
	return 0;
}

注意:与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环。 


3.2范围for的使用条件

1.for循环迭代的范围必须是确定
对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围。
注意:以下代码就有问题,因为for的范围不确定

void TestFor(int array[])//传参得到数组首元素地址,不是数组!
{
	for (auto& e : array)//所以不确定范围
		cout << e << endl;
}

2.迭代的对象要实现++和==的操作。(关于迭代器这个问题,以后会讲,现在提一下,没办法讲清楚,现在大家了解一下就可以了) 


4.指针空值nullptr

在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,我们基本都是按照如下方式对其进行初始化:

void TestPtr()
{
	int* p1 = NULL;
	int* p2 = 0;
	// ……
}

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

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

可以看到,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。

不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如:

void f(int)
{
	cout << "f(int)" << endl;
}
void f(int*)
{
	cout << "f(int*)" << endl;
}
int main()
{
    //调用第一个函数
	f(0);
    //同样调用第一个函数,但此时就有歧义了,可能程序员想的是NULL此时为空指针
	f(NULL);//可能程序员像调用第二个函数就只能用下面的方法了
    //调用第二个函数
	f((int*)NULL);
	return 0;
}

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

注意:

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

🌠马上就进入类和对象的学习啦🌠


=========================================================================

如果你对该系列文章有兴趣的话,欢迎持续关注博主动态,博主会持续输出优质内容

🍎博主很需要大家的支持,你的支持是我创作的不竭动力🍎

🌟~ 点赞收藏+关注 ~🌟

=========================================================================

到了这里,关于【C++】内联函数、auto关键字、基于范围的for循环、指针空值nullptr的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++初阶】引用&内联函数&auto关键字&范围for循环&nullptr

    ========================================================================= 个人主页还有更多系列专栏: 小白不是程序媛 我的小仓库: Gitee C++系列专栏: C++头疼记 ========================================================================= 目录 前言 引用 概念 引用的特点 常引用  引用的使用场景 做参数  做返

    2024年02月08日
    浏览(36)
  • 从C语言到C++(第一章_C++入门_下篇)内联函数+auto关键字(C++11)+范围for +nullptr

    目录 1. 内联函数 1.1 内联函数的概念 1.2 内联函数的特性 1.3 宏的优缺点和替代方法 2. auto(C++11) 2.1 改版前的auto 2.2 C++11的auto 2.3 auto 的使用场景 2.4 使用auto的注意事项 3. 范围 for(C++11) 3.1 范围 for 的用法 3.2 范围 for 的使用条件 4. 指针空值 nullptr 4.1 C++ 98 中的指针空

    2023年04月21日
    浏览(32)
  • 【C++入门】引用、内联函数、auto 关键字

    从语法上理解,引用就是给变量取一个 别名 ,它没有独立空间,和它引用的变量共用一块空间。 例子: 值得注意的是,C++ 中的引用一经初始化,后面是改变不了指向的(这点与Java有所不同),而且引用是必须初始化的。 引用的类型也必须和原变量对应,显然,你肯定不能

    2024年02月16日
    浏览(37)
  • 【C++】引用、内联函数、auto关键字等

    前言:在前面我们讲解了C++入门基础的一些学习例如命名空间、缺省参数、函数重载等。今天我们将进一步的学习,跟着博主的脚步再次往前迈一步吧。 💖 博主CSDN主页:卫卫卫的个人主页 💞 👉 专栏分类:高质量C++学习 👈 💯代码仓库:卫卫周大胖的学习日记💫 💪关注博

    2024年02月20日
    浏览(42)
  • 【C++初阶(三)】引用&内联函数&auto关键字

    目录  前言  1. 引用   1.1 引用的概念   1.2 引用的特性  1.3 引用的权限  1.4 引用的使用  1.5 引用与指针的区别 2. 内联函数 2.1  什么是内联函数 2.2  内联函数的特性  3. auto  3.1 auto简介  3.2 auto使用规则  3.3 auto不能使用的场景 4.  基于范围的for循环  4.1 范围for使用

    2024年02月08日
    浏览(47)
  • 【C++】C++入门第二课(函数重载 | 引用 | 内联函数 | auto关键字 | 指针空值nullptr)

    目录 前言 函数重载 概念 重载函数的条件 C++支持重载函数的原理--名字修饰 引用 概念 特性 常引用(const引用) 使用场景 传值,传引用效率比较 引用和指针的区别 内联函数 概念 特性 auto(C++11) auto简介 auto的使用规则 指针空值nullptr(C++11) C++98中的指针空值 结语

    2024年04月15日
    浏览(37)
  • 【C++基础】auto关键字(C++11)(auto的使用细则;auto不能推导的场景;auto的使用场景;基于范围的for循环)

    在早期C/C++( C++98 )中auto的含义是:使用auto修饰的变量,是具有 自动存储器的局部变量 ,但遗憾的是一直没有人去使用它。因为在函数内定义的变量默认就是局部变量。 C++11 中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的 类型指示

    2023年04月13日
    浏览(37)
  • 【C++】: auto关键字(C++11)+基于范围的for循环(C++11)+指针空值nullptr(C++11)

    随着程序越来越复杂,程序中用到的类型也越来越复杂,经常体现在: 类型难于拼写 含义不明确导致容易出错 std::mapstd::string, std::string::iterator 是一个类型,但是该类型太长了,特别容 易写错。聪明的同学可能已经想到:可以通过typedef给类型取别名,比如: 使用typedef给类

    2024年02月08日
    浏览(33)
  • 【C++入门必备知识:|引用| +|auto关键字| + |范围for|】

    在这里插入图片描述 引用不是新定义一个变量,而是给已经存在的变量取别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存区间。 比如水浒传里的李逵 小名叫铁牛,江湖人称黑旋风。这些都是他,只不过名字不同。 类型 引用变量名=引用实体

    2023年04月22日
    浏览(41)
  • 【C++深入浅出】初识C++下篇(auto关键字、范围for、nullptr指针)

    目录 一. 前言 二. auto 2.1 auto的引入 2.2 auto简介 2.3 auto的使用细则 2.4 auto不能推导的场景 三. 基于范围的for循环(C++11) 3.1 范围for的语法 3.2 范围for的原理 3.3 范围for的使用条件 四. 指针空值nullptr(C++11)         上期我们介绍了c++新增的两个重要语法:引用和内联函数,今

    2024年02月11日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包