C++快速入门
本文为C++快速入门,旨在记录C++学习中的八个重要部分的笔记进行分享,持续更新中,预计更新完成时间:2023年7月30日
C++快速入门
初识C++
编程语言是什么
我们编写程序,就是希望与计算机进行交流,让计算机帮助我们实现我们期望的效果。从这点出发,其实和人与人之间的沟通交流是⼀样的。两个人如果需要正常的沟通交流,必须要满足的条件是,你说的话对方听得懂,对方说的话你也能听得懂。如果条件不满足,你说中文对方不动,对方说英文你不懂。那么此时就没有办法交流。写程序也是这样,我们需要和计算机沟通交流。但是很遗憾,机器听不懂我们人类的语言,也学不会人类的语言。那么我们去学习机器的语言呢?更加麻烦,计算机只认识0和1,不会太复杂的语言。此时我们怎么办?
我们可以借助⼀个“翻译”,把我们的需求翻译成机器语言,说给机器听。但是,这个翻译其实也是⼀个程序,依然没有办法学习人类的语言。幸运的是,这个“翻译员”有自己的独特的语言体系,而这种语言体系比起机器语言来说,要更加容易理解。这就是我们俗称的“编程语言”。
编程语言有很多很多,有些语言更加贴合机器的世界,这些语言对我们人类来说,学习的成本就比较高。也有⼀些语言,更加贴合人类的世界,这类的编程语言,更加符合我们⼈类的⽣活习惯和思维逻辑,学习起来难度要小很多,更加容易⼀些。这样的语言,我们称为“高级语言”,而Java就是⼀种高级语言。
基础编辑器部分
- Dev-Cpp(推荐,有一些代码补全,建议新手使用,自行百度或B站“Dev-C++”)
- Code::Blocks (与Dev-C++类似,自行百度或B站)
- Visual Studio(微软推出的C++集成开发工具,功能全面好用现代化,缺点是体积比较大)
- Visual Studio Code(配置麻烦,需要自行配置MinGW,配置教程点击这里查看,代码补全完善好用)
HeloWorld
“HelloWorld”
是每个编程语言的入门必备,了解最基本的框架。
PS:
“ ; ”
不要漏掉是每一位学习C++的同学的必修课
1 |
|
变量和常量的定义
变量:可以被改变和赋值的量
格式:数据类型 变量名称 = 赋值;
PS:最新的C++编译器支持变量不初始化,即可以不进行赋值操作
常量:一经定义不可改变的量
格式: const 数据类型 常量名称 = 赋值;
关于数据类型请看下一个部分
1 | int a = 10; |
注释
C++的注释方式有两种:
双斜杠后加注释
//
1
2
3
4
5
6
7
8
9
using namespace std;
int main(){
cout << "HelloWorld!" << endl;
// 这⾥就是单⾏注释
// 下⾯这段话,可以在控制台上输出 Hello World!
return 0;
}斜杆加星号包头尾可进行多行注释
/*
*/
1
2
3
4
5
6
7
8
9
10
11
using namespace std;
int main(){
cout << "HelloWorld!" << endl;
/*
这⾥的内容就是多⾏注释的内容
可以同时注释多⾏的内容
*/
return 0;
}
变量命名
标识符的命名规则
- 由字母、数字、下划线组成,不能有其他的组成部分
- 不能以数字开头,需要以字母或者下划线开头
- 不能与系统关键字重复
- 区分大小写
标识符的命名规范
标识符出了在遵循上述的规则之外,还有有⼀定的规范,并不是可以随便写的。标识符最基础的规范就是望文知义,命名要有⼀定的意义,方便我们自己去区分标识符所表达的含义。希望减少拼音的使用,多使用英文单词/复合单词(这样也可以提升下你的英语水平)
关键字是什么?
其实关键字也是遵循上述前两点规则的字符序列,只不过这样的字符序列已经被系统征用并赋予特殊含义了,
我们在定义标识符的时候就不能够再使用了。例如: int、float、const等。
!!!
变量名不要使用C++的保留关键字(不用死记硬背了解一下就行,后面你写得多了,自然就记住了)
数据类型
C++划定在创建一个变量或常量时需要给出对应的数据类型,用于给变量分配内存
整型
作用:表示整数的数据类型
C++表示整型的有以下几种方式,区别在于所占的内存空间不同
1byte(字节) = 8bit(位) ,2^16 = 65536,bit是最底层的单位,即0和1
数据类型 | 占用空间 | 取值范围 |
---|---|---|
short(短整型) | 2byte | -2^15-2^15-1 |
int(整型) | 4byte | -2^31-2^31-1 |
long(长整型) | 4byte(win) 4byte(linux32) 8byte(linux64) | -2^31-2^31-1 |
long long(长长整型) | 8byte | -2^63-2^63-1 |
sizeof关键字
作用:利用sizeof关键字可以得到数据类型的内存大小
语法:sizeof(数据类型/变量)
eg.
1 |
|
浮点型
作用:表示小数
类型:单精度float和双精度double
区别:可表示的有效数字范围不同
数据类型 | 占用空间 | 有效数字范围 |
---|---|---|
float(单精度) | 4byte | 7位 |
double(双精度) | 8byte | 15-16位 |
eg.
1 | int main(){ |
字符型
作用:表示单个字符
语法:char ch = ‘a’;
内存占用:1byte
!!!
字符型不等于字符串,定义的只能是单个字符,使用单引号
- 字符型变量不将其本身存储到内存当中,而是将对应的ASCLL编码存储到内存中去
eg.
1 | int main(){ |
转义字符
作用:用于表示一些不能表示出来的ASCLL字符
1 | int main(){ |
控制台输出结果
字符串型
作用:表示一连串的字符
语法:string 变量名 = “字符串值”;
eg.
1 |
|
布尔类型
作用:表示真[true(1)]和假[false(0)]
eg.
1 | int main(){ |
数据的输入输出
作用:获取键盘输入
语法:cin >> 变量;
eg.
1 | int main(){ |
宏定义
宏定义在C++中是⼀个比较特殊的命令,它可以在⼀定程度上方便开发⼈员的程序设计过程。但是很多的初学者因为不能很好的去理解宏定义,不明白本质是什么,因此在使⽤宏定义的时候经常会出现问题。
- 宏定义:就是在⽂件的头部,使⽤
#define
来定义⼀个标识符⽤来描述⼀个字符串。 - 语法:#define 标识符 字符串
!!!
宏定义一般定义在main函数外,语句后可以没有;
,使用宏定义时,任何出现标识符的地方都会被后面的字符串替换
1 |
|
命名空间(namespace)
- 在C++中,名称(name)可以是符号常量、变量、函数、结构、枚举、类和对象等等。⼯程越大,名称互相冲突性的可能性越⼤。另外使用多个厂商的类库时,也可能导致名称冲突。为了避免,在大规模程序的设计中,以及在程序员使用各种各样的C++库时,这些标识符的命名发⽣冲突,标准C++引入关键字namespace(命名空间/名字空间/名称空间),可以更好地控制标识符的作用域。
我们之前在代码中已经出现过的
std
就是一个命名空间,这个命名空间中包含我们常用的cout,cin等
eg. 命名空间的定义及使用
1 |
|
eg. using语法
1 |
|
!!!
注意事项:不能using两个不同的整个命名空间,然后使用这两个命名空间中相同命名的函数,否则会产生二义性,还是要有命名空间 :: 函数名
的方式使用
运算符
算术运算符
eg. 假设两个数a为10,b为3,则有:
运算符 | 描述 | 示例 |
---|---|---|
+ | 表示两数相加 | a+b= 13 |
- | 表示两数相减 | a-b= 7 |
* |
表示两数相乘 | a*b= 30 |
/ | 表示分子除以分母 | a/b= 3(3.333) |
% | 取模运算符,表示整除后取余数 | a%b= 1 |
++a | 前置自增运算符,变量先+1,再进行表达式运算 | ++a= 11 |
a++ | 后置自增运算符,变量先进行表达式运算,再+1 | a++= 11 |
–b | 前置自减运算符,变量先-1,再进行表达式运算 | –b= 2 |
b– | 后置自减运算符,变量先进行表达式运算,再-1 | b–= 2 |
- 两个整数相除,结果还是整数,即使计算结果不是整数,程序也不会保留小数位,这与数据类型有关
- 基本常识:0不能作为除数,会报错
- 小数和小数可以相除,关键是考虑设置好数据类型
重点部分:理解自增自减
运行以下示例程序,寻找规律
1 |
|
运行结果:
怎么记忆
免责声明
本方法仅用于记忆自增自减,并不代表计算机运行程序的方向性
- ++在本身+1,–在本身-1
- ++(–)在前加了再说,变量在前用了再加(减)
- 基本原理拆解:
1 | int main(){ |
关系运算符
eg. 假设变量A的值为10,变量B的值为20,则
运算符 | 描述 | 示例 |
---|---|---|
== | 等于(等于要使用两个等号,一个等号是赋值,这点要注意) | (A==B) false |
!= | 不等于 | (A!=B) true |
> | 大于 | (A>B) false |
< | 小于 | (A<B) true |
>= | 大于等于**(同数学上一样,只要大于或等于有一个成立,即为真)** | (A>=B) false |
<= | 小于等于**(同数学上一样,只要小或等于有一个成立,即为真)** | (A<=B) true |
1 |
|
逻辑运算符
再次重复
1代表的是真(true),0代表的是假(false)
eg. 假设变量a的值为1,变量b的值为0,则
运算符 | 描述 | 示例 |
---|---|---|
&& | 逻辑与,如果两个操作都为true,则条件为true | (a&&b) false |
|| | 逻辑或,其中一个操作为true,则条件为true | (a||b) true |
! | 逻辑非,用于逆转运算结果,非真即假,非假即真 | !(a&&b) true |
^ | 逻辑异或,相同为假,不同为真 | (a^b) true / (a^a) false |
拓展了解
上面提到的逻辑与和逻辑或,又被称为短路与和短路或,与运算符还有&
,或运算符还有|
,短路与(左边结果为假,右边不参与运算)和短路或(左边结果为真,右边不参与运算),这样的动态运算使短路与和短路或具有内存优势,因此更多是使用它们
赋值运算符
运算符 | 描述 | 示例 |
---|---|---|
= | 简单赋值,将右边的数值赋值给左边的变量 | c = a + b,将a + b的值赋给c |
+= | 加且赋值,将右边的数值加到左边的变量中 | c += a,等同于 c = c + a |
-= | 减且赋值,从左边的变量中减去右边的数值 | c -= a,等同于 c = c - a |
三目运算符
- 三目运算符是一个具有简单逻辑的运算符
- 语法:condition ?value1 :value2;
- 解释:condition处是一个bool类型的变量或者是一个运算结果为bool类型的表达式,如果condition的结果为true时运算结果就为value1,结果为false时运算结果就为value2
eg.
1 |
|
杂项运算符
运算符 | 描述 | 示例 |
---|---|---|
sizeof | 统计内存大小运算符 | sizeof(int)/sizeof(a) |
程序流程结构
C/C++支持最基本的三种程序运行结构:顺序结构、选择结构、循环结构
- 顺序结构:程序按照顺序执行,不进行跳转
- 选择结构:依据条件是否满足,有选择的执行相应功能
- 循环结构:依据条件是否满足,循环多次执行某段代码
选择结构
选择结构:又被称为分支结构,通过判断条件是否符合,选择其中的一条分支继续执行
描述图:
if语句
单行格式if语句(如果if下的语句只有一行,可以不使用
{ }
)1
2
3if(条件){
条件满足时执行的语句
}多行格式if语句(if_else语句)
1
2
3
4
5
6if(条件){
条件满足时执行的语句
}
else{
条件不满足时执行的语句
}多条件if语句(if_else if_else语句)
1
2
3
4
5
6
7
8
9if(条件1){
满足条件1时执行的语句
}
else if(条件2){
满足条件2时执行的语句
}
else{
都不满足时执行的语句
}嵌套if语句
1
2
3
4
5if(条件1){
if(条件2){
在满足条件1的情况下还满足条件2执行的语句
}
}举例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using namespace std;
int main(){
int grade = 600;
if(grade >= 600){
cout << "优秀" << endl;
}
else if(grade >= 500){
cout << "良好" << endl;
}
else{
cout << "继续努力" << endl;
}
return 0;
}
三目运算符
提示
本部分内容在上面的运算符部分已经讲过了,就不再做重复switch语句
作用:执行多条件分支语句
语法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14switch(表达式){
case 结果1 :{
执行语句1
break;
}
case 结果2 :{
执行语句2
break; // break用于结束当前分支语句,没有break,switch语句有穿透性会继续执行
}
default : {
都不符合时执行的语句
break;
}
}eg.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using namespace std;
int main(){
int i = 1;
switch(i){
case 1:
cout << "Hello World!" << endl;
break;
case 2:
cout << "Hello World!2" << endl;
break;
case 3:
cout << "Hello World3" << endl;
break;
default:
cout << "Haaaa" << endl;
break;
}
return 0;
}运行结果:
Hello World!
Process exited after 0.1242 seconds with return value 0
请按任意键继续. . .switch的穿透性
穿透性:指的是当switch的变量和某⼀个case值匹配上之后, 将会跳过后续的case或者default的匹配,直接向后穿透。break可以用于避免穿透性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using namespace std;
int main(){
int i = 1;
switch(i){
case 1:
cout << "春暖花开" << endl;
case 2:
cout << "闷热" << endl;
case 3:
cout << "秋高气爽" << endl;
case 4:
cout << "滴水成冰" << endl;
default:
cout << "火星的" << endl;
}
return 0;
}运行结果:
春暖花开
闷热
秋高气爽
滴水成冰
火星的
Process exited after 0.1461 seconds with return value 0
请按任意键继续. . .
循环结构
循环结构:依据条件是否满足,循环多次执行某段代码
描述图:
while循环
作用:满足循环条件,执行循环语句
语法:while(循环条件){循环语句}
PS
:在执行循环语句时,程序必须提供跳出循环的出口,否则会出现死循环的情况eg.
1
2
3
4
5
6
7
8
9
10
11
using namespace std;
int main(){
int i = 0 ;
while(i < 10){
cout << "***" << endl;
i++;
}
return 0;
}运行结果:
do-while循环
与while语句的区别:while语句先判断再执行,因此while语句最少可以1次都不执行,但do-while语句则先执行语句再判断条件,因此do-while语句最少都要执行1次
语法:do{循环语句}while(循环条件)
eg.
1
2
3
4
5
6
7
8
9
10
11
using namespace std;
int mian(){
int i = 0;
do{
cout << "***" << endl;
i++;
}while(i < 10);
return 0;
}
for循环语句
语法:for(起始表达式;条件表达式;循环体){循环语句}
eg.
1
2
3for(int i = 0; i < 10; i++){
cout << i << endl;
}运行结果:
0
1
2
3
4
5
6
7
8
9
Process exited after 0.08312 seconds with return value 0
请按任意键继续. . .
嵌套循环
作用:在循环体里再嵌套一层循环,解决一些实际问题
eg. 只使用
cout << "@" << " " ;
作为输出语句,输出10列10行的@
1
2
3
4
5
6
7
8
9
10
11
12
13
14
using namespace std;
int main(){
// 内层执行10次,外层才执行1次
for(int i = 0;i < 10;i++){
for(int j = 0;j < 10;j++){
cout << "@" << " " ;
}
cout << endl;
}
return 0;
}运行结果:
@ @ @ @ @ @ @ @ @ @
@ @ @ @ @ @ @ @ @ @
@ @ @ @ @ @ @ @ @ @
@ @ @ @ @ @ @ @ @ @
@ @ @ @ @ @ @ @ @ @
@ @ @ @ @ @ @ @ @ @
@ @ @ @ @ @ @ @ @ @
@ @ @ @ @ @ @ @ @ @
@ @ @ @ @ @ @ @ @ @
@ @ @ @ @ @ @ @ @ @
Process exited after 0.06804 seconds with return value 0
请按任意键继续. . .break
可以用于结束当前循环eg.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using namespace std;
int main(){
for(int i = 0;i < 10;i++){
for(int j = 0;j < 10;j++){
cout << "@" << " ";
if(j > 4){
break; // 这个break在内层循环中,就是说大于4的列数不能输出,最终结果只能输出5列
}
}
cout << endl;
if (i > 3){
break; // 这个break在外层循环中,就是说大于3的行数不能输出,最终结果只能输出4列
}
}
return 0;
}运行结果:
@ @ @ @ @ @
@ @ @ @ @ @
@ @ @ @ @ @
@ @ @ @ @ @
@ @ @ @ @ @
Process exited after 0.09022 seconds with return value 0
请按任意键继续. . .continue
可以跳过当前轮数的循环eg. 通过
continue
语句输出1-20中的偶数1
2
3
4
5
6
7
8
9
10
11
12
13
using namespace std;
int main(){
for(int i = 1;i < 21;i++){
if(i % 2 != 0){
continue; // 这里的continue语句作用是告诉计算机当这个数为单数时请跳到下一次循环,不再执行输出
}
cout << i << " ";
}
return 0;
}运行结果:
2 4 6 8 10 12 14 16 18 20
Process exited after 0.09924 seconds with return value 0
请按任意键继续. . .
goto语句
作用:在程序中,可以任意的设置“标签”,使⽤关键字goto可以直接跳转到指定的标签位置继续执⾏程序!
PS
:在实际开发中不建议使用goto语句,否则逻辑会很乱,不便于阅读eg.
1
2
3
4
5
6
7label1:
cout << "1" << endl;
goto label3;
label2:
cout << "2" << endl;
label3:
cout << "3" << endl;运行结果:
1
3
Process exited after 0.09924 seconds with return value 0
请按任意键继续. . .
数组
- 概述:所谓数组,就是我们在高一数学第一课中讲到的集合(物以类聚),里面存放了相同类型的数据元素
- 特点
- 特点1:数组中的每一个元素都是相同的数据类型
- 特点2:数组是连续的内存位置组成的(这个特点的验证我们可以到指针部分详细的说明)
PS
数组在数据类型设置正确的情况下,不仅仅可以存放数字,还可以存放字符,字符串(需要用单引号或双引号包住,数字则不用)等数据
一维数组
概述:只有一行且有有限的元素的数组
一维数组的定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using namespace std;
int main(){
// 第一种定义方式
// 数据类型 数组名 [元素个数]
int score[10];
// 利用下标法赋值(从0开始)
score[0] = 166;
score[1] = 69;
score[2] = 22;
cout << score[0] << endl; // 166
cout << score[1] << endl; // 69
cout << score[2] << endl; // 22
cout << score[4] << endl; // 0(整型数组默认初始值为0)
// 第二种定义方式
// 数据类型 数组名[元素个数] = {元素1,元素2,元素3,...}
int score1[10] = {0,1,2,3,4,5,6,7,8,9} ;
// 遍历输出
for(int i = 0;i < 10;i++){
cout << score1[i] << " "; // 0 1 2 3 4 5 6 7 8 9
}
cout << endl;
// 第三种定义方式
// 数据类型 数组名[] = {值1,值2,....}
int score2[] = {1,5,46,5} ;
for(int j = 0;j < 4;j++){
cout << score2[j] << " "; // 1 5 46 5
}
return 0;
}获取数组占用内存空间大小
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using namespace std;
int main(){
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
cout << "整个数组占用的内存空间为:" << sizeof(arr) << endl;
cout << "每个元素所占内存空间的大小为:" << sizeof(arr[0]) << endl;
// 获取一维数组长度(使用这个数组所占内存大小除以单个元素内存大小)
// 已知数组数据类型时sizeof(arr[0])可以被替换为sizeof(int)
cout << "数组的长度为:" << sizeof(arr)/sizeof(arr[0]) << endl;
return 0;
}运行结果
整个数组占用的内存空间为:40
每个元素所占内存空间的大小为:4
数组的长度为:10
Process exited after 0.08026 seconds with return value 0
请按任意键继续. . .获取数组首位内存地址(&为取地址符,后续在指针那块会做详细讲解)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
using namespace std;
int main(){
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
// 获取数组的首地址(逻辑与&,又叫取地址符)
cout << "数组的首地址为:" << &arr << endl;
cout << "数组中第一个元素的地址为:" << &arr[0] << endl;
cout << "数组中第二个元素的地址为:" << &arr[1] << endl;
return 0;
}运行结果
数组的首地址为:0x70fdf0
数组中第一个元素的地址为:0x70fdf0
数组中第二个元素的地址为:0x70fdf4
Process exited after 0.08472 seconds with return value 0
请按任意键继续. . .我们可以看到直接去数组的地址得到的地址和数组第一个元素的地址相同,我们可以推测数组记录的地址就是首位元素的地址,再观察第一第二个元素的地址我们可以看到差值为4,在结合数据类型中整型的内存大小为4,就可以证明数组是有连续的内存位置组成的。
修改数组中的元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
using namespace std;
int main(){
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
// 修改数组中的元素
cout << "修改前第一个元素:" << arr[0] << endl; // 1
arr[0] = 100;
cout << "修改后第一个元素:" << arr[0] << endl; // 100
return 0;
}!!!
数组名是常量是一串固定的内存地址,不能修改
二维数组
概述:二维数组在一维数组的基础上增加了一个维度,就类似于一个表格
二维数组的定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
using namespace std;
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;
// 方式2
// 数据类型 数组名[行数][列数] = {{值1,值2},{值3,值4}};
int arr1[2][3] = {
{1,2,3},
{4,5,6}
};
// 方式3
// 数据类型 数组名[行数][列数] = {值1,值2,...};
int arr2[2][3] = {1,2,3,4,5,6};
// 方式4
// 数据类型 数组名[][列数] = {值1,值2,...};
int arr3[][3] = {1,2,3,4,5,6};
// 输出结果(可以通过修改数组名查看4种方式的结果)
for(int i = 0;i < 2; i++){
for(int j = 0 ; j < 3 ;j++){
cout << arr[i][j] << " ";
}
cout << endl;
}
return 0;
}运行结果
1 2 3
4 5 6
Process exited after 0.0901 seconds with return value 0
请按任意键继续. . .二维数组的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using namespace std;
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;
// 二维数组的元素修改跟一维数组是相同的,不做过多举例
return 0;
}运行结果
二维数组的大小为:24
二维数组中一行的大小为:12
二维数组中元素的大小为:4
二维数组的行数为:2
二维数组的列数为:3
二维数组的地址:0x70fdf0
二维数组第一行地址:0x70fdf0
二维数组第二行地址:0x70fdfc
二维数组第一个元素地址:0x70fdf0
二维数组第二个元素地址:0x70fdf4
Process exited after 0.07603 seconds with return value 0
请按任意键继续. . .我们可以看到二维数组的首地址与一维数组的首地址都是首元素的地址,我们在定义二维数组时每一行中都有三个元素,一个元素占用内存为4,则一列占用内存为12,c在16进制中表示的是13,与首地址的差值正好是12
函数
- 作用:将一段代码封装起来,减少重复代码
函数的定义
- 返回值类型
- 函数名
- 参数列表
- 函数体语句
- Return表达式
语法
1 | 返回值类型 函数名 (参数列表){ |
eg. 我们以我们每个程序中都有的主函数为例来看看函数的定义
1 | int main(){ |
eg. 实现一个加法函数,传入两个整型参数,计算结果并返回
- 返回值类型 int
- 函数名 add
- 参数列表 (int a,int b) (这个部分中的参数叫做形参,没有具体的值,需要传入参数)
- 函数体语句 int sum = a + b
- Return语句 return sum
1 | int add(int a,int b){ |
函数的调用
语法:函数名 (实际参数);
如:add(a,b);
eg.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using namespace std;
int add(int a,int b){
int sum = a + b;
return sum;
}
int main(){
// 调用add函数,把10传到a,20传到b,返回并输出结果
cout << add(10,20) << endl; // 30
int num1 = 10;
int num2 = 20;
int result = add(num1,num2);
cout << result << endl; // 30
return 0;
}
值传递
定义:值传递就是函数调用时实参的数值传给形参的过程
值传递时,如果形参发生改变,不会影响实参
eg.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using namespace std;
// 定义一个返回值为空类型的函数,实现一个交换两个参数的功能
void swap(int num1,int num2){
int temp = num1;
num1 = num2;
num2 = temp;
}
int main(){
int a = 12;
int b = 33;
swap(a,b);
cout << a << " " << b << endl; // 12 33
// 我们可以从结果看出num1和num2这两个形参的位置互换并没有导致a和b这两个实参的改变
// 每个变量都有对应的内存地址,由此可见a,b和num1,num2并不在同一地址,传参的过程只是将实参的值写入形参所在的空间(内存地址)
// 关于内存地址的详细内容见指针部分
system("pause");
return 0;
}
函数的常见形式
无参无返
1
2
3
4
5
6void test01(){
cout << "无参无返" << endl;
}
// 调用方法
test01();有参无返
1
2
3
4
5
6void test02(int a){
cout << "有参无返" << endl;
}
// 调用方法
test02(10);无参有返
1
2
3
4
5
6
7int test03(){
return 1000;
}
// 调用方法
int a = test03();
cout << a << endl; // 1000有参有返
1
2
3
4
5
6
7
8int test04(int a){
cout << "a的值为" << a << endl;
return a;
}
// 调用方法
int b = test04(100);
cout << b << endl; // 100完整演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using namespace std;
void test01(){
cout << "无参无返" << endl;
}
void test02(int a){
cout << "有参无返" << endl;
}
int test03(){
return 1000;
}
int test04(int a){
cout << "a的值为" << a << endl;
return a;
}
int main(){
test01();
test02(10);
int a = test03();
cout << a << endl; // 1000
int b = test04(100);
cout << b << endl; // 100
return 0;
}运行结果
无参无返
有参无返
1000
a的值为100
100
Process exited after 0.08018 seconds with return value 0
请按任意键继续. . .
函数的声明
当你把main函数放在前面,其他函数放在后面,但你又在主函数中调用后面的函数的时候,就要在主函数前面进行声明,意思是告诉主函数,有这么个函数在后面可以调用(原因:代码是一行一行从上到下执行的)
作用:告诉编译器函数名称及如何调用函数,函数的实际主体可以单独定义,函数的声明可以多次,函数的定义只能一次
eg.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using namespace std;
// 函数的声明
int max(int num1,int num2);
int main(){
int a = 10;
int b = 20;
cout << max(a,b) << endl;
return 0;
}
// 函数的定义
int max(int num1,int num2){
return num1 > num2 ? num1 : num2;
}最新版本的编译器支持不用声明使用主函数后的函数
函数的高阶用法
函数参数的默认值
在定义参数的时候我们可以给参数一个默认的值,例如:
1
2
3
4
5
6
7
8
9int add(int num1,int num2 = 100){
return num1 + num2;
}
// 调用这个函数的时候可以给num2传入实参也可以不传
int main(){
cout << add(100,200) << endl;
cout << add(100) << endl;
}需要注意的是,有默认值的参数,必须是在参数列表的末尾,否则会报错
函数的重载
如果存在多个函数,满足以下两点,则这两个函数构成了重载关系(Overload)
函数名相同
参数不同
参数的不同体现在数量不同,数据类型的不同
注意:重载只与函数的名字、参数有关,与返回值无关
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using namespace std;
// 重载载函数的定义
void add(int num1, int num2) {
cout << "add(int,int)" << endl;
}
void add(int num1, double num2) {
cout << "add(int,double)" << endl;
}
void add(double num1, int num2) {
cout << "add(double,int)" << endl;
}
void add(double num1, double num2) {
cout << "add(double,double)" << endl;
}
int main() {
// 区分重载函数靠参数类型,数量(按照返回值区分的函数不能重载)
add(10, 20); // 调的是add(int, int)的方法
add(10, 11.3); // 调的是add(int, double)的方法
add(11.2, 10); // 调的是add(double, int)的方法
add(11.1, 22.2); // 调的是add(double, double)的方法
return 0;
}函数的递归
递归,是一种程序设计的思想。在解决问题的时候,可以将问题拆分成若干个小问题。这些小问题的解决方式,与大 的问题解决方式相同。通过解决这些小的问题,逐渐解决这个大的问题。那么什么情况下可以使用递归呢?
- 需要解决的问题可以拆分为若干个小问题,大小问题的解决方法相同
- 有固定规律,自己调用自己
递归涉及到方法的循环调用,因此递归需要设置有效出口条件,避免死递归(无穷递归),导致方法无法结束,方法压栈运行导致栈溢出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using namespace std;
//函数的递归:有固定的规律,方法中调用自己
//设置有效出口条件,避免死递归(无穷递归),导致方法无法结束,压栈运行而出现栈溢出
// 定义一个计算阶乘的函数
int multiply(int num){
if(num == 1){
return 1;
}
return num * multiply(num - 1);
}
int main(){
cout << multiply(5) << endl;
return 0;
}- 函数的递归经典例题:斐波拉契数列
斐波那契数列
题目描述
斐波那契数列是指这样的数列:数列的第一个和第二个数都为 1,接下来每个数都等于前面 2 个数之和。
给出一个正整数a,要求斐波那契数列中第a个数是多少。
输入格式
第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数a(1 <= a <= 30)。
输出格式
输出有n行,每行输出对应一个输入。输出应是一个正整数,为斐波那契数列中第a个数的大小。
样例 #1
样例输入 #1
1
2
3
4
54
5
2
19
1样例输出 #1
1
2
3
45
1
4181
1我的题解(仅供参考)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using namespace std;
int fib(int index){
if(index == 1 || index == 2){
return 1;
}
return fib(index - 1) + fib (index - 2);
}
int main(){
int m,n;
cin >> m;
int time01[m];
for(int i = 0;i < m;i++){
cin >> time01[i];
}
for(int j = 0;j < m;j++){
n = time01[j];
cout << fib(n) << endl;
}
return 0;
}斐波拉契数列函数的简单实现
1
2
3
4
5
6int fib(int num){
if(num == 1 || num == 2){
return 1;
}
return fib(num - 1) + fib(num - 2);
}调用其他文件里的函数
我们程序中默认只能调用当前文件中定义的函数,但是一个大型的项目不止由一个文件来组成,很多时候是需要跨文 件来调用、访问的。例如,我们将常用的数学计算的功能定义在某一个文件中,那么如何调用这个文件中定义的函数呢
首先我们需要知道,.cpp文件中的内容是无法跨文件直接访问的,如果我们需要让某一个函数跨文件访问,需要为其定义一个.h文件,我们称为“头文件”。在头文件中添加函数的声明部分即可。需要使用的时候,直接使用#include来包含指定的头文件即可完成。
1
2
3
4
5
6
7
8
9
10// 文件名:feibo.cpp
int feibo(int index){
if(index == 1 || index == 2){
return 1;
}
return feibo(index - 1) + feibo (index - 2);
}1
2
3
4// 文件名:feibo.h
// 只做声明不做定义
int feibo(int index);1
2
3
4
5
6
7
8
9
10
11// 文件名:main.cpp(可自行选择需要调用函数的文件)
using namespace std;
int main(){
// 此时可以直接调用feibo.cpp中定义的函数
cout << feibo(10) << endl;
return 0;
}
指针
指针的概念
作用:可以通过指针来间接访问内存
内存编号从0开始,一般使用16进制数字表示
可以利用变量保存地址
指针占用的内存地址:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
using namespace std;
int main(){
// 指针也是一种数据类型,看看指针的内存大小
cout << "int类型指针占用内存:" << sizeof(int *) << endl;
cout << "float类型指针占用内存:" << sizeof(float *) << endl;
cout << "double类型指针占用内存:" << sizeof(double *) << endl;
cout << "char类型指针占用内存:" << sizeof(char *) << endl;
return 0;
}运行结果
64位
int类型指针占用内存:8
float类型指针占用内存:8
double类型指针占用内存:8
char类型指针占用内存:8
Process exited after 0.07549 seconds with return value 0
请按任意键继续. . .32位
int类型指针占用内存:4
float类型指针占用内存:4
double类型指针占用内存:4
char类型指针占用内存:4
Process exited after 0.1363 seconds with return value 0
请按任意键继续. . .在32位操作系统下,指针占用4byte内存,在64位操作系统下,指针占用8byte内存
每定义一个变量,程序会向计算机申请一个内存空间,空间的大小取决于数据类型
指针的定义及使用
指针的定义
语法:数据类型 *变量名
eg.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using namespace std;
int main(){
// 定义一个指针
int num1 = 10;
// 指针的定义语法: 数据类型 *变量名
// 指针的数据类型需要和变量的数据类型相同
int *p;
// &取地址符
p = &num1;
cout << "num1的地址是:" << &num1 << endl;
cout << "指针p是:" << p << endl;
return 0;
}运行结果
num1的地址是:0x70fe14
指针p是:0x70fe14
Process exited after 0.1053 seconds with return value 0
请按任意键继续. . .
解引用
语法:*指针名称
作用:通过指针找到对应的内存地址
eg.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using namespace std;
int main(){
// 定义一个指针
int num1 = 10;
// 指针的定义语法: 数据类型 *变量名
// 指针的数据类型需要和变量的数据类型相同
int *p;
// &取地址符
p = &num1;
// 使用指针
// 可以通过解引用的方式来找到指针指向的内存
// 指针前面加一个*表示解引用
cout << "指针p指向的内存的值为:" << *p << endl;
*p = 100; // 可以通过修改指针p指向内存的值来修改变量的值
cout << "现在num1的值:" << num1 << endl;
cout << "现在指针p指向的内存的值是:" << *p << endl;
return 0;
}运行结果
指针p指向的内存的值为:10
现在num1的值:100
现在指针p指向的内存的值是:100
Process exited after 0.03959 seconds with return value 0
请按任意键继续. . .
指针在数组中的应用
- 利用指针访问数组中的元素
1 | using namespace std; |
运行结果
第一个元素是:1
指针访问的第一个元素是:1
1
2
3
4
5
6
7
8
9
10
Process exited after 0.07373 seconds with return value 0
请按任意键继续. . .
指针在函数中的应用
- 作用:利用指针函数修改实参的值
1 |
|
运行结果
&n = 0x70fe1c
&num = 0x70fdf0
10
0x70fe1c
200
Process exited after 0.1821 seconds with return value 0
请按任意键继续. . .
常量引用:在形参前加const可以规避变量因误操作而被修改
空指针和野指针
空指针:指针指向内存编号为0的空间
用途:初始化指针变量
注意:空指针指向的内存不可访问
eg.
1
2
3
4
5
6
7
8
9
10
11
12
using namespace std;
int main(){
// 空指针
int *p = NULL;
// 内存地址0-255为系统保留内存,没有权限访问
// 编译运行后无任何输出
cout << *p << endl;
return 0;
}野指针:指向非法内存地址的指针
eg.
1
2
3
4
5
6
7
8
9
10
11
12
13
using namespace std;
int main(){
// 野指针
// 指针变量p指向内存地址为0x1000的空间
int *p = (int *)0x1000;
cout << *p << endl;
// 编译运行后无输出
return 0;
}
常量指针和指针常量
const修饰指针的三种情况
- const修饰指针——常量指针(指针的指向可以修改,但指向的值不可用修改)
- const修饰常量——指针常量(指针的指向不可用修改,但指向的值可以修改)
- const既修饰指针,有修饰常量(指针指向不可用修改,指向的值不可以修改)
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using namespace std;
int main(){
int num1 = 10;
int num2 = 20;
// 常量指针
const int *p = &num1;
*p = 20; // 运行报错,不能修改指向的值
p = &num2; // 正确,可以修改指针指向
// 指针常量
int * const p2 = &num1;
*p2 = 100; // 正确,可以修改指向的值
p2 = &num2; // 运行报错,不可以修改指针指向
// const修饰指针和常量
const int * const p3 = &num1;
*p3 = 100; // 错误
p3 = &num2; // 错误
return 0;
}
引用
作用:作为一个已定义变量的别名,值和内存地址都与原变量一致
语法:数据类型& 引用名称 = 变量/值
eg.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using namespace std;
int main(){
int num = 10;
// 定义一个引用
int& a = num;
// 比较值
cout << "num = " << num << " " << "a = " << a << endl;
// 比较地址
cout << "&num = " << &num << " " << "&a = " << &a << endl;
a = 100;
cout << "num = " << num << " " << "a = " << a << endl;
return 0;
}运行结果
num = 10 a = 10
&num = 0x70fe04 &a = 0x70fe04
num = 100 a = 100
Process exited after 0.08213 seconds with return value 0
请按任意键继续. . .引用的实质就是一个指针常量
引用在函数中的应用
- 在之前函数的部分中我们说到修改形参不会对实参造成影响,如果我们想通过函数来修改实参要怎么做,引用就可以做到,同样以对换变量的值的函数为例
1 |
|
运行结果
(10,20)
(20,10)
Process exited after 0.08361 seconds with return value 0
请按任意键继续. . .
结构体
结构体的定义
语法:
1
2
3
4
5struct 结构体名称{
// 成员属性
int 成员1;
char 成员2;
}示例程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using namespace std;
// 公有的,C语言的老古董
struct Book{
string name;
string author; // 如果结构体中成员有初始值,赋值不能使用花括号
string date;
}book2,book3; // 全局变量
// 私有的
class Book00{
string name;
string author;
string date;
}
int main(){
// 局部变量 01
Book book1;
book1.name = "西游记";
book1.author = "吴承恩";
book1.date = "很久以前";
// 局部变量02
// 这种方式没有赋值的部分一定是后面的部分
book3 = {"数学","全中国高中生的噩梦","2019吧"};
cout << book1.date << endl;
return 0;
}
结构体的使用
在函数中的使用
1 |
|
在数组中的使用
1 |
|
运行结果
西游记
吴承恩
2020
数学
出版社
2019
Process exited after 0.07819 seconds with return value 0
请按任意键继续. . .
和指针的应用
1 |
|
结构体的嵌套
1 |
|
运行结果
1888
书名:我修改了书名
出版年份:1888
我修改了书名
Process exited after 0.1055 seconds with return value 0
请按任意键继续. . .