C++入门基础指南
个人主页星轨初途个人专栏C语言数据结构C学习竞赛类,C专栏开发类,算法及编程题分享文章目录前言一、C的第一个程序二、命名空间namespace1、namespace的价值2、namespace的定义3. 命名空间使用三、C输入cin输出cout四、缺省参数1、缺省参数的概念2、全缺省和半缺省五、函数重载六、引用1. 引用的概念和定义2. 引用的特性3. 引用的使用场景4、const引用5. 指针和引用的关系七、内联函数inline八、空指针nullptr九、结束语前言嗨我们又见面啦我们在学完数据结构和C语言后我们就开始学习C啦本专栏主要偏向于C开发方向准备好进入C的世界了吗让我们一起探索吧关于C的历史发展及应用这里就不做过多赘述大家可以在网上搜索进行了解我们直接从C的第一个程序进行讲解啦一、C的第一个程序C兼容C语言绝大多数的语法所以C语言实现的hello world依旧可以运行。在C中我们需要把定义文件代码后缀改为.cppVS编译器看到.cpp就会调用C编译器编译Linux下要用g编译不再是gcc。C版本#includestdio.hintmain(){printf(hello world);return0;}虽然兼容C但C有一套自己的输入输出。严格来说C版本的hello world应该是这样写的C版本#includeiostreamusingnamespacestd;// 这里的std cout等我们下面会依次讲解intmain(){couthello world\nendl;return0;}二、命名空间namespace在C中变量、函数和后面要学到的类都是大量存在的。这些名称如果都存在于全局作用域中很容易导致冲突。1、namespace的价值namespace关键字的出现就是针对这种问题的。它的目的是对标识符的名称进行本地化以避免命名冲突或名字污染。比如在C语言中如果你包含stdlib.h后定义一个名为rand的全局变量就会和库里的rand函数冲突。C 引入namespace就是为了更好地解决此类问题。#includestdio.h#includestdlib.hintrand10;intmain(){printf(%d,rand);return0;}效果2、namespace的定义定义命名空间需要使用namespace关键字后面跟命名空间的名字然后接一对{}。namespace本质是定义出一个域这个域跟全局域各自独立。不同的域可以定义同名变量所以下面的rand不在冲突了。namespace只能定义在全局当然还可以嵌套定义。项目工程中多文件中定义的同名namespace会认为是一个namespace自动合并不会冲突。C中域有函数局部域全局域命名空间域类域域影响的是编译时语法查找一个变量/函数/类型出处(声明或定义)的逻辑所有有了域隔离名字冲突就解决了。局部域和全局域除了会影响编译查找逻辑还会影响变量的生命周期命名空间域和类域不影响变量生命周期。C标准库都放在一个叫std(standard)的命名空间中。namespacexingguichutu{intrand10;// 命名空间中可以定义变量intAdd(intleft,intright){// 也可以定义函数returnleftright;}structNode{// 也可以定义类型structNode*next;intval;};// 嵌套定义namespacepg{intrand1;}}3. 命名空间使用编译查找一个变量的声明/定义时默认只会在局部或者全局查找不会到命名空间里面去查找。要使用命名空间中定义的成员有三种方式指定命名空间访问项目中推荐这种方式printf(%d\n,xingguichutu::rand);using将命名空间中某个成员展开项目中经常访问的不存在冲突的成员推荐这种方式usingxingguichutu::Add;展开命名空间中全部成员项目不推荐冲突风险很大。日常小练习程序为了方便推荐使用usingnamespacexingguichutu;C标准库都放在一个叫std(standard) 的命名空间中。这就是为什么第一个程序里有一句using namespace std;。三、C输入cin输出coutiostream是 Input Output Stream 的缩写是标准的输入、输出流库定义了标准的输入、输出对象。std::cin是istream类的对象主要面向窄字符的标准输入流。std::cout是ostream类的对象主要面向窄字符的标准输出流。std::endl是一个函数流插入输出时相当于插入一个换行字符加刷新缓冲区。是流插入运算符是流提取运算符。C语言中用作位运算左移/右移IO流涉及类和对象运算符重载、继承等很多面向对象的知识这些知识我们还没有讲解所以这里我们只能简单认识一下C IO流的用法后面我们会有专门的一个章节来细节IO流库。cout/cin/endl等都属于C标准库C标准库都放在一个叫std(standard)的命名空间中所以要通过命名空间的使用方式去用他们。C IO 的优势使用C输入输出更方便不需要像printf/scanf输入输出时那样需要手动指定格式。C的输入输出可以自动识别变量类型本质是通过函数重载实现的。更重要的是C的流能更好地支持自定义类型对象的输入输出。#includeiostreamusingnamespacestd;intmain(){inta0;doubleb0.1;charcx;// 自动识别类型cinabc;couta b cendl;return0;}注意cout/cin/endl等都属于C标准库放在std命名空间中。日常练习可以使用using namespace std;但在实际项目开发中建议使用std::cout或using std::cout;以防止命名冲突。四、缺省参数1、缺省参数的概念缺省参数是声明或定义函数时为函数的参数指定一个缺省值默认值。在调用该函数时如果没有指定实参则采用该形参的缺省值否则使用指定的实参。如voidFunc(inta0){coutaendl;}// Func(); 会打印 0// Func(10); 会打印 102、全缺省和半缺省全缺省全部形参都给缺省值。半缺省部分形参给缺省值。C规定半缺省参数必须从右往左依次连续缺省不能间隔跳跃给缺省值。注意带缺省参数的函数调用必须从左到右依次给实参不能跳跃给。函数声明和定义分离时缺省参数不能在声明和定义中同时出现规定必须在函数声明中给缺省值。// 全缺省voidFunc1(inta10,intb20,intc30){couta aendl;coutb bendl;coutc cendlendl;}// 半缺省voidFunc2(inta,intb10,intc20){couta aendl;coutb bendl;coutc cendlendl;}五、函数重载C支持在同一作用域中出现同名函数但是要求这些同名函数的形参不同可以是参数个数不同或者类型不同或者类型的顺序不同。这使得C函数调用表现出了多态行为使用更灵活。// 1. 参数类型不同intAdd(intleft,intright);doubleAdd(doubleleft,doubleright);// 2. 参数个数不同voidf();voidf(inta);// 3. 参数类型顺序不同voidf(inta,charb);voidf(charb,inta);注意返回值不同不能作为重载条件因为调用时编译器无法区分。六、引用1. 引用的概念和定义引用不是新定义一个变量而是给已存在变量取了一个别名。编译器不会为引用变量开辟内存空间它和它引用的变量共用同一块内存空间。语法类型 引用别名 引用对象;inta0;intba;// b是a的别名intca;// c也是a的别名intdb;// 也可以给别名取别名这里a, b, c, d的地址完全一样修改其中任何一个其他的都会跟着变。如#includeiostreamusingnamespacestd;intmain(){inta0;intba;// b是a的别名intca;// c也是a的别名intdb;// 也可以给别名取别名couta b c d\n;couta b c d\n;a;couta b c d\n;couta b c d\n;return0;}效果2. 引用的特性引用在定义时必须初始化。一个变量可以有多个引用。引用一旦引用一个实体再不能引用其他实体#includeiostreamusingnamespacestd;intmain(){inta10;// 编译报错: ra: 必须初始化引用//int ra;intba;intc20;// 这里并非让b引用c因为C引用不能改变指向// 这里是一个赋值bc;coutaendl;coutbendl;coutcendl;return0;}3. 引用的使用场景引用传参类似指针传参可以在函数内部修改外部变量且不需要像指针那样解引用代码更简洁安全。#includeiostreamusingnamespacestd;// rx 和 ry 分别是外部传入实参的别名修改它们就是修改外部实参voidSwap(intrx,intry){inttmprx;rxry;rytmp;}intmain(){intx0,y1;// 调用时无需像C语言那样传地址 Swap(x, y)直接传变量即可Swap(x,y);coutx yendl;// 此时外部的 x 和 y 已经被成功交换 [cite: 2213]return0;}引用做返回值减少拷贝提高效率同时可以改变引用对象时同时改变被引用对象。此场景需要注意不能返回局部变量的引用因为局部变量出作用域就销毁了。#includeiostreamusingnamespacestd;intarr[5]{10,20,30,40,50};// 全局数组生命周期在整个程序运行期间// 返回类型为 int意味着返回的是 arr[index] 这块内存空间的别名不会发生值拷贝intcm(intindex){returnarr[index];}intmain(){coutarr[1]endl;// 打印 20// 核心高级用法直接给函数的返回值赋值从而改变了被引用的实际对象cm(1)999;coutarr[1]endl;// 打印 999/* 致命避坑千万不要这么写 int cm() { int temp 10; return temp; // 报错或警告temp是局部变量出了函数大括号内存就被回收返回的将是“野引用” } */return0;}效果4、const引用可以引用一个const对象但是必须用const引用。const引用也可以引用普通对象因为对象的访问权限在引用过程中可以缩小但是不能放大。所谓临时对象就是编译器需要一个空间暂存表达式的求值结果时临时创建的一个未命名的对象C中把这个未命名对象叫做临时对象。constinta10;// int ra a; // 报错权限放大constintraa;// 正确注意临时对象当发生类型转换如double转int或表达式求值如a * 3时编译器会产生一个具有常性的“临时对象”来存储中间值。此时如果用引用去接必须使用const引用。5. 指针和引用的关系C中指针和引用就像两个性格迥异的亲兄弟相辅相成互相不可替代内存引用是取别名不开空间指针存储地址需要开辟空间。初始化与指向引用定义必须初始化且不能改变指向指针可以不初始化且可以随时改变指向。访问引用直接访问指针需要解引用*访问。安全指针容易出现空指针和野指针引用极少出现相对更安全。七、内联函数inline用inline修饰的函数叫做内联函数。编译时C编译器会在调用的地方展开内联函数从而避免了函数调用建立栈帧的开销提高效率。inline的设计目的是为了替代C语言容易出错的宏函数。inline对于编译器而言只是一个建议。它适用于频繁调用的短小函数。对于递归函数或代码较长的函数即使加上inline编译器也会忽略。inline不建议声明和定义分离到两个文件这会导致链接错误。因为inline被展开后就没有函数地址了。inlineintaadd(inta,intb){returnab;}八、空指针nullptr在C语言中NULL实际上是一个宏可能被定义为0或(void*)0。在C中如果NULL被定义为0在面临重载函数f(int)和f(int*)时调用f(NULL)会匹配到f(int)这违背了我们传入空指针的初衷。为了解决这个问题C11 引入了nullptr。nullptr是一种特殊类型的字面量它可以转换成任意其他类型的指针类型但不能被转换为整数类型。因为nullptr只能被 隐式地转换为指针类型而不能被转换为整数类型。在C中定义空指针请务必使用nullptr。#includeiostreamusingnamespacestd;voidf(intx){coutf(int x)endl;}voidf(int*ptr){coutf(int* ptr)endl;}intmain(){f(0);// 本想通过f(NULL)调用指针版本的f(int*)函数但是由于NULL被定义成0调用了f(int x)因此与程序的初衷相悖。f(NULL);f((int*)NULL);// 编译报错: error C2665: f: 2 个重载中没有一个可以转换所有参数类型// f((void*)NULL);f(nullptr);return0;}九、结束语嗨ヾ(o´∀o)本篇到这里就结束啦我们从第一个C程序出发详细讲解了C区别于C语言的基础特性更安全的命名空间、更聪明的输入输出、灵活的缺省参数与重载、好用的引用以及更严谨的内联与空指针。通过本篇我们也正式进入C的大门啦٩(๑❛ᴗ❛๑)۶欢迎大家积极在评论区进行讨论和建议感谢大家的支持啦下一篇我们将要讲解类和对象欢迎大家来学习了解