# 参考资料 [gdb 调试入门,大牛写的高质量指南](https://blog.csdn.net/wu_cai_/article/details/79669842) [Computer Architecture A Quantitative Approach pdf 翻译](https://github.com/Ewenwan/Computer-Architecture-A-Quantitative-Approach) [自动编程体系设想 ](https://blog.csdn.net/jxt1234and2010/article/details/45201421) [程序猿成长计划](https://github.com/mylxsw/growing-up) [数据结构和算法动态可视化](https://visualgo.net/zh) [基础数据结构和算法的纯C语言实现](https://github.com/Ewenwan/Data-Structures-and-Algorithms-in-C) [计算机科学 面试笔记](https://github.com/PLester/Interview-Notebook/tree/master/notes) [计算机科学 面试笔记2 ](https://github.com/CyC2018/CS-Notes/tree/master/docs/notes) [可视化代码过程](http://pythontutor.com/) [各种工程实践代码参考 ](https://github.com/Ewenwan/practice) [c++ 多线程 并发 指南 实战](https://github.com/Ewenwan/Cplusplus-Concurrency-In-Practice) [c/c++教程](https://www.cprogramming.com/) [学习C++,应该循序渐进的看哪些书?](https://www.zhihu.com/question/20410487) [数据结构与算法系列 目录](http://www.cnblogs.com/skywang12345/p/3603935.html) [刷完这65道题,面试通过率翻翻没问题 ](https://www.nowcoder.com/discuss/19305?type=0&order=1&pos=14&page=1) [c++代码实验](https://github.com/Ewenwan/Messy_Test) [源代码查找 热度 书籍 ](https://www.ctolib.com/) [源代码查找 英文](https://searchcode.com/) [免费的编程中文书籍索引](https://github.com/Ewenwan/free-programming-books-zh_CN) [代码面试较好 强烈推荐!!!!!](https://github.com/Ewenwan/code_interview) [nowcoder刷题笔记](https://blog.csdn.net/fk1174/article/details/52825826) [LeetCode刷题笔记](https://blog.csdn.net/fk1174/article/details/52744844) [跟我一起复习C++](https://blog.csdn.net/fk1174/article/details/51841025) [Leetcode常用五大算法思想](https://blog.csdn.net/x_r_su/article/details/52461557) [数据结构 博客参考 树 图 队列](https://blog.csdn.net/neu_chenguangq/article/list/3) [GitHub Pages+Jekyll搭建个人博客](http://www.weixinyu.info/2017/08/Use_GitHubPages_and_Jekyll_build_my_blog/) [Markdown工具集](http://baixin.io/2016/11/markdownTool/) [清华操作系统 github](http://github.com/oscourse-tsinghua) [博客专栏 Linux环境编程](https://blog.csdn.net/column/details/tennysonsky-linux.html) [博客专栏 Linux网络编程](https://blog.csdn.net/tennysonsky/article/category/2835083) [博客专栏 一步步学习C++](https://blog.csdn.net/column/details/tennysonsky.html) [博客专栏 C语言](https://blog.csdn.net/column/details/15161.html) [鱼C工作室 C/C++/Python/Wed/数据结构和算法](http://fishc.com/) [图形界面编程](https://blog.csdn.net/tennysonsky/article/category/1438449) [面试算法博客笔记](https://blog.csdn.net/cheese_pop?t=1) [ 刘国平基础算法 ](https://blog.csdn.net/cyningsun/article/category/720182) [CPP11新特新 代码实战](https://github.com/Ewenwan/cpp11) [新特征 C++11/14/17 concepts and code snippets](https://github.com/Ewenwan/modern-cpp-concepts) [A Detailed Cplusplus Concurrency Tutorial 《C++ 并发编程指南》](https://github.com/Ewenwan/Cplusplus-Concurrency-In-Practice) [基于C++11新标准的并发和多线程编程深度指南 C++ Concurrency In Action](https://github.com/xiaoweiChen/Cpp_Concurrency_In_Action) [《C++17 STL cookbook》英文版的中文翻译 ](https://github.com/Ewenwan/CPP-17-STL-cookbook) [哈希学习](https://blog.csdn.net/LeeWei4939/article/details/78806230) [甲骨文公司编辑器Oracle Solaris Studio 12.4 Information Library (简体中文) c/cpp用户指南 数值计算指南 代码分析器 性能分析器 线程分析器](https://docs.oracle.com/cd/E57201_01/) [哈工大 编译原理 ](http://www.icourse163.org/course/HIT-1002123007) [编译原理——词法分析器实现](https://www.cnblogs.com/zyrblog/p/6885922.html) [编译技术 西电](http://www.xuetangx.com/courses/course-v1:XIYOU+20180208+sp/about) [在线书籍编写模板 gitbook](https://github.com/Ewenwan/gitbook) [循序渐进学习书目](https://github.com/codefollower/My-Blog/blob/master/md-files/issues-001.md) [svn新建trunk 和 分支](https://blog.csdn.net/chaozi2008/article/details/45073949) # Git git clone --recurse-submodules https://github.com/xxxxx.git 注意 clone 的时候一定要带 --recurse-submodules 这个参数,否则会下载不完整。 # 包含内容: 1. c 2. c++ 3. python 4. 汇编语言 5. 数据机构和算法 面试 6. 操作系统os 7. 单片机stm32 arduino Ti-msp430 树莓派 px4 arm 8. 数据挖掘 9. 人机工程学 10. 计算机科学 # 学习C++,应该循序渐进的看哪些书? ## 阶段 1 《Essential C++》 这是一本内容不多但很实用的C++入门书籍,强调快速上手与理解C++编程。 本书主要围绕一系列逐渐复杂的程序问题,以及用以解决这些问题的语言特性展开讲解。 你不只学到C++的函数和结构,也会学习到它们的设计目的和基本原理。 《C++ Primer》 本书对C++基本概念、技术、以及现代C++编程风格进行了全面而且权威的阐述,是C++初学者的最佳指南; 本书可以帮助你编写实用的程序,而无需首先精通每个语言细节。 对于中高级程序员,本书也是不可或缺的参考书。 ## 阶段 2 《Effective C++》和《More effective C++》作者是Scott Meyers。 你应该熟读它们,并清楚地理解每个项目。 该书围绕55条准则,每一条都介绍了一个可让你写出更好的C++程序代码的方法,并以特别设计过的例子详加讨论。 《Exceptional C++(C++编程剖析)》和《More exceptional C++》 这两本书中都包含了40个C++编程问题,这些问题会让你磨练自己的技能,最终成为优秀的C++程序员。 这些问题是Herb Sutter精心挑选,与ISO/ANSI C++官方标准相一致, 帮助程序员在设计、架构和编码过程中保持良好的风格,从而使编写的C++软件更健壮、更高效。 ## 阶段 3 《Inside the C++ object model(深度探索C++对象模型)》 本书专注于C++面向对象程序设计的底层机制, 包括结构式语意、临时性对象的生成、封装、继承,以及虚拟——虚拟函数和虚拟继承, 帮助你理解程序的底层实现,以便写出更高效的代码。 《The design and evolution of C++(C++语言的设计与演化)》 本书作者也是C++语言的设计者Bjarne Stroustrup,作者在书中综合性地介绍了C++的发展历史, C++中各种重要机制的本质意义和设计背景,这些机制的基本用途和使用方法, 讨论了C++所适合的应用领域及其未来的发展前景,既没有忽略关键性的详情,又没有过多地陷入技术细节。 ## 阶段 4 《The C++ standard library(C++标准程序库)》 这是标准模板库字典,你可以在本书中找到STL相关的一切知识。 本书焦点放在标准模板库、检查容器、迭代器、函数对象和STL算法上。 每一个元素都有深刻的呈现,包括其介绍、设计、运用实例、 细节解说、陷阱、意想不到的危险,以及相关类别和函数等。 《Effective STL》 这是Scott Meyers的第三本C++专著,也是学习STL最权威的书籍。 作者对书中的50个指导方针都作了详尽的分析,并配以示例。 通过这些规则,C++开发者可以最大限度地使用STL。 《Generic programming and the STL(泛型编程与STL)》 本书阐述了泛型程序设计的核心理念:concepts(概念)、modeling(模型)和refinement(改善), 并为你展示这些观念如何导出STL的基础概念:iterators(迭代器)、 containers(容器)和function objects(函数对象)。 按照本书所述,你可以把STL想象成一个由concepts组成的library,你将学习到STL正式结构并理解其强大的优势。 ## 阶段 5 《Exceptional C++ style》 作者为Herb Sutter。本书同样提出了40个C++风格相关的问题 ,对一些至关重要的C++细节和相互关系提出了新的见解, 为当今的关键C++编程技术(如泛型编程、STL、异常安全等)提供了新的策略, 帮助开发者在开销与功能之间、优雅与可维护性之间、灵活性与过分灵活之间寻找完美的平衡点。 《C++ template》 这是一本关于C++模板的完整的参考手册和教程,它强调模板的使用实践,包含了现实世界中的例子。 每个C++程序员都应该好好读一读这本书。 《Modern C++ design(现代C++设计)》 作者Andrei Alexandrescu为C++程序员打开了一个新的局面。 本书提供了一些针对软件设计的前沿方法,如联合设计模式、泛型编程, 使程序员可以编写有表现力的、灵活的、高度可重用的代码。 《Thinking in C++(C++编程思想)》 C++ 领域权威著作,介绍了C++实用的编程技术和最佳的实践方法。 # 值得学习的开源代码 ## 1.Webbench Webbench是一个在Linux下使用的非常简单的网站压测工具。 它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的性能, 最多可以模拟3万个并发连接去测试网站的负载能力。 Webbench使用C语言编写, 代码实在太简洁,源码加起来不到600行。 项目主页: http://home.tiscali.cz/~cz210552/webbench.html ## 2. Tinyhttpd tinyhttpd是一个超轻量型Http Server,使用C语言开发,全部代码只有502行(包括注释), 附带一个简单的Client,可以通过阅读这段代码理解一个 Http Server 的本质。 项目主页: http://sourceforge.net/projects/tinyhttpd/ ## 3. cJSON cJSON是C语言中的一个JSON编解码器,非常轻量级,C文件只有500多行,速度也非常理想。 cJSON也存在几个弱点,虽然功能不是非常强大,但cJSON的小身板和速度是最值得赞赏的。 其代码被非常好地维护着,结构也简单易懂,可以作为一个非常好的C语言项目进行学习。 项目主页: http://sourceforge.net/projects/cjson/ ## 4. CMockery cmockery是google发布的用于C单元测试的一个轻量级的框架。 它很小巧,对其他开源包没有依赖,对被测试代码侵入性小。 cmockery的源代码行数不到3K,你阅读一下will_return和mock的源代码就一目了然了。 主要特点: 免费且开源,google提供技术支持; 轻量级的框架,使测试更加快速简单; 避免使用复杂的编译器特性,对老版本的编译器来讲,兼容性好; 并不强制要求待测代码必须依赖C99标准,这一特性对许多嵌入式系统的开发很有用 项目主页: http://code.google.com/p/cmockery/downloads/list ## 5. Libev libev是一个开源的事件驱动库,基于epoll,kqueue等OS提供的基础设施。 其以高效出名,它可以将IO事件,定时器,和信号统一起来,统一放在事件处理这一套框架下处理。 基于Reactor模式,效率较高,并且代码精简(4.15版本8000多行),是学习事件驱动编程的很好的资源。 项目主页: http://software.schmorp.de/pkg/libev.html ## 6. Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。 它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提供动态数据库驱动网站的速度。 Memcached 基于一个存储键/值对的 hashmap。 Memcached-1.4.7的代码量还是可以接受的,只有10K行左右。 项目主页: http://memcached.org/ ## 7. Lua Lua很棒,Lua是巴西人发明的,这些都令我不爽,但是还不至于脸红,最多眼红。 让我脸红的是Lua的源代码,百分之一百的ANSI C,一点都不掺杂。 在任何支持ANSI C编译器的平台上都可以轻松编译通过。我试过,真是一点废话都没有。 Lua的代码数量足够小,5.1.4仅仅1.5W行,去掉空白行和注释估计能到1W行。 项目主页: http://www.lua.org/ ## 8. SQLite SQLite是一个开源的嵌入式关系数据库,实现自包容、零配置、支持事务的SQL数据库引擎。 其特点是高度便携、使用方便、结构紧凑、高效、可靠。足够小,大致3万行C代码,250K。 项目主页: http://www.sqlite.org/ 。 ## 9. UNIX v6 UNIX V6 的内核源代码包括设备驱动程序在内 约有1 万行,这个数量的源代码,初学者是能够充分理解的。 有一种说法是一个人所能理解的代码量上限为1 万行,UNIX V6的内核源代码从数量上看正好在这个范围之内。 看到这里,大家是不是也有“如果只有1万行的话没准儿我也能学会”的想法呢? 另一方面,最近的操作系统,例如Linux 最新版的内核源代码据说超过了1000 万行。 就算不是初学者,想完全理解全部代码基本上也是不可能的。 项目主页: http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6 ## 10. NETBSD NetBSD是一个免费的,具有高度移植性的 UNIX-like 操作系统, 是现行可移植平台最多的操作系统,可以在许多平台上执行, 从 64bit alpha 服务器到手持设备和嵌入式设备。 NetBSD计划的口号是:”Of course it runs NetBSD”。 它设计简洁,代码规范,拥有众多先进特性,使得它在业界和学术界广受好评。 由于简洁的设计和先进的特征,使得它在生产和研究方面, 都有卓越的表现,而且它也有受使用者支持的完整的源代码。 许多程序都可以很容易地通过NetBSD Packages Collection获得。 项目主页: http://www.netbsd.org/ # 0 个类型结构大小 sizeof ## 3. 语法: sizeof有三种语法形式,如下: 1) sizeof( object ); // sizeof( 对象 ); 2) sizeof( type_name ); // sizeof( 类型 ); 3) sizeof object; // sizeof 对象; ## 2. 指针变量的sizeof 4/8 与计算机类型有关,32为计算机,地址长度为4字节 64位的计算机,地长度为 8字节 这里的指针包括所有类型的指针: 字符指针、整形指针、字符串指针、指针的指针、函数指针、数组指针等。 ## 3.数组的sizeof 数组的sizeof值等于数组所占用的内存字节数,如: char a1[] = "abc"; int a2[3]; sizeof( a1 ); // 结果为4,字符串末尾还存在一个NULL终止符 sizeof( a2 ); // 结果为3*4=12(依赖于int,这里int为4字节) // 这里注意 &a2和a2的值是相等的,都是a2[0]的地址 // 但是 &a2 的类型为 int *[10] // 而a2的类型为 int* p //数组元素数量求取 int c1 = sizeof( a1 ) / sizeof( char ); // 总长度/单个元素的长度(知道元素类型) int c2 = sizeof( a1 ) / sizeof( a1[0] ); // 总长度/第一个元素的长度(不知道元素类型) ## 4. sizeof进行结构体大小的判断 需要看编译器是几个字节对齐 的,一般为4字节对齐 typedef struct { int a; // 占据第一个4字节 char b; // 占据第二个4字节 }A_t; typedef struct { int a; // 占据第一个4字节 char b;// 占据第二个4字节中的第一个字节 char c;// 占据第二个4字节中的第二个字节 }B_t; typedef struct { char a;// 占据第一个4字节的第一个字节 int b; // 占据第二个4字节 char c;// 占据第三个4字节的第一个字节 }C_t; # 1. 编程技巧 来自 effective stl [effective stl 博客参考](https://blog.csdn.net/u014110320/article/category/6411316) ## a)浮点数判等 判断两个浮点数a和b是否相等时,不要使用 a==b , 应该判断两者的绝对值之差fabs(a-b)是否小于一个阈值 ,如1e-9 if(fabs(a-b) < 1e-9) ## b) char类型用作数组下标需要注意的问题 应该先将char 强制转换为 unsigned char后在用作下标。 char index_; unsigned char index = (unsigned char)index_; ## c) 向量vector 和 字符串string 优先于 动态分配的数组new[] delete[] vector 和 string 定义的对象 会自动 构造和析构,不用担心内存泄漏的问题 使用new[]分配的动态数组,需要配合 delete[]类释放会造成内存,否者会造成内存泄漏的问题 例如 定义一个二维数组,指针的指针 自己用new实现: ```c int** arr_pp new int* [row_num];// 定义一个存储指针的数组的指针 行数 for(i = 0; i < row_num; ++i) arr_pp[i] = new int[col_num];// 一个一个new 指针每一行是一个行向量的指针 ``` 用 vector实现,一行代码搞定,还不到担心内存泄漏的问题 ```c vector> v_i2(row_num,vector(col_num,0));//初始化为一个0矩阵 vector m; m.reserve(1000);//提前保留1000的内存 //使用 reserve 避免不必要的重新分配 ``` # C++ primer5 语言学习记录 # 复合类型 ## 引用 &   左值引用lvalue reference  别名; int val; &refval = val; 右值引用 rvalue reference ```c int val = 1024;//整形变量 int &refVal = val;// 整形引用 refVal 指向 val ,是val的另一个名字, // 引用必须被初始化,引用不是对象,只是为所存在的对象起的一个别名。 refVal = 2; // 把2 赋值给refVal 也就是 赋值给了 val int i = refVal;   // 相当于 i = val;  int &refVal2 = 10;// 错误,非常量引用只能绑定(bind)在对象向, // 不能与字面值或某个表达式的计算结果绑定在一起, 且引用的类型 必须和 对象的类型一致 const int &rci = 10;// 常量引用可以绑定到 字面值 非常量 一般表达式 double dval = 3.14;// 浮点数 int &refVal3 = dval; // 错误,引用的类型 和 对象 必须一致 ``` ## 指针 pointer * 指向 point to 另外一个对象(其存储地址) ```c int ival = 42; // 整数 int *p = &ival; // 指针定义 p存放 ival对象存放的地址,也即是 p 指向 变量 ival的指针   &ival 为取的 ival变量的 存储地址 double dval = 3.14; // 浮点数 double *pd = &dval; // double 类型 指针 double *pd2 = pd;   // pd存放 的是 dval存放的地址 int *pi = pd; // 错误, 指针pi 的类型 和 右边指针对象 类型 不匹配 pi = &dval; // 错误, 视图把 double 类型对象的地址 给 int类型的指针 错误 左右两边类型 必须匹配 ``` > 利用指针访问对象 取地址内存储的值 解引用符 来访问向 ```c int ival = 42; //整形 变量 int *p = &ival;//指针变量定义初始化, p 存放着 变量ival 在内存中的地址 std::cout << *p;// 表达式里 *p 为 解引用 取的p存放的地址 指向的值 int &ref_i = ival; // 引用声明定义, ref_i 是一个引用 int *p; //指针声明定义,p是一个指针 p = &ival;// 表达式 p为指针(地址),&val 为取的 val 地址 *p = ival;   //表达式 *p 相当于 ival *是一个解引用 int &ref_i2 = *p;// &ref_i2 为引用定义 *p 相当于 ival, *是一个解引用 ```     > 空指针 ```c int *p1 = nullptr;// 等价于 int *p1 = 0; 空指针  p1 不指向 任何 对象 int *p2 = 0; // 空指针     等价于 int *p1 = 0; int *p3 = NULL;// 空指针 等价于 int *p1 = 0; int zero = 0; int *p4 = zero;// 错误,不能把 int 变量直接赋值 给指针 int i = 42; int *pi1 = 0; // pi1 被初始化为空指针,但没有指向 任何对象 int *pi2 = &i;// pi2 被初始化,存有 i 的地址 int *pi3;// 定义 未初始化 pi3 = pi2;// pi3 和 pi2指向同一个 对象 i pi2 = 0; // 现在 pi2 被赋值为 空指针 不指向任何对象  指针被改变, 指向的对象不变 pi2 = &i;// 现在 pi2 又指向 i *pi2 = 0;// 指针pi2 没变 , *pi2 被改变 即pi2指向的对象 i 发生了变化 ``` > 使用指针作为条件 为空指针时,逻辑值为 False,其他指向一个对象的指针(非0指针) 逻辑值 均为 true > 相等 == 操作符  不等 != 操作符  > void* 类型的指针  可一指向 任意类型 的 地址 , 可一存放任意类型的 地址, > 用作函数参数,函数体内使用时需要,转化成实际传入类型的指针 ```c double obj = 3.14, *pd = &obj; // double 类型 的变量 double类型的指针 void *pv = &obj; // obj 可以是任意类型的对象 pv = pd; // pv 可一存放任意类型的指针(地址) double *pd = (double*)(pv);// void* 类型的指针 强制转化成  double* 类型的指针 ``` > 定义多个变量 ```c int i = 1024, *p = &i, &r = i;//i是整形变量 p是一个整形指针 r是一个整形引用 int* p1, p2;// * 仅仅修饰 p1 ,即p1是 指向整形的指针 p2是整形变量 int *p1, *p2;// p1 和 p2 都是指向 整形的指针 ``` > 指向指针的指针 ```c int ival = 1024; int *pi = &ival; //指向整形变量的指针 int **ppi = π //指向整形指针的指针  ppi ---> pi ------> ival std::cout << ival << "\n"// 直接输出 变量值 << *pi << "\n"// 1次解引用 得到变量值 << **ppi<< "\n";//2次解引用 的到变量值 ``` > 指向指针的 引用 指针对象的别名  引用不是对象 不存在 指向引用的指针 ```c int i = 42;//整形对象 int *p;//指向整形类型的指针 int *&r_p = p;//引用r_p的类型为 整形的指针引用int*&,r_p 是指针p的一个别名 //从右向左 离变量最近的符号为 & 即为 引用,* 引用的为 指针类型 r_p = &i;// 相当于 p = &i p 指向 i *r_p = 0;// 相当于 *p = 0 即 i =0 改变了指针指向对象的值   // 这里 *p *解引用指针的类型其实相当于 原变量的别名 引用 ``` ## const 限定符 > 修饰变量后,可以防止变量被修改 ```c const int bufSize = 512;//初始化常量 一旦创建后就不能被修改 bufSize = 1024;// 错误,常量不能被赋值 const int i = get_size();// 一个函数赋值 运行时初始化 const int j = 42;//编译时初始化 const int k;//错误,常量定义式必须初始化 double dvel = 3.14;// 也可以由其他类型变量 强制转换成 常量 const int ci = dvel;// 由double类型变量 强制转换成 整形常量 3 extern const int bufSize = 1024;//定义了一个变量,想在其他文件也使用 bufSize 必须在定义之前 加extern extern const int bufSize;// 另一个文件中 声明 bufSize 后 就可以使用了 ``` > 绑定到常量的引用 const 的引用 即对常量的引用 reference to const 常量 的 别名 不能修改 不存在常量引用 &const 引用不是对象 ```c const int c_i = 1024;   // 常整数 const int &r_c_i = c_i; // 常整数 c_i 的 引用(别名) r_c_i = 42;// 错误r_c_i 是常量引用 不能被修改 int &r_c_i2 = c_i;//错误 常整数 不能赋值给 int变量 左右两边类型必须一样 ``` > 允许将一个常量引用绑定到 非常量对象、字面值,甚至是个 一般表达式 ``` int i = 52; const int &r1 = i; // 允许将 常量引用 const int & 绑定到 int变量上 const int &r2 = 42;// 绑定到 字面值上 const int &r3 = r1 * 2;// 绑定到一个 表达式上 int &r4 = r1 * 2;// 错误,r4是非常量 引用,只能绑定到 其对应类型的对象上 double dval = 3.14;//浮点数 const int &ri = dval;//常量引用 绑定到 非常量上 //相当于 先把 非常量转化成常量 强制类型转换 常量引用实际上绑定了一个临时变量 const int temp = dval;// int i = 42;// int 变量 int &r1 = i;// 整数变量引用 r1为 i 的别名 const int &r2 = i;//常量 引用 r2 绑定到 变量 i上 r1 = 0;// 相当于 i =0 r2 = 0;// 错误 r2 时常量引用 不允许改变 ``` > 指向常量的指针 const int *   指针指向的值不能变  也就是 *p 不能被赋值 不能改变 ```c const double pi = 3.14;//双精度 浮点型 常量 double *ptr_d = π// 错误,浮点型变量指针 不能绑定一个 常量的存储地址 const double *ptr_d_c = π// 双精度常量 针 ptr_d_c  指向一个 双精度常量 *ptr_d_c = 42;// 不能给 pi 赋值 指向常量的指针的解引用相当于 绑定的常量 ,因此不能赋值 doubel dvel = 3.14;//双精度变量 ptr_d_c = &dvel;//允许 常量指针ptr_d_c 指向一个变量dvel 但是不能通过 常量指针ptr_d_c 修改变量dvel ``` > 常量指针 *const 指针本身不能改变 也就是指向不能改变  p不能改变 但是其指向的对象 无影响 ```c int errNumber = 0;// int *const conpErr = &errNumber;// * 可变指针  *const 常量指针不可变 // 指向整形的 常量指针,conpErr 一直指向 errNumber *conpErr = 3;//相当于 errNumber = 3 const double pi = 3.14;// const double *const cd_cp = π//指向 常量的常量指针, // 即 指针本身cd_cp不能变, 其指向的值 *cd_cp也不能变 ``` > 常量表达式 编译时就能确定的量/式子 constexpr int ce = 20; ```c const int *p = nullptr;// 指向整形常量 的指针 constexpr int *p = nullptr;// 指向整形变量的 常量指针 // constexpr 声明中出现 指针 仅仅说明 指针为常量指针 int *const p =nullptr;//指向 整形变量的 常量指针 const int *const p = nullptr;// 指向 整形常量 的常量指针 constexpr const int *p = nullptr;// 指向 整形常量 的常量指针 constexpr int ci = 42; // ci 是整形常量 int j = 0; constexpr int *pci = &j; // 指向整形的 常量指针 ``` ## 处理类型 ### 类型别名  type alias ```c typedef double wages;   // wages 是double类型的 同义词 typedef wages base, *p; // base 也是double类型的 同义词。 p是double * 的同义词 wages d_money = 1.00;//等有价于 double d_money = 1.00 p p_dmoney =   &d_money;//等价于 double *p_dmoney =   &d_money; cout << p_dmoney << endl; // 别名声明 alias declaration using SI = Sales_Item; // SI 是 Sales_Item 的 同义词 SI item;//等价于 Sales_Item item; // 指针、常量const 类别别名 typedef char *pstring;// pstring等价于 char *  指向char 的指针(是指针) const pstring cstr = 0;// cstr是指向 char 的 常量指针 // !!!!不是指向常量字符的 指针 不能直接替换 const修饰的主语是指针 const pstring *ps;     // ps是一个指针 它指向的对象 是 指向char的常量指针 ``` ### auto 类型说明符  让 编译器根据右式 类型 自动推算左式的类型  且用表达式的值 初始化变量 ```c auto item = val1 +val2;//item 初始化为 val1 和 val2相加的结果 类型 相同 // 一条语句定义多个变量时,各变量类型必须一致 auto i=0, *p = &i;//正确 i是整数, p是指向整形的指针 auto sz = 0, pi = 3.4;// 错误 sz 和 pi 类型不一致 // 引用 指针 常量 与 auto int i = 0, &r = i;//r是i的别名 int类型 auto a = r;// a 是一个整形数 // auto 会忽略掉 顶层const const int ci = i, &cir = ci;// 常整数 auto b = ci; // b是一个整数,ci的顶层 const(最外层修饰 为顶层)特性被忽略 auto c = cir; // c是一个整数,ci的顶层 const特性被忽略 auto d = &i; // d是一个指向整形的指针 auto e = &ci; // e是一个指向整形常量的指针 const // 外又被 取地址符号修饰,所以这里的 const是 底层const 不被忽略 // 如果希望 auto 推断出的类型是 一个顶层 const,需要在其前面 明确指出 const auto f = ci; // ci的推演类型是 int ,f是 const int // 将引用设置为 auto 还按之前的初始化规则  保留 顶层 const auto &g = ci; // g 是一个 整形常量的 引用 (别名) auto &h = 42; // 错误,非常量 引用 不能绑定 到 字面值 const auto &j = 42; // 正确, 常量引用可以绑定到 字面值 // 将 指针设置为 auto, 也按之前的初始化规则 保留顶层 const auto *k = &ci; // k为指向 整形常量的 指针 auto *l = 0;   // l为空指针 const auto *m = &ci; // m 为 指向整形常量的 常量指针 int i = 1024; const int c_i = i; auto b = c_i; // b是一个 整数 ,c_i的 顶层 const被忽略 auto e = &c_i;// e 是一个 指向 整形常量的引用  这里的const是底层const 不被忽略 auto k = c_i, &ll = i;// k 是整数, ll 是一个整数引用 auto k = c_i, &o = c_i;// 错误,k 是整数变量 o是一个整数常量引用 类型不一致 auto &m =c_i, *p = &c_i;//正确 m是整数常量引用,p是指向整数常量的指针 auto n = &c_i, *p = &c_i;// 正确 n是整数常量 引用(底层const,不被忽略) p是指向整数常量的指针 ```   ### decltype 类型指示符 用表达式推断类型 但是不用表达式的值初始化变量   ```c decltype(f()) sum = x; // sum 的类型为 函数 f() 的返回值类型 初始化为 x 就像 int sum = x; 一样 const int ci = 0, &cj = ci;// ci 整形常量  cj 整形常量的引用 decltype(ci) x = 0; // x的类型 同ci 是 整形常量 const int 不忽略 顶层 const decltype(cj) y = x; // y的类型 同cj 是 整形常量的引用 const int &  y绑定到x上 decltype(cj) z;     // 错误 z是一个 引用 ,必须初始化 // decltype和引用 指针 int i =42, j=12, *p = &i, &r = i; decltype(r) a = j;// a的类型和r的类型一致,为整数的引用 int& a绑定到j上 decltype(r + 0) b;//正确 加法的结果为 整形 int 因此 b为整形, 这里只定义 没有初始化 decltype(*p) c; // 错误 其实*p 解引用指针 相当于i的别名 也就是引用 // 所有这里 c的类型为 整数引用 int &,是个引用,必须初始化 // decltype 的表达式如果 加上括号,得到的结果将是引用 decltype(i) e; // e 是一个未初始化的 整形变量 decltype((i)) d = e; // d是一个 整数的引用 绑定到 整形变量 e上  双层引用括号 的结果 永远都是 // 赋值表达式 也会产生 引用 引用的类型就是 等式左边变量的类型 如果 i是int i = x的类型是 int& ``` # 标准库 std ## 字符串类 string 类  #include 存储一个可变长度的 字符串   使用内置数组类型实现 ### 范围for  访问所有元素 ```c string str("some string"); for (auto c : str)// auto自动推断类型 cout << c << endl; // 范围for 引用 改变 内容 string s("Hello World!!!"); for (auto &c : s)// c是 字符串中每个元素的一个别名 c = toupper(c);//变成大写 cout << s << endl; ``` ### 下标运算符(索引)[] 和 迭代器 访问单个元素 > string的下标 类型为 string::size_type 无符号数 可用 decltype(s.size())获取 s[0] 是第一个字符 s[s.size()-1]是最后一个字符 ```c // 第一个字符改为大写 string s("some string"); if(!s.empty()) s[0] = toupper(s[0]);//第一个字符改为大写  在 cctype头文件中 // 第一个单词改为大写 for(decltype(s.size()) index = 0; index != s.size() && !isspace(s[index]; ++index)) s[index] = toupper(s[index]); ``` ## 向量 模板  容器container vector 模板  #include 存储一个可变长度的 对象 集合 使用内置数组类型实现 > 因为 vector 可以存放任意类型 所以事先需要知道 存放的对象是什么类型  vector ivec; vector; vector >; ```c // 初始化方式 vector ivec(10,-1);// 直接初始化 10个元素 全为 -1 vector ivec2 = ivec;//拷贝初始化 vector ivec3{10};//一个元素 10 vector ivec3{10,1};//两个元素 10  和 1 vector svec{"a","an","the"};//列表初始化 直接方式 vector svec2 = {"a","an","the"};//列表初始化 拷贝方式 // 默认初始化  vector ivec(10);   // 10个元素,每个值都是0 vector svec(10); // 10个元素,每个值都是空 string 对象 vector svec2{10};// 10个元素,每个值都是空 string 对象 vector svec3{10, "hi"};// 10个 "hi"元素 vector svec3(10, "hi");// 10个 "hi"元素 // 使用 .push_back() 添加元素 vector ivec2; //空vec对象 for(int i = 0; i != 100; ++i) ivec2.push_back(i);// 一次把整数值 放到 ivec2尾部 结束后 ivec2有100个元素 0~99 // 实时添加 string 元素 string word; vector text;//空对象 while (cin >> word) text.push_back(word);// 把word添加到 text 后面 //使用范围for  + 引用 访问 并改变vector元素 vector iv{1,2,3,4,5,6,7,8,9};// 列表直接初始化 for (auto &i : v)// 对于v中每个 元素的 引用 需要改变其值 i *= i;     // 变成原来值 的 平方 for (auto i : v) // 仅读取其中的变量 cout << i << " "; cout << endl; // vector 对象大小 类型为size_type vector::size_type se = iv.size(); // 使用索引[] 访问 计算vector对象元素索引   统计各个分数段上 出现的 成绩个数 // 索引不能添加元素 vector scores(11,0);//11个分数段, 0~9,10~19,...,90~99,100 计数值全部初始化为0 unsigned grade; while (cin >> grade){ if(grade <= 100) ++scores[grade/10]; }     // cin 读入一组词 改成大写 存入 vector中  #include 使用toupper() vector sv; string word1 = "qwe"; cout << word1 << endl; while(cin >> word1){ for (auto &c : word1) c = toupper(c); sv.push_back(word1); cout << word1 << endl; vector } ``` ## 迭代器 访问容器中的 元素 auto b = v.begin(), e = v.end(); b表示v的第一个元素 e表示v尾元素 的下一个位置 类似 指针 ```c // 修改 字符串 第一个元素为大小字符 string s("some string"); if (s.begin() != s.end()){//确保 s非空 auto it = s.begin();// it 指向 s的第一个字符 类似指针 的作用 *it = toupper(*it);// 将当前字符改写成大写形式  *it 解引用迭代器 得到其所指向的 对象  是其指向对象的别名 引用 } // 字符串的第一个单词 改写成大写 for (auto it = s.begin(); it != s.end() && !isspace(*it); ++it) *it = toupper(*it); // 迭代器类型  iterator (具有读写功能)  const_iterator 具有 读功能  不具有写功能 // 对象为常量 只具有常量类型的迭代器 const_iterator  对象为变量具有 iterator 和 const_iterator vector::iterator it;// 迭代器 it 可以读写 vector 类型容器 的元素 string::iterator it2;   // it2 可以读写  string对象 中的字符 vector::const_iterator it3;//it3只能读元素,不能写元素 string::const_iterator it4;     //it4只能读字符,不能写字符 // cbegin()   cend() 返回 常量 迭代器 仅能读取 容器元素 不能修改 vector iv;        // 变量 const vector civ; // 常量 auto it1 = iv.begin(); // it1的类型为 vector::iterator  能读写iv的元素 auto it2 = iv.cbegin(); // it2的类型为 vector::const_iterator  能读iv的元素 不能修改 iv的元素 auto it3 = civ.begin(); // it3的类型为 vector::const_iterator 能读civ的元素 不能修改 civ的元素 // 访问 容器元素对象的 成员函数   (*it).empty  等同于 it->empty()  解引用 和成员访问 vector text; for (auto it = text.cbegin(); it !=text.cend() && !it->empty(); ++it) cout << *it << endl; // 迭代器 运算 auto mid = iv.begin() + iv.size()/2; //指向容器的中间位置 if (it < mid) // 处理前半部分元素 // 两个迭代器相减 得到的类型为 带符号整数  difference_type // 常规二分查找算法 // 升序数组 查找的元素 范围开始 结束 int BinarySearch(int *array, int key, int low, int high) { int mid; while (low <= high)//  缩小范围 { mid = (low + high) / 2;//更新中间元素的 下标 if (key == array[mid])//中间元素是否 等于所查找的元素 return mid+1;//相等 返回元素下标 else if (key < array[mid])//所查元素 比 中间元素小 则在 前区间查找 high = mid - 1;//将区间 右侧 退后 到 中间元素下标前一个元素 搜索 范围为  low,mid-1 else//所查元素 比 中间元素大 则 在后区间查找 low = mid + 1;//将区间 左测 提至 中间元素下标后一个元素   搜索 范围 mid+1,high } return 0; } // 使用迭代器完成二分查找 vector text// 升序容器 auto b = text.begin(), e = text.end();//起始 结束位置 auto mid = b + (e - b)/2;//中间位置 while(low <= end && *mid != key){ if(key < *mid) e = mid - 1;//所查元素 比 中间元素小 则在 前区间查找 else b = mid + 1;// 所查元素 比 中间元素大 则 在后区间查找 mid = b + (e - b)/2;//更新 中间位置 } // 使用索引[] 访问 计算vector对象元素索引   统计各个分数段上 出现的 成绩个数 // 索引不能添加元素 vector scores(11,0);//11个分数段, 0~9,10~19,...,90~99,100 计数值全部初始化为0 unsigned grade; while (cin >> grade){ if(grade <= 100) ++scores[grade/10]; } // 使用迭代器 访问 计算vector对象元素索引   统计各个分数段上 出现的 成绩个数 vector scores(11,0);//11个分数段, 0~9,10~19,...,90~99,100 计数值全部初始化为0 unsigned grade; auto it0 = scores.begin(); while (cin >> grade){ auto it = it0 + grade/10; if(grade <= 100) ++*it; } ``` # 数组  > 数组与标准库 类型 vector 类似,都存放类型相同对象的容器。 > 需要通过其所在的位置访问对象。 > 与vector不同的是,数组大小确定不变,不能随意向数组中增加元素, 数组不允许拷贝,vector允许拷贝。 > 注意数组名 相当于数组首元素的地址 ia[10] ia === &ia[0] ## 【1】定义 ```c constexpr unsigned sz = 42;//constexpr修饰,常量表达式 int arr[10]; //字面值常量初始化 含有10个整数的数组 int arr2[sz]; //常量表达式初始化 含有42个个整数的数组 int *parr[sz];//常量表达式初始化 含有42个指向整数的指针的数组 定义时必须指定数组类型,不能由auto来推断 不存在引用的数组,引用不是对象!!! string nums[] = {"one", "two", "three"};// 数组元素是string对象 string *sp = &nums[0];// p指向nums的第一个元素 string *sp2 = nums; // 等价于 string *sp2 = &nums[0] auto sp3(nums); //sp3 是一个string指针,指向nums的第一个元素 // 而decltype关键字 声明的 不发生上述转换 decltype(nums) sa = {"two", "three", "four"};//sa 是一个 含有3个string对象的 数组 ``` ##【2】显式初始化数组元素 ```c const unsigned sz = 3;// int ia1[sz] = {0, 1, 2};//列表初始化 含有3个元素 int ia2[] = {0, 1, 2};//维度为3 int ia3[5] = {0, 1, 2};//等价于 {0, 1, 2, 0, 0} // 字符数组 char ca1[] = {'C', 'P', 'P'};//列表初始化 char ca2[] = {'C', 'P', 'P', '\0'};//含有显式的 空字符 char ca3[] = "CPP";//字符串字面值初始化 自动添加表示字符串结束的空字符 // string 对象初始化 字符数组 string s("Hello World"); const char *str = s.c_str();// 用string对象初始化 字符数组 需要使用 c_str() 方法 最好再重新拷贝一份 // 数组  初始化 vector 对象 int i_arr[] = {1, 2, 3, 4, 5, 6}; vector ivec(begin(i_arr), end(i_arr));//全部副本 vector sub_ivec(i_arr + 1, i_arr + 4);// 包含 {2, 3, 4, 5}四个元素 // 不允许拷贝和赋值 char ca4 = ca3;// 错误 // 复杂的数组声明定义 int *parr[10];// 是数组,包含10个整形指针的数组 int &rarr[10]=?;//错误,不存在 引用数组,引用不是对象 int (*Parray)[10] = &arr;//是指针,指向一个含有10个整数的数组 int (&Rarray)[10] = arr;//是引用,引用一个含有10个整数的数组 ``` ## 【3】访问数组元素 ### 与标准库类型vector 和 string 一样,数组元素也可以使用 范围for语句 ### 或下标运算符 访问,元素下标从0开始,下标通常定义为 size_t类型,unsigned类型。 ### 标准库类型vector 和 string 下标运算符索引必须为正值 unsigned类型,数组下标运算符索引 为signed类型,内置类型,可以为负值 ```c //下标访问修改元素 unsigned score[11];//11个分数段 unsigned grade; while(cin >> grade){ if(grade <= 100) ++score[grade/10];//对应段 计数+1 } // 范围for 访问修改所有元素 for( auto i:score)//可以设置为 引用就可以修改元素了 cout << i << " "; cout << endl; // 指针访问数组 int iarr[] = {0,1,2,3,4};//含有5个元素 int *pi = iarr;//指向第一个元素的指针 iarr[0] int *pi2 = iarr + 2;//指向第三个元素的指针 iarr[2] auto num = end(iarr) - begin(iarr); // num的值是5 就是iarr包含元素的数量 // ptrdiff_t 类型 是signde类型 结果可能为负 ++pi;//指向第二个元素 iarr[1] j = pi[1]; // 等价于 *(p+1),就是 iarr[2], 就是 2 k = pi[-1]; // 等价于 *(p-1),就是 iarr[0], 就是 0 pi + 3;//指向最后一个元素 *pi;//第二个元素 4 int last =  *(iarr + 4);// 等价于 last = iarr[4]; int *end = &iarr[6];//指向尾后的位置 到达不了    不能执行解引用运算!!!!! // 使用 for fot(int *begin = arr; begin != end; ++begin) cout << *begin << endl;//输出每一个元素 // 使用 while while(begin=0) ++beg;//找第一个负值元素 ``` ## 多维数组 数组的数组 ### 初始化 ```c constexpr size_t row =3, col = 4; int iarr[row][col];//定义未初始化 //下标运算符(size_t 类型) for 循环初始化 for(size_t i = 0; i != row; ++i){ for(size_t j = 0; j != col; ++j){ iarr[i][j] = i * col + j;//元素索引为其 值 cout << iarr[i][j] << ' '; // 访问输出  数组元素 } cout << endl; } // 范围for 初始化 size_t cnt = 0; for (auto &row1 : iarr)//每一行 引用 int (&row1)[4] 是引用 引用一个含有4个整数的数组 { for (auto &col1 : row1){// int &col1 每一行的每一个元素 引用  可以读写 除去最内层外 其它必须使用 引用 col1 = cnt;//赋值 cout << col1 << ' ';// 访问 输出数组元素 ++cnt; } cout << endl; } ``` ### 访问 ```c // 指针访问 int (*p)[4] = iarr;//p 指向一个含有4个整数的数组 iarr的 第一行   圆括号必不可少 p = &iarr[2]; // iarr的 第3行 // for 循环访问 for( auto p = ia; p != ia + row; ++p){// 相当于定义 int (*p)[4] = iarr; p 指向 含有4个整数的数组 for( auto q = *p; q != *p + col; ++q) // *p 为含有4个元素的数组 q指向数组的首元素,即指向一个整数 int *q = *p cout << *q << ' '; cout << endl; } // 使用 标准库函数 begin() 和 end() for( auto p = begin(ia); p != end(ia); ++p){ // 相当于定义 int (*p)[4] = iarr; p 指向 含有4个整数的数组 for( auto q = begin(*p); q != end(*p); ++q) // *p 为含有4个元素的数组 q指向数组的首元素,即指向一个整数 int *q = *p cout << *q << ' '; cout << endl; } // 使用类型别名 using int_arr_tpye = int[4];// int_arr_tpye为包含4个元素的整形数组 typedef int int_arr_tpye[4];// 效果同上 for( int_arr_tpye *p = ia; p != ia + row; ++p){// 相当于定义 int (*p)[4] = iarr; p 指向 含有4个整数的数组 for( int *q = *p; q != *p + col; ++q) // *p 为含有4个元素的数组 q指向数组的首元素,即指向一个整数 cout << *q << ' '; cout << endl; } ``` # 函数 包括:返回类型 函数名字(形参列表) 函数体,函数的返回类型不能是数组或函数类型,但可以是 数组或函数类型的指针 ## 函数定义 阶乘函数 ```c int fact(int val){ int ret = 1; while(val >1) ret *= val--; return ret;// 返回主调函数 结束函数调用 } ``` ## 函数调用 分两步 1 实参初始化形参(形参为引用时,直接使用实参) 2控制权转移给被调用函数(inline 内联函数 之间在原处展开函数) ```c int main(){ int j = fact(5);// 实参5 初始化 形参(int val) cout << "5! = " << j << endl; return 0; } ``` ## 局部对象 > 形参和函数体内定义的变量统称为 局部变量,仅在函数的作用域内可见, 局部自动对象,只存在于函数体执行期间,而局部静态对象,可在函数调用后一直存在 ```c // 定义 int count_call(void){ int c =0;       //局部自动对象 每一次调用都初始化为 0   static int sc =0;//局部静态变量 第一次调用初始化为0 以后每次调用在前一次值上 +1 cout << ++sc << " " << ++c << endl; } // 调用 int main(){ for(int i =0; i<10; ++i) count_call(); //sc 输出 1 2,...10,c 输出一直是1 return 0; } ``` ## 分离式编译 函数声明 与函数定义类似但是 不包括函数体,以;代替函数体 int fact(int val); 函数可以多次声明 ```c // 源文件 fact.cc #include "fact.h" int fact(int val){ int ret = 1; while(val >1) ret *= val--; return ret;// 返回主调函数 结束函数调用 } // 函数声明头文件 fact.h #ifndef FACT_H #define FACT_H int fact(int val);// 函数声明 #endif // 主函数调用 fact_main.c #include #include "fact.h" using namespace std; int main(){ int j = fact(5);// 实参5 初始化 形参(int val) cout << "5! = " << j << endl; return 0; } // 编译 gcc fact_main.c fact.cc -o main ``` ## 参数传递 > 当形参是引用类型时,为引用传递,实际传递的是实参的别名,没有进行拷贝,当实参的值被拷贝给形参时,形参和实参是两个独立的对象 ### 值传递   函数对形参做的所有操作 都不会影响实参 ```c int n=0; int i = n;// n拷贝给i i = 42;//i的值改变, n的值不变    函数对形参做的所有操作 都不会影响实参 例如 fact(i) 不会改变i的值 ``` ### 引用传递 函数对形参做的所有操作 都会影响实参 ```c int n = 0; int &r_i = n;//r_i 是 n 的引用 即别名 同一个变量 r_i = 42;//r_i 和 n 都变成 42 ``` ### 指针形参 ```c int n=0, i =42; int *p_n = &n, *p_i = &i;// 指针 *p_n = 100;// n 的值 变为100 指针p_n(变量你存储的地址) 不变 p_n = p_i;// 现在 p_n 和 p_i 都指向了 i // 指针形参 函数 void reset(int *pi){ *ip = 0;//改变了指针 pi 所指向的对象的值 ip = 0;// 值改变了 形参ip的值 实参未被改变 } // 调用 int i = 42; cout << "address of i =" << &i < lst;// 默认初始化 T类型元素的空列表 模实际需要指定模板T的 具体类型 initializer_list lst{a, b, c...};// 列表中的元素是 const 常量 lst2(lst); // 拷贝一个initializer_list对象,不会新建,原始列表 和 副本 共享元素 lst2 = lst;// 同上 lst.size();// 列表 中的元素 lst.begin();//返回指向lst中首元素的指针 lst.end(); //返回指向lst中尾元素下一个位置的指针 // 因为有 begin() 和 end()对象可以使用 范围for 遍历参数 // 具体 initializer_list ls ;//initializer_list 的元素类型是 string initializer_list li ; //initializer_list 的元素类型是 int // 和vector不同的是 initializer_list中的元素是常量 不能被修改 // 定义函数 void error_msg(initializer_list ls){ for(auto beg = ls.begin(); beg != ls.end(); ++beg) cout << *beg << " "; cout << endl; } //调用函数 //expected 和 actual 是string 对象 if (expected != actual) error_msg({"function", expected, actual}); else error_msg({"function", "okey"}); // 传递了一个含有不同数量元素的 initializer_list // 定义函数 包含 ErrCode void error_msg(ErrCode e, initializer_list ls){ cout << e.msg() << ": " for(const auto &elem : ls )//范围for 遍历 cout << elem << " "; cout << endl; } ``` #### 省略符 ... 形参 ```c void foo(param_list, ...)// void foo(param_list...)// void foo(...)// ``` ## 函数 返回类型 和 return语句 ### 无返回值函数 void函数 无需显示的 return 语句  return 后无值 ```c // 交换两个值的函数 void swap(int &r_i1, int &r_i2){ if (r_i1 == r_i2)// 两个值相等 无需交换 return; // 若 执行 到这里,说明还需要继续完成下面的功能 int temp = r_i1;// r_i1 = r_i2; r_i2 = temp; // 此处 无需 显示的 return 语句   会隐式指向 return } ``` ### 有返回值函数 return 后有值 且返回的对象类型 与 函数定义的 返回类型相同 ```c // 两个 string 对象是否 最短的部分 是相同的 bool str_subrange(const string &str1, const string &str2){ if (str1.size() == str.size()) return str1 == str2; // 之间判断 相同长度间的部分是否相同 auto size = (str1.size() < str2.size()) ? str1.size() : str2.size(); //得到最短 字符串的 长度 for(decltype(size) i = 0; i != size; ++i){ if ( str1[i] != str2[i])// 如果 有 不相等的 字符 返回 false return false;// 有 不相等的 字符 返回 false } return true; // 否者 相等 返回 true } ``` ### 返回类型为 引用时 不会对结果进行拷贝 节省时间  但是 不要 返回 函数内部 临时变量的 引用, 临时变量离开函数后就不存在了 ### 建议 返回 引用对象 是一个 调用函数 之前就纯在的一个变量 ### 同时 返回 非常量的引用 可以作为 左值 被赋值 ```c // 不要 返回 函数内部 临时变量的 引用 string temp("glo"); const string &mainip() { string ret("Empty"); // 函数内部临时变量 return ret; // 错误 ,不能返回 临时变量 作为 函数返回值的 引用 // return temp;// 返回 一个调用函数之前就出现的 变量 的引用    或者 参数 为引用类型的参数也可以 } // 返回 两个字符串中 短的那个 const string &shortString(const string &s1, const string &s2){// 返回 s1 或者 s2的引用 return s1.size() < s2.size() ? s1 : s2;//返回 两个字符串中 短的那个 } // 函数返回类型 为 标准库 类类型 可以直接调用 其成员函数 auto sz = shortString(s1, s2).size()// 得到最短字符串 的长度 // 返回 非常量的引用 可以作为 左值 被赋值   char &get_char(string &str, string::size_type id){ return str[id];// 获取指定位置的 字符 } // 调用 string s("a value"); cout << s << endl;// a value get_char(s, 0) = 'A';// 将s第一个位置上的字符 替换为 大写的 A cout << s << endl;// A value // 而返回为 常量引用 的不能被赋值 shortString("hi", "bye") = "X"; // 错误 函数返回值是个 常量引用 不能被赋值 ``` ### 函数返回类型为 列表 以花括号 {} 包围 ```c vector process(){ if(expected.empty()) return {};// 返回一个 空vector对象 else if (expected == actual) return {"function", "okey"}; else return {"function", expected, actual}; } ``` ### 函数递归  返回值为自身函数 的一个 式子 main 函数不能调用自己 ```c // 计算阶乘的函数 int factorial( int val){ if(val > 1) return val * factorial(val-1); else return 1; } // 递归 返回一个vector的元素函数 void print_vec(vector vi){ auto it = vi.begin(); if(vi.size() > 1) { cout << *it < int(*)[10];// 声明一个函数 其形参为 int i ,返回类型 为 指针 ,指向 一个 含有 10个整数的 数组 // 使用 decltype 知道返回数组  类似类别别名 声明 定义 int odd[] = {1, 3, 5, 7, 9}; int even[] = {0, 2, 4, 6, 8}; decltype(odd) *arrPtr(int i){ //使用decltype(odd) 表面类型与 odd类似 return (i%2) ? &odd : &even;// 返回一个指向数组的指针 } // 声明一个 返回 引用 一个 含有10个string对象的数组 的函数 string ( &func_r(string str) )[10];// 直接声明 // 使用类别别名 typedef string arrS[10];// 类别别 含有10个string对象的数组 using arrS = string[10];// 同上 arrS &func(string str);   // 函数func 返回一个 指向 10个整数的数组的 指针 // 使用 auto 和 尾置返回类型 auto func(string str) -> string(&)[10]; // 使用 decltype 知道返回数组  类似类别别名 声明 定义 string str_arr[] = {"a string", "two"}; decltype(str_arr) &arrPtr(string str); ``` ## 函数 重载  同一个作用域内 的几个函数名字相同,但形参列表不同,成为 重载 (overloaded) ```c // 打印 数组元素的 几个 同名函数 void print(const char *cp);//出入参数为 带有结束符的