公司动态

【C++ 入门】cin/cout、缺省参数、函数重载与引用 ——C++ 基础语法一篇讲透

📅 2026/9/2 5:43:04
【C++ 入门】cin/cout、缺省参数、函数重载与引用 ——C++ 基础语法一篇讲透
文章目录一、C输入输出二、缺省参数三、函数重载四、引用引用的概念和定义引用的特性引用的使用传值返回和传引用返回一、C输入输出iostream全称是 Input Output Stream也就是输入输出流库C 标准用来做输入输出的库里面定义好了输入输出相关的对象。std::cin是istream类创建出来的对象负责普通字符的标准输入就是用来读键盘输入的数据。std::cout是ostream类创建出来的对象负责普通字符的标准输出用来往屏幕打印内容。std::endl本质是一个函数输出的时候用它效果就是换行同时还会把缓冲区的数据立刻刷新输出。叫流插入运算符用来输出数据叫流提取运算符用来读取输入。顺带一提在 C 语言里这两个符号是用来做移位运算左移和右移的。C 的输入输出用起来比 C 的printf、scanf省事很多。printf/scanf得手动写格式控制符而 C 可以自动识别变量是什么类型不用我们手动指定底层靠的是函数重载后面会学。还有一个很大优势C 流可以很方便支持我们自己写的自定义类型做输入输出。IO 流牵扯到类、对象、运算符重载、继承这些面向对象知识点现在还没学到所以这里只需要先会简单用就行后面会有专门章节详细讲 IO 流库。cout、cin、endl这些都属于 C 标准库内容标准库全部放在std命名空间里面所以使用它们的时候得遵守命名空间的使用规则。平时写练习代码经常会写using namespace std;图方便但是真实项目开发不推荐这么写。另外说个小细节代码里就算不写包含stdio.h部分编译器下也能直接用printf和scanf。比如 VS 编译器因为引入iostream的时候顺带把stdio.h也带进来了但别的编译器不一定会这样直接用就可能编译报错。#includeiostreamusingnamespacestd;intmain(){inta0;doubleb0.1;charcz;couta b cendl;std::couta b cstd::endl;scanf(%d%lf,a,b);printf(%d %lf\n,a,b);// 可以自动识别变量类型cinabc;couta b cendl;return0;}用\n的效率会更高除此以外cout和cin的效率其实也是不如printf和scanf的但是我们可以通过取消同步流的操作来解决这个问题代码如下#includeiostreamusingnamespacestd;intmain(){//取消同步流//在io需求比较高的地方比如需要大量输入的竞赛题中加上以下3行代码,可以提高效率ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);}二、缺省参数缺省参数也叫默认参数写函数声明或者函数定义的时候可以给形参预先设置一个默认值。调用这个函数的时候如果你不传对应的实参就会用这个预先写好的默认值如果你传了实参那就用你传过来的数据。缺省参数分两种全缺省、半缺省。全缺省函数所有的形参全都设置上默认值。半缺省只给一部分形参设置默认值。C 语法要求给默认值必须从最右边开始连续设置不能中间隔一个跳着给默认值。调用带缺省参数的函数时传实参也得从左往右挨个传不能跳过前面的参数直接给后面的传参。#includeiostreamusingnamespacestd;// 全缺省voidFunc1(inta3,intb4,intc5){coutaaendl;coutbbendl;coutccendl;}// 半缺省voidFunc2(inta,intb6,intc7){coutaaendl;coutbbendl;coutccendl;}intmain(){Func1();cout--------------------endl;Func1(1);cout--------------------endl;Func2(1,2);return0;}如果函数的声明和定义是分开写的默认参数不能两边都写。语法规定默认参数只写在函数声明那里函数定义部分不要再重复写默认值。// Stack.h#includeiostream#includeassert.husingnamespacestd;typedefintSTDataType;typedefstructStack{STDataType*a;inttop;intcapacity;}ST;voidSTInit(ST*ps,intn4);// Stack.cpp#includeStack.h// 缺省参数不能声明和定义同时给voidSTInit(ST*ps,intn){assert(psn0);ps-a(STDataType*)malloc(n*sizeof(STDataType));ps-top0;ps-capacityn;}// test.cpp#includeStack.hintmain(){ST s1;STInit(s1);// 确定知道要插⼊1000个数据初始化时⼀把开好避免扩容ST s2;STInit(s2,1000);return0;}三、函数重载C 允许同一个作用域里面写好几个名字完全一样的函数但是有条件这些同名函数的形参必须不一样可以是参数数量不一样也可以是参数类型不一样。调用的时候编译器会根据你传进去的实参自动匹配该调用哪一个函数这就是函数重载代码写起来会更灵活属于多态的一种表现。而C 语言不支持这个特性同一个作用域下不能有重名函数一旦重名就直接编译报错。参数类型不同intAdd(inta,intb){coutint Add(int a,int b)endl;returnab;}doubleAdd(doublea,doubleb){coutdouble (double a,double b)endl;returnab;}intmain(){coutAdd(1,2)endl;coutAdd(3.14,2.7)endl;return0;}参数个数不同voidfunc(inta){coutfunc(int a):aendl;}voidfunc(inta,intb){coutfunc(int a,int b):a bendl;}intmain(){func(5);func(3,4);return0;}参数类型顺序不同voidf(inta,charc){coutf(int a,char b)endl;}voidf(charc,inta){coutf(char c, int a)endl;}intmain(){f(1,z);f(z,1);}返回值不同不能作为重载条件因为调用时也无法区分可以构成重载但是调用时会出现歧义(缺省的情况)voidf(){coutf()endl;}voidf(inta1){coutf(int a1)endl;}intmain(){//f();//不可以编译器不知道调用哪一个函数了f(10);}四、引用引用的概念和定义引用并不是创建一个全新的变量而是给已经存在的变量再起一个外号。编译器不会给引用单独分配内存引用和原来的变量用的是同一块内存。打个比方李逵宋江叫他 “铁牛”江湖又叫 “黑旋风”不管叫哪个名字指的都是同一个人。林冲外号豹子头也是同一个道理多个名字对应同一个本体。引用的书写格式类型 引用别名 被引用的变量;#includeiostreamusingnamespacestd;intmain(){inta10;// 引用b和c是a的别名intba;intca;// 也可以给b取别名d相当于还是a的别名intdb;coutaendl;coutbendl;coutcendl;coutdendl;}C 为了不去新增一大堆运算符就把 C 语言已有的符号拿来复用。像之前的、既是移位运算符又用作流输入输出这里的也是既可以做取地址又用来表示引用。我们要根据代码写法和场景自己区分它到底是取地址还是引用。说实话这点很容易搞混觉得干脆多搞几个新符号会更清晰。引用的特性引⽤在定义时必须初始化⼀个变量可以有多个引⽤引⽤⼀旦引⽤⼀个实体再不能引⽤其他实体#includeiostreamusingnamespacestd;intmain(){inta10;intraa;// 必须初始化引用intc20;rac;// 这里是一个赋值并非让b引用cc引用不能改变指向coutaendl;coutraendl;coutcendl;return0;}引用的使用实际写代码的时候引用主要有两个用处引用传参和引用做函数返回值。一方面可以减少数据拷贝提升程序运行效率另一方面修改引用原本的变量也会跟着一起改动。引用传参的效果和指针传参差不多但引用写代码会更简单顺手。引用作为返回值的情况会复杂一些这里只先简单了解后面学到类和对象的时候还会详细深挖。引用和指针在开发里经常配合使用部分功能看起来很像但各自有独有的特性并不能完全互相替换。另外要分清C 的引用和 Java 这类语言里的引用差别很大。最关键的一点C 的引用一旦绑定某个变量之后就不能再改成指向别的变量而 Java 的引用是可以更换指向对象的。有些讲数据结构的书原本算法是用 C 语言指针写的改成 C 版本后就换成引用传参为了简化代码躲开难懂的指针。可不少同学还没学过引用看这类代码的时候就会看得很懵。大部分情况下引用都可以替代指针除了一些特殊情况比如链表的树的节点的定义只能使用指针//交换值#includeiostreamusingnamespacestd;voidswap(int*x,int*y){inttemp*x;*x*y;*ytemp;}voidswap(intx,inty){inttempx;xy;ytemp;}intmain(){inta3;intb9;couta:a b:bendl;swap(a,b);// 用指针couta:a b:bendl;swap(a,b);// 用引用couta:a b:bendl;return0;}//交换指针#includeiostreamusingnamespacestd;// 交换指针voidswap(int**p1,int**p2){int*temp*p1;*p1*p2;*p2temp;}voidswap(int*p1,int*p2){int*tempp1;p1p2;p2temp;}intmain(){inta3,b9;int*paa;int*pbb;cout*papa *pbpbendl;swap(pa,pb);// 用指针cout*papa *pbpbendl;swap(pa,pb);// 用引用cout*papa *pbpbendl;return0;}在大部分的场景下引用都可以代替指针使用但是在链表树的节点定义位置只能使用指针引用无法替代。这是因为C的引用无法改变指向节点一定存在改变指向的场景传值返回和传引用返回传值返回#includeiostreamusingnamespacestd;intfun(){intret0;returnret;}intmain(){intxfun();// x这里接受的其实是ret的拷贝值在fun函数销毁时ret就没了通过临时变量带出//fun() 1;//所以这里就无法直接修改return0;}传引用返回#includeiostreamusingnamespacestd;intfun(){intret0;returnret;}intmain(){intxfun();coutx\n;// 这里可能会是随机值return0;}这里的返回值ret的别名也就是说fun函数的栈帧销毁后还给了操作系统但是他还指向这块空间后续如果这块空间被别人所用就会有影响。相当于野指针访问1.销毁了之后为什么还可以访问–因为销毁了并不代表空间没了内存空间是可以反复使用的销毁的本质是还给操作系统跟租房一样返回的别名就相当于偷偷留了把钥匙但是这样是不可取的。2.这里相当于野指针的访问为什么没报错–越界不一定报错越界写可能会报错在数组中存在越界抽查这个现象。所以我们有时候编译器不会在这里报错的