扬仔的杂货铺

分享微不足道的经验,记录稀奇古怪的玩意

0%

C++基础入门

第一个C++程序

1
2
3
4
5
6
#include <iostream>

int main (){
std::cout << "Hello World!" << std::endl;
return 0;
}

变量

变量存在的意义:方便我们管理内存空间

变量创建的语法:

1
2
3
数据类型 变量名 = 变量初始值;

int a = 10;

常量

作用:用于记录程序中不可更改的数据

C++定义常量的两种方式

  1. #define 宏常量: #define 常量名 常量值
    1. 通常在文件上方定义
  2. const 修饰的变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 常量的定义方式
// 1、#define 宏常量
// 2、const修饰的变量
#define Day 7
int main (){
// Day = 14; 错误,day是常量,一旦修改就会报错
std::cout << "一周总共有:" << Day << "天" << std::endl;
return 0;
}
// ===============
int main (){
const int month = 12;
// month = 24; 是常量无法进行修改
std::cout << "一年总共有:" << month << "月" << std::endl;
return 0;
}

标识符

  1. 标识符不能是关键字
  2. 标识符只能由字母、数字、下划线组成
  3. 标识符第一个字符只能是字母或下划线
  4. 标识符区分大小写

函数的声明

作用:告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义

函数的声明可以写多次,但是定义只能写一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 函数的声明(这样就可以告诉编译器这个函数是存在的)
int get_max(int a ,int b);

int main ()
{
int num1 = 10;
int num2 = 2;
cout << get_max(num1, num2) << endl;
return 0;
}

// 定义
int get_max(int a ,int b){
return a > b ? a : b;
};

函数的分文件编写

作用:让代码结构更加清晰

函数分文件编写一把有4个步骤

  1. 创建后缀名为.h的头文件
  2. 穿件后缀名为.cpp的源文件
  3. 在头文件中写函数的声明
  4. 在源文件中写函数的定义

swap.h

1
2
3
4
#include <iostream>
using namespace std;
// 函数的声明
void swap(int a, int b);

swap.cpp

1
2
3
4
5
6
7
8
9
10
11
#include "swap.h"

// 函数的定义
void swap(int a ,int b)
{
int temp = a;
a = b;
b = temp;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}

main.cpp

1
2
3
4
5
6
7
#include "swap.h"

int main ()
{
swap(1,2);
return 0;
}

CMakeLists.tst

1
2
3
4
5
6
cmake_minimum_required(VERSION 3.22)
project(demo)

set(CMAKE_CXX_STANDARD 14)

add_executable(demo main.cpp swap.cpp)

指针

指针的基本概念

指针的作用:可以通过指针间接访问内存

  1. 如何定义一个指针

    指针定义的语法:数据类型 * 指针变量名

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    int main ()
    {
    int a = 10;
    // 定义一个指针
    int * p;
    // 让指针记录变量a的地址
    p = &a;
    cout << "a的地址为:" << &a << endl;
    cout << "指针p为:" << p << endl;
    return 0;
    }

    /*
    a的地址为:0x16db03868
    指针p为:0x16db03868
    */
  2. 如何使用指针

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 使用指针
    // 指针前加*代表解引用,找到指针指向的内存中的数据
    *p = 1000;
    cout << "a = " << a << endl;
    cout << "*p = " << *p << endl;

    /*
    a = 1000
    *p = 1000
    */

指针所占内存空间

在32位操作系统下:占用4个字节空间

在64位操作系统下:占用8个字节空间

1
2
3
4
5
6
7
8
9
10
11
12
13
int main ()
{
int a = 10;
int * p = &a;
cout << "指针p大小为:" << sizeof (int *) << endl;
cout << "指针p大小为:" << sizeof (p) << endl;
return 0;
}

/*
指针p大小为:8
指针p大小为:8
*/

空指针和野指针

空指针

指针变量指向内存中编号为0的空间

用途:初始化指针变量

注意:空指针指向的内存是不可以访问的

1
2
3
4
5
6
7
8
9
10
11
int main ()
{
//空指针
//1、空指针用于给指针变量进行初始化
int * p = NULL;

//2、空指针是不可以进行访问的
//0~255之间的内存编号是系统占用的,因此不可以访问
*p = 100;
return 0;
}

野指针

指针变量指向非法的内存空间

1
2
3
4
5
6
7
8
9
 int main ()
{
//野指针
//在程序中应该尽量避免野指针
//随便指定的指针地址是没有权限进行操作的
int * p = (int *)0x11000000;
cout << *p << endl;
return 0;
}

const修饰指针

const修饰指针有三种情况

  1. const修饰指针:常量指针

    const int * p = &a;

    特点:指针的指向可以修改,单是指针指向的值不可以改

    1
    2
    *p = 20;	// 错误
    p = &b; // 正确
  2. const修饰常量:指针常量

    int * const p = &a;

    特点:指针的指向不可以改,指针指向的值可以改

    1
    2
    *p = 20;	// 正确
    p = &b; // 错误
  3. const既修饰指针,又修饰常量

    const int * const p = &a;

    特点:指针的指向和指针指向的值都不可以改

    1
    2
    *p = 20;	// 错误
    p = &b; // 错误

指针和数组

作用:利用指针访问数组中的元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main ()
{
//指针和数组
//利用指针访问数组中的元素
int arr[10] ={1,2,3,4,5,6,7,8,9,10};

int * p = arr;//arr 就是数组首地址

cout << "第一个元素:" << arr[0] << endl;
cout << "指针访问第一个元素:" << *p << endl;

for (int i = 0; i < 10; ++i) {
// 利用指针遍历数组
cout << *p << endl;
p++;
}
}

指针和函数

作用:利用指针作为函数的参数,可以修改实参的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 实现两个数字进行交换
void swap(int *a,int *b){
int temp = *a;
*a = *b;
*b = temp;
}

int main ()
{
//指针和函数
//地址传递
int a = 10;
int b = 20;
swap(&a,&b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}

个人理解:

int *a 这里需要传递的是一个地址,* 的意义不是单纯的指针 而是通过地址的取值符号

也就是说,*a 的完整意思是在地址a中保存的值,单个a就表示一个地址

int temp = *a; 的意思不是地址给到temp,而是 **通过地址取值这个组合 **给temp

也就是概念意义上的把地址传递了

指针、数组、函数

案例描述:封装一个函数,利用冒泡排序,实现对整型数组的升序排序

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
void bubbleSort(int * arr,int len) {
for (int i = 0; i < len - 1; ++i) {
for (int j = 0; j < len - i - 1; ++j) {
// 如果 j > j + 1 的值 交换数字
if(arr[j] > arr[j + 1]){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}


int main ()
{
// 1、创建一个数组
int arr[10] = { 4,3,6,9,1,2,10,8,7,5};
// 计算数组排序
int len = sizeof(arr) / sizeof(arr[0]);

// 2、创建函数,实现冒泡排序
bubbleSort(arr,len);

// 3、打印排序后的数组
for (int i = 0; i < len; ++i) {
cout << arr[i] << endl;
}
}

结构体

结构体的基本概念

结构体属于用户自定义的数据类型,允许用户存储不同的数据类型

结构体定义和使用

语法:struct 结构体名{结构体成员列表}

通过结构体创建变量的方式有三种:

  • struct 结构体名 变量名
  • struct 结构体名 变量名 = {成员1值, 成员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
36
37
38
39
40
// 1、创建学生数据类型:学生包括(姓名,年龄,分数)
// 很多的类型放到一起组合成一个数据类型,就是结构体
// 语法 struct 类型名称 {成员列表}
struct Student {
// 成员列表
// 姓名
string name;
// 年龄
int age;
// 分数
int score;
} s4;
// 2、通过学生类型创建具体学生
int main ()
{
// 2.1 struct Student s1
struct Student s1 ;
s1.name = "张三";
s1.age = 20;
s1.score = 100;
// 2.2 struct Student s2 = { ... }
struct Student s2 = {
.name = "李四",
.age = 18,
.score = 98
};
struct Student s3 = {"王五",22,23};
// 2.3 在定义结构体时顺便创建结构体变量(在定义的后面直接起名字)
s4.name = "赵六";
s4.age = 66;
s4.score = 55;

cout << "姓名:" << s1.name << " 年龄:" << s1.age << " 分数:" << s1.score << endl;
cout << "姓名:" << s2.name << " 年龄:" << s2.age << " 分数:" << s2.score << endl;
cout << "姓名:" << s3.name << " 年龄:" << s3.age << " 分数:" << s3.score << endl;
cout << "姓名:" << s4.name << " 年龄:" << s4.age << " 分数:" << s4.score << endl;
// 平时用123,定义的时候struct 也可以省略
// struct Student s1;
// Student s1;
}

结构体数组

作用:将自定义的结构体放入到数组中方便维护

语法struct 结构体名 数组名[元素个数] = { {},{}, ... {} }

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
// 结构体数组
// 1、定义结构体
struct Student
{
// 姓名
string name;
// 年龄
int age;
// 分数
int score;
};

int main ()
{
// 2、创建结构体数组
Student stuArray[3] = {
{"张三", 18, 100},
{"李四", 28, 99},
{"王五", 38, 66}
};
// 3、给结构体数组中的元素赋值
stuArray[2].name = "赵六";
stuArray[2].age = 80;
stuArray[2].score = 60;

for (int i = 0; i < 3; ++i) {
cout << "姓名:" << stuArray[i].name
<< "年龄:" << stuArray[i].age
<< "分数:" << stuArray[i].score << endl;
}
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
// 结构体指针
struct Student
{
// 姓名
string name;
// 年龄
int age;
// 分数
int score;
};

int main ()
{
// 1、创建学生结构体变量
struct Student stu = {"张三", 18, 100};

// 2、通过指针指向结构体变量
struct Student *p = &stu;

// 3、通过指针访问结构体变量中的数据
cout << "姓名:" << p->name
<< " 年龄:" << p->age
<< " 分数:" << p->score << endl;

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
41
42
43
44
45
// 定义学生结构体
struct Student
{
// 姓名
string name;
// 年龄
int age;
// 分数
int score;
};
// 定义老师结构体
struct Teacher
{
// 老师编号
int id;
// 教师姓名
string name;
// 年龄
int age;
// 辅导的学生
struct Student stu;
};

int main ()
{
// 结构体嵌套结构体
// 创建老师
struct Teacher t;
t.id = 10000;
t.name = "老王";
t.age = 50;
t.stu.name = "小王";
t.stu.age = 20;
t.stu.score = 60;

// 3、通过指针访问结构体变量中的数据
cout << "老师编号:" << t.id
<< " 老师姓名:" << t.name
<< " 老师年龄:" << t.age << endl
<< "学生姓名:" << t.stu.name
<< " 学生年龄:" << t.stu.age
<< " 学生分数:" << t.stu.score << endl;

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
41
42
43
44
45
46
// 定义学生结构体
struct Student
{
// 姓名
string name;
// 年龄
int age;
// 分数
int score;
};
// 打印学生信息函数
// 1、值传递
void printStudent1(struct Student s){
s.name = "王五";
cout << "姓名:" << s.name
<< " 年龄:" << s.age
<< " 分数:" << s.score << endl;
}
// 2、地址传递
void printStudent2(struct Student *s){
s -> name = "李四";
cout << "姓名:" << s -> name
<< " 年龄:" << s -> age
<< " 分数:" << s -> score << endl;
}

int main ()
{
// 结构体做函数参数
// 将学生传入到一个参数中,打印学生身上的所有信息

//创建结构体变量
struct Student s;
s.name = "张三";
s.age = 20;
s.score = 85;
cout << "姓名:" << s.name
<< " 年龄:" << s.age
<< " 分数:" << s.score << endl;
printStudent1(s);
printStudent2(&s);
cout << "姓名:" << s.name
<< " 年龄:" << s.age
<< " 分数:" << s.score << endl;
return 0;
}

结构体中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
27
28
29
30
31
32
33
34
35
// const的使用场景
struct Student
{
// 姓名
string name;
// 年龄
int age;
// 分数
int score;
};

// 将函数中的形参改为指针,可以减少内存空间,而且不会复制新的副本出来
void printStudents(const struct Student *s){
// 形参添加const,一旦有修改的操作就会报错,可以防止我们的误操作
// s -> name = "xxx";
// 值传递
cout << "(printStudents)姓名:" << s->name
<< " 年龄:" << s->age
<< " 分数:" << s->score << endl;
}

int main ()
{

//创建结构体变量
struct Student s = {"张三", 15, 70};

// 通过函数打印结构体变量信息
printStudents(&s);
cout << "(main)姓名:" << s.name
<< " 年龄:" << s.age
<< " 分数:" << s.score << endl;

return 0;
}

结构体案例

案例1

案例描述:

学校正在做毕设项目,每名老师带领5个学生,总共有3名老师,需求如下

设计学生和老师的结构体,其中在老师的结构体中,有老师姓名和一个存放5名学生的数组作为成员

学生的成员有姓名、考试分数,创建数组存放3名老师,通过函数给每个老师及所带的学生赋值

最终打印出老师数据以及所带的学生数据

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
48
49
50
51
52
53
struct Student
{
// 姓名
string name;
// 分数
int score;
};
struct Teacher
{
// 姓名
string name;
// 学生成员
struct Student students[5];
};
// 给老师和学生赋值的函数
// 数组名就是地址
void allocateSpace(struct Teacher teacher[],int len)
{
string nameSeed = "ABCDE";
// 给老师开始赋值
for (int i = 0; i < len; ++i) {
teacher[i].name = "Teacher_";
teacher[i].name += nameSeed[i];
// 通过循环给每名老师所带的学生赋值
for (int j = 0; j < 5; ++j) {
teacher[i].students[j].name = "Student_";
teacher[i].students[j].name += nameSeed[j];
teacher[i].students[j].score = rand() % 61 + 40;
}
}

}
int main ()
{
// 随机数种子
srand((unsigned int) time(NULL));

// 1、创建3名老师的数组
struct Teacher teachers[3];
int len = sizeof(teachers) / sizeof(teachers[0]);
// 2、通过函数给3名老师的信息赋值,并给老师带的学生信息赋值
allocateSpace(teachers,3);

// 3、打印所有老师及所带的学生信息
for (int i = 0; i < len; ++i) {
cout << "教师姓名:" << teachers[i].name << endl;
for (int j = 0; j < 5; ++j) {
cout << "\t学生姓名:" << teachers[i].students[j].name
<< " 分数:" << teachers[i].students[j].score << endl;
}
}
return 0;
}

案例2

案例描述:

设计一个英雄的结构体,包括成员姓名,年龄,性别;创建结构体数组,数组中存放5名英雄。

通过冒泡排序的算法,将数组中的英雄按照年龄进行升序排列,最终打印排序后的结果。

五名英雄信息如下:

1
2
3
4
5
{"刘备",23,"男"},
{"关羽",22,"男"},
{"张飞",20,"男"},
{"赵云",21,"男"},
{"貂蝉",19,"女"}
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
48
49
50
51
52
53
54
55
// 1、设计英雄结构体
struct Hero
{
// 姓名
string name;
// 年龄
int age;
// 性别
string sex;
};

void bubbleSortHero(struct Hero heroArray[],int len)
{
for (int i = 0; i < len; ++i) {
for (int j = 0; j < len - i - 1; ++j) {
if (heroArray[j].age > heroArray[j + 1].age){
struct Hero tempHero = heroArray[j];
heroArray[j] = heroArray[j + 1];
heroArray[j + 1] = tempHero;
}
}
}
}

// 打印排序后数组中的信息
void printHero(struct Hero heroArray[], int len)
{
for (int i = 0; i < len; ++i) {
cout << "姓名:" << heroArray[i].name
<< " 年龄:" << heroArray[i].age
<< " 性别:" << heroArray[i].sex
<< endl;
}
}

int main ()
{
// 2、创建一个数组存放五名英雄
struct Hero heroArray[5] = {
{"刘备",23,"男"},
{"关羽",22,"男"},
{"张飞",20,"男"},
{"赵云",21,"男"},
{"貂蝉",19,"女"},
};
// 计算数组大小
int len = sizeof(heroArray) / sizeof(heroArray[0]);
// 3、对数组进行排序,按照年龄进行升序排列
bubbleSortHero(heroArray,len);

// 4、将排序后的结果打印输出
printHero(heroArray,len);

return 0;
}