从零开始:C++ 第一堂课核心知识点 —— 入门笔记
前言C 是一门历史悠久且生命力旺盛的语言从嵌入式到游戏引擎、从音视频处理到高频交易处处都有它的身影。本文整理了 C 第一堂课的核心知识点从发展历史到基本语法特性命名空间、输入输出、缺省参数、函数重载、引用、inline、nullptr用于课后复习与知识沉淀。一、C 概述与发展历史1.1 历史脉络C 起源于 1979 年Bjarne Stroustrup本贾尼·斯特劳斯特卢普在贝尔实验室为应对复杂软件开发任务在 C 语言基础上添加了面向对象编程的特性。1983 年该语言被正式命名为C包含了类、封装、继承等核心概念。1998 年C 的 ANSI/ISO 标准正式发布此后经历了 C03/11/14/17/20/23 等多个版本迭代。1.2 C 参考文档https://legacy.cplusplus.com/reference/—— 以头文件形式呈现内容易读标准只更新到 C11https://zh.cppreference.com/w/cpp—— C 官方文档中文版全面清晰二、第一个 C 程序C 兼容 C 语言绝大多数语法但更推荐使用 C 自己的输入输出方式。文件后缀改为.cpp编译器会以 C 方式编译。// test.cpp#includeiostreamusingnamespacestd;intmain(){couthello world\nendl;return0;}关键点#include iostream是 C 标准输入输出流库它间接包含了stdio.h中的一些内容。cout是标准输出流对象是流插入运算符C 语言中用作左移此处被重载。endl是一个函数效果等同于输出换行符\n后再刷新输出缓冲区。using namespace std;将std命名空间展开日常练习方便项目开发中不推荐冲突风险大。三、命名空间namespace3.1 为什么需要命名空间在 C/C 中变量、函数、类的名称都存在于全局作用域中项目大了极易产生命名冲突。namespace关键字就是为了解决这个问题而生的。#includestdio.h#includestdlib.h// C 语言中这样写会冲突rand 既是函数名又被定义成变量intrand10;intmain(){// 编译报错error C2365: rand: 重定义以前的定义是函数printf(%d\n,rand);return0;}3.2 定义与使用#includestdio.h#includestdlib.hnamespacetx{intrand10;intAdd(intleft,intright){returnleftright;}structNode{structNode*next;intval;};}intmain(){// 默认访问的是全局的 rand 函数指针printf(%p\n,rand);// 指定命名空间中的 randprintf(%d\n,tx::rand);return0;}关键点namespace本质是定义了一个域这个域与全局域各自独立不同的域可以定义同名变量。C 中的域有函数局部域、全局域、命名空间域、类域。域影响编译时查找变量/函数/类型出处的逻辑。namespace只能定义在全局可以嵌套定义。多文件中定义的同名namespace会被编译器合并为同一个。C 标准库全部放在名为std的命名空间中。3.3 使用命名空间成员的三种方式// 方式一指定命名空间访问项目推荐printf(%d\n,tx::a);// 方式二using 将某个成员展开常用且不冲突的成员推荐usingtx::b;printf(%d\n,b);// 方式三展开整个命名空间日常练习方便项目不推荐usingnamespacetx;printf(%d\n,a);关键点编译查找变量声明/定义时默认只会在局部或全局查找不会到命名空间里面查找。所以必须通过上述三种方式之一才能使用命名空间中的成员。四、C 输入与输出#define_CRT_SECURE_NO_WARNINGS1#includeiostreamusingnamespacestd;intmain(){inta0;doubleb0.1;charcx;// 输出couta b cendl;// 输入 —— 自动识别变量类型cina;cinbc;coutaendl;coutb cendl;return0;}关键点cin是istream类的对象cout是ostream类的对象都属于std命名空间。使用 C 输入输出不需要手动指定格式不像printf/scanf编译器通过函数重载自动识别类型。是流提取运算符是流插入运算符C 语言中这两个运算符用作位运算右移/左移此处被重载。在 IO 需求高的场景如大量输入的竞赛题可加上以下三行提升效率ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);五、缺省参数默认参数#includeiostream#includeassert.husingnamespacestd;// 全缺省voidFunc1(inta10,intb20,intc30){couta a, b b, c cendl;}// 半缺省 —— 必须从右往左连续缺省voidFunc2(inta,intb10,intc20){couta a, b b, c cendl;}intmain(){Func1();// 全部用缺省值Func1(1,2);// 部分传参Func2(100);// 只传第一个Func2(100,200);// 传前两个return0;}关键点缺省参数在声明时指定缺省值函数定义处不能再给声明和定义分离时规定声明处给缺省值。C 规定半缺省参数必须从右往左依次连续给缺省值不能跳跃。调用时必须从左到右依次给实参不能跳跃。缺省参数的经典应用场景初始化栈时指定初始容量传参就用指定值不传就用缺省值如STInit(s, 1000)预分配避免扩容。六、函数重载C 支持在同一作用域中出现同名函数只要它们的形参不同参数个数不同 / 类型不同 / 类型顺序不同。C 语言不支持这一特性。#includeiostreamusingnamespacestd;// 1、参数类型不同intAdd(intleft,intright){coutint Add(int left, int right)endl;returnleftright;}doubleAdd(doubleleft,doubleright){coutdouble Add(double left, double right)endl;returnleftright;}// 2、参数个数不同voidf(){coutf()endl;}voidf(inta){coutf(int a)endl;}// 3、参数类型顺序不同voidf(inta,charb){coutf(int a,char b)endl;}voidf(charb,inta){coutf(char b, int a)endl;}intmain(){Add(10,20);// 调用 int 版本Add(10.1,20.2);// 调用 double 版本f();f(10);f(10,a);f(a,10);return0;}关键点返回值不同不能作为重载的条件因为调用时无法区分返回类型。缺省参数与重载结合时要小心歧义如下两个函数同时存在时f1()会报错编译器不知道调哪个voidf1(){}voidf1(inta10){}// 调用 f1() 时两个都匹配产生歧义七、引用7.1 概念与特性引用不是新定义一个变量而是给已存在变量取了一个别名编译器不会为引用变量开辟新内存空间它和原变量共用同一块内存。#includeiostreamusingnamespacestd;intmain(){inta0;// 引用b 和 c 是 a 的别名intba;intca;// 也可以给别名 b 取别名d 本质上还是 a 的别名intdb;d;// 地址完全一样coutaendl;coutbendl;coutcendl;coutdendl;return0;}引用的三大特性引用在定义时必须初始化。一个变量可以有多个引用。引用一旦引用一个实体不能再引用其他实体之后的赋值只是修改值而非改变指向。7.2 引用传参与引用返回引用传参可以替代指针传参简化代码减少拷贝。// 引用传参实现交换voidSwap(intrx,intry){inttmprx;rxry;rytmp;}intmain(){intx0,y1;Swap(x,y);// 调用时直接传变量不需要取地址coutx yendl;return0;}引用返回的一个经典场景让函数返回值可以作为左值使用。intSTTop(STrs){assert(rs.top0);returnrs.a[rs.top];}// 调用处可以直接修改栈顶元素STTop(st1)10;// 引用返回返回值可作左值7.3const引用intmain(){constinta10;// int ra a; // 错误权限放大a 是 const引用不能去掉 constconstintraa;// 正确intb20;constintrbb;// 正确权限缩小普通变量可以被 const 引用// const int 可以引用字面量、表达式结果constintrc30;constintrda*3;// 临时对象具有常性必须用 const 引用doubled12.34;constintred;// 类型转换产生临时对象必须用 const 引用return0;}关键点访问权限在引用过程中可以缩小普通→const但不能放大const→普通。临时对象字面量、表达式结果、类型转换中间值具有常性必须用const引用。7.4 引用与指针的对比对比项引用指针语法概念取别名不开空间存地址要开空间初始化必须初始化建议初始化非必须指向改变不能改变指向可以改变指向访问对象直接访问需要解引用*sizeof引用类型的大小地址空间字节数4/8 字节安全性很少出现空/野引用容易出现空/野指针八、inline内联函数inline修饰的函数叫内联函数编译时在调用处展开省去函数栈帧的建立和销毁提升效率。#includeiostreamusingnamespacestd;inlineintAdd(intx,inty){intretxy;ret1;ret1;ret1;returnret;}intmain(){// 可通过汇编观察是否展开有 call Add 语句即未展开intretAdd(1,2);coutAdd(1,2)*5endl;return0;}关键点inline是对编译器的建议编译器可以忽略。递归函数、代码较长的函数即使加了inline也不会展开。C 引入inline的目的是替代 C 的宏函数。宏函数实现复杂且容易出错括号问题、副作用问题inline函数既有宏的效率又保留了函数的类型检查。VS 编译器 debug 模式下默认不展开inline方便调试。需要到项目属性中开启/Ob2优化。inline函数不建议声明和定义分离声明在.h定义在.cpp因为inline展开后没有函数地址链接时会报错。一个正确的宏函数对比// 正确实现 ADD 宏 —— 必须加足够多的括号#defineADD(a,b)((a)(b))// 错误的宏示例// #define ADD(a, b) a b; // 运算符优先级问题// #define ADD(a, b) (a b) // 外部括号缺失九、nullptr#includeiostreamusingnamespacestd;voidf(intx){coutf(int x)endl;}voidf(int*ptr){coutf(int* ptr)endl;}intmain(){f(0);// 调用 f(int)f(NULL);// 本想调用 f(int*)但 NULL 被定义为 0实际调用了 f(int)f((int*)NULL);// 强制转换后才调用指针版本f(nullptr);// C11明确调用指针版本return0;}关键点NULL在 C 中通常被定义为字面量0而非(void*)0导致重载场景下f(NULL)匹配到f(int)而非f(int*)。C11 引入了nullptr关键字它是一种特殊类型的字面量可以隐式转换为任意类型的指针但不能转换为整数类型。现代 C 中定义空指针一律使用nullptr避免类型歧义。十、设计心得命名空间优先用指定访问域::名称方式—— 项目开发中不要轻易using namespace std避免命名冲突。引用传参替代指针传参—— 引用写起来更简洁且没有空指针风险适合替代函数参数中的一级指针。inline只给短小频繁调用的函数加—— 编译器有最终决定权不要迷信inline修饰符。缺省参数和重载结合时注意歧义—— 缺省参数会让函数调用匹配规则变复杂避免设计出歧义的重载集。空指针只用nullptr—— 它类型安全且语义明确不再用NULL或0表示空指针。十一、小结第一堂课覆盖了 C 从历史到核心语法特性的全景式入门namespace解决了 C 语言长期存在的命名冲突问题cin/cout让输入输出更加类型安全缺省参数、函数重载、引用、inline、nullptr等特性在 C 语言基础上做了大量增强和简化。这些知识点是后续学习类和对象、STL 等高级内容的基础需要熟练掌握。