当前位置: 首页 > news >正文

C++入门:内联函数、auto关键字、基于范围for循环及指针空值nullptr

目录

一. 内联函数

1.1 内联函数的概念

1.2 内联函数的特性

1.3 内联函数和宏的优缺点对比

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

2.1 auto的功能

2.2 auto在使用时的注意事项

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

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


一. 内联函数

1.1 内联函数的概念

内联函数,就是使用inline关键字,让C++编译器在调用函数的位置处将函数在开展被调用的位置,从而减少函数栈帧创建和销毁的时间。

内联函数的声明方法:inline 返回值类型 函数名(参数列表)。下面的代码以add函数为例,演示了内联函数的定义和声明的方法。

inline int add(int x, int y)
{return x + y;
}int main()
{int x = 10, y = 10;int ret1 = add(x, y);int ret2 = add(x, y);return 0;
}
  • 在Debug模式下,内联函数默认不展开,但可以通过更改编译器设置,来让内联函数在Debug模式下也展开。
  • 在Release版本下,内联函数展开。

知识拓展:Debug被称为调试版本,编译器不会对程序进行优化,但可以调试找bug。Release版本被称为发布版本,编译器会对程序进行优化,但是程序员不可以在Release版本下调试。

设置在Debug版本下内联函数展开的方法:

  1. 打开属性设置,选择C/C++ -> 常规,将调试信息格式改为程序数据库。
  2. 选择C/C++ -> 优化,将内联函数扩展改为:只适用于_inline (Ob1)。
图1.1  更改设置使编译器在Debug版本下也展开内联函数

图1.2和1.3展示了使用内联函数的不使用内联函数时,调用add函数的汇编代码的区别。不使用内联函数时,调用add函数要先为通过call指令来跳转,建立函数栈帧后才会执行函数中的指令。使用inline时,汇编语言中不再有call指令,函数的指令直接展开在主函数中。

图1.2  不使用内联函数时的汇编代码
图1.3 使用内联函数时的汇编代码

1.2 内联函数的特性

内联函数是一种以空间换时间的方法

C++内联函数类似于宏,都是在使用的位置展开,从而减少函数栈帧创建和销毁的开销。假设,一个函数(func)编译完成后有10条汇编指令,调用这个函数1000次,使用内联和不使用内联的情况下,汇编指令的条数为:

  • 不使用内联函数:1000 + 10次,call func() 1000次 + 10条函数指令。
  • 使用内联函数:1000*10次,每次调用展开函数,每次调用都需要独立的10条指令。

inline对于编译器来说只是建议,展不展开最终由编译器决定

这一点和register寄存器关键字类似,register关键字的功能是建议将变量存储在寄存器,仅仅是建议,到底要不要将变量放在寄存器由编译器决定而不是register。

  • 对于比较长(指令较多)的函数,即使不进行展开,创建函数栈帧的开销相对于执行函数指令很小,编译器很可能就不展开。
  • 递归函数不适用于inline,因此,对于存在递归调用的函数,即使使用inline进行声明,编译器也不会展开函数。

inline声明和定义不能分离

由于inline会直接在调用函数的位置处展开,在编译阶段生成的符号表中不会存储函数的地址,因此,如果定义和声明分离,则会存在找不到函数的问题,这样会发生链接错误。下面的代码在头文件中使用inline声明sub函数,在func.c文件中定义函数,报错。

//head.h
#include<iostream>
using namespace std;
inline int sub(int x, int y);//func.cpp
#include "head.h"
inline int sub(int x, int y)
{return x - y;
}//test.cpp
#include "head.h"
int main()
{int x = 10, y = 10;int ret1 = sub(x, y);int ret2 = sub(x, y);return 0;
}
图1.4 代码报错信息

总结:内联函数与宏类似,适用于函数代码量少且频繁被调用的场景。

1.3 内联函数和宏的优缺点对比

内联函数和宏共有的优点:

  • 省去了函数栈帧的创建消耗,提高了代码的效率。

内联函数和宏共有的缺点:

  • 代码量变大

内联函数相对于宏的优点:

  • 可读性好,读内联函数与读普通函数无明显区别。
  • 宏本质上是替换,不可以调试,而内联函数在Debug模式下默认不展开,可以进行调试。
  • 宏没有类型检查,而内联函数有类型检查。

下面的代码就会报警告:从“double”转换到“int”,可能丢失数据

int add(int x, int y)
{return x + y;
}int main()
{double x = 10, y = 10;int ret1 = add(x, y);int ret2 = add(x, y);return 0;
}

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

2.1 auto的功能

在C++98和C++03的标准中,auto关键字的作用是使变量出了定义变量的作用域就自动销毁,但是,在默认情况下,变量都是具有auto属性的且出了定义变量的作用域就会自动销毁。因此,在早期的C++标准下,auto关键字没有任何实质性意义。

C++11标准中,auto被赋予了全新的功能,摒弃了C++98和C++11原来的作用。auto的新功能为:自动推断类型。

下面的代码中通过auto来声明变量类型,再通过typied().name来打印类型。typeid().name()获取类型时,经常会省去const。

int main()
{int a = 10;char c = 'a';auto a1 = a;auto a2 = c;auto a3 = 10;auto a4 = 'a';auto a5 = 12.345;//typeid().name() 能够自动识别变量(常量)类型并实现对变量类型的打印//但是,使用typeid获取类型很多时候会舍去constcout << typeid(a1).name() << endl;cout << typeid(a2).name() << endl;cout << typeid(a3).name() << endl;cout << typeid(a4).name() << endl;cout << typeid(a5).name() << endl;//auto类型数据在定义是就必须初始化,因为编译器要通过判断其被初始化的数据的类型来判断auto的类型//auto a6;  //编译不通过return 0;
}

如果定义const int a = 10,在使用auto b = a将a的值赋给b,这时b是可以被修改的,auto不会将a的const属性带给b。如果希望b不能被修改,则应当使用const auto b = a。

int main()
{const int a = 10;auto b = a;cout << b << endl;b = 30;cout << b << endl;//const auto b = a;  //这时b具有只读const属性//b = 40;   //报错return 0;
}

2.2 auto在使用时的注意事项

1、在使用auto声明变量是必须初始化

auto是根据变量被初始化的数据类型来推断变量类型的,如果不初始化,那么就无法确定auto是什么类型的数据,编译会报错。

int main()
{int a = 10;auto b;return 0;
}
图2.1  程序报错信息

2、使用auto在同一行声明多个变量时,类型必须相同

编译器只会对第一个变量的类型进行推导,用推导出来的类型定义后面的变量。

int main()
{auto a = 10, b = 20;  //编译通过auto c = 20, d = 12.23;   //编译报错
}

3、auto不能做为函数的类型

auto需要通过被初始化的数据来推断,而函数形参类型没有初始化,无法推断函数具体的参数类型。

void func(auto x)
{cout << "void func(auto x)" << endl;
}int main()
{func(10);return 0;
}
图2.2  程序报错信息

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

这里不需要纠结原因,明确不用auto声明数组就好。

int main()
{int a[] = { 1,2,3 };//auto a1[] = { 1,2,3 };  //编译报错return 0;
}

5、使用auto声明指针类型和引用类型 

在声明指针类型时,auto*和auto没有任何区别,但使用auto声明引用类型时,就必须写为auto&,&不能丢。

int main()
{int a = 10;auto pa1 = &a;   //auto获取指针类型auto* pa2 = &a;  //auto*获取指针类型auto& ra = a;   //使用auto定义a的引用cout << typeid(a).name() << endl;   //intcout << typeid(pa1).name() << endl;  //int *cout << typeid(pa2).name() << endl;  //int *cout << typeid(ra).name() << endl;   //int *pa1 = 20;   //a = 20;*pa2 = 30;   //a = 30;ra = 40;cout << &a << endl;cout << &ra << endl; //&a和&ra相同return 0;
}
图2.3 程序运行结果

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

在之前使用C语言的时候,要打印数组中的每个元素,我们需要获取数组元素的个数,通过for循环来实现,就有了下面的代码。但是,普通for循环sizeof(arr)/sizeof(arr[0])使用起来相对复杂,有更简单的方法吗?当然有。

int main()
{int arr[] = { 1,2,3,4,5 };for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i){printf("%d ", arr[i]);}return 0;
}

在C++11标准中,给出了基于范围的for循环语法,语法格式为:for(auto x : arr)其实现的功能为:将arr数组中的数据依次赋给x,直到数组的最后一个元素,每一个数据赋给x表示一层循环。下面这段代码使用基于范围的for循环,打印数组中的每个数据。

int main()
{int arr[] = { 1,2,3,4,5 };for (auto x : arr){cout << x << " ";}cout << endl;return 0;
}

不需要程序员去计算数组中元素个数,这里编译器会自动处理。同时,有两点注意事项:

  1. auto x : arr中的x可以被替换为i、j等任意名称。
  2. auto可以被替换为int,但是,如果数组元素的类型发生变化,就需要更改int,因此最好直接声明为auto。

那么,如何通过基于范围的for循环修改数组中元素的值呢?这里就需要引用。

int main()
{int arr[] = { 1,2,3,4,5 };for (auto& a : arr){a += 1;   //数组每个元素+1}for (auto x : arr){cout << x << " ";}cout << endl;return 0;
}
图3.1 程序运行结果

看到这里,可能会有疑惑:引用在有了引用实体之后,就不能再引用其他实体,那么为什么for (auto& a : arr)不存在改变引用实体的问题呢?答:每一层for循环结束后,变量a都会被销毁,进入下一层循环时,a是再次创建,而不是更改以前的引用实体,所有不存在问题。

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

在C++03、C++98和C语言中,使用NULL来表示指针空值。我们可以看到,C++头文件中对NULL的定义如下:

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

也就是说,在C++中,NULL只是将NULL定义为值为0的宏,并没有将其转化为指针类型,所以,NULL存在类型不明确问题。为了解决C++98和C++03中NULL类型冲突的问题,C++11引入了新的指针空值nullptr,其定义为:#define nullptr ((void *)0) -- 将0强转类型转化为void*类型。

下面代码定义了一组函数重载,两个func函数的参数分别为int类型和int*类型,如果传入NULL调用func,我们希望调用的函数为func(int*),但实际上是func(int)被调用了,这是因为NULL被替换为了0,从而误调用了func(int),而传入nullptr就不存在问题。

void fun(int x)
{cout << "void fun(int x)" << endl;
}void fun(int* x)
{cout << "void fun(int* x)" << endl;
}int main()
{//C++98、C++03int* p1 = NULL;int* p2 = 0;  //会与数字0发生冲突//C++11int* p3 = nullptr;fun(NULL);     //void fun(int x)fun(nullptr);  //void fun(int* x)return 0;
}
图4.1 程序运行结果

 

相关文章:

C++入门:内联函数、auto关键字、基于范围for循环及指针空值nullptr

目录 一. 内联函数 1.1 内联函数的概念 1.2 内联函数的特性 1.3 内联函数和宏的优缺点对比 二. auto关键字&#xff08;C11&#xff09; 2.1 auto的功能 2.2 auto在使用时的注意事项 三. 基于范围的for循环&#xff08;C11&#xff09; 四. 指针空值nullptr&#xff08…...

Python遗传算法

1 人工智能概述 2020中国人工智能产业年会在苏州召开&#xff0c;会上发布的《中国人工智能发展报告2020》显示&#xff0c;过去十年(2011-2020) &#xff0c;中国人工智能专利申请量达389571件&#xff0c;占全球总量的74.7%&#xff0c;位居世界第一。 报告指出&#xff0c;…...

GEE学习笔记 六十四:绿色中国报告(个人版)

2019年上半年在遥感圈里最火的一篇文章莫过于这篇《China and India lead in greening of the world through land-use management》&#xff08;China and India lead in greening of the world through land-use management | Nature Sustainability&#xff09;&#xff0c;…...

【Kubernetes】【十八】数据存储 高级存储 配置存储

高级存储 PV和PVC ​ 前面已经学习了使用NFS提供存储&#xff0c;此时就要求用户会搭建NFS系统&#xff0c;并且会在yaml配置nfs。由于kubernetes支持的存储系统有很多&#xff0c;要求客户全都掌握&#xff0c;显然不现实。为了能够屏蔽底层存储实现的细节&#xff0c;方便用…...

传输层TCP与UDP协议

目录 传输层 传输层功能 传输层所提供的服务 传输层的两个协议 TCP协议与UDP协议 端口 端口分类 IP地址和端口的关系 UDP协议 前言&#xff1a; UDP报文格式 检验和的伪首部 伪首部内容 TCP协议 TCP报文格式 TCP协议数据段的理解 TCP的伪首部 伪首部内容 标…...

字节数组的通俗解释

1.字节是通过网络传输信息&#xff08;或在硬盘或内存中存储信息&#xff09;的单位。2.在ASCII码中&#xff0c;一个英文字母&#xff08;不分大小写&#xff09;占一个字节的空间&#xff0c;一个中文汉字占两个字节的空间。注意&#xff1a;utf-8码中一个汉字占三个字节&…...

硬件学习 软件Cadence day06 原理图网表导入PCB (过程和操作的错误),开始的画板

1.新建一个制作芯片的工程 1.打开 File ->New 2.填写信息&#xff0c;设置路径 2.原理图的网表导入 1.打开这个窗口 File -> import ->Logic.. 2.确定信息 3.解决网表导入时出现的错误 1. 第一个案列 (没有找到文件 也是这个) 比如说&#xff1a; WARNING(…...

OCT 医学图像分类

目录1. OCT 图像分类2. OCT图像数据集3. OCT图像预处理4. 特征提取5. 实验结果及分析github地址: https://github.com/aishangcengloua/OCT_Classification 1. OCT 图像分类 视网膜光学相干断层扫描(OCT)是一种成像技术&#xff0c;用于捕获活体患者视网膜的高分辨率横截面。…...

华为OD机试 - 合并数组 | 机试题算法思路 【2023】

最近更新的博客 华为OD机试 - 简易压缩算法(Python) | 机试题算法思路 【2023】 华为OD机试题 - 获取最大软件版本号(JavaScript) 华为OD机试 - 猜字谜(Python) | 机试题+算法思路 【2023】 华为OD机试 - 删除指定目录(Python) | 机试题算法思路 【2023】 华为OD机试 …...

前端开发页面样式通用约定法则

代码组织 以组件为单位组织代码段;制定一致的注释规范;组件块和子组件块以及声明块之间使用一空行分隔,子组件块之间三空行分隔;如果使用了多个 CSS 文件,将其按照组件而非页面的形式分拆,因为页面会被重组,而组件只会被移动;良好的注释是非常重要的。请留出时间来描述…...

向上跳空缺口选股公式,选出回补后再启动的标的

一、向上跳空缺口选股公式 思路&#xff1a;先找出缺口&#xff0c;缺口前后有两根K线&#xff0c;缺口低价是前一根K线的最高价&#xff0c;缺口高价是后一根K线的最低价。&#xff08;如上图&#xff09;收盘价低于缺口低价&#xff0c;即实现缺口回补。回补缺口之后&#xf…...

【IoT】做短视频之前,你需要先做好内容定位

现在做内容无疑要从垂直领域入手&#xff0c;否则你就很难出圈。 干货类或者说垂直领域方向的内容&#xff0c;往往都偏向于枯燥&#xff0c;并且会涉及很多专业性的名词&#xff0c;读者理解起来会困难很多&#xff0c;阅读的兴趣也自然会降低。 这也是笔者个人开始做短视频…...

苏宁基于 AI 和图技术的智能监控体系的建设

汤泳&#xff0c;苏宁科技集团智能监控与运维产研中心总监&#xff0c;中国商业联合会智库顾问&#xff0c;致力于海量数据分析、基于深度学习的时间序列分析与预测、自然语言处理和图神经网络的研究。在应用实践中&#xff0c;通过基于 AI 的方式不断完善智能监控体系的建设&a…...

3、内存管理

文章目录1、内存的基础知识1.1、什么是内存&#xff1f;1.2、进程的运行原理--指令1.3、逻辑地址 & 物理地址1.4、从写程序到程序运行1.5、装入模块到运行1.6、装入的三种方式--绝对装入1.7、装入的三种方式--静态重定位1.8、装入的三种方式--动态重定位&#xff08;重定位…...

【蓦然回首忆Java·基础卷Ⅰ】

文章目录开端通过引用创建对象Java的数据存储方式基本类型包装类和高精度数字操作符自动递增和自动递减老生常谈的问题&#xff1a;和equals()如何重写equals方法&#xff1f;短路字面量科学计数法位运算类型转换初始化和清理方法的重载方法的重写无参构造器this与构造器垃圾收…...

类属性和对象属性

6.3 类属性和对象属性 在类定义中&#xff0c;属性按照归属分为对象属性、类属性。按照调用的私密性分为一般属性和私有属性。 6.3.1 类属性和对象属性 对象属性是最常用到的一种属性。即使我们对上面的类&#xff1a;MyClass1实例化了一个mc的对象&#xff0c;mc对象也不能…...

【TensorFlow 】查看Tensorflow和python对应版本、将现有的TensorFlow更新到指定的版本

1、查看Tensorflow和python对应版本 1.1这里我是在TensorFlow官方网址产看的 1、打开官方网址 https://pypi.org/project/tensorflow/1.1.0rc2/#files但是这个网址好像打不开&#xff0c;点击会出现这样 问题不大 输入Tensorflow然后点击搜索&#xff0c;就会跳转到https://p…...

VO、DTO、BO、PO、DO区别

VO、DTO、BO、PO、DO区别 VO&#xff1a;&#xff08;View Object&#xff09;视图对象&#xff0c;一般位于Controller层&#xff0c;用于展示视图。DTO&#xff1a;&#xff08;Data Transfer Object&#xff09;数据传输对象&#xff0c; 即RPC 接口请求或传输出去的对象&a…...

速看!!!一套能直接拿捏大厂面试官的软件测试面试宝典

3.5.1、说说你们是怎么做自动化测试的☆☆☆☆☆我们的自动化测试主要是web UI的自动化测试&#xff0c;主要用于冒烟测试和主要功能的回归测试或者主流浏览器的兼容性测试&#xff0c;作为手工测试的一种补充&#xff0c;提高测试效率&#xff0c;减少一些重复性的测试工作。1…...

超级完整 的 Maven 讲解 以及私服搭建

第一章 Maven 简介 1.1、Maven 概述 Maven 是一款基于 Java 平台的项目管理和整合工具&#xff0c;它将项目的开发和管理过程抽象成一个项目对象模型&#xff08;POM&#xff09;。开发人员只需要做一些简单的配置&#xff0c;Maven 就可以自动完成项目的编译、测试、打包、发…...

Python中os模块详解

Python os 模块详解 os 模块提供了丰富的文件和目录操作、环境变量访问、进程管理等功能&#xff0c;是与操作系统交互的核心模块之一。 基本导入方式 import os常用目录与文件操作 1️⃣ 获取/设置当前工作目录 os.getcwd() # 获取当前工作目录 os.chdir(/tmp) …...

Redisson学习专栏(五):源码阅读及Redisson的Netty通信层设计

文章目录 前言一、分布式锁核心实现&#xff1a;RedissonLock源码深度解析1.1 加锁机制&#xff1a;原子性与重入性实现1.2 看门狗机制&#xff1a;锁自动续期设计1.3 解锁机制&#xff1a;安全释放与通知1.4 锁竞争处理&#xff1a;等待队列与公平性1.5 容错机制&#xff1a;异…...

# [特殊字符] Unity UI 性能优化终极指南 — LayoutGroup篇

&#x1f3af; Unity UI 性能优化终极指南 — LayoutGroup篇 &#x1f9e9; 什么是 LayoutGroup&#xff1f; LayoutGroup 是一类用于 自动排列子节点 的UI组件。 代表组件&#xff1a; HorizontalLayoutGroupVerticalLayoutGroupGridLayoutGroup 可以搭配&#xff1a; Conte…...

区块链技术赋能供应链金融:重塑信任与效率

在当今全球经济一体化的背景下&#xff0c;供应链金融作为连接产业与金融的桥梁&#xff0c;正面临着前所未有的机遇与挑战。然而&#xff0c;传统的供应链金融模式存在着诸多痛点&#xff0c;如信息不对称、信任缺失、操作效率低下等。随着区块链技术的兴起&#xff0c;这些问…...

线程池RejectedExecutionException异常

文章目录 1、报错2、定位3、修复4、线程池使用的一点思考 1、报错 检索项目日志时&#xff0c;发现一个异常堆栈信息&#xff0c;核心报错&#xff1a; java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.CompletableFuture$AsyncSupply480a10c7…...

Flowise 本地部署文档及 MCP 使用说明

一、Flowise 简介 Flowise 是一个开源的拖放式 UI 工具,用于构建自定义的 LLM 工作流程。它允许用户通过可视化界面连接不同的 AI 组件,无需编写代码即可创建复杂的 AI 应用。 二、Docker 环境安装 1. 构建 Docker 镜像 docker build -t node22-ubuntu-dev .其中Dockerfi…...

Mycat的监控

参考资料&#xff1a; 参考视频 参考博客 Mysql分库分表&#xff08;基于Mycat&#xff09;的基本部署 MySQL垂直分库&#xff08;基于MyCat&#xff09; Mysql水平分表&#xff08;基于Mycat&#xff09;及常用分片规则 视频参考资料及安装包&#xff1a; https://pan.b…...

竞争加剧,美团的战略升维:反内卷、科技与全球化

5月26日&#xff0c;美团发布2025年第一季度业绩报告&#xff0c;交出了一份兼具韧性与创新性的成绩单。 报告显示&#xff0c;公司一季度总营收866亿元&#xff0c;同比增长18%&#xff1b;核心本地商业收入643亿元&#xff0c;同比增长18%&#xff1b;季度研发投入58亿元&a…...

SpringBoot整合Flowable【08】- 前后端如何交互

引子 在第02篇中&#xff0c;我通过 Flowable-UI 绘制了一个简单的绩效流程&#xff0c;并在后续章节中基于这个流程演示了 Flowable 的各种API调用。然而&#xff0c;在实际业务场景中&#xff0c;如果要求前端将用户绘制的流程文件发送给后端再进行解析处理&#xff0c;这种…...

4. Qt对话框(2)

在上节中已经学习了对话框的确认和取消&#xff0c;本节内容继续接上节完成登录对话框实例并得到登录信息。 本文部分ppt、视频截图原链接&#xff1a;[萌马工作室的个人空间-萌马工作室个人主页-哔哩哔哩视频] 1 实现登录对话框 1.1 功能需要 得到登录信息&#xff0c;需要…...