C++语法基础(下)
目录一. 缺省参数1. 全缺省参数2. 半缺省参数二. 引用1. 引用的概念与定义2. 引用的特性3. 引用的使用3.1 引用传参3.2 引用返回值4. const 引用4.1 避免数据拷贝4.2 保证数据不可变性5. 引用与指针对比三. nullptr1. NULL 与 nullptr 的区别四. inline1. 内联函数的概念与作用2. inline 是建议而非强制3. inline 对宏函数的替代4. vs2022 debug 版本默认不展开5. inline 不建议声明与定义分离6. 内联函数与宏函数对比注在看本文是如果感觉内容有点突兀请先浏览《C语法基础上》这篇文章帮助更好理解一.缺省参数缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时如果没有指定实参则采用该形参的缺省值否则使用指定的实参缺省参数分为全缺省和半缺省参数。有些地方把缺省参数也叫默认参数•全缺省就是全部形参给缺省值半缺省就是部分形参给缺省值。C 规定半缺省参数必须从右往左依次连续缺省不能间隔跳跃给缺省值。示例代码#includeiostream using namespace std; //全缺省参数 int add(int x 4, int y 3) { cout x y ; return x y; } //半缺省参数 int sub(int x, int y2) { cout x - y ; return x - y; } int main() { int a 1, b 2; //全缺省时不传参vs传参 cout add() endl; cout add(a, b) endl; //半缺省是时,不传参vs传参 cout sub(5) endl; cout sub(a, b) endl; return 0; }以上代码展示了全缺省参数和半缺省参数的函数定义与使用。 add 函数是全缺省参数示例 sub 函数为半缺省参数示例。通过在 main 函数中调用呈现不传参时使用缺省值传参时使用指定实参的效果。• 带缺省参数的函数调用C 规定必须从左到右依次给实参不能跳跃给实参。• 函数声明和定义分离时缺省参数不能在函数声明和定义中同时出现规定必须函数声明给缺省值。二.引用1.引用的概念与定义引用不是新定义一个变量而是给已存在变量取了一个别名编译器不会为引用变量开辟内存空间它和它引用的变量共用同一块内存空间。比如《水浒传》中李逵宋江叫“铁牛”江湖上人称“黑旋风”林冲外号豹子头。所以一个引用也可以有多个别名。类型 引用别名 引用对象;#includeiostream using namespace std; int main() { int a 2; // b、c 都是 a 的引用别名 int b a; int c a; // 通过 c 自增a/b/c 同步变化 c; cout a b c endl; return 0; }以上C 代码在 main 函数中先定义了变量 a 并赋值为2 。接着定义了 b 和 c 作为 a 的引用由于引用与被引用变量共用内存空间对 c 执行自增操作后 a 、 b 、 c 的值都变为3 运行结果验证C 中引用的这一特性。2.引用的特性①引用在定义时必须初始化②一个变量可以有多个引用③引用一旦引用一个实体再不能引用其他实体3.引用的使用在C 实践中引用主要发挥于引用传参与引用返回值两大场景-引用传参与指针传参功能近似却更具便捷性。通过引用传递参数可规避对象拷贝提升程序运行效率且能直接操作实参改变其值。-引用返回值应用场景较为复杂这里先作简要介绍后续在类和对象章节将深入探讨。它同样能减少拷贝开销有时还可用于实现对特定对象的持续引用操作 。引用与指针在实际运用中相互配合虽功能有所重叠但各自特性鲜明无法相互取代。值得注意的是C 的引用与Java 等语言的引用差异显著。C 中引用一经定义便无法更改指向而Java 里的引用则无此限制可灵活变更指向。部分以C 代码为主的《数据结构》教材会采用C 引用替换指针传参旨在简化程序逻辑规避指针操作的复杂性。引用传参和指针传参#includeiostream using namespace std; // 引用传参 void Swap(int x, int y) { int tmp x; x y; y tmp; } // 指针传参 void Swap(int* x, int* y) { int tmp *x; *x *y; *y tmp; } void test() { int a13,a2 3, b14,b2 4; cout a1 b1 endl; // 引用方式交换 Swap(a1, b1); cout a1 b1 endl; cout a2 b2 endl; // 指针方式交换 Swap(a2, b2); cout a2 b2 endl; } int main() { test(); return 0; }以上C 代码展示了两种实现交换功能的函数一个通过引用传参 Swap(int x, int y) 另一个通过指针传参 Swap(int* x, int* y) 。在 test 函数中对这两个函数进行调用测试分别传入变量和变量地址。运行结果表明二者都能成功实现变量值的交换直观体现引用和指针在参数传递用于修改实参值时的作用。4.const引用在C 里 const 引用即常量引用它是对变量的只读引用但不能赋值。从定义来讲当用 const 修饰引用时就创建了这种只读关系一旦绑定到某个对象通过该引用不能修改所绑定对象的值 。比如 const int ret a; 此后不能执行 ret 新值; 这样的操作 。其主要作用体现在两方面①避免数据拷贝在函数参数传递中若传递大对象按值传递会产生拷贝开销使用 const 引用传递能避免拷贝提升程序性能 。例如函数 void func(const 大对象类型 obj) 调用时不会对实参进行拷贝 。②保证数据不可变性能确保在函数内部不会意外修改引用的数据为数据安全性提供保障 。在多函数调用、多人协作开发等场景下可防止数据被误改让代码逻辑更清晰、可靠 。此外 const 引用还有个特殊之处它可以和声明类型不匹配 比如 const int r 3.14; 编译器会创建临时量来实现转换和绑定 但普通引用不允许这样做 。以下示例#includeiostream using namespace std; int main() { int a 2; const int ra a; //ra;ra不能自加 a; cout a endl; cout ra endl; return 0; }代码展示了 const 引用的特性。在 main 函数中定义变量 a 并赋值为2 接着创建 const int ra 作为 a 的常量引用。由于 ra 是 const 引用不能对其执行自增操作如 ra 被注释掉 但原变量 a 可以自增。运行结果显示 a 自增后的值为3 ra 的值也同步变为3 体现 const 引用只读且与原变量关联的特性。#includeiostream using namespace std; int main() { // const 引用可直接绑定字面量 const int a 2; int b 3; // const 引用只读不能通过 ra 修改 a const int ra a; const int rb b; //但是int ra a;这种写法是错误的 }这段 代码在 main 函数中先定义了常量 a 和变量 b 然后分别创建了它们的 const 引用 ra 和 rb 。代码注释指出 int ra a; 这种写法是错误的 。因为 a 是 const 类型普通引用不能绑定到 const 对象上而 const 引用能确保不会通过引用意外修改对象的值此例直观体现const 引用的使用规则和对象绑定限制。#includeiostream using namespace std; int main() { int a 10; const int ra 30; // 编译报错初始化无法从int转换为int // int rb a * 3; // const 引用可绑定表达式结果 const int rb a * 3; double d 12.34; // 编译报错初始化无法从double转换为int // int rd d; const int rd d; return 0; }这段 代码在 main 函数中展示了 const 引用的特殊用法及普通引用的限制。定义变量 a 后 const int ra 30; 直接绑定字面量体现 const 引用可绑定临时值。而 int rb a * 3; 等普通引用绑定临时值或类型不匹配值会编译报错。 const int rb a * 3; 、 const int rd d; 能成功表明 const 引用可在类型转换等情况下通过创建临时变量实现绑定凸显 const 引用与普通引用在对象绑定上的差异。5.引用与指针对比引用使用建议①函数参数传递当函数需要修改外部变量或希望避免值传递带来的对象拷贝开销时使用引用作为函数参数。比如在自定义类的赋值运算符重载函数ClassName operator(const ClassName other) 中用引用传递参数既避免拷贝又能实现对象间赋值。②简单数据别名需求若只是想给已存在变量起别名方便代码可读性和语义表达可使用引用 。如int num 10; int numRef num; 后续操作numRef就等同于操作num 。③范围for循环在C范围for循环中使用引用可避免遍历容器元素时的拷贝开销。如std::vector vec {1, 2, 3}; for(int element : vec) 直接操作容器内元素本身提高效率。④避免空值风险场景因引用不能为NULL在不希望出现空指针相关问题要求对象必须有效的场景优先考虑引用。三.nullptr在C 中 引入nullptr是为了替代NULL虽然两者都可以 用于表示指针为空的状态但两者存在差异。从类型角度 NULL 本质是整数 0 或预处理器宏定义为 0 常被视为整型虽可赋值给指针但会造成类型不匹配隐患。而 nullptr 是C11引入的关键字类型为 std::nullptr_t 专门用于表示空指针与任何指针类型都能隐式转换类型安全性更好。使用场景上 NULL 在C和早期C代码中广泛使用不过在C 中易导致函数重载歧义因它可被当成整数参与重载决议。 nullptr 则消除了这种歧义在C11及以后代码中推荐使用 nullptr 表示空指针使代码更清晰、安全符合现代C 类型系统规范。#includeiostream using namespace std; void f(int x) { cout f(int x) endl; } void f(int* ptr) { cout f(int* ptr) endl; } int main() { // 0 匹配 int 版本 f(0); // 本想通过f(NULL)调用指针版本的f(int*)函数但是由于NULL被定义成0调用了f(intx)因此与程序的初衷相悖。 f(NULL); f((int*)NULL); // 编译报错error C2665: f2 个重载中没有一个可以转换所有参数类型 // f((void*)NULL); // nullptr 匹配指针版本 f(nullptr); return 0; }这段C 代码定义了两个重载函数 f(int x) 和 f(int* ptr) 。在 main 函数中进行调用测试 f(0) 调用了 f(int x) 。原本想通过 f(NULL) 调用指针版本的函数却因 NULL 被定义为0 实际调用了 f(int x) 。 f((int*)NULL) 能正确调用指针版本而 f((void*)NULL) 编译报错。使用 nullptr 则可准确调用 f(int* ptr) 清晰展现函数重载下指针类型调用及 NULL 与 nullptr 的区别。四.inline1.用inline修饰的函数叫做内联函数编译时C编译器会在调用的地方展开内联函数这样调用内联函数就不需要建立栈帧了从而可以提高效率。2.inline 对于编译器而言只是一个建议也就是说即便加了 inline 编译器也可以选择在调用的地方不展开不同编译器关于 inline 在什么情况展开各不相同因为C标准没有规定这个。注inline适用于频繁调用的短小函数对于递归函数、代码相对多一些的函数加上 inline也会被编译器忽略。3. C语言实现宏函数也会在预处理时替换展开但是宏函数实现很复杂且容易出错并且不方便调试C设计了inline目的就是替代C的宏函数。4.vs2022编译器debug版本下默认是不展开 inline 的这样方便调试debug版本想展开需要设置一下以下两个地方。如下图所示5.inline不建议声明和定义分离到两个文件分离会导致链接错误。因为 inline 被展开就没有函数地址链接时会出现报错。inline在预处理作用// 内联展开无 call 指令直接计算 mov eax,dword ptr [a] mov dword ptr [rsp28h],eax mov eax,dword ptr [a] // a inc eax mov dword ptr [a],eax mov eax,dword ptr [rsp28h] mov ecx,dword ptr [a] // xy add ecx,eax mov eax,ecx mov dword ptr [rsp2Ch],eax mov eax,dword ptr [rsp2Ch] mov dword ptr [ret],eax// 非内联通过 call 调用 add 函数 mov eax,dword ptr [a] mov dword ptr [rsp28h],eax mov eax,dword ptr [a] inc eax mov dword ptr [a],eax mov edx,dword ptr [rsp28h] mov ecx,dword ptr [a] call add (07FF7A0991267h) mov dword ptr [ret],eax汇编指令共性两张图中的汇编指令有不少相同点像 mov 指令用于数据传送比如把内存中 a 地址处的数据传进寄存器或者把寄存器数据存到特定内存地址 inc 指令实现寄存器值自增 add 指令完成寄存器间加法运算。这些指令为理解代码底层操作奠定基础。两张图差异剖析①第一张图缺失 call 指令。这是 add 函数被内联展开所致。内联展开是编译器的一种优化手段它直接将 add 函数的代码体嵌入到调用 add 的地方。如此一来程序执行时就无需进行常规函数调用操作比如不用建立和销毁栈帧。这减少了函数调用的额外开销提高了代码执行效率直接在调用处就执行了原本 add 函数内的操作逻辑 。②第二张图有 call 指令。这表明是常规函数调用方式。当程序执行到 call add 时会暂停当前指令流跳转到 add 函数所在的内存地址这里是 07FF7A0991267h 去执行 add 函数的代码。在跳转前计算机会把返回地址等关键信息压入栈中等 add 函数执行完毕再依据栈中保存的返回地址回到调用 call add 指令的下一条指令处继续执行后续代码 。6.内联函数与宏函数对比

相关新闻

STM32F103 寄存器方式实现流水灯

STM32F103 寄存器方式实现流水灯

STM32F103 寄存器方式实现流水灯 文章目录STM32F103 寄存器方式实现流水灯一、GPIO端口初始化1. 时钟配置2. 设置端口模式二、程序实现1. 流水灯原理2. C语言实现3. 编译和下载时注意的点三、运行效果四、总结五、参考一、GPIO端口初始化 本次实验我没有用 HAL 库,…

2026/9/24 17:57:47 阅读更多 →
AI测试工程师面试问什么:我用一万多字总结了13道高频面试题

AI测试工程师面试问什么:我用一万多字总结了13道高频面试题

AI测试这两年炒得很火热,各大公司纷纷宣布引入AI提升测试效率,招聘岗位也开始出现"AI测试工程师"这个新头衔。但真到了面试环节,很多候选人却发现:网上的AI测试资料要么停留在概念层面,要么就是工具教程&…

2026/9/24 17:57:47 阅读更多 →
MemOS 获取消息接口(POST /product/get/message)完全指南:原始对话历史拉取、参数详解与实战场景

MemOS 获取消息接口(POST /product/get/message)完全指南:原始对话历史拉取、参数详解与实战场景

人工智能大模型Agent 记忆AI AgentRAG知识图谱dsh-plugin 【免费下载链接】MemOS Self-evolving memory OS for LLM & AI Agents: ultra-persistent memory, hybrid-retrieval, and cross-task skill reuse, with 35.24% token savings and DeepSeek Harness support. 项目…

2026/9/24 17:57:47 阅读更多 →

最新新闻

Ubuntu 22.04 Server 安装与初始化配置全攻略

Ubuntu 22.04 Server 安装与初始化配置全攻略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 4:48:41 阅读更多 →
50款Android Studio项目源码导入实战:环境对齐与避坑指南

50款Android Studio项目源码导入实战:环境对齐与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 4:48:41 阅读更多 →
鸿蒙HAP打包上架全流程深度解析与避坑指南

鸿蒙HAP打包上架全流程深度解析与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 4:48:41 阅读更多 →
深入浅出MSP协议:飞控与地面站串口通信实战解析

深入浅出MSP协议:飞控与地面站串口通信实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 4:48:41 阅读更多 →
安卓应用安全基础:权限、组件暴露与加固攻防实践

安卓应用安全基础:权限、组件暴露与加固攻防实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 4:48:41 阅读更多 →
Erlang/OTP 记录(Records)实战指南:定义、创建、访问与编译期元组展开原理

Erlang/OTP 记录(Records)实战指南:定义、创建、访问与编译期元组展开原理

编程语言语言运行时标准库编译器并发编程 【免费下载链接】otp Erlang/OTP 项目地址: https://gitcode.com/gh_mirrors/ot/otp 点击查看 免费下载 Records 是 Erlang/OTP 中用于存储固定数量元素的命名数据结构,其作用与 C 语言中的 struct 类似&#x…

2026/9/25 4:47:41 阅读更多 →

日新闻

AI元人文:从工具使用到思维重构的深度探索

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:00:41 阅读更多 →
Python+CNN车牌识别实战:从数据预处理到模型训练与部署

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:00:41 阅读更多 →
Vim基础操作全攻略:保存退出、模式切换与高频命令实战

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/25 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →