C++入门(3)

作者:小七在进步日期:2026/9/14

在上一篇文章中,我们已经掌握了引用的基本概念与用法。今天继续深入,重点讲解 const 引用、引用与指针的关系,以及 inline 和 nullptr 这两个容易被忽视却十分重要的知识点。

引用

const 引用

引用类型用 const 修饰,我们称之为 const 引用。

这里有一个 int 类型的变量 a,用 const 修饰成了常量。

1	const int a = 10;
2

我们给 a 取别名 b,如果这样取:

1int& b=a;//b是a的引用
2

编译器就会报错。

这种情况本质上涉及权限的放大,我们用 const 修饰 a 本质上是不允许 a 的内容被修改的,而我们的引用 b 是 int 类型的,也就是引用 b 可以修改 a 的内容,这就是权限的放大

1	const int& b = a;
2

这种情况就是权限的平移,还有一种情况,权限的缩小,这是允许的。

1int c = 20;
2int& d = c;//权限的平移
3const int& e = c;//权限的缩小
4

e 作为 c 的别名,但是 e 不能修改该空间中存放的内容,这就是权限的缩小。

除引用外,指针中也有权限放大或缩小的这一说法,与引用一样,指针的权限也只能平移或缩小。

我们再来看两个场景:

1//给表达式取别名
2int a = 10;
3int& b = a + 10;
4
5//类型强转
6double c = 3.14;
7int& d = c;
8

这两个情况下编译器也都报错了。

这是为什么呢?因为涉及权限的放大。

我们之前讲过,函数在返回值的时候会产生临时对象,返回来的是临时对象的值,这里也是一样的。

编译器在计算表达式的时候,会创建临时对象用于存表达式的计算结果,所以我们的引用 b 实际上是给这个临时对象取别名。

临时对象具有常性,而我们的引用 b 类型是 int ,本质上就是一种权限放大,临时对象值不能改变,这样写 b 就可以改变它的值了,是不允许的。

在类型强制转换的过程中,编译器也会创建一个临时对象来存储类型强转后的 c 的值,所以 d 也是给这个临时对象取别名,与给表达式取别名一样,涉及权限放大。

所以在这两种场景下也必须使用 const 引用。

1//给表达式取别名
2int a = 10;
3const int& b = a + 10;
4
5//类型强转
6double c = 3.14;
7const int& d = c;
8

引用和指针的关系

引用和指针在功能上有所重叠,但是是独立的两个东西,相辅相成,没有谁可以替代谁。

  1. 引用的创建不开空间,指针的创建会开空间。
  2. const 修饰引用只有一种方法,const 修饰指针分几种情况。
  3. 指针的使用很容易出现空指针和野指针情况,引用很少会出现野引用,相较之下引用比较安全。
  4. 引用直接使用就可以访问到指向对象,指针需要解引用才可以。
  5. 在sizeof访问引用或指针时,引用大小是引用对象的类型大小,指针大小和类型无关,和环境(X64 和 X86)有关。

此外,从汇编语言的角度来看,引用的底层也是通过传地址来实现的

在汇编层面,我们可以看到引用和指针都是先将变量地址放到寄存器 rax 中,然后将该地址复制过来的。# inline
在语法层面,我们将用 inline 修饰的函数定义为 inline(内联)函数。

1inline 返回类型 函数名(参数列表)
2{
3//函数体
4}
5

inline 函数在编译阶段会在调用位置处被编译器展开成代码。

1#include<iostream>
2using namespace std;
3
4//内联函数
5inline void Func()
6{
7	cout<<"hello,world"<<endl;
8}
9int main()
10{
11	
12	Func();
13	Func();
14	Func();
15	return 0;
16}
17

编译阶段就会被展开成:

1int main()
2{
3	cout<<"hello,world"<<endl;//Func函数体被展开成代码
4	cout<<"hello,world"<<endl;
5	cout<<"hello,world"<<endl;
6	return 0;
7}
8

不是所有用 inline 修饰的函数都会在调用处被展开,我们用 inline 修饰该函数只是给编译器提建议,建议将该函数作为内联函数,采不采纳由编译器决定。

通常只有几行代码且被频繁调用的函数会被编译器展开成内联函数,递归函数特别少。

而且在 vs 的 Debug 模式下该内联函数是默认不展开的,方便调试,如果要看,需要进行以下操作:

  1. 右键单击项目,选择属性,在C/C++ ->常规->调试信息格式选程序数据库。
  2. 优化->内联函数拓展改为只适用于_inline(内联函数)。

然后我们就可以看了。

在汇编命令中,我们如果要调用函数创建函数栈帧是call指令,然后会跳转到对应指令去,执行完才从当前指令继续往下执行,而这里是lea,将该字符串存储到rdx寄存器中去,也就是说,该函数被编译器展开成内联函数了。

C++引入内联函数这一用法,是有它的好处的,我们在调用函数创建函数栈帧时,是比较费时间的,内联函数在调用函数处不创建函数栈帧而是直接把函数展开,可以提高效率。
那我们把所有函数都定义成内联函数不就好了?肯定有人会这么干,所以主动权才交在编译器手中,如果在一个项目中真的所有函数都成了内联函数,全部展开,代码量会暴增,对内存极其不友好。

vs 一个项目中存在多个.h,.cpp文件时,我们对于普通函数通常会将声明放在头文件中,定义放在源文件中,也就是声明和定义分离开来。

内联函数的声明和定义不能分离,必须写在一块。

如果分离了就会出现以下错误:


报错位置位于中间文件main.obj,也就是在编译阶段出了问题。

我们回顾一下一个程序执行的过程,需要经过编译和链接两个阶段。
编译阶段又分为了预处理、编译、汇编三个过程。
在预处理阶段,包含的头文件都会被展开,宏等会进行简单的文本替换,生成.i后缀的文件。
在编译阶段,会进行语义分析,生成汇编代码,然后生成.s为后缀的文件。
在汇编阶段,将汇编代码转换成机器能理解的二进制代码,生成.obj中间文件。
在链接阶段,将这些.obj文件都整合起来,最终生成可执行程序.exe。
链接阶段会去找每个函数对应的链接地址,而内联函数在编译阶段就被展开了,编译器无法通过函数名找到该函数地址,就会报链接错误。

所以内联函数声明和定义不能分开。而普通函数的声明和定义一定要分开,如果一个普通函数定义在头文件,多个源文件都包含了该头文件,在预处理阶段头文件就会被展开,相当于每个源文件都有一份该函数的定义,就会出现重定义问题。

如果用static修饰函数,会把函数设为内部链接属性,这时在头文件中定义就不会出现问题,展开后是多个互不相关的副本。inline是特殊外部链接属性,链接器识别多个inline符号,合并为同一个函数。了解一下就好了。

宏与 inline

  1. 在 C 语言中,宏是在预处理阶段就被展开,而且是纯文本替换,缺少类型检查,不安全,所以 C++ 才引入了内联函数,也是为了解决这个问题。
  2. 宏比较复杂且不容易写,相比之下 inline 会好很多。

nullptr

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

1#ifndef NULL
2     #ifdef __cplusplus
3         #define NULL 0
4     #else
5         #define NULL ((void *)0)
6     #endif
7#endif
8
9

可以看到在 C++ 中 NULL 被替换成了整型 0,C 语言中被替换成了 void* 无类型的空指针,在使用时都会或多或少出现问题。

比如以下程序:

1void F1(int a)
2{
3	cout << "F1 int" << endl;
4}
5
6void F1(int* a)
7{
8	cout << "F1 int*" << endl;
9}
10
11int main()
12{
13	F1(0);
14	F1(NULL);
15	return 0;
16}
17


我们第二个参数传 NULL 是想调用第二个函数的,结果 NULL 替换成 0 了,调用第一个函数去了。

因此 C++ 引入了 nullptr 关键字,它是一种特殊类型的字面量,可以转换成任意的指针类型而非整型,所以用它定义空指针可以避免类型问题。

1int main()
2{
3	F1(0);
4	F1(nullptr);
5	return 0;
6}
7


C++入门(3)》 是转载文章,点击查看原文


相关推荐


引擎从不发一条消息:jeeflow 的两类扩展点与消息模块的分工
mldong2026/9/6

二、引擎有两类"伸手"的地方:拦截器和事件 引擎核心是零框架依赖的,但它知道一件事:业务方总要"在流程跑的时候干点自己的事"——发通知、改业务单据状态、按规则算审批人、写代码做决策。这些需求它不自己实现,而是开出两类扩展点让外面插进来: 拦截器(FlowInterceptor):插在流程执行过程中,能看到、甚至能改正在发生的事。 事件(ProcessEventListener):流程走到某个关键点时喊一声,外面的人听到后自己决定要不要做点什么。 这两个接口的长相,朴素到可以各贴一行: //


一个 SKILL.md 拿下 2.3 万 Star:让 Codex/Claude Code 少说废话、先给答案
JavaGuide2026/8/29

用 Coding Agent 改代码,最磨耐心的情况之一,就是它明明答对了,答案却藏在一堆废话里。 你让 Codex 排查一个接口为什么返回 401,它可能先查认证流程,再分析中间件、Token 和 Cookie,顺手提醒你依赖版本也该升级了。全部看完之后,真正要执行的命令只有一条。 任务再长一点,人更容易跟丢。Agent 写了很多,却没说现在做到第几步;测试失败了,也没告诉你先看哪个文件。最后再来一句“希望这能帮到你”,问题还停在原地。 前几天技术群里有朋友分享了一个名字很有意思的项目 i-h


AI编程实战:让 AI 修 Bug,从报错到修复
机构师2026/8/21

文章目录 AI编程实战:让 AI 修 Bug,从报错到修复写在前面一、贴报错的正确姿势二、三个真实案例三、多轮定位技巧四、修完要回归小结与下篇预告小作业 AI编程实战:让 AI 修 Bug,从报错到修复 写在前面 Debug 是 AI 最擅长、也最常用的场景之一,但很多人习惯只贴一半报错,效果大打折扣。这篇讲怎么让 AI 高效修 Bug。 一、贴报错的正确姿势 把三样东西一次给全:完整报错 + 相关代码 + 环境信息。 运行报错,请帮我定位并修复。 - 报错:(粘贴完整


GitHub #1 拆解|它说自己在进化,但 worker 跑的是你本机权限,不是沙箱
苏灿烤鱼2026/8/8

GitHub #1 拆解|它说自己在进化,但 worker 跑的是你本机权限,不是沙箱 让可复用经验留下,让错误改动能回滚。 ⚡️ 30 秒速读:PrimeIntellect-ai/prime-agent 新上榜即登顶 GitHub Trending #1,今日新增 2,271 星。它用 RLM 把上下文当变量,用可执行 Skill 和 Continual Harness 保存可 refine 的经验。亮点:技能可被 IPython 直接调用,会话可后台续跑;风险:官方明确不是安全沙箱,v


Android 面试系列:Retrofit 的 suspend 是如何实现的?
潜龙勿用之化骨龙2026/7/30

今天这个是送分题,但是仍然有很多人不清楚,今天就聊聊这个 比如: interface UserApi { @GET("user") suspend fun getUser(): User } 调用: val user = api.getUser() 这里 Retrofit 并不是同步等待网络结果。 它只是把原来的异步流程转换成了 Kotlin 协程可以理解的挂起恢复流程。所以你不要再withContext 一下。 Retrofit 是怎么知道这是 suspend 方法?


为什么学习 Go?Go 能做什么?
程序员爱钓鱼2026/7/22

Go 编程实战系列(一) 近年来,无论是云计算、容器技术、微服务,还是人工智能基础设施,Go(又称 Golang)几乎都占据着重要位置。从 Docker、Kubernetes 到 Prometheus、Etcd,再到大量互联网公司的后端服务,Go 已经成为现代服务端开发的重要语言之一。 对于刚开始学习编程的人来说,可能会有这样几个问题: Go 为什么越来越流行? Go 和 Java、Python、Rust、C++ 有什么区别? 学 Go 能做哪些项目? 学完 Go 能找到什么工


Cursor安全插件链:代码审计新范式
lideiguo2026/7/13

一、 引言:当AI代码助手成为安全审计的“双刃剑” 核心问题:随着 Cursor、GitHub Copilot 等 AI 代码助手的普及,开发效率大幅提升,但同时也引入了新的安全风险——AI 生成的代码可能存在漏洞,而开发者可能因过度信任而疏于审计。 新范式诞生:“安全插件链”应运而生,它通过将静态代码分析、依赖检查、漏洞扫描等安全工具与 Cursor 等 IDE 深度集成,在代码生成、修改、提交等关键环节自动介入,形成“开发即审计”的闭环。 本文目标:探讨 Cursor 安全插件链的核心


基于MATLAB图像处理的饮料瓶识别与价格显示系统设计与实现
7zcode2026/7/5

摘要:随着图像处理技术在商品识别、智能零售和自动检测领域的广泛应用,传统依靠人工判断饮料种类和价格的方式存在效率低、主观性强、信息统计不便等问题。针对饮料瓶图像识别与价格自动显示需求,本文设计并实现了一套基于MATLAB图像处理的饮料瓶识别与价格显示系统。 项目概览 项目简介 本文设计并实现了一种基于MATLAB的饮料瓶智能识别与价格统计系统。该系统采用计算机视觉技术,结合HSV颜色空间阈值分割和形状特征分析两种识别方法,实现对常见饮料瓶的自动识别与分类。系统首先通过图像预处理获取饮料


spring-ai-starter-mcp-server-webmvc的包冲突
山塘小鱼儿2026/6/27

搭建spring-ai的mcp服务,引用官网的包 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://m


斥资500元/上亿Token,深度横评4个顶尖模型的真实排名~
AI袋鼠帝2026/6/18

大家好,我是袋鼠帝。 6月,感觉又是模型爆发的月份。 前有MiniMax-M3,然后是Claude Fable 5,到Kimi 2.7-code、GLM-5.2,麻了。 我现在都不想看模型的各种榜单、跑分了,感觉有很多刷榜的嫌疑。 而且,每次我测新模型,都有很多朋友在评论区比较其他模型。 毛选说的好,没有调查就没有发言权,实践是检验真理的唯一标准! 这次就来给大家做个顶级模型横测吧 看了下,kimi最新模型主要是编程能力强,这次我想测多个维度。GLM-5.2的Coding Plan暂时没抢到。。

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读