C++内联函数(编译器角度,汇编角度),auto关键字,范围for语法糖,nullprt与NULL区别等

news2024/11/17 16:16:16

TIPS

  1. 在C++当中有一个东西可以打印类型?typeid(变量名).name()
  2. 数组一旦从参数进入到函数里面,它就已经是个指针了,再也不是一整个数组了
    在这里插入图片描述

内联函数(正常函数定义前加个inline修饰)

  1. 在实际当中,有时候去调用函数的时候会有很多开销与消耗,调用函数的消耗就在于建立函数栈帧,栈帧就是最大的消耗。现在假设有一个函数,这个函数能够被频繁的调用,比如说某个函数被调用1万次,因此需要建立1万次的函数栈帧,太过分了。
  2. 因此如果要解决这个问题的话,在c当中就只能去写一个宏函数,因为宏他实际上并不是函数,它是一种替换,所以说是不需要去频繁的建立函数栈帧。所以说在写宏的时候也根本就不需要类型。在宏里面不需要类型,return与分号。
  3. 因为宏的本质就是替换与展开,在预处理阶段就会完成,因为他是无脑展开,所以说在很多时候运算符的优先级上就会发生问题,因此在写宏的时候,必须全部无死角加上括号,甚至包括每一个单独的变量,比如
#define MAX(a,b) ((a)>(b)?(a):(b))
  1. 宏函数的优势就在于不需要去建立函数栈帧,能够去提高调用效率,但它的缺点就在于很容易出错(必须无死角保险加上括号),有些宏函数很复杂会让代码的可读性变差,并且没有类型的检查,并且不能调试
  2. 在c++当中为了去解决c当中的这些缺陷,所以就有了内联函数。就先正常写一个函数,内联函数就是普通函数,然后在函数定义的时候最前面加上一个inline。如:
inline int add(int a, int b)
{
	return a+b;
}
  1. 以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率,内联函数会在调用的地方展开,也就没有函数的调用,也就没有去建立函数栈帧,并且不复杂,不容易出错,可读性强,也可以进行调试。
  2. 内联函数与宏函数一样,都是展开(当然实际上内联函数并不是全部无脑展开),并且他们都有一个共同的特点,就是适用于短小,逻辑简单,并且需要频繁调用的函数,并不适合代码长的
  3. 因为假设现在我有一个函数,它的汇编指令是50行,然后我需要去调用10000次,如果说这个函数不是内联函数,当然他需要去建立1万次函数栈帧,但是他的汇编指令总共相关的也就10050行(注意call指令);如果说这个函数是内联函数,虽然这1万次的调用都不需要去建立函数栈帧,但是程序相关的汇编指令总共有有500000行(假设内联函数是无脑展开,实际上不是),但汇编指令量级的差距可以很明显的看出来,所以说如果说对于一个代码很长的,逻辑复杂的函数把它当成一个内联函数来看的话,如果多次调用就会导致代码膨胀,所以汇编指令就会变多,所以机器二进制指令也会相应的变多,最终导致这个可执行程序变得更大,最终映射到生活当中的例子就是比方说一个安装包变的非常大 也是不好的。
  4. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。
    在这里插入图片描述
  5. 因此内联函数inline用于那些短小,逻辑简单,并且需要频繁调用的函数。如果说一个函数它并不需要频繁调用,那么建立一些函数栈帧开销也无所谓。
  6. 一定要记住:短小,逻辑简单,频繁调用

编译器角度看内联函数

  1. 但实际上当你把一个函数给它定义成一个内联函数的时候,因为inline对于编译器而言仅仅是一个建议,所以最终能否成为inline内联函数,是由编译器自己决定。
    在这里插入图片描述
  2. 对于那些比较长的或者是递归函数,不要用内联函数。不然就很容易导致代码膨胀,然后实际上因为编译器对你也并不是很放心,就怕你乱搞事情,所以实际在代码编译过程当中,尽管你设置成内联函数了,但是编译器还是会自动去判断,如果情况不允许,他就会否决。
  3. 因为之前讲过内联函数相较于宏函数有一个好处就在于,他是可以支持调试的。但实际上是这样的:因为内联相当于是给他全部展开,所以实际上是不会跳到或进到函数里面去的而是直接这么顺着走下去,所以实际上是不能调试的。但是因为为了让程序员也能够进行调试一下,所以在默认debug模式下,inline是不会生效的,就是为了让程序员能够调试,
  4. 所以即使符合了内联的条件,编译器在默认debug模式下也不搞内联。但是改成release版本就可以了,这时候在底层就已经在搞内联了。
    在这里插入图片描述
  5. 因为release版本是不能很好的去查看汇编代码,也不能够调试,所以如果说硬要去看在内联的情况之下汇编指令是怎么样进行的,但默认debug模式之下又不会给你搞成内联,因此需要设置一下
    在这里插入图片描述
  6. 在debug下修改了一下设置后,就OK了,此时汇编代码就是按内联函数展开的逻辑走的,看一下果然没有call add了
    在这里插入图片描述

汇编底层看内联函数

  1. 因为你从底层的汇编代码去简单的看一下的话,你会发现一个函数,它是否是内联函数,主要在于他调用的时候,如果说他不是内联函数,那么调用的时候就先会执行call指令,然后进入到函数里面的汇编指令当中,并且老老实实的从一开始建立函数栈帧push ebp…到具体的进行某一些操作等等
  2. 但如果你是内联函数的话,因为不需要去建立函数栈帧且相当于是直接展开,所以在汇编指令当中就没有call,所以也就不需要进行跳转,直接往下执行接下来的汇编指令,在接下来的汇编指令当中,也不存在建立函数栈帧的相关指令,直接一些寄存器之间的的简单操作指令。
    在这里插入图片描述

内联函数声明与定义不能分离的底层原因

  1. 因为内联函数的本质就是直接展开,也就是说汇编语句继续执行咵咵下去,所以内联函数的话是不会进入到符号表的,也是不会有地址的,
  2. 因为他以为他直接在调用的地方直接就展开了,所以为什么需要进到符号表里面去的?
  3. 所以内联函数比方说他的声明是在头文件,然后定义是在某个.c文件,这样子不行的,因为当你把头文件进行展开之后,然后到那个内联函数那边,由于是内联函数,所以说是直接展开,但此刻头文件展开部分只有内联函数的声明,所以说他也不知道具体的执行逻辑是什么,所以就不知道展开什么.
  4. 因为既然展开不了,所以说就去找就用非内联函数的方式去走,但因为内联函数是不进入到符号表当中也是没有地址的,所以在等会儿链接的过程当中,符号表那边又找不到他的函数名与地址,所以这时候就会发生链接错误,但是能够编译通过,就是链接不上
  5. 因为内联函数在汇编指令这个层面来看的话是不需要call的,所以说内联函数没有地址的,要啥地址?对于内联函数而言,他的声明与定义是不能够进行分离的,所以不要声明与定义分给直接咵一下全部写在头文件上面。这时候只要包含头文件,因为头文件的内容全部都在预处理阶段会全部替换展开,所以等会儿在内联函数那个部分进行直接展开的时候,就有对应的展开逻辑,不然只包含函数声明的话,内联函数的展开逻辑是不知道的,所以编译时候汇编代码生成不下去,所以那就只能通过call了,所以这时候你虽然编译通过了,到时候链接的时候又over了。如图:链接不上:
    在这里插入图片描述

补充:宏的优缺点与C++替换方案

  1. 宏的优缺点
    优点:
    1.增强代码的复用性。
    2.提高性能。
    缺点:
    1.不方便调试宏。(因为预编译阶段进行了替换)
    2.导致代码可读性差,可维护性差,容易误用。
    3.没有类型安全的检查 。
  2. C++有哪些技术替代宏?
    1. 常量定义 换用const enum
    2. 短小函数定义 换用内联函数

auto 变量名 = 右等式 (可以根据等号右边自动推导类型)

  1. 随着程序越来越复杂,程序中用到的类型也越来越复杂,经常体现在:类型难于拼写,含义不明确导致容易出错。
  2. 在编程时,常常需要把表达式的值赋值给变量,这就要求在声明变量的时候清楚地知道表达式的类型。然而有时候要做到这点并非那么容易,因此C++11给auto赋予了新的含义
  3. auto是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得,也就说可以用auto作为类型来创建变量,数据类型由编译器自动推导
  4. 也就是说auto它可以在代码当中根据右边的表达式自动推导类型,但是他真的有很大的价值吗?其实也没有,但以后会有很大的用途,auto的话一般就是说在类型很长的时候去用比较划算一点,比如说以后要学的迭代器
  5. 具体例子:
    在这里插入图片描述
    auto 关键字注意点:
  6. auto的话要自动识别类型,因此在等号右边必须得有一个推导才可以,auto就是长类型替换,auto可以通过右边表达式的值自动推导出类型。使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。
  7. 用auto声明指针类型时,用auto和auto没有任何区别,对于这个auto而言,就是已经指定了,在等号的右边必须是一个指针或地址,不然报错
    在这里插入图片描述
  8. auto不能作为函数参数
  9. auto不能用来声明数组
  10. auto在实际中最常见的优势用法就是跟以后会讲到的C++11提供的新式for循环,还有lambda表达式等进行配合使用

for 语法糖

  1. 对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。
  2. 与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环
    在这里插入图片描述
  3. 只要是数组都可以这样(for循环迭代的范围必须是确定的),但不能修改数据,但可以去给auto后加个引用符号就ok
    在这里插入图片描述

指针空值nullptr

  1. 在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针。
  2. NULL实际是一个宏,在传统的***C头文件(stddef.h)**中,可以看到如下代码:NULL可能被定义为字面常量0,或者被定义为无类型指针(void)的常量
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
  1. 但在c++的项目里面默认都会定义 _cplusplus

在这里插入图片描述
5. nullptr就是 (void*)0 ,NULL就是0,这两个字面量不一样。NULL 与nullptr 本质上都表示空指针,他们的值都是0,所以指向的都是0位置的那个地址。
6.
1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。
2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/438214.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

13、拦截器

文章目录 1、HandlerInterceptor 接口2、配置拦截器3、拦截器原理 【尚硅谷】SpringBoot2零基础入门教程-讲师:雷丰阳 笔记 路还在继续,梦还在期许 1、HandlerInterceptor 接口 /*** 登录检查* 1、配置好拦截器要拦截哪些请求* 2、把这些配置放在容器中…

爬虫——肯德基

import requests #UA伪装:将对应的User-Agent封装到一个字典中 headers{User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.48 } #指定url url http://www.kfc.com.c…

【jvm系列-09】垃圾回收底层原理和算法以及JProfiler的基本使用

JVM系列整体栏目 内容链接地址【一】初识虚拟机与java虚拟机https://blog.csdn.net/zhenghuishengq/article/details/129544460【二】jvm的类加载子系统以及jclasslib的基本使用https://blog.csdn.net/zhenghuishengq/article/details/129610963【三】运行时私有区域之虚拟机栈…

一图看懂 xlrd 模块:读写 Excel 文件的数据和格式信息, 资料整理+笔记(大全)

本文由 大侠(AhcaoZhu)原创,转载请声明。 链接: https://blog.csdn.net/Ahcao2008 一图看懂 xlrd 模块:读写 Excel 文件的数据和格式信息, 资料整理笔记(大全) 摘要模块图类关系图模块全展开【xlrd】统计常量intdict 模块26 os27 …

【蓝桥杯省赛真题19】python完数及个数 青少年组蓝桥杯python编程省赛真题解析

目录 python完数及个数 一、题目要求 1、编程实现 2、输入输出 二、解题思路

itop-3568开发板驱动学习笔记(22)设备树(一)设备树基础

《【北京迅为】itop-3568开发板驱动开发指南.pdf》 学习笔记 文章目录 设备树简介设备树编译设备树语法设备根节点设备子节点节点名称reg 属性#address-cell 和 #size-cells 属性model 属性status 属性compatible 属性aliases 节点chosen 节点device_type 属性自定义属性 设备树…

使用Docker创建并运行Jenkins详细步骤

文章目录 一、使用Docker搭建Jenkins二、为Jenkins配置执行节点1、进入管理页面2、新建节点3、配置节点信息4、子节点连接master节点5、在子节点机器上运行上面复制下来的命令6、查看子节点是否在线 三、创建一个简单的job1、进入创建页面2、设置job名称3、配置job相关信息4、构…

eBay变现方式有哪些?如何利用好测评自养号?

近年来,越来越多的人选择在eBay开店,甚至很多其他平台的卖家也转型到了eBay。但很多卖家发现,在运营了一段时间后,过了对新账号的流量扶持期,店铺突然出现流量开始下滑的情况,针对这种情况卖家可以采取哪些…

计算机网络(数据链路层)部分习题

1. 通过传统以太网发送中文“华南师范大学计算机学院”,封装成以太网帧,请问该帧的数据字段有效字节是多少?需要填充多少个字节? 答:一个字两个字节,“华南师范大学计算机学院”共11个字,有效字…

Visual Studio容器工具要求在构建,调试或运行容器化项目之前运行Docker

出现此提示,是因为电脑未安装Docker所致,接下来就教大家如何安装Docker。 第一步:下载 地址:Install Docker Desktop on Windows | Docker Documentation 第二步:安装 1、双击Docker Desktop Installer.exe运行安装程…

电子束与材料相互作用Matlab代码

标题 1 题目2 实验原理2.1 蒙特卡洛模拟的基本思想2.2 电子散射的基本概念 3 代码 1 题目 扫描透射电镜(STEM)的基本原理是用极细的扫描电子束透射样品,透射电子直接被具有一定张角的接收器所接收,透射电流的强度直接反应了样品的质量厚度。 对于一定厚度的样品&am…

面试官:一千万的数据,你是怎么查询的?

面试官:一千万的数据,你是怎么查询的? 1 先给结论 对于1千万的数据查询,主要关注分页查询过程中的性能 针对偏移量大导致查询速度慢: 先对查询的字段创建唯一索引 根据业务需求,先定位查询范围&#xff08…

23Java面试专题 八股文面试全套真题(含大厂高频面试真题)

准备篇-01-企业简历筛选规则 准备篇-02-简历注意事项 等写简历的时候看02和04... 准备篇-03-应届生该如何找到合适的练手项目 !准备篇-04-Java程序员的面试过程 Redis篇-01-redis开篇 感觉有点难 Redis篇-02-redis使用场景-缓存-缓存穿透 Redis篇-03-redis使用场景-…

一日一题:第十题---并查集(集合合并)and 二叉树遍历

​作者:小妮无语 专栏:一日一题 🚶‍♀️✌️道阻且长,不要放弃✌️🏃‍♀️ 今天来更前几天做的,怕忘记了hh 目录 并查集题目描述(集合合并)代码对路径压缩的解释 二叉树遍历题目描述代码 并查集 ​​​…

【JavaEE】死锁是什么?如何避免死锁(保姆级讲解)

博主简介:想进大厂的打工人博主主页:xyk:所属专栏: JavaEE初阶 本篇文章将介绍什么是死锁,死锁的四大必要条件,如何去避免死锁~~~ 目录 一、死锁是什么? 二、关于死锁的情况 2.1 一个线程的情况 2.2 两个线程的情…

javaweb家具购物商城的电商设计与实现

目 录 1 绪论 1 1.1 项目背景 1 1.2 研究意义 1 1.3 本系统概述 2 2系统分析 3 2.1 系统需求分析 3 2.1.1 功能需求 3 2.1.2 性能需求 4 2.2 系统可行性分析 4 2.2.1 技术及开发方法可行性 4 2.2.2 管理可行性 4 2.2.3 经济可行性 5 2.3 业…

CPU的一、二、三级缓存

在Java并发编程中,我们经常会遇到共享变量的读写问题,关于这类问题我们经常会说到原子性、可见性、有序性这三大特性,再进一步会了解到总线和CPU的一、二、三级缓存。关于这三个级别的缓存网上文章介绍比较多,今天我们这篇文章&am…

Django REST Framework(DRF)框架之其他常用API的使用

DRF之其他常用API的使用 限流Throttling限流类设置全局默认限流策略基于类视图限流定义限流频次自定义限流类 过滤Filtering查询集过滤查询参数过滤使用过滤器组件使用过滤器字段过滤字段排序 分页Pagination常用分页类全局与局部的使用自定义分页类 异常处理Exceptions常见异常…

Docder 安装——Windows版

Docder 安装——Windows版 docder 下载地址 https://smartidedl.blob.core.chinacloudapi.cn/docker/20210926/Docker-win.exe 1、使用 PowerShell 启用 Hyper-V 以管理员身份打开 PowerShell 控制台,运行以下命令: Enable-WindowsOptionalFeature -On…

在flowforge中使用模版配置来自定义node-red实例

添加模版设置 在FlowForge中,每个项目都是从模板创建的。模板为项目定义了一组预配置的选项。这包括运行时设置- 比较常见就是 Node-RED settings.js文件中设置的值。 模板还定义了项目可以自定义哪些选项。 本篇文章就来解释一下如何向模板对象中添加新的Node-RED运行时选项…