前言
b站C++课程学习笔记整理。
b站视频: 黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难
2. Hello World
- Visual Studio 2022 新建一个项目
- 选择空项目,命名
- 选择源文件,右键-添加-新建项-命名
- 编写代码
1 2 3 4 5 6 7 8
| #include <iostream>
using namespace std; int main(){ cout << "Hello world" << endl; system("pause"); return 0; }
|
注意:可以用\n
来代替endl
。
3. 注释
4. 变量
和Java类似。int a = 10;
1 2 3 4 5 6 7 8
| #include <iostream> using namespace std; int main(){ int a = 10; cout << "a="<< a << endl; system("pause"); return 0; }
|
5. 常量
类似于Java中final
修饰的变量。
两种定义方式:
# define 常量名 常量值
,定义一个宏常量,写在文件上方。(注意没有分号;
)
const 数据类型 常量名 = 常量值
(类似final
)
示例代码:
1 2 3 4 5 6 7 8 9 10
| #include <iostream> #define week 7 using namespace std; int main(){ const int year = 12; cout << "一周一共有"<< week << "天" << endl; cout << "一年一共有" << year << "个月" << endl; system("pause"); return 0; }
|
6. 关键字
变量名不要用关键字修饰。比如变量名不可以为int
。
7. 标识符命名规则
给变量起名,不能是关键字,不能有空格,必须是字母、数字、下划线组成。第一个字符必须为字母或者下划线。字母区分大小写。
8. 数据类型 整型
short
int
long
longlong
。和Java一样。
9. sizeof 关键字
可以统计数据类型所占内存大小。
1 2 3 4 5 6 7 8 9
| #include<iostream> using namespace std; int main() { short num1 = 10; cout << "short占用的内存空间为:" << sizeof(short) << endl; cout << "short占用的内存空间为:" << sizeof(num1) << endl; system("pause"); return 0; }
|
用sizeof
查就好了
10. 实型(浮点型)
float
和double
。
科学计数法:
1 2 3 4 5 6 7 8
| #include<iostream> using namespace std; int main() { float f = 3e2; cout << "f等于" << f << endl; system("pause"); return 0; }
|
11. 字符型
语法:char ch = 'a'
;和java一样。底层是ASCII码存储。
12. 转义字符
表示一些不能显示出来的ASCII字符。都是用’\'开头的。
常用:
字符 |
含义 |
\n |
换行 |
\t |
相当于按了一下TAB |
\\ |
代表一个\ |
示例代码:
1 2 3 4 5 6 7 8 9 10 11
| #include <iostream> using namespace std;
int main(){ cout << "hellow world\n"; cout << "\tnihao\n"; cout << "\\" << endl;
system("pause"); return 0; }
|
结果:
13. 字符串名
两种定义方式:
char 变量名[] = "字符串值"
。其实可以理解成字符串数组。
String 变量名 = "字符串值"
。
#include<string>
在新版的VScode时不用加上的了。
14. 布尔数据类型
true
和false
。定义方式是bool flag = false
;和java的关键字不同。
15. 数据的输入
语法:
cin >> 变量
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12
| #include <iostream> using namespace std;
int main(){ int a = 0; cout << "请赋值" << endl; cin >> a; cout << "输入的变量是" << a << endl;
system("pause"); return 0; }
|
16. 加减乘除
+-*/
17. 取模
%
18. 递增递减
i++
或者++i
;
i--
或者--i
19. 赋值运算
=
,+=
,-=
,*=
,/=
,%=
20. 比较运算
==
>
<
>=
<=
!=
21-23. 与或非
&&
||
!
24-27. if 语句
和Java一样。
28-29. 三目运算符
c = a > b? a : b
和Java一样。
30. switch
和Java一样。
1 2 3 4 5 6
| switch (a) { case 1: 执行; break; }
|
31-32. while
while(){}
。 和Java一样。
33. do…while
do{}while()
34- 40 for循环,break,continue
和Java一样。
41. goto
无条件的跳转到想要的代码。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <iostream> using namespace std;
int main(){ cout << "1" << endl; cout << "2" << endl; goto FLAG; cout << "3" << endl; cout << "4" << endl; FLAG: cout << "5" << endl;
system("pause"); return 0; }
|
输出:
但是goto最好别用。
42. 数组-一维数组定义方式
三种定义方式:
数据类型 数组名[数组长度]
数据类型 数组名[数组长度] = {值1, 值2, ....}
数据类型 数组名[] = {值1, 值2, ....}
和Java那个不推荐的定义方式类似。
43. 一维数组的名称的用途
- 统计整个数组在内存中所占空间的长度
sizeof(arr)
。
- 获取数组在内存中的首地址。
数组中元素的个数:sizeof(arr)/sizeof(arr[0])
;
取址符:&
44-45. 数组元素逆置
老套路
46. 冒泡排序
老套路
47. 二维数组
四种定义方式:
数据类型 数组名[行数][列数]
数据类型 数组名[行数][列数] = {{数据1,数据2}, {数据3,数据4}}
数据类型 数组名[行数][列数] = {值1, 值2, ....}
(和第二种一样,没第二种直观。)
数据类型 数组名[][列数] = {值1, 值2, ....}
它自己能根据后面数据算出来是几行。
48. 二维数组的数组名
行数:sizeof(arr)/sizeof(arr[0])
列数:sizeof(arr[0])/sizeof(arr[0][0])
元素个数: sizeof(arr)/sizeof(arr[0][0])
二维数组的首地址、二维数组的第一行的首地址、二维数组的第一个数据的首地址是一致的。
49. 考试成绩统计
使用String要在头文件声明
50. 函数
函数的定义:
- 返回值类型
- 函数名
- 参数列表
- 函数体语句
- return 表达式
语法:
1 2 3 4 5 6
| 返回值类型 函数名 (参数列表) { 函数体; return xx; }
|
和Java一样(没有Java那些花里胡哨的public
)
51. 函数的调用
和Java一样
52. 值传递
函数调用时实参将数值传入形参。形参改变不会影响实参。
53. 常见样式
- 无参无返(用void)
- 有参无返
- 无参有返
- 有参有返
54. 函数的声明
代码是一行一行执行的。如果函数在main后面,就找不到了,所以需要提前对函数声明。
int max(int a, int b)
。没有花括号。
(新版的VS已经不需要声明了。例如执行如下代码,没有声明,但是是完全OK的。)
1 2 3 4 5 6 7 8 9 10 11
| #include <iostream> using namespace std;
int main(){ int a = 1; int b = 2; cout << max(a, b) << endl; } int max(int a, int b) { return a > b ? a : b; }
|
55. 函数的分文件编写
- 创建后缀名为.h的头文件
- 创建后缀名为.cpp的源文件
- 在头文件写函数的声明
- 在源文件中写函数的定义
这个头文件,有点像Java里面的import
,又有点像继承。
示例代码:
- 头文件
function.h
声明了两个函数’swap’和print2nums
1 2 3 4 5 6 7
| #pragma once #include<iostream> using namespace std;
void swap(int a, int b); void print2nums(int a, int b);
|
1 2 3 4 5 6 7 8 9
| #include "function.h";
void swap(int a, int b) { cout << "交换前的数字是" << a << "和" << b << "\n"; int temp = a; b = temp; a = b; cout << "交换前的数字是" << a << "和" << b << endl; }
|
1 2 3 4 5
| #include "function.h";
void print2nums(int a, int b) { cout << "两个数字是" << a <<"+" << b << endl; }
|
1 2 3 4 5 6 7 8 9 10 11
| #include<iostream>; #include "function.h"; using namespace std;
void main() { int a = 10; int b = 20; swap(a, b); print2nums(a, b); system("pause"); }
|
56. 指针
可以通过指针来保存一个地址。(指针就是个地址)
假设现在有一个变量int a = 10;
- 定义指针:
int * p
;
- 使用取址符记录地址
p = &a;
- 使用指针:可以通过解引用的方式找到指针指向的内存。
*p
:表示这个指针p指向的该地址上存储的值是多少。
57. 指针所占的内存空间
指针存的是一块地址,是一个16进制的数,占用4个字节(32位操作系统)。64位操作系统占8个。
58. 空指针
空指针:指向内存中编号为0的空间。
用途:初始化指针变量。
注意:空指针指向的的内存不可访问(null
)。
定义:int * p = NULL
。
c++中的空是全大写的NULL
。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include<iostream>; #include "function.h"; using namespace std;
void main() { int a = 10; int* p = NULL;
p = &a; cout << "a的值是" << *p << endl; cout << "a的地址值是" << p << endl; system("pause"); }
|
59. 野指针
指针变量指向非法的内存空间。(指向没有申请的,非法的地址空间)
示例代码:
1 2 3 4 5 6 7 8 9
| #include<iostream>; #include "function.h"; using namespace std;
void main() { int * p = (int*)0x0011;
cout << *p << endl; }
|
效果:
60. const修饰指针
const修饰指针的三种情况:
- const修饰指针 — 常量指针:
const int * p
,指针指向的值(*代表值)不可以改,指针指向可以改
flowchart LR
1((pointer))-->a
1((pointer))-.->b
style a fill:#f9f,stroke:#333,stroke-width:4px
- const修饰常量 — 指针常量:
int * const p
,指针指向的值可以改,指针指向不可以改
flowchart LR
1((pointer))-->a
1((pointer))-.-xb
style b fill:#f9f,stroke:#333,stroke-width:4px
- const即修饰指针,又修饰常量,
const int * const p
。都不可以改。
flowchart LR
1((pointer))-->a
1((pointer))-.-xb
style a fill:#f9f,stroke:#333,stroke-width:4px
style b fill:#f9f,stroke:#333,stroke-width:4px
61. 使用指针访问数组
普通方式就像Java一样,for循环,然后遍历。
也可以通过指针,右移的方式遍历。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include<iostream>; #include "function.h"; using namespace std;
void main() { int arr[] = { 0,1,2,3,4,5,6,7,8,9 }; for (int i = 0; i < (sizeof(arr) / sizeof(arr[0])); i++) { cout << arr[i] << endl; }
cout << "--------------------------------" << endl; int* p = arr; for (int j = 0; j < (sizeof(arr) / sizeof(arr[0])); j++) { cout << *p << endl; p++; } }
|
62. 指针和函数
在之前的例子中,如果main函数的实参传入到某函数的形参中,形参的改变是不会改变实参。但是如果main函数的实参的指针传入到某函数作为形参,这个函数是可以通过地址改变实参的。
总结:想要改实参,用地址传递,不想改实参,用值传递。
63. 指针、数组、函数
案例描述:封装一个函数,利用冒泡排序,实现对这个数组的升序排列。
注意,c++不可以直接返回一个数组类型int[] function(){}
这种定义形式是不允许的。要用指针来解决。
例如,要对一个数组进行冒泡排序,代码是:
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
| #include<iostream>; #include "function.h"; using namespace std;
int* bubbleSort(int arr[], int len) { for (int i = 0; i < len - 1; i++) { for (int j = 0; j < len - 1 - i; j++) { if (arr[j + 1] < arr[j]) { int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } return arr; }
void printArray(int* p, int len) { for (int i = 0; i < len; i++) { cout << *p << endl; p++; } }
int main() { int arr[] = { 0,1,7,3,4,5,6,2,8,9 }; int len = sizeof(arr) / sizeof(arr[0]); int* p = bubbleSort(arr, len); printArray(p,len); return 0; }
|
指针可以用来记录一个数组的首地址。这是一个很重要的应用。
64. 结构体
结构体就是用户自定义的数据类型。(就像Java中自己定义一个类Student)
语法:struct 结构体名{结构体成员列表}
。
通过结构体创建变量的方式有三种:
struct 结构体名 变量名
struct 结构体名 变量名 = {成员1值, 成员2值}
- 定义结构体时顺便创建变量
示例: 定义一个学生类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <iostream> using namespace std;
struct student { string name; int age; };
int main(){ struct student s1; s1.age = 21; s1.name = "孙健耕";
cout << s1.age<<s1.name << endl; }
|
其实结构体就是Java里面的那个标准类的青春版(没构造方法和set方法)。
而且,结构体本身不能直接打印。
65. 结构体数组
将自定义的结构体放入数组中方便维护。
语法:struct 结构体名 数组名[元素个数]={{},{},{}}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <iostream> using namespace std;
struct student { string name; int age; };
int main(){ struct student s1; s1.age = 21; s1.name = "孙健耕"; struct student s2; s2.age = 24; s2.name = "Janko";
struct student arr[2] = { s1 ,s2 };
cout << arr[0].age << arr[0].name << endl; }
|
类似于Java。
66. 结构体指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <iostream> using namespace std;
struct student { string name; int age; };
int main(){
student s1 = {"孙健耕",21};
student* p = &s1;
cout << "年龄是:" << p->age << ",姓名是:" << p->name << endl;
|
->
本质上就是访问了成员变量的一种简写而已。
67. 结构体嵌套结构体
比如现在有一个学生结构体,包含姓名年龄,还有一个老师结构体,包含姓名年龄,还有这个老师带的学生结构体。
结构体可以嵌套。注意嵌套顺序。
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
| #include <iostream> using namespace std;
struct student { string name; int age; };
struct teacher { string name; int age; student person; };
int main(){
student s1 = {"孙健耕",21};
teacher t1 = { "Sven",52,s1 };
student* p = &s1; student* p2 = &t1.person;
cout << "年龄是:" << p->age << ",姓名是:" << p->name << endl; cout << "年龄是:" << p2->age << ",姓名是:" << p2->name <<"他的学生名字是" <<p2->name<< endl;
}
|
68. 结构体做函数参数
比如,将学生传入一个参数中,打印学生身上所有的信息。
值传递:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <iostream> using namespace std;
struct student { string name; int age; };
void printStudent(student person) { cout << "姓名是" << person.name << ",年龄是" << person.age<<endl; }
int main(){ student s1 = {"孙健耕",21}; printStudent(s1); return 0; }
|
地址传递:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <iostream> using namespace std;
struct student { string name; int age; };
void printStudent(student* p) { cout << "姓名是" << p->name << ",年龄是" << p->age<<endl; }
int main(){ student s1 = {"孙健耕",21}; student* p = &s1; printStudent(p); return 0; }
|
注意别搞混了,不能直接*p
,因为这个是自定义的结构体。地址传递可以改变实参的。
69. 结构体中const使用场景
作用:使用const
防止误操作。
如果数据样本很大,比如每个student结构体中有很多信息,而且student的数量很大,如果使用值传递,会占用很多的内存空间。这时可以使用地址传递(一个指针也就占4个字节),节约空间。但是值传递可能会有误修改影响实参的操作。这时候可以通过const
来进行保护。
70-71 结构体案例
设计一个英雄结构体,包含姓名,年龄,性别,然后用冒泡排序,按年龄升序排序并打印。
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 41 42 43 44 45 46 47
| #include <iostream> using namespace std;
struct hero { string name; int age; string gender; };
void printArr(hero* p, int len) { for (int i = 0; i < len; i++) { cout << "名字:\t" << p->name << "年龄是:\t" << p->age << "性别是:\t" << p->gender << endl; p++; } }
hero* bubbleSort(hero heroArr[], int len) { for (int i = 0; i < len - 1; i++) { for (int j = 0; j < len - 1 - i; j++) { if (heroArr[j].age > heroArr[j + 1].age) { hero temp = heroArr[j]; heroArr[j] = heroArr[j + 1]; heroArr[j + 1] = temp; } } } return heroArr; }
int main(){ hero h1 = { "刘备",21,"男" }; hero h2 = { "关羽",22,"男" }; hero h3 = { "张飞",20,"男" }; hero h4 = { "赵云",21,"男" }; hero h5 = { "貂蝉",19,"女" };
hero heroArr[] = { h1,h2,h3,h4,h5 }; int len = sizeof(heroArr) / sizeof(heroArr[0]); cout << "排序前打印" << endl; for (int i = 0; i < len; i++) { cout << "名字:\t" << heroArr[i].name << "年龄是:\t" << heroArr[i].age << "性别是:\t" << heroArr[i].gender << endl; } cout << "排序后打印" << endl; printArr(bubbleSort(heroArr, len),len); return 0; }
|