硬件工程师成长之路(6)

系列文章目录

1.元件基础
2.电路设计
3.PCB设计
4.元件焊接
5.板子调试
6.程序设计
7.算法学习
8.编写exe
9.检测标准
10.项目举例


文章目录

前言

一个普通专科生,拿什么拯救你的未来?
史上最详细嵌入式系统设计师修炼手册

送给大学毕业后找不到奋斗方向的你(每周不定时更新)

上海市职业能力考试院
中国计算机技术职业资格网
上海市社会化评审职称申报指南


一、STM32开发环境(工具)之Keil MDK

1、keil(MDK) 5官方下载教程

方法一、keil(MDK) 5官方下载教程

方法二、点击MDK-ARM5.34,即可下载

方法三、MDK5.29,5.30,5.31,5.32,5.33, 5.34和各种pack软件包镜像下载(2021-03-26)

在这里插入图片描述

2、Keil(MDK) 5 软件安装教程

Keil(MDK) 5 软件安装教程

3、(MDK5)安装STM32芯片包-pack文件安装方法

方法一、(MDK5)安装STM32芯片包-pack文件安装方法

方法二、点击,找到对应的芯片包下载即可
在这里插入图片描述

4、为 Keil 更换 Sublime Text Molokai 主题

为 Keil 更换 Sublime Text Molokai 主题

5、Keil MDK的一些推荐功能(编码格式、自动保存、代码提示、动态语法检查、多核编译)

Keil MDK的一些推荐功能(编码格式、自动保存、代码提示、动态语法检查、多核编译)

6、STM32CubeMX安装教程_Windows

方法一、STM32CubeMX安装教程_Windows

方法二、点击即可下载,非百度
在这里插入图片描述

7、STM32软件的烧写方式

STM32软件的烧写方式

关于ST-Link下载STM32程序的使用

8、JLink接口的SWD接法

JLink接口的SWD接法

9、STM32+cubeMX第一个工程,点亮LED

STM32+cubeMX第一个工程,点亮LED

10、STM32新手入门教程

STM32新手入门教程

11、STM32进阶学习教程

STM32进阶学习教程

12、Keil5的仿真调试

Keil5的仿真调试

13、STM32程序移植专题

STM32程序移植专题

14、STM32官方封装库下载方法

STM32官方封装库下载方法

15、第一次建项目之stm32cubemx 错误error: L6236E: No section matches selector - no section to be FIRST/LAST.

stm32cubemx 错误error: L6236E: No section matches selector - no section to be FIRST/LAST.

16、STM32 之一 HAL库、标准外设库、LL库(STM32 Embedded Software)

STM32 之一 HAL库、标准外设库、LL库(STM32 Embedded Software)


C语言在大学基本上都教,所以就不讲了,直接从C++开始。

二、VS2019安装

原文出处
1.首先需要去官网下载安装

2.跳转到官网后,如下图所示,点击社区模块的免费下载,之后应该会自动下载安装工具,如果没有自动下载,可以根据提示手动操作。
在这里插入图片描述
3.从浏览器的下载位置中找到下载好的安装工具,如下图所示,双击运行安装。
在这里插入图片描述
4.等待其下载必要的文件,如下图所示。

在这里插入图片描述
5.下载完成之后会自动安装,安装完毕后会出现如下图所示的界面。
在这里插入图片描述
6.因为我们现在只用来写C语言程序,因此在工作负载中只勾选“使用C++的桌面开发”这一项就可以了,如下图所示。
在这里插入图片描述
7.点击安装位置,设置Visual Studio IDE和下载缓存的位置,如果自己的电脑有多个盘符,不建议安装到C盘,我是安装到了E盘,如下图所示。(在E盘下新建文件夹VS2019)

在这里插入图片描述
8.点击右下角的安装,就开始下载VS2019了,它会边下载边安装,此过程视自己的网速情况,可能会花费较长时间。如下图所示
在这里插入图片描述
9.安装完成之后会提示重启计算机,保存好当前的工作,点击重启按钮即可,如下图所示。
在这里插入图片描述
10.重启后,再把下图所标拉到桌面,快捷方式就弄好了。
在这里插入图片描述
11.桌面快捷方式左下角有箭头怎么去除呢,请参照下文链接
Win10怎么去除桌面快捷方式图标左下角的小箭头
在这里插入图片描述

三、传说中的C++

原文出处

1.学了C语言和C++都能做什么呢?

C语言:操作系统底层、系统驱动、单片机、嵌入式方面 等等;
C++:网络游戏开发、音视频技术、Socket网络通信,另外,苹果/谷歌/微软 等大型软硬件公司的系统或者软件上面都支持C/C++语言的集成开发。

你常用的软件大多数都是C++写的,例如:Office软件:MS Office,WPS Office,OpenOffice/LibreOffice,你所用的Windows也用了大量的C++,你说你在用C#和SQL,那我告诉你你用的 VS IDE 核心部分是C++写的,你用的C#,C# 的.Net执行框架也是C++写的,你用的Sql数据库,是Sql Server吧?很不幸,Sql Server也是cpp写的、你上网页在用浏览器吗?很不幸,浏览器内核都是C++写的,界面大多数浏览器界面也是C++写的,你聊天用的 QQ、YY、Skype 等也是C++写的。这些都是你绝对有在用的,至于其他,还有很多,杀毒软件、PhotoShop、Maya,N多行业软件,几乎所有的端游 等等,都是C++写的。你问我C++能做什么实际的东西,我告诉你什么也做不了,你信吗?哈哈

C++ 的几个常见的发展方向:客户端,游戏,服务端,嵌入式,移动端(移动只要是跨平台的移动端用C++写通用部分,GUI可以用平台特性,也可以用C++的跨平台框架)
针对界面UI方面C++的选择也是很多的,其实C++还是擅长“内功”的方面。如果你想带个GUI界面的话,也有很多类似Qt之类的框架和界面库可以使用,网上有很多,而且很多都跨平台,还开源,不管是 Windows,还是Mac,还是Linux,还是移动端平台,通吃的。本身C/C++就是跨平台的。

另外,说点大家感兴趣的,什么远程控制软件,什么木马,什么外挂等等,可以说95%以上都是用C/C++来写的。

难道这么多的用途还不值得大家来好好学学C/C++吗?

另外,闻道有先后、术业有专攻,C++虽然功能很强大,几乎什么都能做,但有的地方也是不适合,不是不能做而是不适合。比如网页开发,C++也能做,但是还是建议使用Java-Web或者PHP之类的语言来做,毕竟他们就是为了Web开发而生的。

2.从C到C++

1.C语言是结构化和模块化的语言,面向过程。未完全实现解决软件设计危机的目标。
2.C++保留了C语言原有的所有优点,增加了面向对象的机制。

3.vs2019 开始自己的第一个C++程序

原文出处

一、新建项目

1.点击菜单栏–>文件–>新建–>项目,我们可以看到上方存在的语言,平台和项目类型的选项

(1)语言这里我们就选择C++,或者所有语言
在这里插入图片描述
(2)平台这里我们就选择windows,或者所有平台
在这里插入图片描述
(3)项目类型这里选择控制台,或者所有项目类型
在这里插入图片描述
博主这里以C++为例,选择空项目,我们来写一个简单的打印hello world的程序
在这里插入图片描述
2.改变默认路径,填写项目名称,可勾选将解决方案和项目放在同一目录中
在这里插入图片描述
3.可以看到并且可以看到项目结构:

引用
外部依赖项
头文件
源文件
资源文件
在这里插入图片描述
3.右键源文件,点击“添加–>新建项”,改名(我这里是main.cpp),点击添加
在这里插入图片描述
4.生成以下界面
在这里插入图片描述

二、代码演示

1.在main.cpp中输入代码(一定要自己动手输,脑子说会了,手还没说会呢)

#include <stdio.h>

int main()
{
	printf("Hello Word!");
	getchar();
	return 0;
}

每句解释
#include <stdio.h>
在使用标准函数库中的输入输出函数时,编译系统要求程序提供有关的信息(例如对这些输入输出函数的声明),#include<stdio.h>的作用就是用来提供这些信息的,stdio.h是C编译系统提供的一个文件名,stdio是“standard input & output”的缩写,即有关标准输入输出的信息。

在这里的编译预处理命令称为文件包含命令,其作用是在编译之前把程序需要使用的关于系统定义的函数printf()的一些信息文件stdio.h包含进来。以“.h ”作为后缀的文件称为头文件。

int main()
{
}

在C语言当中,一个程序,无论复杂或简单,总体上都是一个“函数”;这个函数就称为“main() 函数”,也就是“主函数”。比如有个“做菜”程序,那么“ 做菜 ”这个过程就是“主函数”。在主函数中,根据情况,你可能还需要调用“买菜,切菜,炒菜”等子函数。
1、int main()是C语言main函数的一种声明方式;

2、int表示函数的返回值类型,表示该主函数的返回值是一个baiint类型的值;

3、main表示主函数,是C语言约定的程序执行入口,其标准的定义格式为int main(int argc, char *argv[]);在int main()中,()中没有数值表示入参为空,等同于int main(void);

printf(“Hello Word!”);
在程序执行到这句话时打印一条信息(内容为Hello World!),
用来表示该程序已经正确执行到这里。
是大型程序最基本也是最常用的调试手段。

getchar();
当程序调用getchar时,程序就等着用户按键,用户输入的字符被存放在键盘缓冲区中,直到用户按回车为止(回车字符也放在缓冲区中)。;

return 0;
函数中写明return 0,表示返回,对应主函数的int,如果“int main”写成“void main”就不需要return 0,因为void无类型,不需要返回值

在这里插入图片描述
2.点击“本地Windows调试器”,运行结果
在这里插入图片描述
在这里插入图片描述
3.另一种写法


#include <iostream>
using namespace std;

int main()
{
    cout << "Hello World!"<<endl;

    return 0;
}

每句解释
#include 是个包含命令,就是把iostream.h这个文件里的内容复制到这个地方
ostream.h是input output stream的简写,意思为标准的输入输出流bai头文件。它包含:
(1)cin>>“要输入的内容”
(2)cout<<“要输出的内容”
这两个输入输出的方法需要#include<iostream.h>来声明头文件。
iostream.h与iostream是不同的。
#include<iostream.h>是在旧的标准C++中使用。在新标准中,用#include。iostream 的意思是输入输出流。#include是标准的C++头文件,任何符合标准的C++开发环境都有这个头文件。还要注意的是:在VS编程时要添加:
using namespace std;
其原因是:后缀为.h的头文件C++标准已经明确提出不支持了,早些的实现将标准库功能定义在全局空间里,声明在带.h后缀的头文件里,C++标准为了和C区别开,也为了正确使用命名空间,规定头文件不使用后缀.h。因此,当使用<iostream.h>时,相当于在c中调用库函数,使用的是全局命名空间,也就是早期的c++实现;当使用的时候,该头文件没有定义全局命名空间,必须使用namespace std;这样才能正确使用cout。

C++中的using namespace std的作用

endl 表示输出换换行符,并刷新缓冲区

\n 表示换行的转义字符

4.按下任意键,退出
在这里插入图片描述

4.简单案例:实现两个数相加,输出结果!

#include <iostream>
using namespace std;

int main()
{
        cout << "Hello World!" << endl;
        cout << "2 + 3 = " << 2 + 3 << endl;
        return 0;
}

每句解释
"2 + 3 = "这个个是在控制台上要显示的字符串;
2 + 3 这个是真正计算的,也可以直接写5;

在这里插入图片描述
在这里插入图片描述

5.简单案例:实现 1~100 所有数相加,输出结果!

#include <iostream>
using namespace std;

int main()
{

        int sum = 0;
        for (int i = 1; i <= 100; i++)
        {
                sum += i;
        }

        cout << "1+2+3+...+100 = " << sum << endl;

        return 0;
}

在这里插入图片描述
在这里插入图片描述

四、C语言学习笔记

C的面向过程和C++的面向对象

C的面向过程和C++的面向对象

1.C++的数据类型与常量

C++的数据类型与常量学习资料
整型呢,一般用来表示整数,没有小数点的,浮点型一般用来表示有多位小数点的数,字符型呢,一般表示一个一个的字符,比如字母 a,b,c 之类的。
在这里插入图片描述

类型呢,又可分为 有符号的 和 无符号的,有符号的可以表示负数,无符号的只能表示正数。但是无符号的最大正数会比有符号的最大正数大很多。
在这里插入图片描述

整型、浮点型、字符型使用重点:要兼顾扩展性与占用空间两方面的需求。小了不够用,容易溢出越界,大了呢既占用空间,又不利于传输(速度)。
在这里插入图片描述

常量示例代码:

#include <iostream>
using namespace std;

#define price 30

int main()
{
	cout << "张三要去买苹果了。。。。。。。\r\n";
	int jin = 5;
	cout << "张三要买" << jin << "斤苹果,苹果的价格是:" << price << "元\\斤\r\n";
	cout << "张三总共要付:" << jin * price << "元。" << endl;

	return 0;
}

在这里插入图片描述

在这里插入图片描述

本节要求,自己编写代码实现输出单引号和双引号!
在这里插入图片描述

2.C++中的变量

C++中的变量

1、什么是变量?

变量,顾名思义,就是在程序的运行过程中值是可以改变的。
一个变量包括变量的类型、名字和变量的值。

2、变量名字的命名规则:

C++规定变量的名字只能由数字、字母、下划线这么三种字符组成。而且第一个字符不能是数字,必须是字母或者下划线。
举几个例子:
Sum, add, total, helloWorld, user1, _Pass, my_num_1 等等,但是 3gx 这种是错误的,不能是数字开头。
注意:大小写是区分的,Sum 和 sum 以及 SUM、suM 都认为是不同的变量。

3、命名建议:

①、最好以变量的用途来命名:
例如:
求和:sum
学生:student
苹果:apple

另外,大家也可以百度一下 “匈牙利命名法”,在变量前面加一个字母来表示变量的类型:
iSum,cSex 等等,i 表示 整型的 int,c 表示字符型的 char 等等。

这样程序更易于维护,如果代码量比较大的话时间久了就容易忘了,如果变量的名字起的让人一看就知道是干嘛用的,这不是能节省很多时间么。而且以后来说可能代码不是你一个人看,还有别的人看呢!

②、最好不要用中文来命名,如果你英文不是很好的话,用拼音也比用中文专业。

③、命名长度:C++中没有强制规定命名标识符的长度,但各个具体的编译器厂商一般都有限制。有的不能超过32个字符等等。反正也没必要那么长,尽量能标识该变量的用途就可以了。

4、const常量与宏定义区别

const常量与宏定义区别

C/C++中宏定义和常变量的区别

const型变量和#define宏定义的区别

(1)编译器处理方式不同

define宏是在预处理阶段展开。

const常量是编译运行阶段使用。

(2) 类型和安全检查不同

define宏没有类型,不做任何类型检查,仅仅是展开。

const常量有具体的类型,在编译阶段会执行类型检查。

(3) 存储方式不同

define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。

const常量会在内存中分配(可以是堆中也可以是栈中)。

(4)const 可以节省空间,避免不必要的内存分配。

const定义的常量在程序运行过程中只有一份拷贝,而 #define定义的常量在内存中有若干个拷贝。

(5) 提高了效率。

编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。

使用注意

根据以上分析,如果宏定义仅仅是使用形如:

#define PI 3.14159

这样的方式来声明常量,那么不如使用:

const float PI = 3.14159;
更能避免错误。

还有一点顺便要提到的,既然宏定义是直接做替换,若是没有注意到这一特性,就容易出现偏离编程意愿的结果,来看一个例子:

#define Square(x) x*x

这里试图声明一个宏定义Square来计算某数的平方,可能会出现这样的情况:

int x = 1, y = 2;

int result = Square(x+y);

结果应该为3的次方也就是9,那么实际上结果是:

x+y*x+y = 5

故而结论是,使用小括号 括起来就行了,像下面这样

#define Square(x) ((x)*(x)) //为防止类似冲突,将外面也整个括起来

3、C++中的运算符

C++中的运算符

基本的算术运算符
+:加法运算符或正值运算符:2+3,+3;
-:减法运算符或负值运算符:5-2,-3;
*:乘法运算符:3 * 5;
/:除法运算符:5/3;这个算出来等于1,求商;
%:求余运算符:5%3,要求两侧均为整数。这个算出来等于2,求余;

知识点一:
5/3 大家知道结果是多少吗?猜一猜,这里不说5/3了,简单点,就说5/2,很多学员肯定马上就回答了:2.5,到底结果是不是 2.5 呢,我们到VS2013中来测试一下。详情见视频语音讲解。
int x = 5, y = 2;
cout << "5 / 2 = " << x / y << endl;

所以,这里面的 / 是求商的意思。5除以2,商=2,余数是1,这个大家小学的时候应该学过。同理,5/3=1,商是1,余数2;
4/2=2,商是2,余数是0;那我要问了,3/5呢?3/5=0,余数是3;

% 就是求余的功能,我们不妨来试一下

那么有的会问了,我想正确的得到 5除以2等于2.5,怎么办呢?别着急,不是还有浮点类型的数嘛!

float x = 5.0, y = 2.0;
cout << "x / y = " << x / y << endl;

再问大家一个问题:
float x = 5.0;
int y = 2;
cout << "x / y = " << x / y << endl;
这个结果是什么呢?大家可以自己动手试试!答案是:2.5

知识点二:
在这里插入图片描述
知识点三:
在这里插入图片描述
不管是 a++ 还是 ++a执行完之后,a变量自己的值都进行了+1,但是针对整个 a++或者++a的值就不一样了。a++执行之后,这个表达式的值还是a+1之前的值,++a执行之后,这个表达式的值是a+1之后的值。

4、赋值运算符和赋值表达式

赋值运算符和赋值表达式

1、赋值运算符:
总结一个原则:多的给少的会丢失,少的给多的没事儿。
例如:int x = 3.5; 赋值完了之后 3.5 这个浮点类型的值肯定丢失小数点部分,变成了整形的3;
float y = 2; 赋值完了之后 2 的值不会有任何丢失,不过变成了浮点类型的数:2.0;

另外,不仅仅有小数点部分的丢失,取值范围有可能越界,例如:
short 能表示的范围是:-32768~+32767,最大值也才3万多,那我要是这样赋值呢?
short q = 50000; 大家可以把这句话放到 vs2013 中实践一下,发现复制完成后,q的值变负数了,说白了也就是越界了。为什么会变成负数呢,这里涉及到补码的东西,以后会给大家讲解。
一个水桶容量就2升,你非要装4升,那桶还不撑坏了呀?

另外,把一个负数赋值给一个无符号的数也会发生问题的,例如:
unsigned int x = -2; 这样的问题也不应该发生。

总结:赋值运算的时候要注意的两点:精度的丢失,取值范围的越界。

2、复合赋值运算符:
上节课给大家讲解了:
int x = 0;
x = x + 1; //可以替换为 x++; 既简单又方便
那我要问:x = x + 8 呢?有什么简单的写法吗?单纯 x++ 或者 ++x 都不行啊。
这里给大家一种新的简便写法:x += 8; 就可以啦。同理,其他的还有很多,例如:
int x = 10;
x -= 2; //x的值为8

下面我把支持这类操作的运算符都给大家列出来:
+=, -=, *=, /=, %=, <<=, >>=, &=, ^=, |=

3、逗号表达式:
逗号表达式在以后的编程中也会经常遇到,其规则是:所有以逗号间隔的表达式都进行计算,各个表达式的计算顺序按照从左往右。整个表达式的值是最后一个逗号表达式的值。 例如:
int x = 0;
int y = 0;
x +=2, y+= 3; //此时X=2;Y=3;
int q = ((x++), (++y)); //此时只输出++y;但是X++也计算了。
我要问问大家了,q的值是多少呢?

4、小作业:
①、
int x = 2;
x = 3 5, x4;
经过计算之后,变量 x 的值是多少呢?

在这里插入图片描述

②、自学运算符的优先级:
在这里插入图片描述

5、C++的语句与输入输出

C++的语句与输入输出

1、语句:

① 一般语句以半角的分号 ; 结尾。
② 复合语句:把多条语句合成在一起,就是复合语句。那么如何来合成多条语句呢?可以用一对大括号 {} 来包装。例如:
{
int x = 2;
++x;
}

2、输入与输出:

咱们之前的课程中给大家进行了相关变量值的输出,使用的是 cout,那么在C++中进行打印输出一般是用输入与输出流库中的 cin 和 cout 来实现的。
cin 和 cout 的定义是在 iostream 中,命名空间为 std,所以如果我们的程序中要使用 cin 和 cout 就必须要加上以下两条语句:
#include
using namespace std;

在进行输入和输出的时候,我们经常使用 << 和 >> 符号。例如:cout << “ x+y = ” << 5 << endl;
<< 是流插入运算符;>> 是流提取运算符;
所以,cout << “ x+y = ” << 5 << endl; 这句话的意思是将字符串 "x+y = " 先传递给 cout,即:流插入,之后再把 常量 5 插入到 cout,之后 endl 结束。大家可以把 << 和 >> 看成是流的方向,看往哪个方向流,这样能更好理解一些。
如果你要输出,那么肯定是要把字符串或者变量什么的传递给 cout 所以箭头要指向 cout,如果你要进行输入,那么就要从 cin 提取内容赋值给变量:例如:
int x = 0;
cin >> x;
从标准设备输入一个整形的值给变量 x,这个标准输入设备一般指键盘。执行到这句 cin >> x; 的时候程序会卡住,等待用户的输入,输入完成后继续往下走。cin 和 cout 的 >> 和 << 会智能的判断参数的类型,不管是整形还是字符型或者是字符串型,他都可以智能识别并进行输入与输出操作。

另外,注意,不管是 cin 也好,cout 也好,<< 和 >> 一次性只能输出或者输入一个,例如以下的写法是错误的:
cout << a, b, c;
cin >> a, b, c;

cin 的分隔符一般用回车(Enter),下面演示给大家看!

备注:一般 C++ 中的 cin 和 cout 是适用于我们当前使用的控制台类型的工程,这样才能看到输入与输出的结果。以后大家接触到带界面的程序了,cin 和 cout 也就用不上了。
在这里插入图片描述

3、兼容C语言的输入与输出:

在这里插入图片描述
举例:setprecision是一个计算机函数,功能是控制输出流显示浮点数的有效数字个数 ,如果和fixed合用的话,可以控制小数点后面有几位。
//cout<<fixed<<setprecision(2)<<123.456<<endl;/如果在这个位置就加上fixed的话,后面的输出全部都按照fixed处理/
别忘了添加它的头文件:#include “iomanip”
在这里插入图片描述

6、if 语句实现逻辑运算与冒号表达式

if 语句实现逻辑运算与冒号表达式

1、逻辑运算符的运算

①、&& 逻辑与 相当于其他语言中的 AND
②、|| 逻辑或 相当于其他语言中的 OR
③、! 逻辑非 相当于其他语言中的 NOT
在这里插入图片描述
在这里插入图片描述

2、冒号表达式:

冒号表达式又叫条件表达式,是唯一一个三目运算符,就是需要三个参数的运算符。先举个 if 语句的例子,求出 变量 x 和 y 的最大值,并打印出来:
int max = 0;
int x = 0, y = 0;
cin >> x >> y;
if (x > y)
{
max = x;
}
else
{
max = y;
}

cout << “最大数是:” << max << endl;

求 x 和 y 中的较大数,我用了一个 if-else,那么有没有简单一点的方法呢?答案是当然有,就是要讲解的这个冒号表达式:
max = x > y ? x : y;
就这么一句话,就可以将 x 和 y 中较大的数的值赋值给 max 变量,怎么样,方便吧?
在这里插入图片描述

3、冒号表达式的规则:

表达式1 ? 表达式2 : 表达式3
若表达式1为真,则返回表达式2的值,若表达式1为假,则返回表达式3的值。如此而已!

7、用switch语句实现多分支选择结构

用switch语句实现多分支选择结构

1、为什么会有switch语句?

if-else语句只有两个分支,如果条件比较少的情况下可以使用if-else,可如果条件比较多,就会出现很多个 if-else if-else if-else-if 等。
这样会导致代码冗余,不易于阅读,同时如果条件很多的情况下效率也不高,因为系统要计算每一个if中的表达式去进行比较。所以在条件比较多的情况下建议使用switch语句来解决。

2、switch语句的格式:

switch(表达式)
{
case 常量表达式1:语句1
break;
case 常量表达式2:语句2
break;
case 常量表达式3:语句3
break;

case 常量表达式n:语句n
break;
default:语句n+1;
break;
}
格式说明:
①、switch后面括号内的表达式必须是数值类型(包括字符类型)的数据,不能使用字符串;
②、如果switch表达式的值与某一个case子句中的常量表达式的值匹配时,就执行此case子句中的内嵌语句,如果所有的case常量表达式都不匹配,那么就执行default子句的内嵌语句;
③、每个case标签的常量表达式的值必须不能相同,否则会出现错误的情况,而且以当前vs2013的编译器来说编译也会不通过的;
④、每个case语句,包括default语句的顺序不影响结果,可以任意放置,比如把default放在上面也是一样的;
⑤、switch语句也是本着从上往下的顺序执行,可这个从上往下是以第一个case匹配到的常量表达式开始,往下执行。如果执行完一个case语句之后不想继续执行其他case语句可以使用break关键字跳出switch结构即可;

3、实例题目:

键盘输入0-6之间的整数,分别打印出代表每周的星期日、星期一、…、星期六,请使用switch语句来实现。同时,如果是周六和周日的话要打印出“今天是周末”的字符串。
在这里插入图片描述

8、while 循环语句

while 循环语句

1、while 循环语句的格式:

while(表达式A)
语句B

其执行流程是:当表达式A的值为真(非0)时,执行下面的语句B(即循环体),否则不执行语句B。也就是先判断表达式A,后执行语句B。

备注:
①、语句B不限于一条语句,可以用 {} 括起来的多条语句,否则while语句循环体的范围只到while后面的第一个分号处结束;
②、在循环体的语句B中应有使循环趋于结束的语句,一定避免死循环现象的发生,这是个低级错误。
在这里插入图片描述

2、do-while 循环语句的格式:

do
语句B
while(表达式A)

跟 while 循环的格式也是大同小异。但是跟 while 有个很重要的区别:先执行循环体语句B,之后再判断表达式A
所以,do-while 循环至少会执行一次循环体语句B,而 while 循环则可能一次都不执行循环体语句B,因为条件不满足。大家明白了吗?
在这里插入图片描述

9、for循环语句及break和continue的作用

for循环语句及break和continue的作用

1、for循环语句的格式:

for(表达式A; 表达式B; 表达式C)
语句X

注意:表达式A、B、C 之间的分割是分号; 不是逗号啊,不要犯错!

2、用 break 语句提前结束循环过程并跳出:

之前给大家讲解 switch-case语句的时候,跟大家说过,break可以跳出当前的switch-case语句结构。同理break语句也可以使用在循环语句中,使用之后直接提前跳出当前的循环过程。break后面如果有语句也不执行。
在这里插入图片描述

3、用 continue 语句提前结束本次循环:

continue 语句一般用于循环语句中,作用是提前结束本次循环,即跳过循环体语句中X中尚未执行的语句,直接进行下一次的循环。
在这里插入图片描述

4、举例实现求出100~200之间的素数并打印出来:

#include <iostream>
using namespace std;

int main()
{
        cout << "100~200之间的素数如下:" << endl;

        int x = 0;
        for (x = 100; x <= 200; ++x)
        {
                int y = 2;
                for (y = 2; y < x / 2; ++y)
                {
                        if (x % y == 0)
                        {
                                break;
                        }
                }

                if (y < x / 2) continue;

                cout << x << endl;
        }

        cout << endl;
        return 0;
}

在这里插入图片描述

10、水仙花数

求水仙花数
输出所有的“水仙花数”,什么是水仙花数呢?所谓的水仙花数就是指一个3位数,其各个位数字的立方和等于该数本身。立方大家都知道吧,比如2的立方就是 2x2x2=8。例如:153就是一个水仙花数,因为:
1的立方=1x1x1=1
5的立方=5x5x5=125
3的立方=3x3x3=27

他们的和就是:1+125+27=153
大家明白了吗?先自己动手实现一下啊!
在这里插入图片描述

11、函数简介

函数简介

1、定义函数的形式:

返回类型 函数名(形式参数表列)
{
声明部分;
执行语句;
}

例如:

void print_msg() //没有参数,没有返回值
{
    cout<<"hello world." << endl;
}

int add(int x, int y) //有两个参数,有返回值
{
    return x+y;
}

2、函数的简单调用:

#include <iostream>
using namespace std;

int add(int x, int y)
{
        return x + y;
}

int main()
{
        int sum = add(2,5);
        cout << "sum = " << sum << endl;
        return 0;
}

3、形参与实参:

在上面的例子中 add 函数的参数 int x, int y 就是形式参数,简称形参,又叫虚拟参数,因为函数调用之前他们并没有申请任何内存;
而在调用 add 函数的实际参数 2 和 5 就是实际参数;

4、函数的前置声明

在这里插入图片描述

5、递归调用

在这里插入图片描述

11、局部变量和全局变量

局部变量和全局变量

1、多文件共同访问一个全局变量:

①、在其中一个C/Cpp文件中定义并初始化全局变量,例如:
int sum = 0;
②、在C/Cpp文件都能访问到的一个h头文件中声明该全局变量为支持多文件访问的:
extern int sum; //此时只是声明,所以不需要初始化
③、在其他C/Cpp文件中 #include 这个头文件之后,就可以直接使用这个全局变量 sum 了。

备注:不能在 h 文件中直接定义这个全局变量 int sum = 0; 之前看到很多网友都是这么做的,这样的话如果有多个C/Cpp文件 include 包含这个头文件的时候就会提示 sum 重复定义了。所以一定要在 C/Cpp文件中定义该全局变量,之后在 h 头文件中声明该全局变量才行哦。

2、static 静态类型变量:

在这里插入图片描述
在这里插入图片描述
static 静态类型局部变量只初始化一次,之后的调用都不进行初始化!

12、数组与一维数组

数组与一维数组

1、如何定义数组?

上面已经粗略的给大家定义了一个学号的数组:int stu_no[60];
其中 int 就是这个数组中所有元素的类型,stu_no 就是数组的名字,以后访问数组中任何一个元素都得靠他,60就是这个数组中元素的个数。
所以数组的基本定义格式如下:
类型名 数组名[常量表达式];

注意,中括号内部的常量表达式用以表示数组中元素的个数,这个必须是个常量或常量表达式,不能是个变量或者一个不确定的值,不然编译阶段就直接报错了。因为定义好了之后,系统马上就根据这个个数来分配空间了。如果是个变量,系统就不知道要分配多少空间。

数组名字的命名规则跟变量是一样的,只能使用数字、字母、下划线,而且数字不能做开头。

2、如何访问数组中的元素:

数组必须先定义之后再使用,需要通过下标来访问,具体的访问格式如下:
数组名[元素下标];
这个元素的下标是从0开始的,范围是0~个数-1,例如:int stu_no[60]; 那么下标就是 0~59,大家明白了吗?
备注:这个下标可以是常量也可以是变量,但必须在规定的范围内访问,不然会出现访问越界,导致程序崩溃的重大低级问题。

3、一维数组的初始化:

①、在定义数组的时候就对数组的全部元素初始化:
int stu_no[5] = {101, 102, 103, 104, 105};

②、在定义数组的时候对部分元素进行初始化:
int stu_no[5] = {101, 102};
后面未初始化的默认赋初值0

③、在定义数组的时候就对数组的全部元素初始化,可以不指定数组长度:
int stu_no[] = {101, 102, 103, 104, 105};

不管是定义的时候指定长度还是不指定长度,总结起来就一个规则,定义的时候让系统知道数组的长度就可以了,也就是定义即确定大小。养成一个好习惯初始化数组:int stu_no[5] = {0};

4、案例

不太好的写法:
在这里插入图片描述
正常写法:
在这里插入图片描述

总结:如果数组里面没有0,自己又初始化了0,那么结果是最小值是0;
在这里插入图片描述

5、小作业

一整形数组中有10个数,分别是:5,8,9,0,2,1,4,7,6,3;
对数组进行排序,并输出排序后的数组内容。
自己写的:
小感悟:每轮对比,一定能把最小的找到,所以,从后往前一个个确定;
在这里插入图片描述
别人写的:
C++排序算法代码汇总

12、字符数组

字符数组
题目:字符数组定义如下:
char szbuf[100] = “hello, friends, my name is cctry.com. what is your name ?”;
复制代码

遍历字符数组 szbuf,将其中的字符 i 替换成 @ 符号,并统计其个数。最后将统计的个数及整个字符串的内容都输出出来?
大家试试吧!
在这里插入图片描述

13、使用字符串处理函数操作字符数组

使用字符串处理函数操作字符数组

1、目的:

之前也跟大家说过字符串在以后的编程过程中会非常频繁的用到,所以C/C++语言为了提升开发效率,本身提供了很多对字符串进行操作的函数,不用大家自己再实现。他们已经成为C/C++的标准,所以任何一个支持C/C++标准的编译器都支持这些函数的。他们被包含在 string.h 或 string 头文件中。所以要在代码中添加:
#include <string.h> //C语法
或者
#include //C++语法

2、字符串连接函数:strcat

该函数的定义原型为:
char * strcat (char destination[], const char source[]);
相关的说明在这里:http://www.cplusplus.com/reference/cstring/strcat/
其作用就是将第二个参数的字符串连接到第一个参数的字符串结尾,所以要保证第一个参数的字符数组大小够用,能装的下第1个和第2个字符串的总长度才行,不然就会发生内存溢出啦!
返回值是第一个字符串的首地址,关于地址这个话题在下几节课会给大家讲解的。

代码举例:
char des[50] = "hello "; //保证des的空间足够大
char src[] = “cctry.com”;
strcat(des, src);
cout << "des = " << des << endl;

问大家个问题,这里面des字符数组的大小最小能定义成多少?15?16?17?为什么呢?

3、字符串拷贝函数:strcpy

该函数的定义原型为:
char * strcpy( char destination[], const char source[]);
相关的说明在这里:http://www.cplusplus.com/reference/cstring/strcpy/
其作用就是将第2个参数的字符串拷贝到第一个参数的字符数组中,所以要保证第1个参数的字符数组大小够用。注意:第2个参数的结束符 ‘\0’ 也会拷贝过去哦。
返回值是第一个字符串的首地址,关于地址这个话题在下几节课会给大家讲解的。

代码举例:
char des[50] = {0};
char src[] = “cctry.com”;
strcpy(des, src);
cout << "des = " << des << endl;
问大家个问题,这里面des字符数组的大小最小能定义成多少?为什么呢?

4、字符串比较函数:strcmp

该函数的定义原型为:
int strcmp (const char str1[], const char str2[]);
相关的说明在这里:http://www.cplusplus.com/reference/cstring/strcmp/
其作用就是对比第1个和第2个参数的字符数组字符串,逐个字母比对,直到字符串结束。即比较每个字母的ASCII码值。
当第1个参数大于第2个参数,返回 > 0 的数,当第1个参数小于第2个参数,返回 < 0 的数,当第1个参数和第2个参数相等,返回0

代码举例:
char des[50] = “hello”;
char src[] = “cctry.com”;
int iret = strcmp(des, src);
cout << "iret = " << iret << endl;

5、字符串求长度函数:strlen

该函数的定义原型为:
size_t strlen (const char str[]);
相关说明在这里:http://www.cplusplus.com/reference/cstring/strlen/
其作用就是求得参数字符串的长度,通过返回值返回。

代码举例:
char des[50] = “hello”;
int len = strlen(des);
cout << "len = " << len << endl;

这里面 len 的值是5,而不是50,为什么呢?50是des中总共能容得下的字符的个数,而不是实际字符串的长度。
所以这里面一个字符串占用的字节数和字符串的长度是两个不同的概念。

6、字符串的长度和占用字节数的区别:

char des[50] = “hello”;
这个字符数组里面存的是一个字符串 hello,那么这个des字符数组所包含的字符串长度是:strlen(des)
那么,这个字符数组所占用的字节数呢?怎么求?sizeof(des)

int des[50];
sizeof(des) = ?

7、小作业:

不用系统提供的strcat函数,自己使用字符数组编写一个函数,实现两个字符串的连接功能。
在这里插入图片描述

14、地址与指针

地址与指针

&符号是取地址符号,&a 就是取变量a的地址。

在这里插入图片描述

1、变量与指针:

在C/C++语言中可以通过取地址符号&得到变量的地址,例如:
int a = 5;
int* pa = &a;

那么,通过变量的地址,能否得到变量自身呢?答案是可以的!
int a = 5;
int* pa = &a;
pa = 6;
即:在指针变量的前面加上一个
就能得到指针指向的变量自身。
所以对一个变量的修改,既可以通过该变量自身(直接修改),也可以通过指针/地址来实现修改(间接修改)。

2、指针变量的定义及初始化:

①、指针变量的定义格式如下:基类型 * 指针变量名;
②、符号* 既可以靠近基类型,也可以靠近指针变量名,例如:
int* p; 和 int *p; 都是正确的。
③、指针变量可以在定义的时候就初始化,也可以先定义后初始化,也可以在以后的任意一个时间去指向某个变量的地址:
int a = 5;
int *pa;
pa = &a;
int *pb = &a;

④、基类型就是该指针变量指向的变量的类型。例如:
int* pa; 这个指针变量的定义,就是定义了一个指向int类型的指针变量pa;你就不能把一个float类型的变量地址赋给他。例如:
int* pa;
float a = 2.6;
pa = &a;
这种写法是错误的。
⑤、指针变量可以指向同一类型的变量,例如:
int a = 5, b = 6;
int *p = &a;
p = &b;
即:指针变量p既可以指向变量a的地址,也可以指向变量b的地址。
在这里插入图片描述
输出还是X=5;Y=6;

方法一:指针:
在这里插入图片描述
方法二:引用:
在这里插入图片描述

输出:X=6;Y=5;
小感悟:&取地址,相当于告诉别人你家位置;*指针,相当于万能钥匙,就差一个你家地址,告诉别人后,他们就能看到你家的数值是多少,甚至懒得看,把你家拆了重建;

15、数组与指针

数组与指针
这是为什么?????
在这里插入图片描述小作业:
输入一个字符串,例如:
a123x456__17960?302ab5876
将其中连续的数字作为一个整数,依次存放到一个数组中a中,例如:123放在a[0]中,456放在a[1]中。统计共有多少个整数,并输出这些整数。
自己敲的代码:


#include <iostream>
#include<cstring>
using namespace std;

 int main()
{
    char string[28] = { "a123x456__17960 ? 302ab5876" };
    int a[30] = { 0 };
    char b[30] = { 0 };
    int y = 0;
    int z = 0;
    for (int i = 0; i < 28; i++)
    {
        if ((string[i] >= 48) && (string[i] <= 57))//将数字挑出来
        {
            b[y] = string[i];
            y++;
        }
        else
        {
            if (strlen(b) != 0)
            {
                int num = atoi(b);//将字符串变为整数
                a[z] = num;
                z++;
                y = 0;
                memset(b, '\0', strlen(b));//将数组清空
            }
            else
            {
                continue;
            }  
        }
    }
    cout << "一共有" << z << "个整数。" << endl;
    for (int t = 0; t < z; t++)
    {
        cout << "a[" << t << "] = " << a[t] << endl;
    }
    return 0;
}

在这里插入图片描述

16、结构体类型

结构体类型

1、自定义数据类型:

C/C++语言本身提供了很多基本数据类型,例如:int、float、char 等供我们使用。但是程序编写的过程中问题往往比较复杂,基本的数据类型有时候不能满足我们的需求,所以C/C++语言允许开发者根据自己的需要自定义数据类型,接下来要讲解的结构体struct、联合体union、枚举类型enum,类类型class 等就是用户自定义的数据类型。这些用户自定义的数据类型跟C/C++语言提供的基本类型一样,都可以用来定义变量。只不过在这些自定义数据类型在使用之前要先由用户声明出来才行。

2、定义结构体的必要性:

之前有给大家举过学生的例子,这里接着来说下。例如一个学生的信息包括:姓名,学号,性别,年龄 等等。按照我们之前的做法,可以使用数组来定义:
string name[100]; //姓名
int num[100]; //学号
char sex[100]; //性别
int age[100]; //年龄
这样虽然也能满足需求,但是比较麻烦,如果想获得一个学生的信息需要从4个数组中分别找到该学生的所有信息,而且没有什么关联性,容易乱,能不能把一个学生的信息都统一到一起呢?把他们当做一个组合项,在一个组合项中包含若干个类型的数据项。C/C++语言允许用户自己定义这样的数据类型,这样的类型就称作结构体。
例如:
struct Student
{
string name;
int num;
char sex;
int age;
};

这样就声明了一个结构体类型 Student,struct 是结构体类型的关键字,不能省略。

3、结构体类型的声明:

struct 结构体类型名
{
//成员表;
};

struct 是声明该类型为结构体类型的关键字,不能省略。结构体类型名就是该结构体类型的名字,以后可以直接拿这个类型名来定义变量,就跟使用int,double一样用。类型名的命名规则跟变量一样,可以是数字、字母、下划线,且数字不能开头。上面例子中的 Student 就是结构体的类型名。接下来的一对大括号内的成员表包含了该结构体中的全部成员。上例中的 name、num、sex、age 都是结构体中的成员。在声明一个结构体类型时必须对各成员进行类型声明,即:
类型名 成员名;
例如:int num;

备注:C语言中结构体的成员只能是数据,C++对此进行了扩充,结构体的成员既可以包含数据,也可以包含函数,其实在C++中 struct 跟 class 从使用角度来说差别不大,这个以后在讲解面向对象时候的class时再跟大家详细的讲解!

4、结构体类型变量的定义及初始化:

A、定义:
结构体类型声明完了之后就可以定义变量了,如下:
Student zhangsan, lisi;
这种是非常常用的一种定义结构体类型变量的方法。
当然也可以在声明结构体类型的时候就定义变量,当然这种是不常用的方法:
struct Student
{
string name;
int num;
char sex;
int age;
} zhangsan, lisi;

B、初始化:
Student zhangsan = {“张三”, 1001, ‘m’, 25};
备注:初始化参数的顺序一定要和结构体类型声明的成员表顺序一致才行,不然会报错而且会错误的赋值。
在这里插入图片描述

5、结构体类型变量成员的访问:

一定要初始化,再调用

结构体变量名.成员名
可以用这种方式来访问。
例如:
Student zhangsan = {“张三”, 1001, ‘m’, 25};
zhangsan.num = 29;
int num = zhangsan.num;

17、结构体数组与指针

结构体数组与指针

1、结构体类型和数组配合使用:

在这里插入图片描述

2、指向结构体变量的指针:

结构体变量指针的作用在上面的知识点中已经说的够详细的了,下面就给大家演示下如何来用!
之前给大家说过,结构体类型变量引用其成员的时候可以使用符号 . 来引用,例如:
Student stu;
stu.num = 102;

但如果是指针类型呢?我们可以这样做:
Student stu;
Student* pstu = &stu;
(*pstu).num = 102;

没问题吧?pstu是指向stu变量的指针,所以前面加上符号*就变成stu变量本身了。变成 stu本身了之后再用 . 来引用就可以了。

但是这么做可以说代码量少还可以,如果代码量比较多,写起来比较麻烦。所以C/C++中规定结构体类型的指针变量可以用 -> 符号来引用其成员,即如下:
Student stu;
Student* pstu = &stu;
pstu->num = 102;

这样写起来就简单了。就是把 . 换成 -> 就可以了,方便吧?
在这里插入图片描述

18、枚举类型及定义新的类型名字

枚举类型及定义新的类型名字

1、枚举类型:

如果一个变量只能有几种可能的值,这样的变量可以定义成枚举类型。所谓的 “枚举” 是指可以将变量的值一一列举出来,变量的值只能在列举出来的值的范围内,其他的值对该变量没有意义,例如:星期几、人种颜色、性别、月份 等等。枚举类型也是一种自定义类型。

2、枚举类型的声明:

声明枚举类型用 enum 开头,例如:
enum ESex
{
ESex_Male, //男性
ESex_FMale //女性
};

以上就是定义了一个枚举类型 ESex,大括号内部的 ESex_Male、ESex_FMale 称为枚举元素或枚举常量。表示这个枚举类型可能的值。

注意事项:
①、枚举元素按常量处理,所以称作枚举常量。他们不是变量,所以不要对他们进行赋值,即枚举元素的值是固定的;
例如:ESex_Male = 8; 这种是错误的,大家可以在vs2013中试试;

②、枚举元素是常量,所以其也是有值的,他们的值是一个整数,按照元素声明时候的顺序从0开始依次进行+1操作,默认的值就是:0,1,2,3,…
例如,上面的ESex枚举类型中,ESex_Male 的值默认是0,ESex_FMale 的默认值是1,依此类推。

③、枚举元素有默认的值,但也可以在声明的时候指定值,例如:
enum EWeekDay
{
EWeekDay_1 = 3,
EWeekDay_2 = 4,
EWeekDay_3 = 5,
EWeekDay_4,
EWeekDay_5,
EWeekDay_6,
EWeekDay_7,
};
其中从 EWeekDay_4 开始未赋值,所以按照他的上一个元素的值+1的规则进行默认赋值,也就是 EWeekDay_3 + 1 = 6。
这里面有个注意事项,即,上面赋值的最好是依次增大,不然有可能会造成两个枚举元素是一样的值,例如:
enum EWeekDay
{
EWeekDay_1 = 3,
EWeekDay_2 = 2,
EWeekDay_3 = 1,
EWeekDay_4,
EWeekDay_5,
EWeekDay_6,
EWeekDay_7,
};

④、枚举值可以用来进行跟整数一样的判断,比较,switch-case 等操作,例如:
int ab = 2;
if(EWeekDay_1 > ab )
{
//…
}

⑤、虽然枚举类型的变量可以看做是整形类型的变量,但是不能把普通的整数赋值给枚举类型变量,例如:
EWeekDay day = 2;
这种是错误的,除非进行强制类型转换,但是不建议。最好还是:
EWeekDay day = EWeekDay_2;

3、枚举类型举例:枚举类型和结构体类型结合:

struct Student
{
    string name;
    int num;
    ESex sex;
    int age;
};

Student stu;
stu.sex = ESex_Male;

4、用 typedef 类型声明新的类型名字:

除了可以用 struct 结构体,union 联合体,enum 枚举 等自定义类型以外,还可以使用 typedef 声明一个新的类型名字来代替已有的类型名。注意是新的类型名字,只是名字而已,不是一种全新的类型,只是改个名字而已。
例如,我们定义一个无符号的整型int变量可以这样来定义:unsigned int a = 5;
类型的名字比较长,unsigned int,而且以后所有定义无符号的整型int变量都得这么写,那么有没有简单的写法呢,typedef就派上用场了,咱们可以给 unsigned int 改个名字,例如:
typedef unsigned int uint;
uint a = 5;
所以,以后所有的 unsigned int 都可以改成 uint 了,方便吧?同理,其他的类型也都可以使用 typedef 改名,例如:
typedef int myint;
typedef unsigned long ulong;
typedef Student StuT;
typedef EWeekDay EWDay;
以上,都是可以的哦!

5、typedef与宏定义:

typedef详解以及与宏定义#define的区别

19、引用及new和delete的使用

引用及new和delete的使用

1、何为变量的引用?

通过之前对指针的讲解,相信大家对于指针来说都比较熟悉了,指针里面存的是某个变量的地址。那么这节课给大家讲解一下变量的引用,跟指针有点像。他是C++对于C语言的一个重要的扩充。C语言中没有引用,C++有引用,而且C++中更建议大家多用引用少用指针。

变量的引用就是一个变量的别名,变量和变量的引用代表着同一个变量。 例如:
int a = 5; //语句1
int& b = a; //语句2
int* p = &a; //语句3
这里面a是一个普通的int类型变量,b呢,就是变量a的一个引用,p呢就是指向变量a地址的一个指针变量。
其中语句2中的 & 符号是引用的声明符号,不是取地址哦,语句3中的 & 符号确实是取地址符。
如何来区分呢?大家记住:紧跟在数据类型后面的&符号就是引用的声明符号,其他情况都可以认为是取地址符号。

2、引用的注意事项:

①、引用不是一种独立的数据类型,引用只有声明,没有定义。必须先定义一个变量,之后对该变量建立一个引用。也就是说有变量才有变量的引用,不可能先声明一个引用而不去引用任何变量,这点跟指针不同,指针可以先声明,之后的任意时刻指向某个变量的地址,引用就不是;
例如:int &b; //先声明定义一个引用是错误的
②、声明一个引用时,必须同时对其初始化,即声明该引用代表哪一个变量。这个跟第①点要表达的意思一样。有一种例外的情况,当一个函数的参数是某个变量的引用时,形参不必在声明中初始化,他的初始化是在函数调用时的虚实结合实现的,即作为形参的引用是实参的别名;
void swap(int& a, int& b);

③、声明一个引用后,不能再让其作为另一个变量的引用了。例如:
int a1 = 2, a2 = 5;
int& b = a1; //正确
int& b = a2; //错误

④、不能建立引用数组,例如:
int a[5] = {0};
int& b[5] = a; //错误
int& c = a[0]; //正确(C++新标准支持)

⑤、可以建立引用的引用(C++新标准支持),也可以建立引用的指针,例如:
int a = 3;
int& b = a; //正确
int& c = b; //正确
int* p = &b; //正确,得到的是变量a的地址
*p = 5;
c = 6;

3、使用new和delete动态分配内存:

在以后的开发过程中,因为局部变量的局限性,只能在其作用域内使用。因此,我们需要动态的分配和撤销内存空间,使其可以在任何函数中使用。例如:
char* get_same_string(char* p1, char* p2)
{
//
}
get_same_string 函数的作用是从参数p1和p2中找出相同的部分,例如,p1的内容是:“aabbcc”,p2的内容是:“kkbcyy”,他们相同的子串就是 “bc” 对吧?我想把这个结果通过函数的返回值给传出去。所以函数的返回值是一个 char* 类型,如果在函数中定义一个局部变量 szret[100] 数组,用这个数组来存储相同部分的子串 “bc”,那么就不能返回,为什么呢?因为 szret 是局部变量,作用域只是在函数的内部,超过函数的作用域之后 szret 的内存就可能被释放了。所以用它来返回之后,在函数的外部再去使用是非常不安全的,也是错误的。所以这种情况就可以使用 new 动态分配内存来解决。

4、new 出来的变量/内存的生命周期:

C++ 中的 new操作符 和C语言中的 malloc 函数类似,如果你不主动 delete 掉这段申请的内存的话,它会一直存在,直到进程结束后系统会回收掉这段资源;而如果你delete掉这段申请的内存,则这段申请到的内存的生命周期为从你new(申请一段内存)到你delete(释放掉这段内存)这段时间。

五、C++学习笔记

1、VS2013编程实用技巧

VS2013编程实用技巧

1、C++语言中的保留关键字:

C++中的保留关键字很多,这些关键字是C++语言默认保留的,所以作为开发人员来说不能用这些关键字来作为变量的名字等用途。用了之后就会编译报错的哦,所以大家以后实用的时候要注意一点!
关于C++中的保留关键字都有哪些详见这篇帖子:关键字

2、为什么调试的控制台的黑窗口一闪而过?

很多C++小白用户前期第一次使用VC6.0或者VS2013进行调试的时候,一般都是按F5,之后程序启动起来,黑窗口一闪而过,连程序执行的结果什么的都没看到,类似这样的情况如何操作呢?
①、getchar 法;
②、断点法;
③、Ctrl+F5 法;
④、工具栏添加快捷按钮点击法;!

3、清理工程源码,删除无用文件,大大减少工程体积,方便分享:

大家在写好了一个程序的源码之后,可能要传给别人,与好友之间分享资料。或者是自己的代码遇到了一些问题没办法解决请求好友帮忙,也要把工程源码打包给别人。或者是大家要把自己的工程源码打包之后发布到咱们VC驿站上面,但是论坛的附件是有大小限制的。如何将工程中一些没用的文件删除掉,这样无论是传递给别人或者发布到网上方便别人下载等等,都是非常不错的,那么哪些文件是没用的呢?哪些文件可以删除呢,而且还要保证工程代码的完整性,其他人收到这个工程源码之后,用VS打开即可编译生成EXE/DLL呢?下面给大家说一下:
①、工程目录下的Debug/Release文件夹都可以删除,包括两层结构;
②、工程目录下的:SolutionName.sdf、ipch文件夹 以及 *.pch 等文件都可以直接删除,大大减少体积;
备注:VC6.0的话有个 *.ncb 文件也可以删除。
③、如果不想让VS在工程目录中生成②的文件,可以按照如下设置:
工具(T)->选项(O)…->文本编辑器->C/C+±>高级->回退位置->
始终使用回退位置->True
回退位置已在使用时,不警告->True
回退位置->留空
经过以上设置之后,VS会把 sdf、pch 等相关文件生成在系统的临时目录中,从而不生成在工程目录中,所以以后打包的时候就方便了,不用每次都清理

2、类的声明

C的面向过程和C++的面向对象
类的声明

1、类类型的声明:

C++中声明一个类类型跟声明一个结构体类型很像。例如,咱们之前讲解的声明一个结构体的类型如下:

struct Student
{
    string name;
    int num;
    char sex;
    int age;
};

一个学生的结构体类型Student,包含学生的一些属性,如:姓名、学好、性别、年龄。基于这个结构体类型我们改一下,改成生成一个类类型:

class Student
{
    string name;
    int num;
    char sex;
    int age;
};

大家看到了吗?声明一个学生的类类型和声明一个学生的结构体类型很像很像,表面上看就差了一个关键字:struct 和 class.

备注:这里面在给大家说说有关属性和方法的名字的问题。
class类中的name,num,sex 之类的按照咱们上节课的只是可以叫做属性,print_name 这个函数呢,可以叫做行为或者方法。
这里面交给大家一些更通用的叫法:name,num,sex 之类的又可以叫做成员变量,print_name 可以叫做成员函数。

2、成员访问限定符:

C++针对类的成员,设定了三种方式的访问限定符:public、private、protected(用的不多)
public:意为共有的,公开的,公用的 成员,既可以被本类中的成员函数引用,也可以被类的作用域的其他函数所引用,即从类的外部是可以调用的;
例如,我家的房子就好比是一个类,如果来客人了,那么我可能招呼客人到客厅,这个客厅就是public类型的成员,外部可以访问,客人也可以到客厅中喝水,看电视等等;

private:意为私有的,私生的 成员,只能被本类的成员函数所引用,类外部不能调用(友元类可以,这个以后说明),
例如,我家的房子就好比是一个类,卧室就是private类型的成员,是一个相对隐私的地方。如果来客人了,我不希望客人访问我的卧室,除非经过我的允许(让成员函数去访问);

protected:意为受保护的成员,不能被类外访问,这点类似private,但是可以被派生类的成员函数访问,有关派生类的说明,以后会讲解。

附:在 class 中,public、private、protected 可以出现多次,也没有先后顺序之分,例如:

class Student
{
public:
    string name;
    int num;
    int age;

private:
    char sex;

public:
    void print_name()
    {
        cout << "name = " << name << endl;
    }
};

3、如何决定成员变量及成员函数的访问权限:

一般情况下,如果不希望外界访问本类的成员变量,那么就可以把成员变量都声明为私有的。但是有个前提必须提供可以访问的接口函数,不然一个类的所有成员都是private私有的话就没有意义了,相当于闭关锁国一样,不跟任何外界打交道,这点肯定不行。比如在Student这个类中,如果把sex性别这个成员变量设置为私有的,那么Student类就应该提供可以修改或者读取sex成员变量的成员函数,并且这个函数是public的,不然外界想要知道sex变量的值就没办法获取了。大家说是不是?

当然,也可以把一些成员设置为私有的,但是不对外提供修改的接口,因为可能这个属性或者成员变量对于类对象来说可能不想被外面知道或者修改,比如,这个学生是否脚臭,这个属性对别人有意义吗?鞋一穿上之后谁也不知道你脚臭不臭,只有自己知道,而且自己知道就够了。外界不需要知道。我也没熏到你,对不对?我也没在公共场合拖鞋,对不对?

还有一种情况就是将成员函数声明为私有的,这说明这个私有的成员函数只能被该类的其他成员函数所调用,是作为辅助的接口函数的,是在类的内部使用,不对外公开。例如:
在这里插入图片描述

4、struct 和 class 关键字的区别:

上面已经说过了,在C++中已经对struct进行了扩充,不仅可以声明成员变量,还可以声明成员函数,class关键字本身就是从struct发展而来的,所以struct支持的,class都支持。那么在C++中他们有什么异同呢?
最重要的一个区别就是:class默认的成员访问限定符是private,struct默认的成员访问限定符是public.

在这里插入图片描述

3、类的成员函数

类的成员函数

1、类的普通成员函数:

类的普通成员函数跟普通的函数没什么太大的区别,也都是有参数的类型和形参,还有返回值之类的,唯一的区别就是必须由该类的实例化对象去调用。
这里面为什么强调是类的普通成员函数呢,难道还有不普通的吗?当然有,那就是类的静态成员函数,这个以后再做讲解!

2、inline 内联函数:

之前给大家讲解过普通的全局函数,刚刚又给大家讲解了类的成员函数,这里面又出来个inline内联函数是个什么东西呢?给大家讲解一下!
实际上inline内联函数是从C语言的宏发展而来的。例如,程序main函数中直接执行以下三行代码:

int a = 5;
++a;
int b = a + 3;

和将该三行代码封装成一个函数让main函数调用的开销是完全不一样的。函数调用的过程中需要将参数压栈等等操作,详情可以看下这篇帖子:
https://www.cctry.com/thread-289482-1-1.html

C语言中可以用宏来实现一些相对简单的函数,例如:

#define MAX_NUM(x, y) (x > y ? x : y)

调用的时候代码可以这样写:

int ret = MAX_NUM(3, 6);

调用的时候感觉 MAX_NUM 像个函数,但是他是个宏,宏跟函数的区别是,在编译阶段就将宏的代码展开直接替换调用宏的地方。所以省去了函数调用的压栈、出栈等开销。所以执行效率方面要比函数高。
但是宏定义写起来比较难度倒是不大,就是麻烦一些,而且代码的可阅读性会变差。所以C++中引入了inline内联函数这么个东西,用inline关键字声明的函数,可以在调用的时候,将函数的代码直接嵌入到调用的地方,所以大大的减少了函数调用的开销,提高了效率。

例如:

class Student
{
public:
    string name;
    int num;
    int age;

private:
    char sex;
    inline int max_num(int x, int y)
    {
        return x > y ? x : y;
    }

public:
    int get_max_num(int a, int b, int c)
    {
        int max_ab = max_num(a, b);
        return max_ab > c ? max_ab : c;
    }

    void print_name()
    {
        cout << "name = " << name << endl;
    }
};

备注1:默认情况下,在类体中直接定义/实现的函数,C++会自动的将其作为inline内联函数来处理,所以类似上面的代码:max_num、get_max_num、print_name 函数都会被看成是 inline 内联函数。而在类体外部定义的函数C++则会将其作为普通的类的成员函数来处理。那么如何在类体外部定义成员函数呢,接着往下看!

备注2:也不是把所有的函数都声明成 inline 就是好事儿,如果函数的执行体很大,很耗时,那么就不适合作为 inline 内联函数,只有当函数的执行体很小,只有几行代码,而且会被频繁的调用的时候才适合作为 inline 内联函数的。这点还请大家谨记。

3、类的声明和实现分离:

一般情况下,可以把类的声明和实现都写在一起.但是,随着类的功能越来越多,类中的成员变量和成员函数也是越来越多,类的代码长度就越来越大,而且随着长度的增加不太便于阅读,层次不清晰,让人一眼望过去就一大面。那怎么办呢?
好的做法就是将类的声明和成员函数的定义分离开,成员函数的声明放在类的内部,实现或者定义放在类的外部,加上作用域限定一下就行,类似下面这样:
在这里插入图片描述

4、使用多文件分离类的声明和实现:

通过上面的学习,我们已经成功的将类的声明与实现分离了,但是呢,代码还是在一个文件中,不够清晰。接下来给大家介绍一种更贴近我们平时编程开发的方法:将类的声明放到 .h 头文件中,将类的实现放到 .cpp 实现文件中,谁要使用这个类,就 include 包含 .h 类的头文件就可以啦!接下来视频演示一下如何进行 h 和 cpp 的分离,具体看视频教程演示。
(模板类的代码必须都放在 .h 文件中,没办法实现 .h 和 .cpp 的分离,这个以后会讲解的)

这里面也可以直接使用VS2013添加类向导的方式来添加类到我们的工程中。起好类的名字之后,VS2013会自动添加该类的 h 头文件和 cpp 文件到我们的工程中,具体看教程演示!
所以,大家以后从一些开源网站,或者从咱们VC驿站下载的一些开源的类来使用的时候,作者都是提供关于该类的一个 .h 文件和一个 .cpp 文件。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

4、this指针

this指针
什么情况下需要手动加上this:

class Student
{
public:
    char name[50];
    int num;
    int age;

public:
    void set_age(int age)
    {
            age = age;
    };
};

大家看到了吗?set_age 函数的参数名字和成员变量的名字一样,都是age,那么这样的情况下,大家说说成员变量的age是否能真的赋值呢?大家不妨先把视频教程暂停,自己实践下,之后再继续播放教程!
不同的编译器可能实现的结果不同,但是在我们的vs2013中,答案是否定的。成员变量age并没有通过函数 set_age 被正确的赋值。那么这个时候怎么办呢?解决的方法是将 set_age 函数改成如下:

void set_age(int age)
{
    this->age = age;
};

5、类的构造函数

类的构造函数

1、什么是构造函数?

构造函数就是解决上面的问题而存在的。构造函数是一种特殊的成员函数,与其他成员函数不同,不需要用户来主动调用它,构造函数会在对象被建立时自动被调用的。作用就是用来处理对象的初始化操作。

2、构造函数的注意事项:

①、构造函数的名字必须与类名同名,不能随意命名,这样的话才能让编译器认为该函数是构造函数,而不是类的普通成员函数;
②、构造函数不具有任何类型,不返回任何值,连 void 都不是;
③、构造函数不需要用户调用,也不应该被用户调用,它是对象建立的时候自动被系统调用,用来初始化刚刚建立的对象的;
④、如果用户没有定义自己的类的构造函数,那么系统会自动生成一个默认的构造函数,只不过该构造函数的函数体是空的,也就是什么都不做。

6、函数的重载与默认参数

函数的重载与默认参数

1、函数重载的由来:

在平时的编程过程中,一个函数可能就实现一个功能,多个功能就需要多个函数。但有时候多个功能之间很像,只是一些细节有差异,那么类似这样的功能能不能写成一个函数呢?例如,要求2个数中比较大的数,这两个数的类型有时候是int类型,有时候是float类型,那么我们按照之前的写法应该是这样封装函数:

int max_int(int a, int b);
float max_float(float a, float b);

所以,用户在调用的时候就要根据不同的参数类型,调用不同名字的函数,如果要求两个int类型的数中的比较大的就要调用 max_int,如果要求两个float类型的数中的比较大的就要调用 max_float,能不能叫一个名字呢?让系统自己去适配到底调用哪个?答案是可以的。例如:

int max_num(int a, int b);
float max_num(float a, float b);

实际上这就是函数的重载。
C++允许同一函数名定义多个函数,这些函数的参数类型和个数可以不相同,而且至少要有一个不相同,如果都相同的话就会报重复定义的链接错误了。使一个函数名可以多用。

2、函数重载的要求:

重载函数的参数个数、参数类型、参数顺序 三者中必须至少有一种不同(不然会产生调用疑惑)。函数的返回值类型可以相同也可以不同。

另外,在跟大家说一下,函数的重载一般都是功能相近,或者功能类似的函数进行重载,不能把一些功能相差很大,或者完全不相关的函数叫同一个名字,语法上是没有错误,但是违背函数重载设计的初衷啦。

3、函数的默认参数:

那么现在呢,我想让这个函数更灵活一些,有的时候我只想求最大值,有的时候我只想求最小值,有的时候我同时求最大值和最小值。
void get_min_max(int src[], int arr_len, int* max_v, int* min_v);
能不能不让我每次都得定义两个 int 类型的变量用来接收数组中的最大值和最小值。我需要最大值就给我最大值,我需要最小值就给我最小值。如何才能办到呢?

4、函数默认参数的注意事项:

①、在函数声明的时候指定,如果没有函数的声明则可以放在函数的定义中,但是声明和定义只能选一个;
②、从第一个有默认值的参数开始,后面的所有参数必须都有默认值才行;
③、调用的时候如果要自定义非第一个默认值的参数,那么前面所有的参数都要明确的写上默认值才行;
④、从使用角度来说函数的默认值比重载更方便,从函数内部实现角度来说比函数的重载更复杂。

7、类的构造函数与析构函数

类的构造函数与析构函数

1、构造函数中的参数初始化表:

用这种方式实现的构造函数体积小,代码显得更简洁,显得比较NB哈!那么怎么用呢,咱们举个例子:

class CStudent
{
public:
    char name[50];
    char sex;
    int num;
    int age;

    CStudent(char* pname, char t_sex, int t_num, int t_age);
};
CStudent::CStudent(char* pname, char t_sex, int t_num, int t_age) :sex(t_sex), num(t_num), age(t_age)
{
    strcpy(name, pname);
}

2、析构函数:

析构函数也是一个在类中跟构造函数类似的特殊功能的成员函数。只不过它的作用是与构造函数相反,是在对象的生命周期结束的时候会被自动调用的。在C++中析构函数的名字跟类名相同,并且前面带上一个取反的符号~,表达的意思也就是跟构造函数的过程相反。

默认情况下,如果类的设计者没有自己定义析构函数,那么编译器会自动为该类生成一个默认的析构函数,只不过函数体是空的,也就是什么都没做。所以,如果需要在对象被删除的时候做一些操作的话,那么就得自己定义析构函数喽。

以下几种情况会自动调用析构函数:
①、如果在一个函数中定义了一个局部变量的对象,那么当这个函数执行结束时也就是该变量对象生命周期结束的时候,所以析构函数会被自动调用;
②、全局变量或者static类型的变量,他们的生命周期一般是在程序退出的时候,这时候该对象的析构函数才会被调用;
③、如果是用new操作符动态的创建了一个对象,只有当用delete进行释放该对象的时候,析构函数才会被调用;
在这里插入图片描述

3、析构函数的特点:

析构函数不返回任何值,没有函数类型,也没有任何函数的参数。由于上面这些特点,所以析构函数不能被重载,所以说一个类可以有多个构造函数,但只能有一个析构函数。

六、常用的函数

1.数组清空

#include<iostream>

#include<cstring>

usingnamespacestd;

intmain()

{

charbuffer[]="Helloworld\n";

cout<<"Bufferbeforememset:"<<buffer<<endl;

memset(buffer,'\0',strlen(buffer));//数组清空
cout<<"Bufferaftermemset:"<<buffer<<endl;

return0;

}

2、C语言中怎么将字符123转化成数字123

C语言中bai可以调用atoi()函数将字符串数字转du换zhi成整型数,所需要头文件为daostdlib.h

函数原zhuan型:int atoi( char *s) ;

函数说明:shu将字符串s转换为整型值,返回给调用者。
参考代码:
在这里插入图片描述

3、怎么样得出整形数组的元素个数!

七、硬件工程师 VS 软件工程师

原文出处
关于软件工程师和硬件工程师总有太多的话题。

常态往往是这样滴:

板子出问题了,
硬件工程师:肯定是软件的原因!
软件工程师:绝对是硬件的问题!
在这里插入图片描述
如果。。。。我要是都会呢。。。。。

后记

1、算法学习

学习资料

坐在马桶上学算法
啊哈算法

①、排序

算法中的排序问题总结

相关推荐
©️2020 CSDN 皮肤主题: 技术工厂 设计师:CSDN官方博客 返回首页