# C++基础入门

### 引导 CodeMonkey 

*熟悉codeMonkey的指令和学习兴趣。*

- -  Hello,小朋友们大家好，你们可以称呼我为喵老师，为啥叫喵老师呢，因为老师非常的喜欢猫。家里也养了几只可爱的猫。不知道你们 喜欢不喜欢猫呢？ 

- -  在我们上C课程之前呢，相信小朋友们都没有系统的了解过编程，了解过C,有些学生呢之前学过我们的图形化编程Scratch，有的呢学过python，不过呢，这些对于我们C++来说呢，有帮助，但是不是那么重要。没学过的小朋友呢，我们现在就先来熟悉和学习一下scratch和CodeMonkey吧。 

- -  从Scratch Jr到Scratch到codeMonkey，我们已经知道和学习了许多编程知识。请你跟着老师的思路来回忆一下： 

1. 1. 1. 所谓计算机编程，就是使用程序设计的语言，与计算机进行对话，以便要求计算机完成指定任务的过程；
      2. 在Scratch中，我们可以建立不同的角色对象，利用这些对象的指令积木，我们可以让它们做出相应的动作，或者执行相应的运算,加减乘除，求余数，绝对值等等；
      3. 在编程过程中，我们可以通过“回答”把信息提供给我们的程序，而程序会根据我们提供的回答进行运算，比如判断你输入的是不是正确答案，然后再以“说”的方式，把程序的“想法”反馈给你；
      4. 使用Scratch编写的程序，有顺序、选择（即如果……那么……）、循环（即重复执行）三种最基本的结构，一个程序会用到这三种结构中的一到多个；
      5. 我们可以使用Scratch提供的基本积木完成任务，也可以把一定数量的基本积木“组合”起来，做成“自制积木”，便于我们多次完成同一类型的任务，并让程序的结构更加清晰。
      6. 如果Scratch提供的基本模块没有我们要的功能，我们可以通过“选择扩展”菜单，把更多功能引入到程序中。比如我们最常用的画笔和视频侦测就是最好的例子……

以上都是我们在使用Scratch编程过程中获得的宝贵经验。这些经验，对于我们下一步的学习也是至关重要的，所谓“温故而知新”，讲的就是这个道理。如果你能把以前所学的编程知识与下面要学习的知识结合起来，一定可以起到事半功倍的效果！
因此，我们这个阶段的课程名称是“从Scratch到C++”，这正是一个温故而知新的过程。 

-  那codemonkey呢？他让我们知道了什么？ 知道了什么是编程。

*引入什么是编程？什么是指令？引入C**语言的形成。开始第一个C**程序。*


编程是指使用特定的计算机语言，通过编写代码来解决问题、完成任务或创建软件的过程。



例如，我们可以使用 codemonkey 语言编写一个程序来让猴子移动：

猴子移动 

step 10  step表示移动，后面的数字可以让猴子向前移动多少步

猴子转弯 

turn left

turn right



我们写的这些单词用来控制猴子移动，这就是编程。

我们生活中有很多编程的例子。比如

1.  手机应用：手机上的各种应用程序，如微信、支付宝、抖音、游戏等，其背后都是由编程语言实现的。比如，通过编写代码实现用户界面设计、数据处理、网络通信等功能。 
2.  网页浏览：当你在浏览器上浏览网页时，无论是静态网页还是动态交互式网页（例如百度、淘宝等），都是通过HTML、CSS、JavaScript等编程语言构建和运行的。 
3.  智能家居：家中的智能设备如智能音箱（如天猫精灵）、智能电视、智能灯泡等，可以通过编程实现语音控制、远程操作等功能。 
4.  交通出行：现在的导航软件（如高德地图、百度地图）可以规划路线、预测到达时间，背后是通过大数据分析和算法编程实现的；自动驾驶汽车更是离不开编程技术的支持。 
5.  银行系统：银行的各种业务处理系统，包括ATM机、网上银行、手机银行等，都需要编程来实现账户管理、交易处理、风险控制等功能。 
6.  社交媒体：像微博、微信朋友圈、Facebook等社交媒体平台，用户发布的内容推送、评论互动、数据分析等功能都基于编程实现。 
7.  健康监测：智能手环、智能手表等健康监测设备，能够记录并分析用户的运动数据、睡眠质量等信息，这些都是通过嵌入式编程实现的。 
8.  教育学习：在线教育平台，如网易云课堂、Coursera等，课程内容展示、进度跟踪、习题测试等功能都是依靠编程技术来开发的。 



以上这些例子只是冰山一角，实际上，现代生活中的很多方面都已经深深地打上了编程的烙印。

那我们说了这么多，也该说到我们的C++了。直接说C++，就不得不提C语言，因为他们一脉相承，知道了C语言，其实也就知道了C++、你知道什么是C语言，你知道C语言能干什么吗？

你肯定不知道啊，你知道你也不用来这里上课啦哥们。好尴尬呀。

所以，我们先提出四个问题，来看看，到底什么是C语言。让你在刚开始学习的时候就不迷路。

1、C语言是什么，C++是什么？

2、学完之后能做什么？

3、学C语言有什么好处

4、正确的学习方法

希望听完这节课呢，让你有一种恍然大明白的感觉

开始来了哟。



C语言是1978年1月1号，美国贝尔实验室推出的一门非常哇塞且牛叉的计算机语言。

这里面有几个关键词，时间-1978年1月1日，地点，美国贝尔实验室  发明人，贝尔实验室的科学家， 作用，哇塞和牛叉。 什么东西，计算机语言。

那么：什么是计算机语言呢?  还有C语言到底有多哇塞牛叉呢？你们也不知道啊，且听我给你一一道来。

 首先呢咱们先来看一下什么是计算机语言？所谓计算机语言就是人与计算机之间进行信息交流沟通的一种特殊的语言。

举个例子，比如说哥们现在要看电脑上的这个视频怎么办，你能跟他直接喊吗，肯定是不行的，至少以前是不行的，现在为什么机器能听懂，是因为有语言识别的机器能把你的语言翻译成机器的语言。计算机是听不懂人类的语言的事后，是不是一脸懵逼啊。我现在叫我手机他会答应我么，会搭理我么，是不是得有固定的口令啊。我们只能通过计算机语言去告诉他，听懂了之后，他就把这个视频给打开了。那很多哥们就会想了，我平时也不会什么计算机语言啊，我照样看视频，玩手机，打游戏啊。

是的，你说的没错，但是你造吗？咱们平时接触到的所有的软件，注意哦，是所有，无一例外，都是我们程序员提前用计算机语言写好的代码。这些代码就是计算机语言，只不过程序员把这些软件给我们打包起来了。才形成了各种各样的软件给我们用。比如我们按下的遥控器，按下的鼠标，都是一些程序员给我们写好以后，我们才能用。

我们来画一个图，先是老的电视机，只能收到什么台就播放什么台，然后是加了遥控器的电视，我们可以通过上下左右来切换电视的节目。其次是网络电视，我们可以搜索，或者打开应用来播放想看的视频。功能是不是越来越多，其实本质变没变，没变，还是电视在放映视频。然后我们又开发了语言助手，把我们的语言转化成了遥控器上的按键。让语言控制电视来工作。对不对，这是一环套着一环。

相信到这里，大家就能理解什么是计算机语言了吧。他就是人与计算机之间沟通交流的方式，点一下遥控器，按一下鼠标，按两下鼠标，按哪个应用，都是计算机语言的表现形式。

而C语言就是众多计算机语言当中，非常哇塞牛X的一种。为什么说它非常哇塞牛X呢？

就得从C语言能做什么说起。咱哥们以安卓手机为例，首先所有app在运行的时候都会涉及到这四层，而我们平时所使用的各种各样的app，是基于安卓的操作系统来进行开发的，也就是说如果没有安卓操作系统，那么这些软件都是用不了的，那么这些app的开发的难度较低，主要是写一些页面，用来展示各种各样的效果就可以了

，不用考虑跟硬件相关的东西，因为只要用到硬件了，都是操作系统来管的，比如说现在呢哥们有一个app要开始扫码了，用到手机上的摄像头了，那么此时就会一层一层的往下通知，最终是因为有操作系统去调用摄像头的驱动

，所以说呢才能让摄像头开始工作，但是如果说没有操作系统，没有驱动程序，那么app上所有的功能都用不了，甚至手机都无法开机，所以说操作系统跟驱动程序是非常的重要的，而这么重要的东西其实就是用C语言来开发的

。所以C是万物之母。他做的往往都是底层最核心的内容，而这些内容用其他的计算机语言，一般是无法实现的。

他能干的别人干不了，别人能干的，他都能干，只不过不屑于去干，明白吧。比如你们的python,人工智能，大数据，各种开发，都是应用，而C是什么，创造。所以说C是哇塞和牛X的。包括微软的底层系统，苹果的底层系统，都是基于C，有了C，有了C++,才有了这些我们今天所用到的这些硬件，软件。



```
C语言呢在企业当中一般是可以用来进行开发，可以用来创建各种软件应用程序，包括桌面应用程序、移动应用程序、网站、游戏、人工智能、机器学习、数据分析等等。
```



1. 创建网站：编程可以用来创建各种类型的网站，包括静态网站、动态网站、电子商务网站等。通过编程，可以实现网站的设计、开发、测试和部署等各个方面。

1. 开发应用程序：编程可以用来开发各种类型的应用程序，包括桌面应用程序、移动应用程序、游戏等。通过编程，可以实现应用程序的设计、开发、测试和发布等各个方面。

1. 自动化：编程可以用来自动化各种重复性的任务，例如数据输入、文件处理、邮件发送等。通过编程，可以实现自动化的脚本和程序，从而提高工作效率和减少错误。

 

1. 

![img](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\a1ec08fa513d269733cc2831d1999df74316d829-1699954957778-3.jpeg)

 

1. 数据分析：编程可以用来处理和分析各种类型的数据，例如文本、图像、音频、视频等。通过编程，可以实现数据的清洗、转换、分析和可视化等各个方面。

1. 人工智能：编程可以用来创建各种类型的人工智能系统，例如机器学习、深度学习、自然语言处理等。通过编程，可以实现人工智能系统的训练、测试和部署等各个方面。

1. 区块链：编程可以用来创建各种类型的区块链应用程序，例如数字货币、智能合约等。通过编程，可以实现区块链应用程序的设计、开发、测试和部署等各个方面。

1. 虚拟现实：编程可以用来创建各种类型的虚拟现实应用程序，例如游戏、培训、模拟等。通过编程，可以实现虚拟现实应用程序的设计、开发、测试和部署等各个方面。

 

![img](https://pics0.baidu.com/feed/d4628535e5dde711514d6553218de1179c166162.jpeg@f_auto?token=6e35c549677d52cc2c1498fb3a97644b)

 

编程可以用来创造各种类型的软件和应用程序，从而实现各种不同的功能和目标。无论是个人、企业还是政府机构，都可以通过编程来实现自己的需求和目标。

他到底是谁发明的呢？

这个人，丹尼斯·里奇

[https://baike.baidu.com/item/%E4%B8%B9%E5%B0%BC%E6%96%AF%C2%B7%E9%BA%A6%E5%8D%A1%E5%88%A9%E6%96%AF%E6%B3%B0%E5%B0%94%C2%B7%E9%87%8C%E5%A5%87/58101832#%E6%A6%82%E8%A7%88](https://baike.baidu.com/item/丹尼斯·麦卡利斯泰尔·里奇/58101832#概览)

神话的传奇的一生。

不光是c语言，unix操作系统，让计算机行业的发展足足加快了几百年。

计算机是如何运行程序的呢？第一个程序又是如何制作出来的呢？



计算机程序（Computer Program）。是一组计算机能识别和执行的指令，运行于电子计算机上，满足人们某种需求的信息化工具。通俗的来讲程序就是用一些简单的命令拼合而成的方法，他们可以指挥小猴子的运行，原理是因为他们可以控制计算机的运行。



它以某些程序设计语言编写，运行于某种目标结构体系上。打个比方，程序就如同以英语（程序设计语言）写作的文章，要让一个懂得英语的人（编译器）同时也会阅读这篇文章的人（结构体系）来阅读、理解、标记这篇文章。一般的，以英语文本为基础的计算机程序要经过编译、链接而成为人难以解读，但可轻易被计算机所解读的数字格式，然后放入运行。

编程就是自己利用英语和数学，以及逻辑思维关系，控制计算机实现一定目的的过程。

### 然后我们来说说学习它的好处。

其实啊它的好处还是非常的多的，比如说第一个可以通过各种各样的考试。

我们学习它主要就是用它来打比赛，升学，加分。

锻炼逻辑思维，到后面学算法，解题，跟奥数是一样的哈。

然后说大学，一旦编程思维建立了，考试，考研，就业，大企业，都是有用的，但是我们现在说这个有点早。

还有其他好处，学了C++,其它编程语言，几乎都可以自学了。这就是他的强大之处。



是不是都马上着急开始怎么学了啊？没问题，本课程就会从零开始，帮你搭建一个完整的C语言知识体系，整体的部分由四部分内容组成，首先呢我们会先学习一些比较简单的内容对C语言有一个初步的认知

比如说如何编写代码结构啊，如何运行代码啊还有如何查看程序的运行结果呀。



然后呢我们会去学习一些C语言的基础语法，疏通一下皮毛

那么接着我们会去学习C语言里面的高阶知识点初窥一下门禁

那么到这里呢，其实我们已经把语法都学完了，但是光学语法还远远不够啊，算法啊，数据结构啊。



### 最后我们来说说如何学习，如何学好。





### 学习方法

1、认识同学，回顾一二级的考点和知识点

2、作业的要求，作业虽然不是必须要求完成，但是为了给所有人一个交代，抽时间认真思考。（作业都不多，只是需要动一点点脑筋）

3、关于笔记，有些同学习惯写自己的笔记方式，都有手机或者平板或者电脑，比如有道云，比如processon,知道思维导图，流程图是干什么用的。利用好工具梳理好自己学到的知识。

4、如何学好C++

跟着老师发思路去学，每一节课，都有不多的知识点去学习，这个知识点一定是这章里面的核心，基础中的基础，一定要会的，然后呢，在做题的过程中逐渐的去扩展。

先做轮廓，再抠细节，避免细节一节课讲太多，结果用不着，不理解，就又忘了。



C生万物
编程之本
长远IT发展的首选
C语言是母体语言，是人机交互接近底层的桥梁
学会C/C++，相当于掌握技术核心，知识点一竿子打通，举一反三。
技术，一般每10年就有一次变革，近50年间，C/C++位置长期霸占前三名，没有丝毫撼动，只要计算机本质和原理不改变，C++永不过时！

5、多主动思考

挤一点时间多多练习，思考，有问题先尝试自己解决。

6、扩充知识面

计算机基础、硬件、算法和数据结构、操作系统、计算机网络、AI、人工智能、项目实战

##### 批处理

改成bat放在文件夹下面，运行就行了，中文乱码的话另存为编码ac

```
@echo off
setlocal enabledelayedexpansion

rem 请输入要替换的文本，例如：B
set /p w=请输入要改的文件格式（后缀名），回车结束
set /p str1=请输入要替换的文字，回车结束
set /p str2=请输入要替换后的文字（若是删除则直接回车）并回车结束

rem 获取当前目录下所有文件
for /f "delims=" %%f in ('dir /b /a-d "*.%w%"') do (
  rem 获取文件名
  set file_name=%%f
  
  rem 构造新的文件名
  set new_file_name=!file_name:%str1%=%str2%!
  
  rem 重命名文件
  ren "%%f" "!new_file_name!"
)

echo 所有文件名已成功修改。
endlocal

```



#### git资源

1、使用steam++代理使用github,现在叫watt toolkit

[瓦特工具箱(Steam++官网) - Watt Toolkit (steampp.net)](https://steampp.net/)

![image-20231207123457983](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231207123457983.png)

2、使用git，打开vscode

3、关闭所有文件夹![image-20231207122310952](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231207122310952.png)

4、打开源代码管理![image-20231207122458047](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231207122458047.png)

5、克隆你要克隆的仓库

选择从github克隆，克隆前，应该注意fork到你的项目里。

![image-20231207122539943](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231207122539943.png)

6、![image-20231207122629917](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231207122629917.png)



创建新叉子

fork是存储库的副本。分叉存储库允许您在不影响原始项目的情况下自由地尝试更改。查看现有货叉。

必填字段用星号（*）标记。

存储库所有者和名称

仅复制主分支

通过添加自己的分支，为liyupi/codefather做出贡献。了解更多信息。

您正在您的个人帐户中创建一个分叉。

7、选择你克隆到的位置

![image-20231207122927139](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231207122927139.png)



> git clone https://github.com/solyn/codefather.git e:\work\CodeStudent\codefather --progress
> fatal: unable to access 'https://github.com/solyn/codefather.git/': SSL certificate problem: unable to get local issuer certificate

错误： git SSL certificate problem: unable to get local issuer certificate
这个问题是由于没有配置信任的服务器HTTPS验证。默认，cURL被设为不信任任何CAs，就是说，它不信任任何服务器验证。

只需要执行下面命令就可以解决：

```
git config --global http.sslVerify false
```

![image-20231207123218100](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231207123218100.png)



![image-20231207123257962](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231207123257962.png)

然后呢就下载完了



使用模版创建自己的静态网站

[VuePress (vuejs.org)](https://vuepress.vuejs.org/zh/)

开源框架。

学习一下。

然后呢，下载模版。![image-20231207131942168](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231207131942168.png)



下载完用vscode打开，检查有没有node

node -v

![image-20231207132112522](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231207132112522.png)

![image-20231207132132536](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231207132132536.png)



2、npm install 安装依赖。

3、安装完后，点开package.json ，执行 docs:dev

4、选中，右键，运行，就可以

![image-20231207132428190](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231207132428190.png)



##### 详解目录结构

1、public，存放静态资源文件，图片 ，图标等等

![image-20231207132545263](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231207132545263.png)

2、sidebars存放侧边栏配置

3、theme 主题目录，是二次开发的一些主题代码

4、config是vuepress的核心配置文件，基本上都是修改这个来定制自己的网站

​		改变内容

​		改变样式

​		使用插件

​		前面的简单，就是一些seo,基本header，百度统计，permalink是永久链接的意思，建议开启，因为不开的话，每次结构目录改变，原本位置的内容就会消失。不利于搜索引擎优化。可以查看官方文档，定制不同的规则。

​	热更新，修改config后，网站自动更新	extra..来设置热更新范围

​	markdown配置，帮助我们把写好的markdown文件渲染到网页上，比如配置开启行号，以及更细的渲染规则

 	temeConfig 修改主题配置

##### 	侧边栏：

​				**不光要生成文章，还要根据文章的小标题，来自动生成导航侧边栏**

​				1、如果有很多的文章，把文章按照目录分类，比如学习导航，c++导航

​				2、在sidebars中，根据每一个目录，设置一个配置菜单，例如![image-20231207134003849](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231207134003849.png)

​				3、把目录中所有文章，设置到children里

​				4、在sidebar配置中，引入我们刚刚定义好的侧边栏文件。给他起一个分组名，例如学习路线。

![image-20231207134113469](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231207134113469.png)

​			5、底部和右侧侧边栏，修改对应的配置文件就行

​			![image-20231207134300177](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231207134300177.png)

​	**默认显示的都是每个文件下面的README.md文件。**

5、其他文件是定制不同的侧边栏，导航栏，底部信息，吸附侧边栏等等

5、外层是写的一些markdown文档

6、插件生态plugins

​			["@vuepress/back-to-top"], 跳转到顶部插件

​			["@vuepress/medium-zoom"],  图片放大插件

​			"seo", 插件

​			["vuepress-plugin-tags"], 显示文章标签，需要提前在文章顶部定义

​				![image-20231207135034920](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231207135034920.png)

​			"vuepress-plugin-code-copy", 复制代码插件

​			![image-20231207135121555](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231207135121555.png)

7、懒加载

["img-lazy"],



7、利用好学校的资源，不懂的话，可以问沟通的老师。

8、编程要踩一些坑

​	一、linux要学会用，熟悉使用命令行，可以学会更多计算机原理层面的知识，避免拖拖拽拽和双击屏幕

​	二、学习算法的同时，注重一些工程，百分之九十九的软件都是工程化的，一些简单的且没有太多技术含量的东西，学算法只是其中一种解决能力。

​	三、不要只学习不产出

​    四、技术重要，但是不要钻牛角尖，学会人际交往，有产品思维。 留足空间。不要预先去优化，先完成整体的思路，然后细节根据我们前进的产品方向去做优化。

#### 2、回顾过往和当前的目标

#### 3、在过往的基础上，有深度的学习，学会他



## 0 计算机基础

### 0.1 计算机的发展史

#### 0.1.1 计算器的发展

##### 0.1.1.1 手工计算

当谈到计算机的发展史时，我们从计算的原始阶段开始，然后逐步探索计算机科学的进展。

在人类历史的早期阶段，原始人们使用各种物理工具进行计算。

比如：打结计算（古人利用绳结来表示数字，不同的颜色，长度也可以表示不同的意思。想想有什么缺点：）

计算珠（棍子中间插着珠子来统计，故事：画一个圈，成了糖葫芦）

算筹（横竖算筹统计法、 运筹帷幄的典故）

算盘（珠算、中国古代的一项重要发明。在阿拉伯数字出现前，算盘是世界广为使用的计算工具。现在，算盘在亚洲和中东的部分地区扔）



##### 0.1.1.2机械计算

以上工具通过移动、摆动或组合来表示和处理数字，帮助人们进行简单的算术运算。

公元前3世纪的古希腊，发明家兼数学家阿基米德设计了一种叫做“阿基米德螺旋”的装置，用于计算近似值和计数。

16世纪，约翰·纳皮尔斯发明了一种被称为纳皮尔斯的骨髓棍的工具，用于进行乘法和除法的计算。

17世纪40年代，法国，有个叫布莱士帕斯卡，1642年，帕斯卡造出了一台机械计算器（即帕斯卡计算器）。

戈特弗里德·威廉·莱布尼茨—二进制

然而，真正意义上的计算机开始在19世纪末和20世纪初出现。英国数学家查尔斯·巴贝奇设计了一种称为差分机的计算机，用于执行多项式函数的差分计算。

接着，巴贝奇与他的合作者埃达·洛夫莱斯共同设计了分析机，这被认为是世界上第一台通用计算机。然而，由于各种原因，这两个机器都没有建造完成。







##### 0.1.1.3电子计算

[英国](https://baike.baidu.com/item/英国/144602?fromModule=lemma_inlink)数学家艾伦・麦席森・[图灵](https://baike.baidu.com/item/图灵/121208?fromModule=lemma_inlink)(1912―-1954年)于1936年提出的一种抽象的计算模型，即将人们使用纸笔进行数学运算的过程进行抽象，由一个虚拟的机器替代人类进行数学运算。

[图灵机_百度百科 (baidu.com)](https://baike.baidu.com/item/图灵机/2112989)



到了20世纪40年代，电子计算机的概念开始出现。 1946年，美国的ENIAC（电子数字积分计算机）是第一台全电子通用计算机，它使用了真空管和电路来进行计算。ENIAC被用于进行一系列计算任务，如弹道计算和核物理研究。

在ENIAC之后，人们开始设计和制造更先进和更小型的计算机。20世纪50年代和60年代，出现了许多重要的计算机，如EDVAC、UNIVAC、IBM 700系列等。



##### 0.1.1.4新时代

这些早期计算机在硬件和软件方面的进步促进了计算机科学的快速发展。随着时间的推移，计算机的体积越来越小，处理能力越来越强大，存储容量也越来越大。笔记本电脑、手机的发展等。

1. 学习计算机硬件的发展：了解计算机硬件组成部分的发展，如中央处理器（CPU）、内存、存储设备等的进化。
2. 探索操作系统的发展：从早期的批处理系统到现代的图形用户界面和分布式操作系统，了解操作系统的发展历程。
3. 了解计算机网络的发展：学习互联网的起源和演变，以及计算机网络技术的发展，如局域网、广域网、无线网络等。
4. 研究计算机编程语言的发展：了解早期的汇编语言，到高级编程语言的出现和发展，如Fortran、C、Java、Python等。
5. 了解人工智能和量子计算的发展：学习人工智能和量子计算领域的最新进展，如机器学习、深度学习、量子计算等。

[计算机的发展历史汇总 (baidu.com)](https://baijiahao.baidu.com/s?id=1735046171786766661&wfr=spider&for=pc)

### 0.2 计算机硬件基础

[计算机硬件基础知识 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/228743849)

#### 	0.2.1硬件的组成

##### 			**1.控制器：**

​					是计算机的指挥系统，负责控制计算机其他硬件的运行，相当于人的大脑

##### 		    **2.运算器：**

​					包括数学运算和逻辑运算，相当于人的大脑

​					控制器+运算器=CPU（中央处理器）

##### 			**3.存储器：**

​					存储数据

​					①**内存（主存）**：如内存条（又称随机存储器），**存取速度快**，但是基于电工作，一旦断电数据就丢失，**只能临时存储**。称为**主存的原因**是所有的数据指令存取都必须经过内存传输。类似于人的记忆功能，容易断片。

​					②**外存：**如硬盘，**存取速度相对内存很慢，数据可以近似“永久”保存**。类似于使用的笔记本子

##### 			**4.输入设备（Input）：**

​					计算机接收外界输入数据的工具，一般如键盘、鼠标等！

##### 			**5.输出设备（Output）：**

​					计算机向外输出数据的工具，如显示屏、打印机等



#### 		0.2.2计算机系统

##### 					windows

##### 					mac

##### 					linux

##### 					鸿蒙

##### 					安卓

##### 					ios

#### 		0.2.3计算机应用程序（软件）



### 0.3 C++语言的形成（什么是c++）

#### 	0.3.1 自然语言的形成
##### 		0.3.2.1 人与人之间交流的语言

#### 	0.3.2 计算机语言的发展史
##### 			0.3.2.1 人与计算机之间交流的语言
​				C，C++，C#，java，python，go等等的编程语言

​				计算机处理的是电信号

##### 			0.3.2.2 计算机语言的发展过程

![image-20240228181101304](C:\Users\Administrator\Desktop\日常教案\C++喵老师精品课件制作中\C++讲义文稿.assets\image-20240228181101304.png)

​				 1、二进制的01010101  科学家

​							第一个阶段，机器语言，机器语言是由二进制0和1按一定规则组成的、能被计算机直接理解和执行的指令集合。机器语言中的每一条语句实质上是一条指令。只有用机器语言编写的程序才能被计算机识别，其他任何语言都要翻译成机器语言以后，才能被计算机识别。

对于普通人来说，机器语言就是一长串的0101代码，没有任何意义。因此我们说机器语言是一种难学，难懂，很复杂的这么一种语言。只有非常专业的专业人员才能使用机器语言，而研究这一门语言的人呢也就是我们以前常说的科学家。

但是机器语言就没好处么，当然有的，机器语言能被计算机直接所执行，他的运行速度是非常快的，并且他所占的内存空间也非常的小。





​				 2、发明助记符  把二进制的一段一段用自然语言来标记 形成汇编语言   科研人员

​				 3、Fortran、COBOL、ALGOL 60、Pascal、B语言等早起语言的形成  早起程序员

​				 4、C语言、C++形成高级语言     高级程序员

​				 5、各自使用C和定制开发，出现了不同的分歧、美国国际标准 ANSI C  -也叫c89,又升级了c90、 有了国际标准后，才能让大家你编写的程序我也能运行。

​				6、之后就有了java、c#、python、go、等高级语



### 0.2 C++编辑器的了解

写作业我们需要作业本，写材料我们需要Word文档。写代码，就需要编译器。**总的来说，你所谓的编程语言其实就是软件，叫做编译器，它读取文本文件，做了许多处理，并生成二进制文件。**再说简单一点，编译器就是翻译器。你写的代码对于计算机来说就是外语，编译器将你的代码翻译成计算机能看懂的东西，使其可以被运行。

###### 主流编译器VS（Visual Studio）

###### 考试编译器Dev-C++

#### VSCode和AI智能环境的安装

##### 1、下载vscode

​	打开浏览器—搜索vscode—选择官网—可翻译—选择下载

2、安装

​	选择安装—狂点下一步

3、汉化

​	点开扩展，搜索汉化插件

4、安装编译器/已有编译器devc++

已经有的



![image-20231206190731538](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231206190731538.png)

![image-20231206191349871](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231206191349871.png)

![image-20231206191426725](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231206191426725.png)



没有的

5、安装智能提示

​	搜索codegeex




## 1 C++初识

### 1.1  第一个C++程序

编写一个C++程序总共分为4个步骤

* 创建项目
* 创建文件
* 编写代码
* 运行程序

#### 1.1.1 创建项目

​	Visual Studio是我们用来编写C++程序的主要工具，但是我们考试用到的是DEV C++ 所以我们都得会，主要以DEV C++进行开发练习。

#### 1.1.2 创建文件

右键源文件，选择添加->新建项

给C++文件起个名称，然后点击添加即可。



#### 1.1.3 编写代码

```c++
#include<iostream>
using namespace std;

int main() {

	cout << "Hello world" << endl;

	return 0;
}
```

#### 1.1.4 运行程序


### 1.2 注释

**作用**：在代码中加一些说明和解释，方便自己或其他程序员程序员阅读代码

**两种格式**

1. **单行注释**：`// 描述信息` 
   - 通常放在一行代码的上方，或者一条语句的末尾，==对该行代码说明==
2. **多行注释**： `/* 描述信息 */`
   - 通常放在一段代码的上方，==对该段代码做整体说明==

> 提示：编译器在编译代码时，会忽略注释的内容





尝试开发多行代码，使用同一个字符串，然后利用变量来简化这一过程。



### 1.3 变量

**作用**：给一段指定的内存空间起名，方便操作这段内存

**语法**：`数据类型 变量名 = 初始值;`

**示例：**

```C++
#include<iostream>
using namespace std;

int main() {

	//变量的定义
	//语法：数据类型  变量名 = 初始值

	int a = 10;

	cout << "a = " << a << endl;
	
	return 0;
}
```



> 注意：C++在创建变量时，必须给变量一个初始值，否则会报错















### 1.4  常量

**作用**：用于记录程序中不可更改的数据

C++定义常量两种方式

1. **\#define** 宏常量： `#define 常量名 常量值`
   * ==通常在文件上方定义==，表示一个常量


2. **const**修饰的变量 `const 数据类型 常量名 = 常量值`
   * ==通常在变量定义前加关键字const==，修饰该变量为常量，不可修改



**示例：**

```C++
//1、宏常量
#define day 7

int main() {

	cout << "一周里总共有 " << day << " 天" << endl;
	//day = 8;  //报错，宏常量不可以修改

	//2、const修饰变量
	const int month = 12;
	cout << "一年里总共有 " << month << " 个月份" << endl;
	//month = 24; //报错，常量是不可以修改的
	

	return 0;
}
```










### 1.5 关键字

**作用：**关键字是C++中预先保留的单词（标识符）

* **在定义变量或者常量时候，不要用关键字**



C++关键字如下：

| asm        | do           | if               | return      | typedef  |
| ---------- | ------------ | ---------------- | ----------- | -------- |
| auto       | double       | inline           | short       | typeid   |
| bool       | dynamic_cast | int              | signed      | typename |
| break      | else         | long             | sizeof      | union    |
| case       | enum         | mutable          | static      | unsigned |
| catch      | explicit     | namespace        | static_cast | using    |
| char       | export       | new              | struct      | virtual  |
| class      | extern       | operator         | switch      | void     |
| const      | false        | private          | template    | volatile |
| const_cast | float        | protected        | this        | wchar_t  |
| continue   | for          | public           | throw       | while    |
| default    | friend       | register         | true        |          |
| delete     | goto         | reinterpret_cast | try         |          |

`提示：在给变量或者常量起名称时候，不要用C++得关键字，否则会产生歧义。`











### 1.6 标识符命名规则

**作用**：C++规定给标识符（变量、常量）命名时，有一套自己的规则

* 标识符不能是关键字
* 标识符只能由字母、数字、下划线组成
* 第一个字符必须为字母或下划线
* 标识符中字母区分大小写

> 建议：给标识符命名时，争取做到见名知意的效果，方便自己和他人的阅读















## 2 数据类型

C++规定在创建一个变量或者常量时，必须要指定出相应的数据类型，否则无法给变量分配内存

### 2.1 整型

**作用**：整型变量表示的是==整数类型==的数据

C++中能够表示整型的类型有以下几种方式，**区别在于所占内存空间不同**：

| **数据类型**        | **占用空间**                                    | 取值范围         |
| ------------------- | ----------------------------------------------- | ---------------- |
| short(短整型)       | 2字节                                           | (-2^15 ~ 2^15-1) |
| int(整型)           | 4字节                                           | (-2^31 ~ 2^31-1) |
| long(长整形)        | Windows为4字节，Linux为4字节(32位)，8字节(64位) | (-2^31 ~ 2^31-1) |
| long long(长长整形) | 8字节                                           | (-2^63 ~ 2^63-1) |













### 2.2 sizeof关键字

**作用：**利用sizeof关键字可以==统计数据类型所占内存大小==

**语法：** `sizeof( 数据类型 / 变量)`

**示例：**

```C++
int main() {

	cout << "short 类型所占内存空间为： " << sizeof(short) << endl;

	cout << "int 类型所占内存空间为： " << sizeof(int) << endl;

	cout << "long 类型所占内存空间为： " << sizeof(long) << endl;

	cout << "long long 类型所占内存空间为： " << sizeof(long long) << endl;

	system("pause");

	return 0;
}
```





> **整型结论**：==short < int <= long <= long long==















### 2.3 实型（浮点型）

**作用**：用于==表示小数==

浮点型变量分为两种：

1. 单精度float 
2. 双精度double

两者的**区别**在于表示的有效数字范围不同。

| **数据类型** | **占用空间** | **有效数字范围** |
| ------------ | ------------ | ---------------- |
| float        | 4字节        | 7位有效数字      |
| double       | 8字节        | 15～16位有效数字 |

**示例：**

```C++
int main() {

	float f1 = 3.14f;
	double d1 = 3.14;

	cout << f1 << endl;
	cout << d1<< endl;

	cout << "float  sizeof = " << sizeof(f1) << endl;
	cout << "double sizeof = " << sizeof(d1) << endl;

	//科学计数法
	float f2 = 3e2; // 3 * 10 ^ 2 
	cout << "f2 = " << f2 << endl;

	float f3 = 3e-2;  // 3 * 0.1 ^ 2
	cout << "f3 = " << f3 << endl;

	return 0;
}
```





### 练习题

1、计算机提示，输入您的姓名，性别，身高，体重，年龄。

​	并且，把输入的身高，转化成cm，mm，以及米。

2、输入长方形的宽和高，求长方形的面积。

3、输入一个数字，表示几个月，假设每1月为30天，求这个月一共有多少秒。例子输入 2 表示两个月  2*30=60天，求60天包含了多少秒？

4、输入身高，把身高转化为英寸和英尺；一英寸=0.0254米  一英尺=0.3048米；

5、输入我国人口，输入世界人口，计算我国人口占世界人口的百分比；



### 2.4 字符型

**作用：**字符型变量用于显示单个字符

**语法：**`char ch = 'a';`



> 注意1：在显示字符型变量时，用单引号将字符括起来，不要用双引号

> 注意2：单引号内只能有一个字符，不可以是字符串



- C和C++中字符型变量只占用==1个字节==。
- 字符型变量并不是把字符本身放到内存中存储，而是将对应的ASCII编码放入到存储单元



示例：

```C++
int main() {
	
	char ch = 'a';
	cout << ch << endl;
	cout << sizeof(char) << endl;

	//ch = "abcde"; //错误，不可以用双引号
	//ch = 'abcde'; //错误，单引号内只能引用一个字符

	cout << (int)ch << endl;  //查看字符a对应的ASCII码
	ch = 97; //可以直接用ASCII给字符型变量赋值
	cout << ch << endl;

	system("pause");

	return 0;
}
```

ASCII码表格：

| **ASCII**值 | **控制字符** | **ASCII**值 | **字符** | **ASCII**值 | **字符** | **ASCII**值 | **字符** |
| ----------- | ------------ | ----------- | -------- | ----------- | -------- | ----------- | -------- |
| 0           | NUT          | 32          | (space)  | 64          | @        | 96          | 、       |
| 1           | SOH          | 33          | !        | 65          | A        | 97          | a        |
| 2           | STX          | 34          | "        | 66          | B        | 98          | b        |
| 3           | ETX          | 35          | #        | 67          | C        | 99          | c        |
| 4           | EOT          | 36          | $        | 68          | D        | 100         | d        |
| 5           | ENQ          | 37          | %        | 69          | E        | 101         | e        |
| 6           | ACK          | 38          | &        | 70          | F        | 102         | f        |
| 7           | BEL          | 39          | ,        | 71          | G        | 103         | g        |
| 8           | BS           | 40          | (        | 72          | H        | 104         | h        |
| 9           | HT           | 41          | )        | 73          | I        | 105         | i        |
| 10          | LF           | 42          | *        | 74          | J        | 106         | j        |
| 11          | VT           | 43          | +        | 75          | K        | 107         | k        |
| 12          | FF           | 44          | ,        | 76          | L        | 108         | l        |
| 13          | CR           | 45          | -        | 77          | M        | 109         | m        |
| 14          | SO           | 46          | .        | 78          | N        | 110         | n        |
| 15          | SI           | 47          | /        | 79          | O        | 111         | o        |
| 16          | DLE          | 48          | 0        | 80          | P        | 112         | p        |
| 17          | DCI          | 49          | 1        | 81          | Q        | 113         | q        |
| 18          | DC2          | 50          | 2        | 82          | R        | 114         | r        |
| 19          | DC3          | 51          | 3        | 83          | S        | 115         | s        |
| 20          | DC4          | 52          | 4        | 84          | T        | 116         | t        |
| 21          | NAK          | 53          | 5        | 85          | U        | 117         | u        |
| 22          | SYN          | 54          | 6        | 86          | V        | 118         | v        |
| 23          | TB           | 55          | 7        | 87          | W        | 119         | w        |
| 24          | CAN          | 56          | 8        | 88          | X        | 120         | x        |
| 25          | EM           | 57          | 9        | 89          | Y        | 121         | y        |
| 26          | SUB          | 58          | :        | 90          | Z        | 122         | z        |
| 27          | ESC          | 59          | ;        | 91          | [        | 123         | {        |
| 28          | FS           | 60          | <        | 92          | /        | 124         | \|       |
| 29          | GS           | 61          | =        | 93          | ]        | 125         | }        |
| 30          | RS           | 62          | >        | 94          | ^        | 126         | `        |
| 31          | US           | 63          | ?        | 95          | _        | 127         | DEL      |

ASCII 码大致由以下**两部分组**成：

* ASCII 非打印控制字符： ASCII 表上的数字 **0-31** 分配给了控制字符，用于控制像打印机等一些外围设备。
* ASCII 打印字符：数字 **32-126** 分配给了能在键盘上找到的字符，当查看或打印文档时就会出现。













### 2.5 转义字符

**作用：**用于表示一些==不能显示出来的ASCII字符==

现阶段我们常用的转义字符有：` \n  \\  \t`

| **转义字符** | **含义**                                | **ASCII**码值（十进制） |
| ------------ | --------------------------------------- | ----------------------- |
| \a           | 警报                                    | 007                     |
| \b           | 退格(BS) ，将当前位置移到前一列         | 008                     |
| \f           | 换页(FF)，将当前位置移到下页开头        | 012                     |
| **\n**       | **换行(LF) ，将当前位置移到下一行开头** | **010**                 |
| \r           | 回车(CR) ，将当前位置移到本行开头       | 013                     |
| **\t**       | **水平制表(HT)  （跳到下一个TAB位置）** | **009**                 |
| \v           | 垂直制表(VT)                            | 011                     |
| **\\\\**     | **代表一个反斜线字符"\"**               | **092**                 |
| \'           | 代表一个单引号（撇号）字符              | 039                     |
| \"           | 代表一个双引号字符                      | 034                     |
| \?           | 代表一个问号                            | 063                     |
| \0           | 数字0                                   | 000                     |
| \ddd         | 8进制转义字符，d范围0~7                 | 3位8进制                |
| \xhh         | 16进制转义字符，h范围0~9，a~f，A~F      | 3位16进制               |

示例：

```C++
int main() {
	
	
	cout << "\\" << endl;
	cout << "\tHello" << endl;
	cout << "\n" << endl;

	system("pause");

	return 0;
}
```













### 2.6 字符串型

**作用**：用于表示一串字符

**两种风格**

1. **C风格字符串**： `char 变量名[] = "字符串值"`

   示例：

   ```C++
   int main() {
   
   	char str1[] = "hello world";
   	cout << str1 << endl;
   
   	return 0;
   }
   ```

> 注意：C风格的字符串要用双引号括起来

1. **C++风格字符串**：  `string  变量名 = "字符串值"`

   示例：

   ```C++
   int main() {
   
   	string str = "hello world";
   	cout << str << endl;
   	
   	return 0;
   }
   ```
   
   

> 注意：C++风格字符串，需要加入头文件==#include\<string>==













### 2.7 布尔类型 bool

**作用：**布尔数据类型代表真或假的值 

bool类型只有两个值：

* true  --- 真（本质是1）
* false --- 假（本质是0）

**bool类型占==1个字节==大小**

示例：

```C++
int main() {

	bool flag = true;
	cout << flag << endl; // 1

	flag = false;
	cout << flag << endl; // 0

	cout << "size of bool = " << sizeof(bool) << endl; //1

	return 0;
}
```















### 2.8 数据的输入

**作用：用于从键盘获取数据**

**关键字：**cin

**语法：** `cin >> 变量 `

示例：

```C++
int main(){

	//整型输入
	int a = 0;
	cout << "请输入整型变量：" << endl;
	cin >> a;
	cout << a << endl;

	//浮点型输入
	double d = 0;
	cout << "请输入浮点型变量：" << endl;
	cin >> d;
	cout << d << endl;

	//字符型输入
	char ch = 0;
	cout << "请输入字符型变量：" << endl;
	cin >> ch;
	cout << ch << endl;

	//字符串型输入
	string str;
	cout << "请输入字符串型变量：" << endl;
	cin >> str;
	cout << str << endl;

	//布尔类型输入
	bool flag = true;
	cout << "请输入布尔型变量：" << endl;
	cin >> flag;
	cout << flag << endl;
	system("pause");
	return EXIT_SUCCESS;
}
```













## 3 运算符

**作用：**用于执行代码的运算

本章我们主要讲解以下几类运算符：

| **运算符类型** | **作用**                               |
| -------------- | -------------------------------------- |
| 算术运算符     | 用于处理四则运算                       |
| 赋值运算符     | 用于将表达式的值赋给变量               |
| 比较运算符     | 用于表达式的比较，并返回一个真值或假值 |
| 逻辑运算符     | 用于根据表达式的值返回真值或假值       |

### 3.1 算术运算符

**作用**：用于处理四则运算 

算术运算符包括以下符号：

| **运算符** | **术语**   | **示例**    | **结果**  |
| ---------- | ---------- | ----------- | --------- |
| +          | 正号       | +3          | 3         |
| -          | 负号       | -3          | -3        |
| +          | 加         | 10 + 5      | 15        |
| -          | 减         | 10 - 5      | 5         |
| *          | 乘         | 10 * 5      | 50        |
| /          | 除         | 10 / 5      | 2         |
| %          | 取模(取余) | 10 % 3      | 1         |
| ++         | 前置递增   | a=2; b=++a; | a=3; b=3; |
| ++         | 后置递增   | a=2; b=a++; | a=3; b=2; |
| --         | 前置递减   | a=2; b=--a; | a=1; b=1; |
| --         | 后置递减   | a=2; b=a--; | a=1; b=2; |

**示例1：**

```C++
//加减乘除
int main() {

	int a1 = 10;
	int b1 = 3;

	cout << a1 + b1 << endl;
	cout << a1 - b1 << endl;
	cout << a1 * b1 << endl;
	cout << a1 / b1 << endl;  //两个整数相除结果依然是整数

	int a2 = 10;
	int b2 = 20;
	cout << a2 / b2 << endl; 

	int a3 = 10;
	int b3 = 0;
	//cout << a3 / b3 << endl; //报错，除数不可以为0


	//两个小数可以相除
	double d1 = 0.5;
	double d2 = 0.25;
	cout << d1 / d2 << endl;

	return 0;
}
```

> 总结：在除法运算中，除数不能为0





**示例2：**

```C++
//取模
int main() {

	int a1 = 10;
	int b1 = 3;

	cout << 10 % 3 << endl;

	int a2 = 10;
	int b2 = 20;

	cout << a2 % b2 << endl;

	int a3 = 10;
	int b3 = 0;

	//cout << a3 % b3 << endl; //取模运算时，除数也不能为0

	//两个小数不可以取模
	double d1 = 3.14;
	double d2 = 1.1;

	//cout << d1 % d2 << endl;

	system("pause");

	return 0;
}

```

> 总结：只有整型变量可以进行取模运算



**示例3：**

```C++
//递增
int main() {

	//后置递增
	int a = 10;
	a++; //等价于a = a + 1
	cout << a << endl; // 11

	//前置递增
	int b = 10;
	++b;
	cout << b << endl; // 11

	//区别
	//前置递增先对变量进行++，再计算表达式
	int a2 = 10;
	int b2 = ++a2 * 10;
	cout << b2 << endl;

	//后置递增先计算表达式，后对变量进行++
	int a3 = 10;
	int b3 = a3++ * 10;
	cout << b3 << endl;

	system("pause");

	return 0;
}

```



> 总结：前置递增先对变量进行++，再计算表达式，后置递增相反





##### C/C++ 之 幂次运算

因在 C/C++ 中’^‘代表异或，所以不可以直接以n^m 表示n的m次方。具体实现方式如下：

1、常规实现
`#include<math.h>`

`int val = pow(n,m);//表示n的m次方`

`#include<math.h>`

`int val = pow(3,4);//表示 3的4次方 = 81`

2、特殊情况
在C/C++中，10的n次可直接表示为 1en，如下

`int val = 1e3; // 此时val = 1000`

同时，2的n次方可表示为 2 << (n - 1)，即将2左移n-1位，或 1 << n，即将1左移n位。因为2本身为2的1次，1为2的0次，且计算机本质操作的是二进制，所以此处可以直接利用左移去实现。如

int val1 = 2 << 2; // val1 = 8
int val2 = 1 << 3;// val2 = 8





### 3.2 赋值运算符

**作用：**用于将表达式的值赋给变量

赋值运算符包括以下几个符号：

| **运算符** | **术语** | **示例**   | **结果**  |
| ---------- | -------- | ---------- | --------- |
| =          | 赋值     | a=2; b=3;  | a=2; b=3; |
| +=         | 加等于   | a=0; a+=2; | a=2;      |
| -=         | 减等于   | a=5; a-=3; | a=2;      |
| *=         | 乘等于   | a=2; a*=2; | a=4;      |
| /=         | 除等于   | a=4; a/=2; | a=2;      |
| %=         | 模等于   | a=3; a%2;  | a=1;      |



**示例：**

```C++
int main() {

	//赋值运算符

	// =
	int a = 10;
	a = 100;
	cout << "a = " << a << endl;

	// +=
	a = 10;
	a += 2; // a = a + 2;
	cout << "a = " << a << endl;

	// -=
	a = 10;
	a -= 2; // a = a - 2
	cout << "a = " << a << endl;

	// *=
	a = 10;
	a *= 2; // a = a * 2
	cout << "a = " << a << endl;

	// /=
	a = 10;
	a /= 2;  // a = a / 2;
	cout << "a = " << a << endl;

	// %=
	a = 10;
	a %= 2;  // a = a % 2;
	cout << "a = " << a << endl;

	system("pause");

	return 0;
}
```









### 3.3 比较运算符

**作用：**用于表达式的比较，并返回一个真值或假值

比较运算符有以下符号：

| **运算符** | **术语** | **示例** | **结果** |
| ---------- | -------- | -------- | -------- |
| ==         | 相等于   | 4 == 3   | 0        |
| !=         | 不等于   | 4 != 3   | 1        |
| <          | 小于     | 4 < 3    | 0        |
| \>         | 大于     | 4 > 3    | 1        |
| <=         | 小于等于 | 4 <= 3   | 0        |
| \>=        | 大于等于 | 4 >= 1   | 1        |

示例：

```C++
int main() {

	int a = 10;
	int b = 20;

	cout << (a == b) << endl; // 0 

	cout << (a != b) << endl; // 1

	cout << (a > b) << endl; // 0

	cout << (a < b) << endl; // 1

	cout << (a >= b) << endl; // 0

	cout << (a <= b) << endl; // 1
	
	system("pause");

	return 0;
}
```



> 注意：C和C++ 语言的比较运算中， ==“真”用数字“1”来表示， “假”用数字“0”来表示。== 













### 3.4 逻辑运算符

**作用：**用于根据表达式的值返回真值或假值

逻辑运算符有以下符号：

| **运算符** | **术语** | **示例** | **结果**                                                 |
| ---------- | -------- | -------- | -------------------------------------------------------- |
| !          | 非       | !a       | 如果a为假，则!a为真；  如果a为真，则!a为假。             |
| &&         | 与       | a && b   | 如果a和b都为真，则结果为真，否则为假。                   |
| \|\|       | 或       | a \|\| b | 如果a和b有一个为真，则结果为真，二者都为假时，结果为假。 |

**示例1：**逻辑非

```C++
//逻辑运算符  --- 非
int main() {

	int a = 10;

	cout << !a << endl; // 0

	cout << !!a << endl; // 1

	system("pause");

	return 0;
}
```

> 总结： 真变假，假变真





**示例2：**逻辑与

```C++
//逻辑运算符  --- 与
int main() {

	int a = 10;
	int b = 10;

	cout << (a && b) << endl;// 1

	a = 10;
	b = 0;

	cout << (a && b) << endl;// 0 

	a = 0;
	b = 0;

	cout << (a && b) << endl;// 0

	system("pause");

	return 0;
}

```

> 总结：逻辑==与==运算符总结： ==同真为真，其余为假==







**示例3：**逻辑或

```c++
//逻辑运算符  --- 或
int main() {

	int a = 10;
	int b = 10;

	cout << (a || b) << endl;// 1

	a = 10;
	b = 0;

	cout << (a || b) << endl;// 1 

	a = 0;
	b = 0;

	cout << (a || b) << endl;// 0

	system("pause");

	return 0;
}
```

> 逻辑==或==运算符总结： ==同假为假，其余为真==

















## 4 程序流程结构

C/C++支持最基本的三种程序运行结构：==顺序结构、选择结构、循环结构==

* 顺序结构：程序按顺序执行，不发生跳转
* 选择结构：依据条件是否满足，有选择的执行相应功能
* 循环结构：依据条件是否满足，循环多次执行某段代码



### 4.1 选择结构

#### 4.1.1 if语句

**作用：**执行满足条件的语句

if语句的三种形式

* 单行格式if语句

* 多行格式if语句

* 多条件的if语句

  

1. 单行格式if语句：`if(条件){ 条件满足执行的语句 }`

   ![img](assets/clip_image002.png)

   示例：

   ```C++
   int main() {
   
   	//选择结构-单行if语句
   	//输入一个分数，如果分数大于600分，视为考上一本大学，并在屏幕上打印
   
   	int score = 0;
   	cout << "请输入一个分数：" << endl;
   	cin >> score;
   
   	cout << "您输入的分数为： " << score << endl;
   
   	//if语句
   	//注意事项，在if判断语句后面，不要加分号
   	if (score > 600)
   	{
   		cout << "我考上了一本大学！！！" << endl;
   	}
   
   	return 0;
   }
   ```
   
   


> 注意：if条件表达式后不要加分号







2. 多行格式if语句：`if(条件){ 条件满足执行的语句 }else{ 条件不满足执行的语句 };`

![img](assets/clip_image002-1541662519170.png)



示例：

```C++
int main() {

	int score = 0;

	cout << "请输入考试分数：" << endl;

	cin >> score;

	if (score > 600)
	{
		cout << "我考上了一本大学" << endl;
	}
	else
	{
		cout << "我未考上一本大学" << endl;
	}

	return 0;
}
```











3. 多条件的if语句：`if(条件1){ 条件1满足执行的语句 }else if(条件2){条件2满足执行的语句}... else{ 都不满足执行的语句}`

![img](assets/clip_image002-1541662566808.png)







示例：

```C++
	int main() {

	int score = 0;

	cout << "请输入考试分数：" << endl;

	cin >> score;

	if (score > 600)
	{
		cout << "我考上了一本大学" << endl;
	}
	else if (score > 500)
	{
		cout << "我考上了二本大学" << endl;
	}
	else if (score > 400)
	{
		cout << "我考上了三本大学" << endl;
	}
	else
	{
		cout << "我未考上本科" << endl;
	}


	return 0;
}
```









**嵌套if语句**：在if语句中，可以嵌套使用if语句，达到更精确的条件判断



案例需求：

* 提示用户输入一个高考考试分数，根据分数做如下判断
* 分数如果大于600分视为考上一本，大于500分考上二本，大于400考上三本，其余视为未考上本科；
* 在一本分数中，如果大于700分，考入北大，大于650分，考入清华，大于600考入人大。



**示例：**

```c++
int main() {

	int score = 0;

	cout << "请输入考试分数：" << endl;

	cin >> score;

	if (score > 600)
	{
		cout << "我考上了一本大学" << endl;
		if (score > 700)
		{
			cout << "我考上了北大" << endl;
		}
		else if (score > 650)
		{
			cout << "我考上了清华" << endl;
		}
		else
		{
			cout << "我考上了人大" << endl;
		}
		
	}
	else if (score > 500)
	{
		cout << "我考上了二本大学" << endl;
	}
	else if (score > 400)
	{
		cout << "我考上了三本大学" << endl;
	}
	else
	{
		cout << "我未考上本科" << endl;
	}

	system("pause");

	return 0;
}
```







**练习案例：** 三只小猪称体重

有三只小猪ABC，请分别输入三只小猪的体重，并且判断哪只小猪最重？![三只小猪](assets/三只小猪.jpg)









#### 4.1.2 三目运算符

**作用：** 通过三目运算符实现简单的判断

**语法：**`表达式1 ? 表达式2 ：表达式3`

**解释：**

如果表达式1的值为真，执行表达式2，并返回表达式2的结果；

如果表达式1的值为假，执行表达式3，并返回表达式3的结果。

**示例：**

```C++
int main() {

	int a = 10;
	int b = 20;
	int c = 0;

	c = a > b ? a : b;
	cout << "c = " << c << endl;

	//C++中三目运算符返回的是变量,可以继续赋值

	(a > b ? a : b) = 100;

	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;


	return 0;
}
```

> 总结：和if语句比较，三目运算符优点是短小整洁，缺点是如果用嵌套，结构不清晰









#### 4.1.3 switch语句

**作用：**执行多条件分支语句

**语法：**

```C++
switch(表达式)

{

	case 结果1：执行语句;break;

	case 结果2：执行语句;break;

	...

	default:执行语句;break;

}

```







**示例：**

```C++
int main() {

	//请给电影评分 
	//10 ~ 9   经典   
	// 8 ~ 7   非常好
	// 6 ~ 5   一般
	// 5分以下 烂片

	int score = 0;
	cout << "请给电影打分" << endl;
	cin >> score;

	switch (score)
	{
	case 10:
	case 9:
		cout << "经典" << endl;
		break;
	case 8:
		cout << "非常好" << endl;
		break;
	case 7:
	case 6:
		cout << "一般" << endl;
		break;
	default:
		cout << "烂片" << endl;
		break;
	}

	system("pause");

	return 0;
}
```



> 注意1：switch语句中表达式类型只能是整型或者字符型

> 注意2：case里如果没有break，那么程序会一直向下执行

> 总结：与if语句比，对于多条件判断时，switch的结构清晰，执行效率高，缺点是switch不可以判断区间















### 4.2 循环结构

#### 4.2.1 while循环语句

**作用：**满足循环条件，执行循环语句

**语法：**` while(循环条件){ 循环语句 }`

**解释：**==只要循环条件的结果为真，就执行循环语句==

![img](assets/clip_image002-1541668640382.png)







**示例：**

```C++
int main() {

	int num = 0;
	while (num < 10)
	{
		cout << "num = " << num << endl;
		num++;
	}
	

	return 0;
}
```



> 注意：在执行循环语句时候，程序必须提供跳出循环的出口，否则出现死循环









**while循环练习案例：**==猜数字==

**案例描述：**系统随机生成一个1到100之间的数字，玩家进行猜测，如果猜错，提示玩家数字过大或过小，如果猜对恭喜玩家胜利，并且退出游戏。



![猜数字](assets/猜数字.jpg)

![while循环案例-猜数字](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\while循环案例-猜数字.png)



```
随机数的方法：
https://blog.csdn.net/lizhichao410/article/details/125219623
```

```c++
#include <iostream>
using namespace std;
#include <ctime>;
int main() {
	//循环
	//1、输出1-10
	//2、输入一个数，输出比他小的所有数，直到0
	//	int n = 0;
	//	cin >> n;
	//	while (n > 0) {
	//		cout << --n << endl;
	//	}

	//3、猜数字 rand()可以产生随机数，那么给他取余%100 可以生成0-99的随机数
	//一直是42随机数，需要利用当前系统时间生成随机数，防止每次都一样 //srand(unsigned int)time(NULL);
	//这一行记住就行，C语言的随机数写法，这条指令的意思是利用系统时间来初始化系统随机数的种子值，使得每次运行由于时间不同产生而产生不同的随机数序列。需要引入#include <ctime>;
    srand(time(nullptr));
	int n = rand() % 100 + 1;
	cout << n << endl;
	int a = 0;
	while (n != a) {
		cout << "猜数字，请输入一个100以内的数字" << endl;
		cin >> a;
		if (a < n) {
			cout << "小了" << endl;
		} else if (a > n) {
			cout << "大了" << endl;
		} else {
			cout << "恭喜您" << endl;
		}
	}

	return 0;
}

```









#### 4.2.2 do...while循环语句

**作用：** 满足循环条件，执行循环语句

**语法：** `do{ 循环语句 } while(循环条件);`

**注意：**与while的区别在于==do...while会先执行一次循环语句==，再判断循环条件

![img](assets/clip_image002-1541671163478.png)



**示例：**

```C++
int main() {

	int num = 0;

	do
	{
		cout << num << endl;
		num++;

	} while (num < 10);
	
	
	system("pause");

	return 0;
}
```



> 总结：与while循环区别在于，do...while先执行一次循环语句，再判断循环条件













**练习案例：水仙花数**

**案例描述：**水仙花数是指一个 3 位数，它的每个位上的数字的 3次幂之和等于它本身

例如：1^3 + 5^3+ 3^3 =  153

请利用do...while语句，求出所有3位数中的水仙花数

思路：

首先，你得知道三位数是100—999.

如何循环出这些数？

如何对这些循环出来的数进行判断？

如何把结果返回出来？





~~~c++
	
		要求一个数的个位、十位、百位和千位的值，可以使用以下算法：

1. 声明一个变量 `num`，用于存储输入的数。
2. 分别计算个位、十位、百位和千位的值：
   - 个位的值可以通过对 `num` 取模 10 （`num % 10`）得到。
   - 十位的值可以通过对 `num` 除以 10 取模 10 （`(num / 10) % 10`）得到。
   - 百位的值可以通过对 `num` 除以 100 取模 10 （`(num / 100) % 10`）得到。
   - 千位的值可以通过对 `num` 除以 1000 取模 10 （`(num / 1000) % 10`）得到。
3. 得到个位、十位、百位和千位的值后，可以使用它们进行后续操作或输出。

以下是一个使用 C++ 编写的示例代码：

```cpp
#include <iostream>
using namespace std;

int main() {
    int num;
    cout << "请输入一个整数：";
    cin >> num;

    int ones = num % 10;          // 个位
    int tens = (num / 10) % 10;   // 十位
    int hundreds = (num / 100) % 10;   // 百位
    int thousands = (num / 1000) % 10;   // 千位

    cout << "个位：" << ones << endl;
    cout << "十位：" << tens << endl;
    cout << "百位：" << hundreds << endl;
    cout << "千位：" << thousands << endl;

    return 0;
}
~~~





```c++
#include <iostream>
using namespace std;

int main() {
	//水仙花数
	//1、将所有的三位数输出，100-999
	//2、获取每个位置上的数
	//3、判断他是不是水仙花数，个位数的3次方，加上十位数的3次方，加上百位数的数的3次方=它本身。
	//例如：153=1^3+5^3+3^3;
	//	int ge = 3;
	//	int shi = 5;
	//	int bai = 1;
	//	int he = ge * ge * ge + shi * shi * shi + bai * bai * bai;
	//	if (he == 153) {
	//		cout << "是水仙花";
	//	} else {
	//		cout << "不是水仙花";
	//	}
	int n = 100;
	do {
		int ones = n % 10;
		int tens = (n / 10) % 10;
		int hundreds = (n / 100) % 10;//如果确定数字是三位，那就不需要取模10了。这样避免他输入4位数
		int sum = ones * ones * ones + tens * tens * tens + hundreds * hundreds * hundreds;
		if (sum == n) {
			//cout << "是水仙花";
			cout << n << ",";
		} else {
			//cout << "不是水仙花";
		}
		n++;
	} while (n < 1000);

	return 0;
}
```









#### 4.2.3 for循环语句

**作用：** 满足循环条件，执行循环语句

**语法：**` for(起始表达式;条件表达式;末尾循环体) { 循环语句; }`



**示例：**

```C++
int main() {

	for (int i = 0; i < 10; i++)
	{
		cout << i << endl;
	}
	
	system("pause");

	return 0;
}
```







**详解：**

![1541673704101](assets/1541673704101.png)



> 注意：for循环中的表达式，要用分号进行分隔

> 总结：while , do...while, for都是开发中常用的循环语句，for循环结构比较清晰，比较常用



```
//三种方法输出奇数和偶数
	//for循环更加灵活，用于解决确定个数的循环。
	/*for (int i = 2; i <= 100; i=i+2) {
		cout << i<<endl;
	}*/
	/*int i = 1;
	while (i <= 100) {
		cout << i << endl;
		i = i + 2;
	}*/

	/*int i = 2;
	do{
		cout << i << endl;
		i = i + 2;
	} while (i <= 100);*/
```





**练习案例：敲桌子**

案例描述：从1开始数到数字100， 如果数字个位含有7，或者数字十位含有7，或者该数字是7的倍数，我们打印敲桌子，其余数字直接打印输出。

![timg](assets/timg.gif)



##### 练习案例：最大值，最小值，平均值，和









#### 4.2.4 嵌套循环

**作用：** 在循环体中再嵌套一层循环，解决一些实际问题

例如我们想在屏幕中打印如下图片，就需要利用嵌套循环

![1541676003486](assets/1541676003486.png)











**示例：**

```C++
int main() {

	//外层循环执行1次，内层循环执行1轮
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			cout << "*" << " ";
		}
		cout << endl;
	}

	system("pause");

	return 0;
}
```













**练习案例：**乘法口诀表

案例描述：利用嵌套循环，实现九九乘法表

![0006018857256120_b](assets/0006018857256120_b.jpg)





### 4.3 跳转语句

#### 4.3.1 break语句

**作用:** 用于跳出==选择结构==或者==循环结构==

break使用的时机：

* 出现在switch条件语句中，作用是终止case并跳出switch
* 出现在循环语句中，作用是跳出当前的循环语句
* 出现在嵌套循环中，跳出最近的内层循环语句



**示例1：**

```C++
int main() {
	//1、在switch 语句中使用break
	cout << "请选择您挑战副本的难度：" << endl;
	cout << "1、普通" << endl;
	cout << "2、中等" << endl;
	cout << "3、困难" << endl;

	int num = 0;

	cin >> num;

	switch (num)
	{
	case 1:
		cout << "您选择的是普通难度" << endl;
		break;
	case 2:
		cout << "您选择的是中等难度" << endl;
		break;
	case 3:
		cout << "您选择的是困难难度" << endl;
		break;
	}

	system("pause");

	return 0;
}
```



**示例2：**

```C++
int main() {
	//2、在循环语句中用break
	for (int i = 0; i < 10; i++)
	{
		if (i == 5)
		{
			break; //跳出循环语句
		}
		cout << i << endl;
	}

	system("pause");

	return 0;
}
```



**示例3：**

```C++
int main() {
	//在嵌套循环语句中使用break，退出内层循环
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			if (j == 5)
			{
				break;
			}
			cout << "*" << " ";
		}
		cout << endl;
	}
	
	system("pause");

	return 0;
}
```















#### 4.3.2 continue语句

**作用：**在==循环语句==中，跳过本次循环中余下尚未执行的语句，继续执行下一次循环

**示例：**

```C++
int main() {

	for (int i = 0; i < 100; i++)
	{
		if (i % 2 == 0)
		{
			continue;
		}
		cout << i << endl;
	}
	
	system("pause");

	return 0;
}
```



> 注意：continue并没有使整个循环终止，而break会跳出循环











#### 4.3.3 goto语句

**作用：**可以无条件跳转语句



**语法：** `goto 标记;`

**解释：**如果标记的名称存在，执行到goto语句时，会跳转到标记的位置



**示例：**

```C++
int main() {

	cout << "1" << endl;

	goto FLAG;

	cout << "2" << endl;
	cout << "3" << endl;
	cout << "4" << endl;

	FLAG:

	cout << "5" << endl;
	
	system("pause");

	return 0;
}
```



> 注意：在程序中不建议使用goto语句，以免造成程序流程混乱













## 5 数组

### 5.1 概述

所谓数组，就是一个集合，里面存放了相同类型的数据元素



**特点1：**数组中的每个==数据元素都是相同的数据类型==

**特点2：**数组是由==连续的内存==位置组成的













![1541748375356](assets/1541748375356.png)













### 5.2 一维数组

#### 5.2.1 一维数组定义方式

一维数组定义的三种方式：

1. ` 数据类型  数组名[ 数组长度 ]; `
2. `数据类型  数组名[ 数组长度 ] = { 值1，值2 ...};`
3. `数据类型  数组名[ ] = { 值1，值2 ...};`



示例

```C++
int main() {

	//定义方式1
	//数据类型 数组名[元素个数];
	int score[10];

	//利用下标赋值
	score[0] = 100;
	score[1] = 99;
	score[2] = 85;

	//利用下标输出
	cout << score[0] << endl;
	cout << score[1] << endl;
	cout << score[2] << endl;


	//第二种定义方式
	//数据类型 数组名[元素个数] =  {值1，值2 ，值3 ...};
	//如果{}内不足10个数据，剩余数据用0补全
	int score2[10] = { 100, 90,80,70,60,50,40,30,20,10 };
	
	//逐个输出
	//cout << score2[0] << endl;
	//cout << score2[1] << endl;

	//一个一个输出太麻烦，因此可以利用循环进行输出
	for (int i = 0; i < 10; i++)
	{
		cout << score2[i] << endl;
	}

	//定义方式3
	//数据类型 数组名[] =  {值1，值2 ，值3 ...};
	int score3[] = { 100,90,80,70,60,50,40,30,20,10 };

	for (int i = 0; i < 10; i++)
	{
		cout << score3[i] << endl;
	}

	system("pause");

	return 0;
}
```



> 总结1：数组名的命名规范与变量名命名规范一致，不要和变量重名

> 总结2：数组中下标是从0开始索引









#### 5.2.2 一维数组数组名

一维数组名称的**用途**：

1. 可以统计整个数组在内存中的长度
2. 可以获取数组在内存中的首地址





**示例：**

```C++
int main() {

	//数组名用途
	//1、可以获取整个数组占用内存空间大小
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

	cout << "整个数组所占内存空间为： " << sizeof(arr) << endl;
	cout << "每个元素所占内存空间为： " << sizeof(arr[0]) << endl;
	cout << "数组的元素个数为： " << sizeof(arr) / sizeof(arr[0]) << endl;

	//2、可以通过数组名获取到数组首地址
	cout << "数组首地址为： " << (int)arr << endl;
	cout << "数组中第一个元素地址为： " << (int)&arr[0] << endl;
	cout << "数组中第二个元素地址为： " << (int)&arr[1] << endl;

	//arr = 100; 错误，数组名是常量，因此不可以赋值


	system("pause");

	return 0;
}
```



> 注意：数组名是常量，不可以赋值

> 总结1：直接打印数组名，可以查看数组所占内存的首地址

>总结2：对数组名进行sizeof，可以获取整个数组占内存空间的大小









课程安排

1、AI代码

2、自动生成

3、一维数组练习



**练习案例1**：五只小猪称体重

**案例描述：**

在一个数组中记录了五只小猪的体重，如：int arr[5] = {300,350,200,400,250};

找出并打印最重的小猪体重。









**练习案例2：**数组元素逆置

**案例描述：**请声明一个5个元素的数组，并且将元素逆置.

(如原数组元素为：1,3,2,5,4;逆置后输出结果为:4,5,2,3,1);

















#### 5.2.3 冒泡排序

**作用：** 最常用的排序算法，对数组内元素进行排序

1. 比较相邻的元素。如果第一个比第二个大，就交换他们两个。
2. 对每一对相邻元素做同样的工作，执行完毕后，找到第一个最大值。
3. 重复以上的步骤，每次比较次数-1，直到不需要比较

![1541905327273](assets/1541905327273.png)

**示例：** 将数组 { 4,2,8,0,5,7,1,3,9 } 进行升序排序

```C++
int main() {

	int arr[9] = { 4,2,8,0,5,7,1,3,9 };

	for (int i = 0; i < 9 - 1; i++)
	{
		for (int j = 0; j < 9 - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}

	for (int i = 0; i < 9; i++)
	{
		cout << arr[i] << endl;
	}
    
	system("pause");

	return 0;
}
```



1. 冒泡排序（Bubble Sort）：这是一种简单的排序算法，它重复地遍历要排序的数列，比较相邻的两个元素，如果顺序错误就把它们交换过来。遍历数列的工作是重复地进行直到没有再需要交换，也就是说该数列已经排序完成。

   ```c++
   #include <iostream>
   using namespace std;
   
   void bubbleSort(int arr[], int n) {
      // 冒泡排序
      for (int i = 0; i < n - 1; i++) {
          for (int j = 0; j < n - i - 1; j++) {
              if (arr[j] > arr[j + 1]) {
                  swap(arr[j], arr[j + 1]);
              }
          }
      }
   }
   
   int main() {
      // 定义一个数组
      int arr[] = {64, 34, 25, 12, 22, 11, 90};
      // 获取数组大小
      int n = sizeof(arr) / sizeof(arr[0]);
   
      // 调用冒泡排序函数
      bubbleSort(arr, n);
   
      // 打印排序后的数组
      cout << "排序后的数组为: \n";
      for (int i = 0; i < n; i++) {
          cout << arr[i] << " ";
      }
      cout << endl;
   
      return 0;
   }
   ```

   

2. 选择排序（Selection Sort）：这种排序算法每一次从待排序的数据元素中选出最小（或最大）的一个元素，将其放到序列的起始位置，直到全部待排序的数据元素排完。

   ```
   #include <iostream>
   using namespace std;
   
   // 选择排序
   void selectionSort(int arr[], int n) {
      // 选择排序，从左到右，每次循环确定一个最小值
      for (int i = 0; i < n - 1; i++) {
          // 记录最小值索引
          int min_index = i;
          // 内层循环，从i+1开始，确定最小值
          for (int j = i + 1; j < n; j++) {
              // 如果发现有更小的值，更新最小值索引
              if (arr[j] < arr[min_index]) {
                  min_index = j;
              }
          }
          // 交换位置，把最小值放到正确位置
          swap(arr[min_index], arr[i]);
      }
   }
   
   int main() {
      // 定义一个数组
      int arr[] = {64, 34, 25, 12, 22, 11, 90};
      // 获取数组大小
      int n = sizeof(arr) / sizeof(arr[0]);
   
      // 调用选择排序
      selectionSort(arr, n);
   
      // 打印排序后的数组
      cout << "排序后的数组为: \n";
      for (int i = 0; i < n; i++) {
          cout << arr[i] << " ";
      }
      cout << endl;
   
      return 0;
   }
   ```

   

3. 插入排序（Insertion Sort）：这种排序算法是直接将待排序的元素一个一个地插入到已经排序好的序列中的适当位置。

   ```c++
   #include <iostream>
   using namespace std;
   
   // 插入排序函数
   void insertionSort(int arr[], int n) {
       // 从第二个元素开始，与前面的元素比较，如果大于前面元素，则交换位置
       // 从第二个元素开始，对每个元素插入到已经排好序的部分的适当位置上
       for (int i = 1; i < n; i++) {
       int key = arr[i]; // 当前元素
       int j = i - 1; // 从已经排好序的部分的最后一个元素开始向前查找
   
       // 向前查找，直到找到合适的位置
       while (j >= 0 && arr[j] > key) {
           arr[j + 1] = arr[j]; // 把前面的元素向后移动一个位置，空出插入位置
           j--; // 继续向前查找
       }
   
       // 找到合适的位置，插入当前元素
       arr[j + 1] = key;
       }
   }
   
   int main() {
      // 定义一个数组
      int arr[] = {64, 34, 25, 12, 22, 11, 90};
      // 计算数组大小
      int n = sizeof(arr) / sizeof(arr[0]);
   
      // 调用插入排序函数
      insertionSort(arr, n);
   
      // 打印排序后的数组
      cout << "排序后的数组为: \n";
      for (int i = 0; i < n; i++) {
          cout << arr[i] << " ";
      }
      cout << endl;
   
      return 0;
   }
   ```

   ![image-20231209194428018](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231209194428018.png)

4. 希尔排序（Shell Sort）：这种排序算法是插入排序的一种优化版本，它的基本思想是将待排序的数列分割成多个子序列，然后对每个子序列分别进行插入排序，最后再将子序列合并成一个有序序列。

5. 快速排序（Quick Sort）：这种排序算法采用分治的思想，将待排序的数列分成两个子序列，然后递归地排序这两个子序列，直到子序列的长度为1或0，然后再将两个子序列合并成一个有序序列。

6. 归并排序（Merge Sort）：这种排序算法是将两个或两个以上的有序表合并成一个新的有序表，基本思想是将两个有序表合并成一个有序表。

7. 堆排序（Heap Sort）：这种排序算法利用堆这种特殊的数据结构进行排序。堆是一种特殊的二叉树，它的每个结点的值都大于等于（或小于等于）其子结点的值。





### 5.3 二维数组

二维数组就是在一维数组上，多加一个维度。

![1541905559138](assets/1541905559138.png)

二维数组是指由一系列一维数组组成的数组，一维数组的元素也是一个数组。在 C 和 C++ 中，二维数组经常用于表示表格、矩阵等数据结构。

二维数组的声明和使用如下：

#### 5.3.1 二维数组定义方式

二维数组定义的四种方式：

1. ` 数据类型  数组名[ 行数 ][ 列数 ]; `
2. `数据类型  数组名[ 行数 ][ 列数 ] = { {数据1，数据2 } ，{数据3，数据4 } };`
3. `数据类型  数组名[ 行数 ][ 列数 ] = { 数据1，数据2，数据3，数据4};`
4. ` 数据类型  数组名[  ][ 列数 ] = { 数据1，数据2，数据3，数据4};`



> 建议：以上4种定义方式，利用==第二种更加直观，提高代码的可读性==![二维数组定义方式](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\二维数组定义方式.png)



示例：

```

    //声明一个5x3的二维数组
    int arr[5][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}, {13, 14, 15}};

    //访问二维数组的元素
    cout << "Element at (0, 0) is " << arr[0][0] << endl;
    cout << "Element at (0, 1) is " << arr[0][1] << endl;
    cout << "Element at (0, 2) is " << arr[0][2] << endl;
    cout << "Element at (1, 0) is " << arr[1][0] << endl;
    cout << "Element at (1, 1) is " << arr[1][1] << endl;
    cout << "Element at (1, 2) is " << arr[1][2] << endl;
    cout << "Element at (2, 0) is " << arr[2][0] << endl;
    cout << "Element at (2, 1) is " << arr[2][1] << endl;
    cout << "Element at (2, 2) is " << arr[2][2] << endl;
```





```C++
int main() {

	//方式1  
	//数组类型 数组名 [行数][列数]
	int arr[2][3];
	arr[0][0] = 1;
	arr[0][1] = 2;
	arr[0][2] = 3;
	arr[1][0] = 4;
	arr[1][1] = 5;
	arr[1][2] = 6;

	for (int i = 0; i < 2; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			cout << arr[i][j] << " ";
		}
		cout << endl;
	}

	//方式2 
	//数据类型 数组名[行数][列数] = { {数据1，数据2 } ，{数据3，数据4 } };
	int arr2[2][3] =
	{
		{1,2,3},
		{4,5,6}
	};

	//方式3
	//数据类型 数组名[行数][列数] = { 数据1，数据2 ,数据3，数据4  };
	int arr3[2][3] = { 1,2,3,4,5,6 }; 

	//方式4 
	//数据类型 数组名[][列数] = { 数据1，数据2 ,数据3，数据4  };
	int arr4[][3] = { 1,2,3,4,5,6 };
	
	system("pause");

	return 0;
}
```



> 总结：在定义二维数组时，如果初始化了数据，可以省略行数













#### 5.3.2 二维数组数组名



* 查看二维数组所占内存空间
* 获取二维数组首地址





**示例：**

```C++
int main() {

	//二维数组数组名
	int arr[2][3] =
	{
		{1,2,3},
		{4,5,6}
	};

	cout << "二维数组大小： " << sizeof(arr) << endl;
	cout << "二维数组一行大小： " << sizeof(arr[0]) << endl;
	cout << "二维数组元素大小： " << sizeof(arr[0][0]) << endl;

	cout << "二维数组行数： " << sizeof(arr) / sizeof(arr[0]) << endl;
	cout << "二维数组列数： " << sizeof(arr[0]) / sizeof(arr[0][0]) << endl;

	//地址
	cout << "二维数组首地址：" << arr << endl;
	cout << "二维数组第一行地址：" << arr[0] << endl;
	cout << "二维数组第二行地址：" << arr[1] << endl;

	cout << "二维数组第一个元素地址：" << &arr[0][0] << endl;
	cout << "二维数组第二个元素地址：" << &arr[0][1] << endl;

	system("pause");

	return 0;
}
```



> 总结1：二维数组名就是这个数组的首地址

> 总结2：对二维数组名进行sizeof时，可以获取整个二维数组占用的内存空间大小













#### **5.3.3 二维数组应用案例**

**考试成绩统计：**

案例描述：有三名同学（张三，李四，王五），在一次考试中的成绩分别如下表，**请分别输出三名同学的总成绩**

|      | 语文 | 数学 | 英语 |
| ---- | ---- | ---- | ---- |
| 张三 | 100  | 100  | 100  |
| 李四 | 90   | 50   | 100  |
| 王五 | 60   | 70   | 80   |



![二维数组案例-考试成绩统计](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\二维数组案例-考试成绩统计.png)

**参考答案：**

```C++
int main() {

	int scores[3][3] =
	{
		{100,100,100},
		{90,50,100},
		{60,70,80},
	};

	string names[3] = { "张三","李四","王五" };

	for (int i = 0; i < 3; i++)
	{
		int sum = 0;
		for (int j = 0; j < 3; j++)
		{
			sum += scores[i][j];
		}
		cout << names[i] << "同学总成绩为： " << sum << endl;
	}

	system("pause");

	return 0;
}
```













## 6 函数

### 6.1 概述

**作用：**将一段经常使用的代码封装起来，减少重复代码

一个较大的程序，一般分为若干个程序块，每个模块实现特定的功能。

### 6.2 函数的定义

函数的定义一般主要有5个步骤：

1、返回值类型 

2、函数名

3、参数表列

4、函数体语句 

5、return 表达式

![函数的定义](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\函数的定义.png)

**语法：** 

```C++
返回值类型 函数名 （参数列表）
{

       函数体语句

       return表达式

}
```



* 返回值类型 ：一个函数可以返回一个值。在函数定义中
* 函数名：给函数起个名称
* 参数列表：使用该函数时，传入的数据
* 函数体语句：花括号内的代码，函数内需要执行的语句
* return表达式： 和返回值类型挂钩，函数执行完后，返回相应的数据





**示例：**定义一个加法函数，实现两个数相加

```C++
//函数定义
int add(int num1, int num2)
{
	int sum = num1 + num2;
	return sum;
}
```











### 6.3 函数的调用

**功能：**使用定义好的函数

**语法：**` 函数名（参数）`

**示例：**

```C++
//函数定义
int add(int num1, int num2) //定义中的num1,num2称为形式参数，简称形参
{
	int sum = num1 + num2;
	return sum;
}

int main() {

	int a = 10;
	int b = 10;
	//调用add函数
	int sum = add(a, b);//调用时的a，b称为实际参数，简称实参
	cout << "sum = " << sum << endl;
	a = 100;
	b = 100;

	sum = add(a, b);
	cout << "sum = " << sum << endl;

	system("pause");

	return 0;
}
```

> 总结：函数定义里小括号内称为形参，函数调用时传入的参数称为实参









### 6.4 值传递

* 所谓值传递，就是函数调用时实参将数值传入给形参
* 值传递时，==如果形参发生，并不会影响实参==

![函数-值传递](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\函数-值传递.png)

**示例：**

```C++
void swap(int num1, int num2)
{
	cout << "交换前：" << endl;
	cout << "num1 = " << num1 << endl;
	cout << "num2 = " << num2 << endl;

	int temp = num1;
	num1 = num2;
	num2 = temp;

	cout << "交换后：" << endl;
	cout << "num1 = " << num1 << endl;
	cout << "num2 = " << num2 << endl;

	//return ; 当函数声明时候，不需要返回值，可以不写return
}

int main() {

	int a = 10;
	int b = 20;

	swap(a, b);

	cout << "mian中的 a = " << a << endl;
	cout << "mian中的 b = " << b << endl;

	system("pause");

	return 0;
}
```



> 总结： 值传递时，形参是修饰不了实参的









### **6.5 函数的常见样式**

常见的函数样式有4种

1. 无参无返
2. 有参无返
3. 无参有返
4. 有参有返

**示例：**

```C++
//函数常见样式
//1、 无参无返
void test01()
{
	//void a = 10; //无类型不可以创建变量,原因无法分配内存
	cout << "this is test01" << endl;
	//test01(); 函数调用
}

//2、 有参无返
void test02(int a)
{
	cout << "this is test02" << endl;
	cout << "a = " << a << endl;
}

//3、无参有返
int test03()
{
	cout << "this is test03 " << endl;
	return 10;
}

//4、有参有返
int test04(int a, int b)
{
	cout << "this is test04 " << endl;
	int sum = a + b;
	return sum;
}
```











### 6.6 函数的声明

**作用：** 告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。



*  函数的**声明可以多次**，但是函数的**定义只能有一次**



**示例：**

```C++
//声明可以多次，定义只能一次
//声明
int max(int a, int b);
int max(int a, int b);
//定义
int max(int a, int b)
{
	return a > b ? a : b;
}

int main() {

	int a = 100;
	int b = 200;

	cout << max(a, b) << endl;

	system("pause");

	return 0;
}
```











### 6.7 函数的分文件编写

**作用：**让代码结构更加清晰

函数分文件编写一般有4个步骤

1. 创建后缀名为.h的头文件  
2. 创建后缀名为.cpp的源文件
3. 在头文件中写函数的声明
4. 在源文件中写函数的定义

**示例：**

```C++
//swap.h文件
#include<iostream>
using namespace std;

//实现两个数字交换的函数声明
void swap(int a, int b);

```

```C++
//swap.cpp文件
#include "swap.h"

void swap(int a, int b)
{
	int temp = a;
	a = b;
	b = temp;

	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
}
```

```C++
//main函数文件
#include "swap.h"
int main() {

	int a = 100;
	int b = 200;
	swap(a, b);


	return 0;
}

```

### 6.8 地址传递和引用传递

在 C++ 中，地址传递是指将一个对象的地址（即内存地址）传递给另一个对象。这意味着，当一个对象被传递给函数时，实际上传递给函数的是该对象的地址。

在 C++ 中，可以通过将对象作为指针传递给函数来实现地址传递。例如：

```cpp
#include <iostream>

void printAddress(int* ptr) {
   std::cout << "Address of the integer: " << ptr << std::endl;
}

int main() {
   int x = 42;
   printAddress(&x); // 传递 x 的地址给函数
   return 0;
}
```

在这个例子中，`printAddress` 函数接受一个整数指针作为参数。在 `main` 函数中，我们通过 `&` 操作符获取 `x` 的地址，然后将其传递给 `printAddress` 函数。

需要注意的是，当使用地址传递时，需要确保传递给函数的指针是有效的，即指向一个存在的对象。如果传递了一个无效的指针，可能会导致程序崩溃或者产生未定义的行为。



```c++
#include <iostream>

void swapByAddress(int *a, int *b) {
	int temp = *a;
	*a = *b;
	*b = temp;
}

void swapByAddress1(int &a, int &b) {
	int temp = a;
	a = b;
	b = temp;
}

int main() {
	int a = 5, b = 10;
	std::cout << "Before swap: a = " << a << ", b = " << b << std::endl;
	swapByAddress(&a, &b);
	std::cout << "After swap: a = " << a << ", b = " << b << std::endl;

	swapByAddress1(a, b);
	std::cout << "After swap: a = " << a << ", b = " << b << std::endl;
	return 0;
}

```



在这个例子中，我们定义了一个名为 `swapByAddress` 的函数，它接受两个整数指针作为参数。在函数内部，我们使用临时变量 `temp` 存储第一个变量的值，然后将第二个变量的值赋给第一个变量，最后将第一个变量的值赋给第二个变量，从而实现了两个变量的交换。

在 `main` 函数中，我们定义了两个整数变量 `a` 和 `b`，并将它们的地址分别存储在 `&a` 和 `&b` 中。然后我们调用 `swapByAddress` 函数，将 `&a` 和 `&b` 作为参数传递给该函数，从而实现了 `a` 和 `b` 值的交换。最后，我们输出交换前后的值以验证交换的正确性。



使用地址传递和引用传递是实现变量交换的不同方法，它们之间存在一些差异。

1. 语法不同：使用地址传递时，需要将变量的地址存储在变量名前加上 `&` 符号，而使用引用传递时，只需将变量名存储在变量名前加上 `&` 符号即可。
2. 语义不同：使用地址传递时，传递的是变量的地址，而使用引用传递时，传递的是变量的引用。这意味着在使用地址传递时，需要确保传递的地址是有效的，即指向一个存在的对象，否则可能会导致程序崩溃或者产生未定义的行为。而使用引用传递时，不需要担心地址的有效性，因为引用本身就是一个内存地址，可以直接访问该地址所代表的对象。
3. 效率不同：使用引用传递的效率通常比使用地址传递更高，因为引用传递可以直接访问对象的内存地址，而不需要额外的内存分配和指针操作。

总的来说，使用引用传递是一种更简洁、更安全、效率更高的方法来实现变量交换。然而，在某些情况下，使用地址传递可能更为方便，例如当需要对数组或结构体中的多个元素进行交换时，使用地址传递可以避免重复计算内存地址。

在 C++ 中，如果将一个变量传递给另一个函数，并且该变量不需要被修改，那么我们可以使用引用传递，而不需要传递变量的值和地址。在这种情况下，我们只需要传递变量的引用，即 `&var`，而不需要传递 `var` 的值和地址。

如果我们需要在函数中修改传递给我们的变量，那么我们需要使用地址传递，即传递变量的值和地址，即 `&var`。

下面是一个示例：

```cpp
#include <iostream>

void printValueAndAddress(int &var) {
   std::cout << "Value: " << var << std::endl;
   std::cout << "Address: " << &var << std::endl;
}

int main() {
   int x = 10;
   printValueAndAddress(x);
   return 0;
}
```

在这个例子中，我们在 `printValueAndAddress` 函数中接受一个整数变量的引用作为参数。在 `main` 函数中，我们将整数变量 `x` 的引用传递给 `printValueAndAddress` 函数。在函数内部，我们可以直接访问传递给我们的变量的值和地址。

如果我们将 `x` 的值传递给函数，而不是它的引用，那么我们需要使用地址传递，即传递 `&x`。下面是一个使用地址传递的示例：

```cpp
#include <iostream>

void printValueAndAddress(int *var) {
   std::cout << "Value: " << *var << std::endl;
   std::cout << "Address: " << var << std::endl;
}

int main() {
   int x = 10;
   printValueAndAddress(&x);
   return 0;
}
```

在这个例子中，我们在 `printValueAndAddress` 函数中接受一个整数变量的指针作为参数。在 `main` 函数中，我们将整数变量 `x` 的地址（即 `&x`）传递给 `printValueAndAddress` 函数。在函数内部，我们可以直接访问传递给我们的变量的值和地址。



习题：

1、116页计算组合数

2、计算多边形面积

3、计算bool型返回值

4、冒泡排序

### 6.9 全局变量和局部变量，以及作用域

函数外部的是全局变量，内部的是局部变量或者内部变量

作用域：全局变量没有被函数括起来，他是从变量定义的位置开始到文件结束

内部变量，只在括号里，从定义位置开始到括号结束。

使用全局变量可以方便地实现全局数据共享，但同时也存在一些优缺点。

**优点**：

1. 全局变量可以在整个程序中访问，而不需要通过对象或指针进行访问，这使得全局变量的使用更加方便。
2. 全局变量可以被多个对象或函数共享，从而实现数据共享。
3. 全局变量在程序的整个生命周期内都存在，直到程序退出时才被销毁，这可以节省内存空间。

**缺点：**

1. 全局变量可以被任何对象或函数修改，这可能会导致数据不一致性和潜在的程序错误。
2. 全局变量在程序中的任何地方都可以被访问，这可能会导致命名冲突和代码可读性降低。
3. 全局变量不能被嵌套的代码块保护，这可能会导致意外的访问和修改。
4. 全局变量不能被delete删除，这可能会导致内存泄漏。

因此，在使用全局变量时，需要特别注意命名冲突、数据一致性和内存管理等问题，以确保程序的正确性和稳定性。

**局部变量是指在程序的某个范围内定义的变量，通常由函数或代码块定义。与全局变量相比，局部变量具有以下优点：**

1. 局部变量只在其定义的范围内访问，避免了全局变量的命名冲突和意外访问。
2. 局部变量在离开其作用域时被销毁，可以有效地释放内存空间。
3. 局部变量可以被嵌套的代码块保护，确保不会被意外访问或修改。

**但是，局部变量也有其缺点：**

1. 局部变量不能被其他对象或函数访问，这可能会导致数据不一致性和潜在的程序错误。
2. 局部变量不能被delete删除，这可能会导致内存泄漏。

因此，在使用局部变量时，需要特别注意命名冲突、数据一致性和内存管理等问题，以确保程序的正确性和稳定性。

举例：

1、main函数中定义的变量也是局部变量，这一点需要记住，与其他语言不同，我们也试过。

2、全局变量数组初始化值全部为0，而局部变量是随机的，那是因为局部变量主要受到栈空间大小的限制，大数组需要注意的是，通俗的说就是，局部变量的数组不能特别大，而全局变量随便。



练习：120页



### 7.0 递归算法

C++中的递归是一种函数调用自身的技术，它可以在函数中调用自身的同名函数，从而实现重复执行相同或类似操作的功能。递归函数在执行过程中，将自身的调用地址压入栈中，当函数执行完毕后，栈中的地址弹出，返回到调用它的函数继续执行。

递归函数通常具有以下特点：

1. 递归函数必须有一个明确的终止条件，当满足终止条件时，递归函数不再调用自身，而是开始返回。
2. 递归函数必须能够通过递归调用得到正确的结果，即在最后一次递归调用之后，函数能够得到一个明确的解。
3. 递归函数的递归深度不能过大，否则会导致栈溢出，导致程序崩溃。

下面是一个简单的递归函数示例，该函数实现了一个数字的阶乘：

```cpp
int factorial(int n) {
   if (n == 0) {
       return 1;
   } else {
       return n * factorial(n-1);
   }
}
```

在这个例子中，函数`factorial`接受一个整数参数`n`，如果`n`等于0，则返回1，否则返回`n`乘以`factorial(n-1)`的结果。这就是递归调用，它调用自身的同名函数，从而实现计算阶乘的功能。



还可以求，x的n次方。

利用递归求解x的n次方，可以通过递归调用自身来实现。具体来说，当n大于0时，我们可以将x的n次方分解为x的(n-1)次方的结果乘以x，即x^n = x^(n-1) * x。因此，我们可以定义一个递归函数，用于计算x的n次方，如下所示：

```cpp
int power(int x, int n) {
   if (n == 0) {
       return 1;
   } else {
       return x * power(x, n - 1);
   }
}
```

这个函数接受两个参数，分别为底数x和指数n。当n等于0时，函数返回1，因为任何数的0次方都是1。否则，函数返回x乘以底数x的(n-1)次方的结果，即x^n = x * x^(n-1)。

分析：

1. 递归调用自身，实现了计算x的n次方；
2. 递归终止条件为n等于0，此时返回1；
3. 递归过程中，函数将n不断减1，直到n等于0，从而实现计算x的n次方；
4. 递归调用过程中，函数栈会不断增加，直到达到最大递归深度，然后引发栈溢出错误，导致程序崩溃；
5. 为了避免栈溢出，需要设置递归调用最大深度，例如可以使用递归函数的迭代实现或者使用循环代替递归。



## 7 指针

### 7.1 指针的基本概念

指针（Pointer）是一种变量类型，其值是另一个变量的内存地址。在 C 和 C++ 中，指针用于访问和操作指向的变量。使用指针可以节省内存空间，提高程序的效率，但同时也增加了程序的复杂性和风险，因为指针可能会指向一个无效的内存地址，导致程序崩溃或产生不可预料的结果。



**指针的作用：** 可以通过指针间接访问内存



* 内存编号是从0开始记录的，一般用十六进制数字表示
* 可以利用指针变量保存地址

  

### 7.2 指针变量的定义和使用

指针变量定义语法： `数据类型 * 变量名；`



**示例：**

```C++
int main() {

	//1、指针的定义
	int a = 10; //定义整型变量a
	
	//指针定义语法： 数据类型 * 变量名 ;
	int * p;

	//指针变量赋值
	p = &a; //指针指向变量a的地址
	cout << &a << endl; //打印数据a的地址
	cout << p << endl;  //打印指针变量p

	//2、指针的使用
	//通过*操作指针变量指向的内存
	cout << "*p = " << *p << endl;

	system("pause");

	return 0;
}
```

```c++
#include <iostream>
using namespace std;

int main()
{
  //声明一个整数变量
  int x = 10;
  //声明一个指针变量，指向变量x
  int *ptr;
  //将指针指向变量x
  ptr = &x;
  //打印指针的值，即变量x的内存地址
  cout << "Address of x is " << ptr << endl;
  //使用指针访问变量x的值
  cout << "Value of x is " << *ptr << endl;
  //将指针指向变量x的下一个内存地址
  ptr++;
  //打印指针的值，即变量x的下一个内存地址
  cout << "Address of x+1 is " << ptr << endl;
  //使用指针访问变量x的下一个内存地址的值
  cout << "Value of x+1 is " << *ptr << endl;

  return 0;
}
```

指针变量和普通变量的区别

* 普通变量存放的是数据,指针变量存放的是地址
* 指针变量可以通过" * "操作符，操作指针变量指向的内存空间，这个过程称为解引用



> 总结1： 我们可以通过 & 符号 获取变量的地址

> 总结2：利用指针可以记录地址

> 总结3：对指针变量解引用，可以操作指针指向的内存











### 7.3 指针所占内存空间



提问：指针也是种数据类型，那么这种数据类型占用多少内存空间？



**示例：**

```C++
int main() {

	int a = 10;

	int * p;
	p = &a; //指针指向数据a的地址

	cout << *p << endl; //* 解引用
	cout << sizeof(p) << endl;
	cout << sizeof(char *) << endl;
	cout << sizeof(float *) << endl;
	cout << sizeof(double *) << endl;

	system("pause");

	return 0;
}
```



> 总结：所有指针类型在32位操作系统下是4个字节











### 7.4 空指针和野指针

**空指针**：指针变量指向内存中编号为0的空间

**用途：**初始化指针变量

**注意：**空指针指向的内存是不可以访问的



**示例1：空指针**

```C++
int main() {

	//指针变量p指向内存地址编号为0的空间
	int * p = NULL;

	//访问空指针报错 
	//内存编号0 ~255为系统占用内存，不允许用户访问
	cout << *p << endl;

	system("pause");

	return 0;
}
```















**野指针**：指针变量指向非法的内存空间

**示例2：野指针**

```C++
int main() {

	//指针变量p指向内存地址编号为0x1100的空间
	int * p = (int *)0x1100;

	//访问野指针报错 
	cout << *p << endl;

	system("pause");

	return 0;
}
```





> 总结：空指针和野指针都不是我们申请的空间，因此不要访问。









### 7.5 const修饰指针

const修饰指针有三种情况

1. const修饰指针   --- 常量指针
2. const修饰常量   --- 指针常量
3. const即修饰指针，又修饰常量




**示例：**


```c++
int main() {

	int a = 10;
	int b = 10;

	//const修饰的是指针，指针指向可以改，指针指向的值不可以更改
	const int * p1 = &a; 
	p1 = &b; //正确
	//*p1 = 100;  报错
	

	//const修饰的是常量，指针指向不可以改，指针指向的值可以更改
	int * const p2 = &a;
	//p2 = &b; //错误
	*p2 = 100; //正确

    //const既修饰指针又修饰常量
	const int * const p3 = &a;
	//p3 = &b; //错误
	//*p3 = 100; //错误

	system("pause");

	return 0;
}
```



> 技巧：看const右侧紧跟着的是指针还是常量, 是指针就是常量指针，是常量就是指针常量









### 7.6 指针和数组

**作用：**利用指针访问数组中元素

**示例：**

```C++
int main() {

	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };

	int * p = arr;  //指向数组的指针

	cout << "第一个元素： " << arr[0] << endl;
	cout << "指针访问第一个元素： " << *p << endl;

	for (int i = 0; i < 10; i++)
	{
		//利用指针遍历数组
		cout << *p << endl;
		p++;
	}

	system("pause");

	return 0;
}
```











### 7.7 指针和函数

**作用：**利用指针作函数参数，可以修改实参的值



**示例：**

```C++
//值传递
void swap1(int a ,int b)
{
	int temp = a;
	a = b; 
	b = temp;
}
//地址传递
void swap2(int * p1, int *p2)
{
	int temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}

int main() {

	int a = 10;
	int b = 20;
	swap1(a, b); // 值传递不会改变实参

	swap2(&a, &b); //地址传递会改变实参

	cout << "a = " << a << endl;

	cout << "b = " << b << endl;

	system("pause");

	return 0;
}
```



> 总结：如果不想修改实参，就用值传递，如果想修改实参，就用地址传递













### 7.8 指针、数组、函数

**案例描述：**封装一个函数，利用冒泡排序，实现对整型数组的升序排序

例如数组：int arr[10] = { 4,3,6,9,1,2,10,8,7,5 };



**示例：**

```c++
//冒泡排序函数
void bubbleSort(int * arr, int len)  //int * arr 也可以写为int arr[]
{
	for (int i = 0; i < len - 1; i++)
	{
		for (int j = 0; j < len - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}

//打印数组函数
void printArray(int arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << endl;
	}
}

int main() {

	int arr[10] = { 4,3,6,9,1,2,10,8,7,5 };
	int len = sizeof(arr) / sizeof(int);

	bubbleSort(arr, len);

	printArray(arr, len);

	system("pause");

	return 0;
}
```



> 总结：当数组名传入到函数作为参数时，被退化为指向首元素的指针









## 8 结构体

### 8.1 结构体基本概念

结构体属于用户==自定义的数据类型==，允许用户存储不同的数据类型



### 8.2 结构体定义和使用

**语法：**`struct 结构体名 { 结构体成员列表 }；`

通过结构体创建变量的方式有三种：

* struct 结构体名 变量名
* struct 结构体名 变量名 = { 成员1值 ， 成员2值...}
* 定义结构体时顺便创建变量

**示例：**

```C++
//结构体定义
struct student
{
	//成员列表
	string name;  //姓名
	int age;      //年龄
	int score;    //分数
}stu3; //结构体变量创建方式3 


int main() {

	//结构体变量创建方式1
	struct student stu1; //struct 关键字可以省略

	stu1.name = "张三";
	stu1.age = 18;
	stu1.score = 100;
	
	cout << "姓名：" << stu1.name << " 年龄：" << stu1.age  << " 分数：" << stu1.score << endl;

	//结构体变量创建方式2
	struct student stu2 = { "李四",19,60 };

	cout << "姓名：" << stu2.name << " 年龄：" << stu2.age  << " 分数：" << stu2.score << endl;


	stu3.name = "王五";
	stu3.age = 18;
	stu3.score = 80;
	

	cout << "姓名：" << stu3.name << " 年龄：" << stu3.age  << " 分数：" << stu3.score << endl;

	system("pause");

	return 0;
}
```



> 总结1：定义结构体时的关键字是struct，不可省略

> 总结2：创建结构体变量时，关键字struct可以省略

> 总结3：结构体变量利用操作符 ''.''  访问成员









### 8.3 结构体数组

**作用：**将自定义的结构体放入到数组中方便维护

**语法：**` struct  结构体名 数组名[元素个数] = {  {} , {} , ... {} }`

**示例：**

```C++
//结构体定义
struct student
{
	//成员列表
	string name;  //姓名
	int age;      //年龄
	int score;    //分数
}

int main() {
	
	//结构体数组
	struct student arr[3]=
	{
		{"张三",18,80 },
		{"李四",19,60 },
		{"王五",20,70 }
	};

	for (int i = 0; i < 3; i++)
	{
		cout << "姓名：" << arr[i].name << " 年龄：" << arr[i].age << " 分数：" << arr[i].score << endl;
	}

	system("pause");

	return 0;
}
```











### 8.4 结构体指针

**作用：**通过指针访问结构体中的成员



* 利用操作符 `-> `可以通过结构体指针访问结构体属性



**示例：**

```C++
//结构体定义
struct student
{
	//成员列表
	string name;  //姓名
	int age;      //年龄
	int score;    //分数
};


int main() {
	
	struct student stu = { "张三",18,100, };
	
	struct student * p = &stu;
	
	p->score = 80; //指针通过 -> 操作符可以访问成员

	cout << "姓名：" << p->name << " 年龄：" << p->age << " 分数：" << p->score << endl;
	
	system("pause");

	return 0;
}
```



> 总结：结构体指针可以通过 -> 操作符 来访问结构体中的成员













### 8.5 结构体嵌套结构体

**作用：** 结构体中的成员可以是另一个结构体

**例如：**每个老师辅导一个学员，一个老师的结构体中，记录一个学生的结构体

**示例：**

```C++
//学生结构体定义
struct student
{
	//成员列表
	string name;  //姓名
	int age;      //年龄
	int score;    //分数
};

//教师结构体定义
struct teacher
{
    //成员列表
	int id; //职工编号
	string name;  //教师姓名
	int age;   //教师年龄
	struct student stu; //子结构体 学生
};


int main() {

	struct teacher t1;
	t1.id = 10000;
	t1.name = "老王";
	t1.age = 40;

	t1.stu.name = "张三";
	t1.stu.age = 18;
	t1.stu.score = 100;

	cout << "教师 职工编号： " << t1.id << " 姓名： " << t1.name << " 年龄： " << t1.age << endl;
	
	cout << "辅导学员 姓名： " << t1.stu.name << " 年龄：" << t1.stu.age << " 考试分数： " << t1.stu.score << endl;

	system("pause");

	return 0;
}
```



**总结：**在结构体中可以定义另一个结构体作为成员，用来解决实际问题









### 8.6 结构体做函数参数 

**作用：**将结构体作为参数向函数中传递

传递方式有两种：

* 值传递
* 地址传递

**示例：**

```C++
//学生结构体定义
struct student
{
	//成员列表
	string name;  //姓名
	int age;      //年龄
	int score;    //分数
};

//值传递
void printStudent(student stu )
{
	stu.age = 28;
	cout << "子函数中 姓名：" << stu.name << " 年龄： " << stu.age  << " 分数：" << stu.score << endl;
}

//地址传递
void printStudent2(student *stu)
{
	stu->age = 28;
	cout << "子函数中 姓名：" << stu->name << " 年龄： " << stu->age  << " 分数：" << stu->score << endl;
}

int main() {

	student stu = { "张三",18,100};
	//值传递
	printStudent(stu);
	cout << "主函数中 姓名：" << stu.name << " 年龄： " << stu.age << " 分数：" << stu.score << endl;

	cout << endl;

	//地址传递
	printStudent2(&stu);
	cout << "主函数中 姓名：" << stu.name << " 年龄： " << stu.age  << " 分数：" << stu.score << endl;

	system("pause");

	return 0;
}
```

> 总结：如果不想修改主函数中的数据，用值传递，反之用地址传递







### 8.7 结构体中 const使用场景

**作用：**用const来防止误操作

**示例：**

```C++
//学生结构体定义
struct student
{
	//成员列表
	string name;  //姓名
	int age;      //年龄
	int score;    //分数
};

//const使用场景
void printStudent(const student *stu) //加const防止函数体中的误操作
{
	//stu->age = 100; //操作失败，因为加了const修饰
	cout << "姓名：" << stu->name << " 年龄：" << stu->age << " 分数：" << stu->score << endl;

}

int main() {

	student stu = { "张三",18,100 };

	printStudent(&stu);

	system("pause");

	return 0;
}
```









### 8.8 结构体案例

#### 8.8.1 案例1

**案例描述：**

学校正在做毕设项目，每名老师带领5个学生，总共有3名老师，需求如下

设计学生和老师的结构体，其中在老师的结构体中，有老师姓名和一个存放5名学生的数组作为成员

学生的成员有姓名、考试分数，创建数组存放3名老师，通过函数给每个老师及所带的学生赋值

最终打印出老师数据以及老师所带的学生数据。



**示例：**

```C++
struct Student
{
	string name;
	int score;
};
struct Teacher
{
	string name;
	Student sArray[5];
};

void allocateSpace(Teacher tArray[] , int len)
{
	string tName = "教师";
	string sName = "学生";
	string nameSeed = "ABCDE";
	for (int i = 0; i < len; i++)
	{
		tArray[i].name = tName + nameSeed[i];
		
		for (int j = 0; j < 5; j++)
		{
			tArray[i].sArray[j].name = sName + nameSeed[j];
			tArray[i].sArray[j].score = rand() % 61 + 40;
		}
	}
}

void printTeachers(Teacher tArray[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << tArray[i].name << endl;
		for (int j = 0; j < 5; j++)
		{
			cout << "\t姓名：" << tArray[i].sArray[j].name << " 分数：" << tArray[i].sArray[j].score << endl;
		}
	}
}

int main() {

	srand((unsigned int)time(NULL)); //随机数种子 头文件 #include <ctime>

	Teacher tArray[3]; //老师数组

	int len = sizeof(tArray) / sizeof(Teacher);

	allocateSpace(tArray, len); //创建数据

	printTeachers(tArray, len); //打印数据
	
	system("pause");

	return 0;
}
```









#### 8.8.2 案例2

**案例描述：**

设计一个英雄的结构体，包括成员姓名，年龄，性别;创建结构体数组，数组中存放5名英雄。

通过冒泡排序的算法，将数组中的英雄按照年龄进行升序排序，最终打印排序后的结果。



五名英雄信息如下：

```C++
		{"刘备",23,"男"},
		{"关羽",22,"男"},
		{"张飞",20,"男"},
		{"赵云",21,"男"},
		{"貂蝉",19,"女"},
```









**示例：**

```C++
//英雄结构体
struct hero
{
	string name;
	int age;
	string sex;
};
//冒泡排序
void bubbleSort(hero arr[] , int len)
{
	for (int i = 0; i < len - 1; i++)
	{
		for (int j = 0; j < len - 1 - i; j++)
		{
			if (arr[j].age > arr[j + 1].age)
			{
				hero temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}
//打印数组
void printHeros(hero arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << "姓名： " << arr[i].name << " 性别： " << arr[i].sex << " 年龄： " << arr[i].age << endl;
	}
}

int main() {

	struct hero arr[5] =
	{
		{"刘备",23,"男"},
		{"关羽",22,"男"},
		{"张飞",20,"男"},
		{"赵云",21,"男"},
		{"貂蝉",19,"女"},
	};

	int len = sizeof(arr) / sizeof(hero); //获取数组元素个数

	bubbleSort(arr, len); //排序

	printHeros(arr, len); //打印

	system("pause");

	return 0;
}
```



# 

## 9 文件操作

![image-20231229180546725](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231229180546725.png)



fstream的由来。



#### 写入文本文件：

文本文件一般以行的形式组织数据。

包含头文件：#include <fstream>

写文件类 ofstream(output file stream)

实现步骤

```c++
#include <iostream>
#include <fstream>

int main() {
   // 创建一个文件输出流对象，用于写入数据到文件中
   std::ofstream outfile("output.txt");

   // 检查文件输出流是否成功打开
   if (!outfile) {
       std::cerr << "无法打开文件！" << std::endl;
       return 1;
   }

   // 向文件中写入一些文本
   outfile << "这是文件中的文本。" << std::endl;

   // 关闭文件输出流
   outfile.close();

   std::cout << "文件已成功写入！" << std::endl;
   return 0;
}

```

在这个示例中，我们首先包含了`iostream`和`fstream`头文件。然后，我们创建了一个名为`outfile`的`std::ofstream`对象，用于写入数据到名为`output.txt`的文件中。接下来，我们使用`<<`运算符向文件中写入一些文本。最后，我们使用`close()`方法关闭文件输出流，并输出一条成功消息。

请注意，如果指定的文件不存在，`std::ofstream`将自动创建一个新文件。如果文件已经存在，它将清空文件内容。如果你希望保留现有文件中的内容，请使用`appending`参数将`std::ofstream`的构造函数：

 复制 插入 更多

```cpp,cpp
std::ofstream outfile("output.txt", std::ios::app);
```

这是不用构造函数，用类实例化来实现：

```c++

// 创建一个文件输出流对象，用于写入数据到文件中
	ofstream fout;
	fout.open("output.txt");

	// 检查文件输出流是否成功打开
	if (!fout) {
		std::cerr << "无法打开文件！" << std::endl;
		return 1;
	}

	// 向文件中写入一些文本
	fout << "这是文件中的文本111111。" << std::endl;
	fout<<"西施\n";
	

	// 关闭文件输出流
	fout.close();

	std::cout << "文件已成功写入！" << std::endl;
	return 0;
```

#### 指定文件目录

```
fout.open("D:\\output.txt");
```

![image-20231229185826010](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231229185826010.png)

#### 读取

```c++
// 创建一个文件输出流对象，用于写入数据到文件中
	//用于创建一个名为"out.txt"的输出文件，并将文件路径设置为"D:/c++垃圾/out.txt"。
	//
	//std::ofstream：这是C++标准库中的一个类，用于操作文件流。它表示一个输出文件流，用于将数据写入文件。
	//
	//of：是一个变量名，用于存储创建的输出文件流对象。
	//
	//("D:/c++垃圾/out.txt", ios::app)：这是一个构造函数，用于初始化输出文件流对象。它接受两个参数：
	//
	//"D:/c++垃圾/out.txt"：这是文件路径，表示要创建的输出文件的路径。
	//ios::app：这是一个枚举值，表示文件打开模式。ios::app表示追加模式，即在现有文件内容之后继续写入新内容，而不是覆盖现有内容。
	ofstream of("D:/c++垃圾1/out.txt", ios::app);

	if (!of) {
		cerr << "无法打开文件" << endl;
	}

	of << "aaaa1111" << endl;

	of.close();


	ifstream fasdfasdfad("D:/c++垃圾/out.txt");
	// 检查文件是否成功打开
	if (!fasdfasdfad) {
		cerr << "无法打开文件！" << endl;
		return 1;
	}
	// 定义一个字符缓冲区，用于存储文件内容
	string buffer;

	// 循环读取文件中的每一行
	while (getline(fasdfasdfad, buffer)) {
		// 输出读取到的每一行
		cout << buffer << endl;
	}
	fasdfasdfad.close();
```

[c++输入文件流ifstream用法详解-CSDN博客](https://blog.csdn.net/sinat_36219858/article/details/80369255)





### 重定向文件操作

这种方法的原理呢，就是把输入输出函数进行重定向，指到文件里面。

```c++
#include <iostream>
using namespace std;

int main() {


	//输出，写入文件 write read
//	freopen("D:/c++垃圾/out.txt", "w", stdout);
//	printf("\t别董大\t");
//	printf("\t\t高适*唐");
//	printf("千里黄云白日曛\n北风吹雁雪纷纷\n莫愁前路无知己\n天下谁人不识君");
//	cout << "11111" << endl;
//
//	fclose(stdout);


	//输入，读取文件
	freopen("D:/c++垃圾/out.txt", "r", stdin);
	//string以行读取，进行输入
//	string temp;
//	while (cin >> temp) {
//		//printf("%d\t", temp);
//		cout << temp << "\n";
//	}
	//字符串得用temp[2]来用，因为中文占用两个字符。
	char temp[2];
	int a = 0;
	while (cin >> temp) {
		//printf("%d\t", temp);
		cout << temp << " ";
		a++;
	}
	cout << a;
	fclose(stdin);



	return 0;
}

```



## 排序算法

#### 选择排序

```c++
#include <iostream>
using namespace std;

// 选择排序
void selectionSort(int arr[], int n) {
   // 选择排序，从左到右，每次循环确定一个最小值
   for (int i = 0; i < n - 1; i++) {
       // 记录最小值索引
       int min_index = i;
       // 内层循环，从i+1开始，确定最小值
       for (int j = i + 1; j < n; j++) {
           // 如果发现有更小的值，更新最小值索引
           if (arr[j] < arr[min_index]) {
               min_index = j;
           }
       }
       // 交换位置，把最小值放到正确位置
       swap(arr[min_index], arr[i]);
   }
}

int main() {
   // 定义一个数组
   int arr[] = {64, 34, 25, 12, 22, 11, 90};
   // 获取数组大小
   int n = sizeof(arr) / sizeof(arr[0]);

   // 调用选择排序
   selectionSort(arr, n);

   // 打印排序后的数组
   cout << "排序后的数组为: \n";
   for (int i = 0; i < n; i++) {
       cout << arr[i] << " ";
   }
   cout << endl;

   return 0;
}
```



#### 插入排序

```c++
#include <iostream>
using namespace std;

// 插入排序函数
void insertionSort(int arr[], int n) {
    // 从第二个元素开始，与前面的元素比较，如果大于前面元素，则交换位置
    // 从第二个元素开始，对每个元素插入到已经排好序的部分的适当位置上
    for (int i = 1; i < n; i++) {
    int key = arr[i]; // 当前元素
    int j = i - 1; // 从已经排好序的部分的最后一个元素开始向前查找

    // 向前查找，直到找到合适的位置
    while (j >= 0 && arr[j] > key) {
        arr[j + 1] = arr[j]; // 把前面的元素向后移动一个位置，空出插入位置
        j--; // 继续向前查找
    }

    // 找到合适的位置，插入当前元素
    arr[j + 1] = key;
    }
}

int main() {
   // 定义一个数组
   int arr[] = {64, 34, 25, 12, 22, 11, 90};
   // 计算数组大小
   int n = sizeof(arr) / sizeof(arr[0]);

   // 调用插入排序函数
   insertionSort(arr, n);

   // 打印排序后的数组
   cout << "排序后的数组为: \n";
   for (int i = 0; i < n; i++) {
       cout << arr[i] << " ";
   }
   cout << endl;

   return 0;
}
```

自我主意版：

```c++
//插入排序
#include <iostream>
using namespace std;

int main() {

	int arr[] = {8, 5, 9, 11, 22, 4, 3, 1};
	int n = sizeof(arr) / sizeof(arr[0]);

	for (int i = 1; i < n; i++) {
		int value = arr[i];
		//表示空着的数组位置
		int index = i;
		for (int j = i - 1; j >= 0; j--) {
			if (value < arr[j]) {
				arr[j + 1] = arr[j];
				index = j;
			} else {
				break;
			}
		}
		arr[index] = value;

	}
	for (int i = 0; i < n; i++) {
		cout << arr[i] << endl;
	}

	return 0;
}
```





#### 冒泡排序

```c++
#include <iostream>
using namespace std;

void bubbleSort(int arr[], int n) {
   // 冒泡排序
   for (int i = 0; i < n - 1; i++) {
       for (int j = 0; j < n - i - 1; j++) {
           if (arr[j] > arr[j + 1]) {
               swap(arr[j], arr[j + 1]);
           }
       }
   }
}

int main() {
   // 定义一个数组
   int arr[] = {64, 34, 25, 12, 22, 11, 90};
   // 获取数组大小
   int n = sizeof(arr) / sizeof(arr[0]);

   // 调用冒泡排序函数
   bubbleSort(arr, n);

   // 打印排序后的数组
   cout << "排序后的数组为: \n";
   for (int i = 0; i < n; i++) {
       cout << arr[i] << " ";
   }
   cout << endl;

   return 0;
}
```



#### 桶排序

```c++
#include <iostream>
using namespace std;

int main() {
	int arr[100] = {0};
	cout << "请输入数字，表示你要排序的数组的长度。" << endl;
	int n;
	cin >> n;
	cout << "请依次输入你要排序的数组里的值。一共" << n << "个" << endl;
	for (int i = 0; i < n; i++) {
		int a;
		cin >> a;
		arr[a]++;
	}
	//
	cout << "输出结果：" << endl;
	for (int i = 0; i < 100; i++) {
		if (arr[i] != 0) {
			cout << i << " ";
			arr[i]--;
			i--;
		}
	}




}
```




### 快速排序

### 堆排序







## STL

### 基本概念

stl,(standard template library) 标准模板库，是惠普实验室开发的一系列软件的统称。现在出现在c++中，但在被引入c++之前，该技术已经出现了很长一段时间了。

stl广义上可以分为三类，algorithm(算法）、container(容器)、和iterator(迭代器)，容器和算法通过迭代器可以进行无缝的连接。

几乎所有的代码都蔡邕了模板类，和模板函数的方式。相比于传统的由函数和类组成的库来说，提供了更好的代码重用机会。

stl被组织为下面13个头文件。:<algorithm>、<deque>、<functional>、<iterator>、<vector>、<list>、<map>、<memory>、<numeric>、<queue>、<set>、<stack>和<utility>.

STL(Standard Template Library,标准模板库)是C++标准库中的一部分,提供了许多高效的数据结构和算法,使得C++程序员可以更加方便地编写高效的代码。STL提供了容器类、算法类、模板元编程等丰富的功能,使得C++程序员可以更加高效地解决问题。

STL中最常用的容器类包括:

1. vector:动态数组容器,可以用来存储动态数组类型的数据。
2. array:定长数组容器,可以用来存储定长数组类型的数据。
3. list:双向列表容器,可以用来存储线性表类型的数据。
4. deque:双端队列容器,可以用来存储双端队列类型的数据。
5. stack:栈容器,可以用来存储栈类型的数据。
6. queue:队列容器,可以用来存储队列类型的数据。
7. priority_queue:优先队列容器,可以用来存储优先队列类型的数据。

STL中最常用的算法类包括:

1. for_each:对容器中的每个元素执行给定的操作。
2. transform:对容器中的每个元素执行给定的操作,并将结果存储在另一个容器中。
3. find_if:查找容器中满足给定条件的元素。
4. count_if:统计容器中满足给定条件的元素的数量。
5. sort:对容器中的元素进行排序。
6. reverse:对容器中的元素进行反转。

STL中最常用的模板元编程包括:

1. 模板特化:为特定类型提供特定的模板函数实现。
2. 模板参数推导:根据函数参数的类型自动推导出模板参数的类型。
3. 模板特化:为特定类型提供特定的模板函数实现。

STL的优点包括:

1. 封装性好:STL将容器类、算法类和模板元编程等模块封装在一起,使得程序员可以更加方便地使用它们。
2. 性能高:STL采用高效的算法和数据结构,使得其性能比普通C++代码更高。
3. 易用性好:STL提供了丰富的模板函数和类,使得程序员可以更加方便地编写代码。

STL的缺点包括:

1. 学习成本高:STL包含了许多高级的模板编程概念,使得程序员需要花费更多的时间来学习和掌握它。
2. 编译时间长:STL使用了模板元编程技术,使得编译时间比普通C++代码长。
3. 内存占用高:STL使用动态内存分配,可能会导致内存占用比普通C++代码更高。

### 使用stl的好处

1、stl是c++的一部分，因此不要额外安装什么，它被内建在你的编译器内。

2、stl的一个重要特点是数据结构和算法的分离，尽管这个是简单的概念，但是这个分离确实使得STL变得非常通用。

例如：在stl中的vector容器中，可以放入元素、基础数据类型变量、元素的地址：

stl的sort()函数可以用来操作vector,list等容器。

3、程序员可以不用思考stl具体的视线过程，只要能够熟练使用stl就OK了，这样他们就可以把精力放在程序开发的其他方面，比如优化业务逻辑。

4、stl具有高可重用性，高性能，高移植性，跨平台的优点。

​	高可重用性：stl中几乎所有的代码都蔡邕了模板类和模版函数的方式实现，这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。关于模版的认识，已经给大家介绍了。

​    高性能：如map可以高效的从十万条记录里查找出指定的记录，因为map是采用红黑树的变体实现的。（红黑树是平衡二叉树的一种）

​	高移植性：如在项目A上用STL编写的模块，可以直接移植到项目B上。

​	跨平台：如果windows的visual studio编写的代码可以在Mac OSd XCode上直接编译。



### 容器分类

序列式容器

​	每个元素都有固定的位置，取决于插入的时机和地点，和元素的值无关

​	vector、deque、list、stack、queue



关联式容器

​	元素的位置取决于特定的排序规则，与插入的顺序无关

 set 、multiset、map、multimap

详细区别：

| 数据结构                 | 描述                                                         | 实现头文件 |
| ------------------------ | ------------------------------------------------------------ | ---------- |
| 向量(vector)             | 连续存储的元素                                               | <vector>   |
| 列表（list)              | 由节点组成的双向链表，每个节点包含一个元素                   | <list>     |
| 双队列（deque)           | 连续存储的指向不同元素的指针所组成的数组                     | <deque>    |
| 集合<set>                | 由节点组成的红黑树，每个节点都包含着一个元素，节点之间以某种作用于元素对的谓词排列，没有两个不同的元素能够拥有相同的次序 | <set>      |
| 多重集合<multiset>       | 允许存在两个次序相等的元素的集合                             | <set>      |
| 栈(stack)                | 后进先出的值的排列                                           | <stack>    |
| 队列（queue)             | 先进先出的值的排列                                           | <queue>    |
| 优先队列(priority_queue) | 元素的次序是由作用于所存储的值对上的某种谓词决定的一种队列   | <queue>    |
| 映射（map)               | 有键值对醉成的集合，以某种谓词决定的一种队列                 | <map>      |
| 多重映射(multimap)       | 允许键值对有相等的次序的映射                                 | <map>      |

#### vector

##### 1、vector容器简介

​	vector是将元素置于一个动态数组中加以管理的容器

​	vector可以随机存取元素（支持索引值直接存取，用【】操作服或者at()方法。

​	vector尾部添加或者移除元素非常快，但是在中部或者头部插入元素或者移除元素比较费力

##### 2、vector对象的默认构造

	`vector` 是一个 C++ 容器类 `vector` 的对象,它用于存储动态数组类型的数据。`vector` 对象的默认构造是指在创建 `vector` 对象时,不提供任何参数,或者提供的参数都是默认值时,所创建的 `vector` 对象的构造方式。

在 C++ 中,`vector` 对象的默认构造如下:

1. 如果未提供任何参数,则创建一个空的 `vector` 对象,即不包含任何元素的动态数组。![image-20240102160620142](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20240102160620142.png)

2. 如果提供一个整数参数,则创建一个大小为该整数的空 `vector` 对象,其中每个元素默认值为 `T()`。其中,`T` 是 `vector` 所存储元素的类型,`T()` 是该类型的默认构造函数所创建的对象。

3. 如果提供一个迭代器参数,则创建一个新的 `vector` 对象,其中包含从该迭代器起始位置到该迭代器结束位置的所有元素,且不包含该迭代器指向的元素。

   ![image-20240102161125418](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20240102161125418.png)

下面是一个使用默认构造创建 `vector` 对象的示例代码:

```cpp
#include <iostream>
#include <vector>

using namespace std;

int main() {
   // 创建一个空的 vector 对象
   vector<int> v1;
   
   // 创建一个大小为 5 的空 vector 对象
   vector<int> v2(5);
   
   // 创建一个新的 vector 对象,其中包含从迭代器 begin 到 end 之间的所有元素
   vector<int> v3(begin, end);
   
   return 0;
}
```

在这个示例中,我们分别创建了三个 `vector` 对象:第一个对象 `v1` 是空的,第二个对象 `v2` 是大小为 5 的空 `vector` 对象,第三个对象 `v3` 是从迭代器 `begin` 到 `end` 之间的元素组成的 `vector` 对象。

##### 3、vector的赋值

	`vector` 赋值是指将一个数据容器中的元素复制到另一个数据容器中。在 C++ 中,可以使用 `assign` 函数或者直接用等号 `=` 来赋值。

以下是使用 `assign` 函数赋值的示例代码:

```cpp
#include <iostream>
#include <vector>

using namespace std;

int main() {
  // 创建一个 vector 对象 v1
  vector<int> v1 = {1, 2, 3, 4, 5};
  
  // 创建一个 vector 对象 v2
  vector<int> v2;
  
  // 使用 assign 函数将 v1 中的元素复制到 v2 中
  v2.assign(v1.begin(), v1.end());
  
  // 输出 v2 中的元素
  for (int i = 0; i < v2.size(); i++) {
     cout << v2[i] << " ";
  }
  cout << endl;
  
  return 0;
}
```

输出结果为:

```
1 2 3 4 5
```

以下是使用等号 `=` 赋值的示例代码:

```cpp
#include <iostream>
#include <vector>

using namespace std;

int main() {
  // 创建一个 vector 对象 v1
  vector<int> v1 = {1, 2, 3, 4, 5};
  
  // 创建一个 vector 对象 v2
  vector<int> v2;
  
  // 将 v1 中的元素复制到 v2 中
  v2 = v1;
  
  // 输出 v2 中的元素
  for (int i = 0; i < v2.size(); i++) {
     cout << v2[i] << " ";
  }
  cout << endl;
  
  return 0;
}
```

输出结果为:

```
1 2 3 4 5
```

这两种方法都可以用来将一个数据容器中的元素复制到另一个数据容器中。![image-20240102161214659](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20240102161214659.png) 

![image-20240102165235346](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20240102165235346.png)

##### 4、vector的大小

![image-20240102165638464](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20240102165638464.png)



![image-20240102172134321](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20240102172134321.png)

##### 5、vector元素的访问方式

​	下标取值，会有越界问题，如果访问超过vector下标的时候，可以导致程序异常。

Vector 是一个动态数组容器，可以用来存储任何类型的数据，包括整数、浮点数、对象等。Vector 提供了多种访问方式，以下是一些常用的访问方式：

1. 迭代器访问：使用迭代器可以遍历 Vector 中的所有元素，并对其进行访问。可以使用 `begin()` 和 `end()` 方法获取迭代器范围，然后使用 `*` 运算符进行访问。

```cpp
#include <iostream>
#include <vector>

int main() {
  std::vector<int> vec = {1, 2, 3, 4, 5};
  for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {
      std::cout << *it << std::endl;
  }
  return 0;
}
```

2. 直接访问：可以直接访问 Vector 中的元素，通过下标索引进行访问。可以使用 `[]` 运算符进行访问，例如 `vec[0]` 表示第一个元素，`vec[1]` 表示第二个元素等。

```cpp
#include <iostream>
#include <vector>

int main() {
  std::vector<int> vec = {1, 2, 3, 4, 5};
  std::cout << vec[0] << std::endl;
  std::cout << vec[1] << std::endl;
  return 0;
}
```

3. 范围 for 循环访问：使用范围 for 循环可以方便地对 Vector 中的元素进行访问。例如，可以使用 `for (const auto &elem : vec)` 进行访问。

```cpp
#include <iostream>
#include <vector>

int main() {
  std::vector<int> vec = {1, 2, 3, 4, 5};
  for (const auto &elem : vec) {
      std::cout << elem << std::endl;
  }
  return 0;
}
```

4. 访问函数：Vector 类提供了 `at` 和 `front` 函数用于访问特定的元素，以及 `back` 函数用于访问最后一个元素。

```cpp
#include <iostream>
#include <vector>

int main() {
  std::vector<int> vec = {1, 2, 3, 4, 5};
  std::cout << vec.at(0) << std::endl;
  std::cout << vec.front() << std::endl;
  std::cout << vec.back() << std::endl;
  return 0;
}
```

以上是 Vector 元素的访问方式，可以根据具体需求选择合适的访问方式。

## C++二级目录

| C++中级学习内容 |                                                              |                                                              |                            |            |            |          |          |      |              |
| --------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -------------------------- | ---------- | ---------- | -------- | -------- | ---- | ------------ |
| 数组            | 复习一维数组                                                 | 二维数组                                                     | 字符类型数组/字符串        |            |            |          |          |      |              |
| 函数            | 函数                                                         | 递归算法                                                     | 变量作用范围（全局、局部） |            |            |          |          |      |              |
| 文件和结构体    | 文件操作                                                     | 结构体                                                       |                            |            |            |          |          |      |              |
| 指针及其应用    | 指针变量                                                     | 指针与数组                                                   | 指针与字符串               | 指针与函数 | 结构体指针 |          |          |      |              |
| 算法            | 高精度                                                       | 简单排序                                                     | 简单查找                   | 枚举算法   | 递归       | 动态规划 | 分治算法 | 贪心 | 基础搜索算法 |
| STL             | 容器：1.序列式：vector、list、deque、stack。2.关联式：set、map | 算法：1.常用搜索算法：find（）、search（）；2.常用排序算法：sort（）、merge（）、reverse（）；3.常用比较算法：equal（） | 迭代器：iterator           |            |            |          |          |      |              |





## 时间复杂度和空间复杂度

什么是时间复杂度和空间复杂度：

第一组案例：

1、for(i=1;i<=n;i++)

执行次数是n+1，因为不管<=最后一次有没有往后增加，都要有一次比较，所以是**n+1**

2、for(i=1;j<=n;j++){

​	x++；

}

**执行x++的次数是n次。**外层依然是n+1次比较

3、for(i=1;i<=n;i++){

​	for(j=1;j<=n;j++)

}

执行次数是n*(n+1);  n是外层的次数，n+1是内层的判断次数

4、for(i=1;i<=n;i++){

​	for(j=1;j<=n;j++)

​		x++;

}

**x++的执行次数是n*n,也就是n的平方次。**

第一层for比较次数是n+1;

第二层是n*(n+1);



上难度，第二组，这个程序的执行次数是：

5、

```
for(i=1;i<=n;i++){
	for(j=1;j<=i;j++)
	{
		for(k=1;k<=j;k++){
			x++;
		}
	}
}
```

上面这段代码，需要从下往上去分析，最里面一组，x++执行次数是j次。

上一层就是i次，最上层是n次

所以等于n\*i\*j次 

因为i，j，k都是变化的，具体怎么求

1、∑符号表示求和，∑读音为sigma，英文意思为Sum，Summation,就是和。

![image-20231227112935158](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231227112935158.png)

![image-20231227113014240](E:\HZ\匠心精作C++从0到1入门编程-学习编程不再难资料\第1阶段C++ 匠心之作 从0到1入门资料\学习C++不再难资料\C++基础入门讲义\C++基础入门.assets\image-20231227113014240.png)















6、

```
for(i-1;i<=n;i=i*2){

	x++;
	s=0;

}
```







## 需求分析

关于企业开发流程和需求评审的实际案例。

通过讨论需求背景、功能模块、技术选型等方面。帮助员工明确需求并制定实施计划。员工通过需求评审会讨论了公众号管理系统的需求背景、功能模块和实施方案。他们确定了最核心的功能，并提出了一个开源项目作为参考。他们还讨论了需求的优先级和预计的工作时间。这个视频对于了解企业开发流程和需求评审的小技巧非常有帮助。

具体步骤：1、讲述需求背景

2、基于需求背景做功能模块的分解

3、做市场调研，看有没有做过的，现成的开发框架，比如github.哪些可用，哪些不可用，原因是什么

4、技术选型。

5、需求排期，开发核心功能，先跑通。

6、确认技术的关键难点，主要是针对没做过的，分析是否能实现。

7、设计具体功能的视线，设计库表，设计接口，关键的实现细节

8、工作拆解，和准备。



【时间线】
\- 00:00 - 介绍公众号管理系统的需求背景，包括通过程序自动回复用户消息的限制和多个公众号的管理需求。
\- 01:16 - 讨论如果负责这个项目，会考虑通过什么流程和方式来实现需求。
\- 03:30 - 总结了需求的功能模块，包括多个公众号的切换、菜单栏内容管理、配置自动回复的消息规则等。
\- 04:44 - 调研了市面上已有的公众号管理系统，确定了一个符合需求的开源项目作为参考。
\- 08:34 - 提出了实施方案，包括调研现有方案、拆解需求、排期工作等。
\- 12:08 - 建议先调研现有方案的代码，确定是否使用现有方案或自主实现。
\- 14:00 - 讨论了需求的优先级，确定了最核心的功能是多个公众号的切换、菜单栏设置和配置自动回复。
\- 16:41 - 提出了预计的工作时间，大约需要3-5天完成第一期需求的开发和测试。

内容由AI自动总结, 总结内容仅供参考~ [@Curtis6](https://space.bilibili.com/314158424) 触发了视频总结, 触发方式是 @有趣的程序员 总结一下







## 计算机行业学习顺序

1、计算机组成原理

​	视频—哈工大刘宏伟计算机组成原理

​	书籍—唐硕飞老师

2、计算机网络

3、数据库

4、操作系统

​	视频-哈工大李志军老师的操作系统

​	书籍-深入理解

5、数据结构与算法





## 分享人生经历

程序员的人生“经验”，或许是失败的经验，但是希望能给有共同经历的一些人，或者面临选择的后来人，一点点失败的参考。

15年毕业，西安某某大学的软件工程专业毕业，名字就不说了，怕给学校抹黑。

在学校的时候，尽管学的还是比较努力的，因为家是农村的嘛，接触电脑很少很少，上大学后才开始接触电脑，小时候去过网吧，但是基本上都是玩一些单机游戏，流星蝴蝶剑，侠盗飞车之类的。网游玩的很少，因为吧，没成年，不会注册账号......。曾经有一个网吧的大哥哥好心给了我一个问道的账号，导致我写在手上，不敢洗手。后来因为后面的几个字模糊了，硬是猜了半个通宵没猜出来。哈哈，还有更有意思的，后来就偷偷记住了爷爷的身份证...，玩网络游戏没人教我，根本不知道游戏里的数据能保存。每次去网吧都会重新用爷爷的身份证创建一个新的账号，以及新的角色，从头开始。以至于我玩了半年多，最多也不过带了个海龟...（玩过的应该能理解有多好笑啊）。

就这样，上大学竟然选了一个当时公认比较难的软件工程专业。因为我一个姑父，现在已经不是我姑父的人推荐给我，说这个行业未来会有很大的发展。强烈建议我选。好吧。那就选吧。上学的前段时间我接触互联网的机会真的很少，因为我手机是个诺基亚，身边的朋友都用上了智能手机的时候，我也就看看他们玩。不过那时候，上软件课也没那么早接触C语言。都是导师先教了我们开机，打字，以及各种office以后才开始接触C，那个时候我都是怕父母的钱白花，心里励志这个大学要好好努力怎么样怎么样。那个时候office我学的也是班里最好的，特别是用excel做一些系统的时候，成就感满满。特别值得一提的是，每次周末休息班里其他同学都睡懒觉，或者去网吧的时候，我每次多8点多出去坐车去西安国家图书馆，3层，去那里找一些计算机相关的书去看，一坐坐一天，下午回来吃一个学校周边7块钱的快餐，一荤两素，可以加饭，免费喝汤的那种，大学基本上去的次数最多的就是这家了，所以记忆犹新。直到后来开C语言课的时候，都是怀揣着崇拜的感觉去上课的，一听说是一门语言，我都害怕学不好，因为我英语就学的稀烂，全是死记硬背。说起英语我必须得提一嘴，也是由于小学五年根本没有接触过，上初中老师问英文字母都会背吧，全班都说会，没一个人说不会，老师就问不会的举手，我看没人举手，就也不敢举手，所以老师就说，那既然大家都会，那就不重复教了，直接开始上how are you? 而且上课还一直是英文，很少说中文，导致我从一开始就跟不上，压根不明白这个英语老师在说什么，导致成绩始终就不合格。留下了终生的遗憾算是。所以如果以后我有孩子一定会让他从小学习，哪怕学不好，起码要学会脸皮厚，有不会的就去问老师。学习和工作一样，你不说谁知道你不会，谁知道你嫌弃工资低？

好了，言归正传，说到C语言的时候，一开始就很崇敬，很害怕，特别是宿舍里当时有人还提前学过了C语言，能说一大堆我不理解的东西，那个时候我害怕极了......

然后就是正式C语言，第一节课就让我们输出hello word，讲了讲丹尼斯里奇，讲了一些c语言的起源之类的，下课我都还是蒙的，在一个黑框框里显示英文，输入的还是英文，这就让我本来英语不好的人压力山大。后续的课也是这样懵懵懂懂，跟其他专业的学生聊天的过程中听说我们学C语言也是一个个崇敬的不得了，说你们好牛哦，怎么样。我心里智能唉声叹气，没电脑，没手机的我根本都没有想到过在网上找一些视频资料去学，只是不断的去图书馆，这种很笨很笨的学习方法。所以我真的很羡慕现在的学生，如果我现在能跟之前的我说话，我一定会把现在的学习资源分享给他，现在的B站，现在的很多视频，教程，网上一大堆，一定不要吝啬在学习上花的钱。他们的环境真的太好了，还有很多良心的up主把学习经验，学习方法分享给他们，真的太棒了。唉。

不能光说学习，说一点大学的其他生活，社团，没错，我本身虽然很内向，但是为了锻炼自己还是加入了社团，而且是舞蹈社，当时入会考核问我为什么要加入，有什么特长，我二话不说翻了几个跟头，并且下了个一字马。把他们负责考评的看的一愣一愣的，当场就让我通过了。对，没错，我学过舞蹈，而且是高中学的古典舞。理由嘛，就别问了，实在不好意思说为啥学这个。当时就是热血上头。现在身上有一些毛病，绝对和当时的学舞脱不了关系。唉。

在社团的生活是除了上课和图书馆以外最喜欢去的地方了，排舞，练舞，还顺带参加了几次学校组织的迎新晚会，双旦晚会，才艺比赛之类的，获得过奖项。

丰富的课外生活和C语言学习的枯燥乏味就形成了鲜明的落差，人嘛，总是趋利避害的，心里自然就把学习放到了后面，以至于学完C语言之后的点评里，我也只是潦草的做了一个简单的九九乘法表。还是抄的。看着其他同学做的什么学校管理系统，虽然是黑窗口版本的，羡慕不已。第一次产生了无力感。

参加课外活动算什么放飞自我，不算，因为这次无力感，让我甚至产生了专业的不自信，不感兴趣，虽然不至于翘课，但是开始跟着班里的学生上网吧，起因是由于一款游戏。—英雄联盟。

不能不知道这个游戏啊，因为一瞬间风靡全国，宿舍，教室，几乎所有人都在讨论。让我不得不去接触这款游戏，加上那个时候校外网吧费用还是比较便宜的，尤其是通宵，十二点到早上6点，只要五块还是六块，不记得了。除了包夜还有早场，也是从6点到中午12点，也非常便宜。于是就每到这个时间，校外好几个网吧都是爆满，有一句话怎么说来着，网吧五连坐，一次没赢过。

哈哈，又菜又爱玩说的就是我，经常0-5被队友喷。大学几年里一直就没少去，到毕业，甚至到现在，段位也始终没超过白银三。

人生也并不是只有C，还有诗和远方。

一次偶然的机会接触了Html和Css,很快的就可以入手做一个自己的网站，真的很好玩，所见即所得，比c语言的黑窗口好玩太多了。于是我又爱上了学习，并且在我的强力要求下，我爸给我买了一台联想笔记本，大概4000多块钱。这样我就不用自习的时候去学校机房了，我是这样想的。实际上在这个笔记本刚拿到手里，下载，LOL启动，要不是校园网实在卡。拯救了我，我说不定也就会在召唤师峡谷度过我的大学时光了。因为实在太卡，只能用来学习了，当时已经有人买书了html和css的，还有一些21天学会什么什么的各种书。我借了过来看了看，跟着书上开始学，兴趣是最好的老师，果然没错，因为兴趣，自学了html,css，javascript,为了做出后台后来请教老师学习了比较容易上手的C#，这也成为了我日后成为一名.net程序员的原因。

借助着学习的知识，和另一个舍友组队，他样式表写的比我认真，我是那种喜欢把所有功能做出来，效果做出来的人，而他是那种会把一个东西做的比较好看的那种人。而且我经常会遇到一些盒子问题，加上ie的助力，会有很多盒子塌陷，样式表错乱的问题存在。他这方面比我强我得承认，而且人美术好。于是我们俩一起参加了西安的某个大学生电子创意大赛。得以入围。

我们做的项目是一个校园点餐系统，当时美团还处于刚起步，还没有多少人知道，而且美团当时主要是针对的团购，还不是外卖。我们学校学生要么是食堂，要么就是校门口买吃的，经常一个学生领着七八个人的饭从外面买回来喂给舍友吃。

于是我们就考虑学校食堂的压力，和外面小商贩的问题，自己设计并且制作了，前台，后台，数据库等都具备的校园点餐系统。

在答辩的时候，我们讲了我们的系统，下面的评委提出了很多的意见：比如付款，比如食品安全，比如配送问题，比如餐品凉了怎么办,因为确实，那个时候还没有美团外卖，没有饿了么，没有成功的先例，让他们看到事情的可行性，所以提出了很多质疑

针对付款问题，我们也做了衡量，因为那个时候在线支付还没有流行，支付宝有是有，淘宝买东西都还有很多的人存在信任问题，不怎么网购。甚至还考虑了线下支付线上支付兼容的形式，主要是当时主要的购买形式还是线下零钱交易。

针对学校担心的食品质量问题，我们也有很好的后台记录，因为哪个商家卖的，哪个学生买的，一目了然，不会出现责任不清的这种情况。起码比外面买有保障的多。

配送问题我们是分两部分，一是让商家配送，二是给我们学生提供勤工俭学的机会，由专人负责从商家去拿。

关于配送距离，餐品问题，我们设计的是校园团购，辐射范围就是校园周边，以及周边不过一条街的范围。都是走着去走着回就能到的地方。

现在回过头来看，美团外面和饿了么如何成功，肯定离不开线上支付的时代发展潮流。

我们那个时候要啥没啥，难怪过了入围赛，答辩的时候就给我们毙了。但是当时获奖的设计里，一个是互联网卖鞋的，依托于物流，一个是互联网卖水果，算是社区团购，人家答辩的理由是他爸有资源，可以批发到这些新鲜的水果，让在他们网站上就可以买到。

所以我们是败给了资本吗？姑且算是吧。不过这也不算完，后来还有人托人专门到宿舍找我，说他爸愿意出五万块，投资我们把这个项目做出来卖给他。谈的时间不多，我们由于走不出比赛失败的阴影，和失败带来的不自信，加上确实没有成功的先例，技术能力也比较有限，婉拒了这份合作。你们说这个遗憾吗？如果当时同意了会怎么样？五万对于我们俩个学生来说，很多很多了，但是五万真的能把产品做出来上线吗，不能，我们知道我们的技术能做到前后台，本地数据库能跑起来就不错了。那个时候我们哪知道还有云服务器这个玩意啊。更别提服务器应该如何管理这些事情。还是知识，知识，知识啊。



再后来就是毕业了，毕业论文和答辩因为有了参加比赛的经验，我直接就独立完成了网上售卡系统的制作，这个选题绝对是当时学校给的选题里最难的了，其他的学生管理系统，课程管理系统等等我都会，平常也做过类似的，已经不屑于去做了。这个网上售卡也就是出售一些点卡，充值卡之类的网站。实现了前端展示，后端编辑，修改，上架，前台就直接变化的效果，数据存储利用了连接sqlserver的能力。语言是C#，框架是.net framework吧应该。当时比较潮流的语言了。

不出意外的答辩肯定是过了，毕业证顺利拿下了。

先是校招，没赶上，回家了，去了趟临汾，主要是奔着喜欢去的，没有谈过什么恋爱，就因为之前的一个约定，去了人家所在的城市，甚至想过未来，没几天，因为没有过工作，身上也没什么钱，去一个山西面馆，做了网络运营，那个地方没有一家软件开发公司，为了生活找了个运营的工作，想着留下来一起在这个城市努力吧，没想太多。

这个工作负载维护网站，给数据库里别人注入的数据给删掉，我问他们公司的其他人为什么不直接写点判断，或者正则表达式把输入过滤一下，他们说没我想的这么简单，是有竞争对手不断的给他们网站注册垃圾数据，网站他们还是找人开发的，自己不会。我想替他们解决，可是还没来得及解决，我就被分手了。

当我兴高采烈的告诉他我找到工作的时候，在她这个城市，工资1200的时候，她就不愿意见我了，让我走吧，我问是因为我没钱吗？她问我那你有啥？我愣住了，那一刻，离开，成了必须。回到了西安，住回了宿舍，整日沉默寡言。



那段时间，舍友越来越少，有的回了家，有的去了外地，有的面试过了找到了工作，看着身边的舍友一个个离开，我知道自己也要活下去，活的更好，给别人看看，我不差。不知道自己要干什么行业，就写了web前端工程师。第一次去面试有面试，机试，因为是毕业生，所以面试不是很复杂，就问问哪个学校，学校的学习成绩，和参加社团的经验，好在我还有比赛的经验，所以基本只要是公司要毕业生，就能进入下一个环节。机试的环节让我做布局，网站卖的是伟哥，就是那个药的网站，我做了半个小时，做了上半部分和banner，面试官看了我写的代码，可能是太小白了吧，就让我回去等通知了，然后就没有信了，这是第一次面试。

然后是第二次，就成功了，工资1500，在西安比较大的一个医院里的设备科，负责上传一些医药器材，管管数据库，有人带，但是带的不怎么样，他说的我听不明白他就自己干自己的了。早上10点上班，下午4点下班。主要是医院驻场做开发，没有网，说是军队医院禁止联网，也没学到东西，用的oracle,除了看看电视，打开他们那个网站源码让自己看以外，也没什么了，我自己当然是看不明白了，也就记得路上吃的挺多......）干了能有两周？天天没有网，看代码，还没有人教你这个代码干嘛，你想查还查不了，实在干不下去，就跟老板说了句学不会，走了。有点失落，但是不多，主要是迷茫。不知道该干嘛。



这个时候有的同学说去北京参加活动，被360公司递出了橄榄枝，薪资入职就有10000，于是我心里就下定了决心，10000是吧，北京是吧，我来了。

欲知北京的发展如何，请期待下文，北漂8年。

