
引言1.1什么是CC是由Bjarne Stroustrup在1979年于贝尔实验室开始开发的最初在C语言基础上添加了面向对象编程特性1983年正式命名为C。它是一种通用编程语言支持面向对象、泛型和函数式等多种编程范式。1.2为什么要学习C?主要应用领域大型系统软件编译器、数据库、操作系统、浏览器游戏引擎开发UE4、Cocos2d-x音视频处理FFmpeg、WebRTC服务端开发游戏服务、流媒体、量化交易嵌入式开发智能手环、车载系统、智能家居PC客户端开发WPS等C Qt机器学习引擎底层算法实现1.3C的版本演变版本年份重要特性C981998首个官方标准引入STLC032003修复bug引入tr1库C112011重大更新lambda、范围for、右值引用、智能指针、线程库C142014对C11的扩展和完善C172017if constexpr、折叠表达式、filesystemC202020协程、概念、模块化C232023小版本更新C26制定中目前工业界普遍使用C11/14/17建议学习时以C11为基础。2.1第一个C程序#include iostream // 输入输出流库 using namespace std; // 使用标准命名空间 int main() { cout hello world\n endl; return 0; }刚开始看到C这个程序的时候会陌生iostream using namespace std;为什么要这样写——cout hello world\n endl。接下来会一一讲解3 命名空间——namespace什么是命名空间在C/C中变量、函数和后面要学到的类都是大量存在的这些变量、函数和类的名称将都存在于全局作用域中可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化以避免命名冲突或名字污染namespace关键字的出现就是针对这种问题的。再讲解之前先来看看下面一行的代码#include stdio.h #include stdlib.h int rand 0; int main() { printf(%d ,rand); return 0; }这个代码会编译错误因为#include stdlib.h这个头文件里定义了rand函数而这里想把rand作为变量但是编译器觉得他是函数造成两边冲突了。未来解决这种问题就有了命名空间——namespace定义方式namespace名字自己命名#include stdio.h #include stdlib.h namespace myroom { int rand 10; } int a 0; int main() { int a 1; printf(%p\n ,rand);//00007FFAF69F2420#include stdlib.h里面的rand printf(%d\n ,myroom::rand);//命名空间里面的rand printf(%d\n ,a);//打印main函数里面的a printf(%d\n ,::a);//::前面什么都不写就是默认打印全局变量的a return 0; } myroom::成员——访问命名空间里面的 其中::叫做域作用限定符前面加上命名空间的名字就是访问命名空间里面的成员注意这里命名空间域里面的变量还是全局变量但是他只是受到了这个命名空间的限制。因此这个namespace的域不能定义在局部变量里面如下#include stdio.h #include stdlib.h int main() { namespace myroom { int rand 10;//这是错误的 } return 0; }命名空间的嵌套namespace room { namespace myroom { int rand 0; int Add(int left, int right) { return left right; } struct Node { struct Node* next; int val; }; } namespace hisroom { int rand 1; int Add(int left, int right) { return left right; } struct Node { struct Node* next; int val; }; } } //嵌套方面 int main() { printf(%d\n, room::myroom::rand); printf(%d\n,room::hisroom::rand); printf(%d\n,room::myroom::Add(1,2)); printf(%d\n, room::hisroom::Add(1, 3)); return 0; }在多个文件中定义同名的namespace它们会自动合并就像是在同一个命名空间中一样。命名空间的使用编译查找变量时默认只在局部域和全局域查找不会到命名空间域中查找。通常来说一般有三种使用方法1、指定命名空间的访问——空间名空间成员2、展开某个成员以后访问都不需要加上域限定符号。using 空间名空间成员3、展开整个命名空间——在项目上不推荐using 空间名展开了之后所展开的变量或者是域变成了上一级的全局变量namespace N { int a 0; int b 1; } // 方式1指定命名空间访问推荐 int main() { printf(%d\n, N::a); return 0; } // 方式2展开某个成员 using N::b; int main() { printf(%d\n, b); // 可以直接使用b printf(%d\n, N::a); // a仍需指定 return 0; } // 方式3展开整个命名空间日常练习可用项目不推荐 using namespace N; int main() { printf(%d\n, a); printf(%d\n, b); return 0; }总结在没有使用using声明或using指令展开命名空间的情况下查找优先级为函数局部域 全局域 未展开的命名空间域。如果使用了using声明或using指令来展开命名空间则展开后的命名空间域中的名称将与全局域中的名称具有相同的查找优先级但仅限于被展开的部分但仍然低于函数局部域的优先级。4 C输入输出基本语法对象说明std::cin标准输入流istream类对象std::cout标准输出流ostream类对象std::endl换行 刷新缓冲区流插入运算符输出流提取运算符输入以上语法不细讲后面再详细说先知道用法就可以了。#include iostream//里面定义了cout和cin相当于C语言的stdio.h using namespace std;//cout和cin是用了一个叫做std的命名空间域包括在里面了所有要展开才能用 //这个后续会了解先记住 int main() { int a 0; double b 0.1; char c x; // 输出 cout a b c endl;//a b c // 输入自动识别类型 cin a; cin b c; // 仍然可以使用printf/scanf包含iostream后间接包含了stdio.h scanf(%d%lf, a, b); printf(%d %lf\n, a, b); return 0; }注意和的之间只能输入一个操作数#include iostream一般都会间接的包含stdio.h这个看编译器的情况5 缺省参数概念缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时如果没有指定的实参则采用该形参的缺省值否则使用指定的实参。有些地方把缺省参数也叫默认参数void Func(int a 0) { cout a endl; } int main() { Func(); // 输出0 Func(10); // 输出10 }全缺省和半缺省#include iostream using namespace std; void Func(int a 0) { cout a endl; } // 全缺省 void Func1(int a 10, int b 20, int c 30) { cout a a endl; cout b b endl; cout c c endl endl; } // 半缺省 void Func2(int a, int b 10, int c 20) { cout a a endl; cout b b endl; cout c c endl endl; } int main() { Func(); // 没有传参时使用参数的默认值 Func(10); // 传参时使用指定的实参 Func1(); Func1(1); Func1(1, 2); Func1(1, 2, 3); //Func2(); Func2(100); Func2(100, 200); Func2(100, 200, 300); return 0; }使用规则调用时必须从左到右依次传参不能跳跃声明和定义分离时缺省参数只在声明中给出6 函数重载概念同一作用域中允许存在同名函数但要求形参不同参数个数或类型不同。类型一共有三种不同的类型// 1. 参数类型不同 int Add(int left, int right) { return left right; } double Add(double left, double right) { return left right; } // 2. 参数个数不同 void f() { cout f() endl; } void f(int a) { cout f(int a) endl; } // 3. 参数类型顺序不同 void f(int a, char b) { cout f(int,char) endl; } void f(char b, int a) { cout f(char,int) endl; }注意:返回值的不同不能作为重载条件因为不能区分void f1() {} void f1(int a 10) {} // f1(); // 编译报错存在歧义不知道调用哪个上面确实是构成函数重载但是调用的时候会存在歧义所有无参调用的时候不能这样写如果真要这样就使用命名空间隔开但是隔开之后不构成函数重载因为他们不在同一作用域7、引用概念引用是给已存在变量取一个别名不开辟新内存与被引用变量共用同一块内存空间。特性1——必须初始化int ra; // ✗ 错误必须初始化2——一个变量可以有多个引用int a 0; int b a; // b是a的引用别名 int c a; // c也是a的引用 int d b; // d还是a的引用 d; // a也变成了13——引用一旦绑定不能再改变指向int a 10, c 20; int b a; b c; // 这是赋值不是让b引用ca变成了20使用场景传参void Swap(int rx, int ry) { int tmp rx; rx ry; ry tmp; } int main() { int x 0, y 1; Swap(x, y); // 直接传变量即可 cout x y endl; }引用做返回值int STTop(ST rs) { return rs.a[rs.top]; // 返回栈顶元素的引用 } int main() { ST st1; STInit(st1); STPush(st1, 1); STPush(st1, 2); cout STTop(st1) endl; // 输出2 STTop(st1) 10; // 栈顶元素变成12 cout STTop(st1) endl; // 输出12 }这里有个印象后面类和对象重点说const引用const int a 10; // int ra a; // ✗ 错误权限放大a是const不能给普通引用 const int ra a; // ✓ 正确 int b 20; const int rb b; // ✓ 正确权限缩小允许 // 临时对象具有常性 const int rb a * 3; // ✓ 正确,给表达式结果取别名是可以的这里会生成一个临时变量再赋值给rb // int rb a * 3; // ✗ 错误 const int rd 12.34; // ✓ 正确double转int产生临时对象 // int rd 12.34; // ✗ 错误 //const引用可以给常量取别名 const int re 3;如果原变量本身的权限只能读不能写那么引用的时候也要引用。但是如果原变量本身权限可以读可以写用const让引用进行权限缩小是可以的。关于临时对象后面会重点用到。指针和引用的区别特性引用指针是否开空间不开空间是别名开空间存地址必须初始化必须初始化建议但不必须能否改变指向不能可以访问对象直接访问需要解引用*sizeof结果引用类型的大小地址空间大小4/8字节安全性更安全少有空指针问题可能出现空指针/野指针inline内联函数C语言用宏函数实现代码展开但宏存在很多问题写法复杂、容易出错、无法调试。C引入inline关键字来替代宏函数。// 宏函数的正确写法 #define ADD(a, b) ((a) (b)) // 内联函数更安全可调试 inline int Add(int a, int b) { return a b; }inline的特性编译器会在调用处展开函数体避免栈帧开销inline只是建议编译器可以选择不展开递归、函数体过大等会被忽略适合频繁调用的短小函数注意inline函数声明和定义不要分离到两个文件// F.h inline void f(int i);//被展开了之后地址就没了 // F.cpp void f(int i) { cout i endl; } // main.cpp #include F.h f(10); // ✗ 链接错误找不到函数地址因为inline被展开后没有函数地址链接时找不到。应把定义直接放在头文件中。nullptr再C中NULL会被认为是一个宏定义为0而不是像C语言那样认为是(void*)*0因此在C中为了表示空指针就有了nullptrvoid f(int x) { cout f(int) endl; } void f(int* ptr) { cout f(int*) endl; } int main() { f(0); // 调用 f(int) f(NULL); // 调用 f(int) —— 本意是想调用指针版本但是实际上是0因为NULL是被定义成了宏 f(nullptr); // 调用 f(int*) ✓ 正确 }如果写成了void*)0,两边都不能匹配f((void*)0)——不成立总结C11中引⼊nullptrnullptr是⼀个特殊的关键字nullptr是⼀种特殊类型的字⾯量它可以转换成任意其他类型的指针类型。使⽤nullptr定义空指针可以避免类型转换的问题因为nullptr只能被隐式地转换为指针类型⽽不能被转换为整数类型。目录引言1.1什么是C1.2为什么要学习C?1.3C的版本演变2.1第一个C程序3 命名空间——namespace什么是命名空间命名空间的嵌套命名空间的使用4 C输入输出基本语法5 缺省参数概念全缺省和半缺省使用规则6 函数重载概念类型7、引用概念特性1——必须初始化2——一个变量可以有多个引用3——引用一旦绑定不能再改变指向使用场景传参引用做返回值const引用指针和引用的区别inline内联函数inline的特性nullptr