75

GitHub - Ewenwan/ShiYanLou: 学习C & C++ & python&汇编语言 数据结构...

 4 years ago
source link: https://github.com/Ewenwan/ShiYanLou
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

README.md

参考资料

自动编程体系设想

程序猿成长计划

数据结构和算法动态可视化

基础数据结构和算法的纯C语言实现

计算机科学 面试笔记

计算机科学 面试笔记2

可视化代码过程

各种工程实践代码参考

c++ 多线程 并发 指南 实战

c/c++教程

学习C++,应该循序渐进的看哪些书?

数据结构与算法系列 目录

刷完这65道题,面试通过率翻翻没问题

c++代码实验

源代码查找 热度 书籍

源代码查找 英文

免费的编程中文书籍索引

代码面试较好 强烈推荐!!!!!

nowcoder刷题笔记

LeetCode刷题笔记

跟我一起复习C++

Leetcode常用五大算法思想

数据结构 博客参考 树 图 队列

GitHub Pages+Jekyll搭建个人博客

Markdown工具集

清华操作系统 github

博客专栏 Linux环境编程

博客专栏 Linux网络编程

博客专栏 一步步学习C++

博客专栏 C语言

鱼C工作室 C/C++/Python/Wed/数据结构和算法

图形界面编程

面试算法博客笔记

刘国平基础算法

CPP11新特新 代码实战

新特征 C++11/14/17 concepts and code snippets

A Detailed Cplusplus Concurrency Tutorial 《C++ 并发编程指南》

基于C++11新标准的并发和多线程编程深度指南 C++ Concurrency In Action

《C++17 STL cookbook》英文版的中文翻译

哈希学习

甲骨文公司编辑器Oracle Solaris Studio 12.4 Information Library (简体中文) c/cpp用户指南 数值计算指南 代码分析器 性能分析器 线程分析器

哈工大 编译原理

编译原理——词法分析器实现

编译技术 西电

在线书籍编写模板 gitbook

循序渐进学习书目

包含内容:

	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 博客参考

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实现:
	int** arr_pp new int* [row_num];// 定义一个存储指针的数组的指针 行数
	for(i = 0; i < row_num; ++i)
	    arr_pp[i] = new int[col_num];// 一个一个new 指针每一行是一个行向量的指针
用 vector实现,一行代码搞定,还不到担心内存泄漏的问题
	vector<vector<int>> v_i2(row_num,vector<int>(col_num,0));//初始化为一个0矩阵
	vector<int> m;  
        m.reserve(1000);//提前保留1000的内存
	//使用 reserve 避免不必要的重新分配 

C++ primer5 语言学习记录

复合类型

引用 &   左值引用lvalue reference  别名; int val; &refval = val; 右值引用 rvalue reference

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 另外一个对象(其存储地址)

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类型的指针 错误  左右两边类型 必须匹配

利用指针访问对象 取地址内存储的值 解引用符 来访问向

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, *是一个解引用

   

空指针

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* 类型的指针  可一指向 任意类型 的 地址 , 可一存放任意类型的 地址,

用作函数参数,函数体内使用时需要,转化成实际传入类型的指针

double obj = 3.14, *pd = &obj; // double 类型 的变量 double类型的指针
void *pv = &obj; // obj 可以是任意类型的对象
pv = pd; // pv 可一存放任意类型的指针(地址)
double *pd = (double*)(pv);// void* 类型的指针 强制转化成  double* 类型的指针

定义多个变量

int i = 1024, *p = &i, &r = i;//i是整形变量 p是一个整形指针 r是一个整形引用
int* p1, p2;// * 仅仅修饰 p1 ,即p1是 指向整形的指针 p2是整形变量
int *p1, *p2;// p1 和 p2 都是指向 整形的指针

指向指针的指针

int ival = 1024;
int *pi = &ival; //指向整形变量的指针
int **ppi = &pi; //指向整形指针的指针  ppi ---> pi ------> ival
std::cout << ival << "\n"// 直接输出 变量值
      << *pi  << "\n"// 1次解引用 得到变量值
      << **ppi<< "\n";//2次解引用 的到变量值

指向指针的 引用 指针对象的别名  引用不是对象 不存在 指向引用的指针

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 限定符

修饰变量后,可以防止变量被修改

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 引用不是对象

        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 不能被赋值 不能改变

const double pi = 3.14;//双精度 浮点型 常量
double *ptr_d = &pi;// 错误,浮点型变量指针 不能绑定一个 常量的存储地址
const double *ptr_d_c = &pi;// 双精度常量 针 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不能改变 但是其指向的对象 无影响

int errNumber = 0;//
int *const conpErr = &errNumber;// * 可变指针  *const 常量指针不可变 
                               // 指向整形的 常量指针,conpErr 一直指向 errNumber
*conpErr = 3;//相当于 errNumber = 3
const double pi = 3.14;//
const double *const cd_cp = &pi;//指向 常量的常量指针,
                               // 即 指针本身cd_cp不能变, 其指向的值 *cd_cp也不能变

常量表达式 编译时就能确定的量/式子 constexpr int ce = 20;

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

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 类型说明符  让 编译器根据右式 类型 自动推算左式的类型  且用表达式的值 初始化变量

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 类型指示符 用表达式推断类型 但是不用表达式的值初始化变量  

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  访问所有元素

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]是最后一个字符

// 第一个字符改为大写
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<vector >;

// 初始化方式
vector<int> ivec(10,-1);// 直接初始化 10个元素 全为 -1
vector<int> ivec2 = ivec;//拷贝初始化
vector<int> ivec3{10};//一个元素 10 
vector<int> ivec3{101};//两个元素 10  和 1
vector<string> svec{"a","an","the"};//列表初始化 直接方式
vector<string> svec2 = {"a","an","the"};//列表初始化 拷贝方式

// 默认初始化 
vector<int> ivec(10);    // 10个元素,每个值都是0 
vector<string> svec(10); // 10个元素,每个值都是空 string 对象
vector<string> svec2{10};// 10个元素,每个值都是空 string 对象
vector<string> svec3{10, "hi"};// 10个 "hi"元素
vector<string> svec3(10, "hi");// 10个 "hi"元素

// 使用 .push_back() 添加元素
vector<int> ivec2; //空vec对象
for(int i = 0; i != 100; ++i)
ivec2.push_back(i);// 一次把整数值 放到 ivec2尾部 结束后 ivec2有100个元素 0~99

// 实时添加 string 元素
string word;
vector<string> text;//空对象
while (cin >> word) 
text.push_back(word);// 把word添加到 text 后面

//使用范围for  + 引用 访问 并改变vector元素
vector<int> 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<int>::size_type se = iv.size();

// 使用索引[] 访问 计算vector对象元素索引   统计各个分数段上 出现的 成绩个数
// 索引不能添加元素
vector<unsigned> 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 <cctype> 使用toupper()
vector<string> 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尾元素 的下一个位置 类似 指针

// 修改 字符串 第一个元素为大小字符
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<int>::iterator it;// 迭代器 it 可以读写 vector<int> 类型容器 的元素
 string::iterator it2;   // it2 可以读写  string对象 中的字符
 vector<int>::const_iterator it3;//it3只能读元素,不能写元素
 string::const_iterator it4;     //it4只能读字符,不能写字符

 // cbegin()   cend() 返回 常量 迭代器 仅能读取 容器元素 不能修改
 vector<int> iv;        // 变量
 const vector<int> civ; // 常量
 auto it1 = iv.begin(); //  it1的类型为 vector<int>::iterator  能读写iv的元素
 auto it2 = iv.cbegin(); // it2的类型为 vector<int>::const_iterator  能读iv的元素 不能修改 iv的元素
 auto it3 = civ.begin(); // it3的类型为 vector<int>::const_iterator  能读civ的元素 不能修改 civ的元素

  // 访问 容器元素对象的 成员函数   (*it).empty  等同于 it->empty()  解引用 和成员访问
  vector<string> 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<int> 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<unsigned> 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<unsigned> 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】定义

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】显式初始化数组元素

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<int> ivec(begin(i_arr), end(i_arr));//全部副本
vector<int> 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类型,内置类型,可以为负值

//下标访问修改元素
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<end){//指针指向相关的对象 可以比较大小(单位(间隔)一样大)
	cout << *begin  << endl;//输出每一个元素
	++begin;
}

//标准库函数 begin() end() 函数得到指针
int iarr = {0,1,2,3,4};//函数5个元素
int *beg = begin(iarr);//首元素指针
int *end = end(iarr);//尾后指针                 不能执行解引用运算!!!!!
while(beg != end && *beg >=0) ++beg;//找第一个负值元素

多维数组 数组的数组

初始化

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;
 }	

访问

// 指针访问
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;
}

函数

包括:返回类型 函数名字(形参列表) 函数体,函数的返回类型不能是数组或函数类型,但可以是 数组或函数类型的指针

函数定义 阶乘函数

int fact(int val){
	int ret = 1;
	while(val >1) ret *= val--;
	return ret;// 返回主调函数 结束函数调用
}

函数调用 分两步 1 实参初始化形参(形参为引用时,直接使用实参) 2控制权转移给被调用函数(inline 内联函数 之间在原处展开函数)

int main(){
int j = fact(5);// 实参5 初始化 形参(int val)
cout << "5! = " << j << endl;
return 0;
}

局部对象

形参和函数体内定义的变量统称为 局部变量,仅在函数的作用域内可见, 局部自动对象,只存在于函数体执行期间,而局部静态对象,可在函数调用后一直存在

// 定义
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); 函数可以多次声明

// 源文件 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 <iostream>
#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

参数传递

当形参是引用类型时,为引用传递,实际传递的是实参的别名,没有进行拷贝,当实参的值被拷贝给形参时,形参和实参是两个独立的对象

值传递   函数对形参做的所有操作 都不会影响实参

int n=0;
int i = n;// n拷贝给i
i = 42;//i的值改变, n的值不变    函数对形参做的所有操作 都不会影响实参 例如 fact(i) 不会改变i的值

引用传递 函数对形参做的所有操作 都会影响实参

int n = 0;
int &r_i = n;//r_i 是 n 的引用 即别名 同一个变量
r_i = 42;//r_i 和 n 都变成 42    

指针形参

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 <<endl;
reset(&i);// i 的值改变为0
cout << "i = " << i << endl;// i 的值改变为0
cout << "address of i =" << &i <<endl;// i 的存储地址未改变

// C 程序中 通常使用 指针类型的形参 来访问和改变 函数外部的对象
// C++ 程序中,建议使用引用类型的形参代替 指针形参,这样会更安全,也省时间(引用 无拷贝操作)

// 使用 引用避免拷贝 拷贝大的类对象或容器 都比较低效 费时

引用形参

 void reset(int &i){
  i = 0;// 改变了i所引用的对象
 }
 // 调用
 int j = 42;
 reset(j);// 采样传 引用方式,它的值被改变 调用时 形参i 只是 实参j的一个别名,在函数reset内部对i的操作,即对j的使用
 cout << "j = " << j << endl;

常量引用形参 当函数无须 改变引用参数的值时,最好将其声明为 常量引用

// 比较两个字符串的长度
bool isShorter(const string &rs1, const string &rs2){
	return rs1.size() < rs2.size();
}

使用引用形参 返回额外信息

// 函数返回多个值 返回字符在某个字符串中第一次出现的位置,并返回出现的 总次数
string::size_type find_char(const string &crs, char c, string::size_type &occurs){
				// 字符串        查找的字符      出现的次数
	auto ret = crs.size();//初始化 第一次出现的位置
	occurs = 0;//初始化 出现的次数
	for(decltype(ret) i = 0; i != crs.size(); ++i){
	  if(crs[i] == c){// 出现字符 c
	    if (ret == crs.size())//位置 还未改变 为  第一次出现 
	    ret  = i;//记录第一次出现的位置
	    ++occurs;//出现次数 +1  通过形参引用间接返回 出现的次数
	  }
	}  
	return ret;//返回第一次出现的位置
}

// 调用
string s("some string");
string::size_type count = 0;
auto index = find_char(s, 'o', count);

// 判断 string对象是否是 一个句子
 bool is_sentence(const string &crs){
	  // 如果 find_char() 的string参数 必须为 string & 那么不能直接把 const string & 带入
	  // 需要再定义一个 string对象, 另其为 crs 的副本,再带入
	 string::size_type count = 0;
	 return (find_char(crs, '.', count) == (crs.size() - 1)) &&  count == 1;
 }

数组形参 必须确保使用数组时不会越界  通常需要给一个结束标志

1 不允许拷贝数组 
2 在使用数组时会将其转换成指针(指向数组首元素的指针)
// 三个函数等价 形参 都是 const int * 类型
void print(const int*);
void print(const int[]);
void print(const int[10]);// 这里的维度表示期望数组含有多少元素,实际不一定
//调用
int i = 0;
j[2] = {0, 1};
print(&i); //&i 是int *类型 可以赋值给 const int *类型
print(j);  //j 为 &j[0] 是 int * 类型

使用标记指定数组长度   要求数组本身包含 一个结束标志  典型 C风格字符串 带有一个空字符

// C风格字符串 带有一个空字符
void print(const char *cp){
  if(cp)// 确定 cp 不是一个空指针
    while(*cp) // 只要 指针所指的字符不是 空字符
       cout << *cp++;// 打印 字符
}

使用标准库规范 传递 数组元素的 首末地址

void print(const int *beg, const int *end){
  while(beg != end) 
    cout << *beg++ << endl;//输出当前元素 并将指针向前移动一个位置
}
// 调用
int j[2] =  {0, 1};
print(begin(j), end(j));// 使用标准库 begin() 和 end() 函数

显示传递 一个数组大小的形参

void print(const int ia[], size_t size){
  for (size_t i = 0; i != size; ++i) 
     cout << ia[i] << endl;// 等价于 cout << *(ia+i) << endl;
}
// 调用
int j[2] =  {0, 1};
print( j, end(j) - begin(j) );// 使用标准库 begin() 和 end() 函数 做差来得到数组的大小

数组引用形参 int (&r_arr)[10] 是一个含有10个元素数组的 引用  直接包含了 数组大小的信息

void print(int (&r_arr)[10]){ // 注意形式 int (&r_arr)[10]   而 int &arr[10] 成了 包含引用的数组(还不存在,引用不是对象)
  for (auto elem : r_arr)// 变量数组每个元素  范围for
    cout << elem << endl;
}
// 调用 调用时 必须是 大小为10的整形 数组作为实参才可以
int k[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
print(k);// 大小必须为10 多一点 少一点 都不行

多维数组形参 数组指针 形参

void print(int (*p_arr)[10], int rowSize){}// 形参是 指向 一个含有10个整数的指针

main函数 主函数参数列表

// argc:argument counter 参数计数,argv:argument vector 参数 字符串 容器
int main(int argc, char *argv[]){}
int main(int argc, char **argv){}//第二个参数为 字符串数组 即数组的数组 指针的指针   argv 指向 char*
//程序执行 program -d -o outfile data0
则 argc = 5
argv[0] = "program"; // 程序名
argv[1] = "-d";      // 第一个参数 开始
argv[2] = "-o";      // 第二个参数
argv[3] = "outfile"; // 第三个参数
argv[4] = "data0";   // 第四个参数  最后一个指针
argv[5] = 0;   // 最后一个指针之后 的元素为0

含有可变形参的函数 1 传递一个 initializer_list 标准库类型,2 特殊参数类型 省略号 ...

initializer_list 形参  实参数量未知  但是全部实参类型 都 相同

// initializer_list 提供的操作
initializer_list<T> lst;// 默认初始化 T类型元素的空列表 模实际需要指定模板T的 具体类型
initializer_list<T> lst{a, b, c...};// 列表中的元素是 const 常量
lst2(lst); // 拷贝一个initializer_list对象,不会新建,原始列表 和 副本 共享元素
lst2 = lst;// 同上
lst.size();// 列表 中的元素
lst.begin();//返回指向lst中首元素的指针
lst.end();  //返回指向lst中尾元素下一个位置的指针
// 因为有 begin() 和 end()对象可以使用 范围for 遍历参数

// 具体
initializer_list<string> ls ;//initializer_list 的元素类型是 string
initializer_list<int> li ;   //initializer_list 的元素类型是 int
// 和vector不同的是 initializer_list中的元素是常量 不能被修改

// 定义函数
void error_msg(initializer_list<string> 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<string> ls){
	 cout << e.msg() << ": "
	 for(const auto &elem : ls )//范围for 遍历
	  cout << elem << " ";
	 cout << endl;
} 

省略符 ... 形参

void foo(param_list, ...)//
void foo(param_list...)//
void foo(...)//

函数 返回类型 和 return语句

无返回值函数 void函数 无需显示的 return 语句  return 后无值

// 交换两个值的函数
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 后有值 且返回的对象类型 与 函数定义的 返回类型相同

// 两个 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
}

返回类型为 引用时 不会对结果进行拷贝 节省时间  但是 不要 返回 函数内部 临时变量的 引用, 临时变量离开函数后就不存在了

建议 返回 引用对象 是一个 调用函数 之前就纯在的一个变量

同时 返回 非常量的引用 可以作为 左值 被赋值

// 不要 返回 函数内部 临时变量的 引用
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"; // 错误 函数返回值是个 常量引用 不能被赋值

函数返回类型为 列表 以花括号 {} 包围

vector<string> process(){
	if(expected.empty())
	  return {};// 返回一个 空vector对象
	else if (expected == actual)
	  return {"function", "okey"};
	else 
	  return {"function", expected, actual};
}

函数递归  返回值为自身函数 的一个 式子 main 函数不能调用自己

// 计算阶乘的函数
int factorial( int val){
	if(val > 1)
	   return val * factorial(val-1);
	else 
	    return 1;
}
// 递归 返回一个vector的元素函数
void print_vec(vector<int> vi){
	auto it = vi.begin();
	if(vi.size() > 1)
	 {
	   cout << *it <<endl;
	   vi.erase(it);
	   print_vec(vi);
	}
	else 
	   cout << *it <<endl;
}

返回 数组 指针 数组不能被 拷贝 所以函数不能返回数组 但可以返回 数组的指针 或引用

// 使用类别别名
typedef int arrT[10];// arrT是一个类别别名 表示一个 含有 10个整数 的 数组
using arrT = int[10];// 同上
arrT* func(int i);   // 函数func 返回一个 指向 10个整数的数组的 指针

//直接声明
int arr[10]; //arr 是一个 含有 10个整数的数组
int *arr_p[10]; //arr_p 是一个数组   含有 10个指整形针 的 数组
int (*p2)[10] = &arr; // p2 是一个指针, 指向一个含有 10个整数的 数组 arr
// 函数声明 类似
Type (*function_name(parameter_list))[dimension]
int ( *func(int i) )[10];// 声明一个函数 其形参为 int i ,返回类型 为 指针 ,指向 一个 含有 10个整数的 数组

// 函数指针数组
多个函数可以像数组一样被调用
int (*fun_p_a[])(char *tmp); //函数指针数组

// 使用 auto 和 尾置返回类型
auto func(int i) -> 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)

// 打印 数组元素的 几个 同名函数
void print(const char *cp);//出入参数为 带有结束符的 

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK