当前位置: 首页 > news >正文

CB1-2-基础启航

C/C++道经第1卷 - 第2阶 - 基础启航

文章目录

  • S01. 环境搭建
  • S02. 基础知识
    • E01. 预处理机制
      • 1. include 指令
      • 2. define 指令
      • 3. undef 指令
      • 4. 预定义宏
      • 5. 条件编译
    • E03. 程序运行原理
      • 1. GCC
      • 2. Make
      • 3. CMake
    • E04. 代码注释
    • E05. 输出语句
      • 1. printf
      • 2. puts
      • 3. cout
      • 4. 特殊字符
    • E06. 输入语句
      • 1. scanf
      • 2. gets
      • 3. cin
  • S03. 常量变量
    • E01. 定义常量
    • E02. 定义变量
    • E01. 数据类型
      • 1. 数字类型
      • 2. 字符类型
      • 3. 布尔类型
      • 4. 字符串类型
      • 5. 类型转换
      • 6. 类型计算
      • 7. 结果溢出
  • S04. 运算符号
    • E01. 基本运算符
      • 1. 数学运算符
      • 2. 赋值运算符
      • 3. 自运算符
      • 4. 关系运算符
      • 5. 逻辑运算符
      • 6. 位运算符
    • E02. 其他运算符
      • 1. 三目运算符
      • 2. 数学工具类
  • S05. 流程控制
    • E01. 选择结构
      • 1. IfElse结构
      • 2. Switch结构
    • E02. 循环结构
      • 1. While循环
      • 2. DoWhile循环
      • 3. For循环
    • E03. 流控关键字
      • 1. continue
      • 2. break
  • S06. 数组类型
    • E01. 一维数组
      • 1. 一维数组声明
      • 2. 一维数组遍历
    • E02. 多维数组
      • 1. 二维数组声明
      • 2. 二维数组遍历
  • S07. 高级知识
    • E01. 函数
      • 1. 函数创建
      • 2. 函数传参
      • 3. 函数递归
      • 5. Lambda函数
    • E02. 指针
      • 1. 指针创建
      • 2. 指针传递
      • 3. 常量指针
      • 4. 指针数组
      • 5. 指针函数
    • E03. 结构体
      • 1. 结构体
      • 2. 枚举类
    • E04. 类型别名
      • 1. typedef
      • 2. using
    • E05. 内存操作
      • 1. malloc
      • 2. calloc
      • 3. realloc
      • 4. new
    • E06. 文件操作
      • 1. fopen
      • 2. fstream

心法:本章使用 C++ 项目进行练习

练习项目结构如下

|_ v1-2-basic-start

S01. 环境搭建

心法:参考 CB1-1-新手村(一)S01 笔记。

S02. 基础知识

C++ 是在 C 语言的基础上发展而来的,它保留了 C 语言的大部分特性,同时进行了许多扩展和改进。

C 主要是面向过程的编程语言,它强调将程序分解为一系列的函数,通过函数的调用和数据的传递来完成特定的任务。程序的执行流程是按照函数的调用顺序依次进行的。

C++ 支持多种编程范式,包括面向过程、面向对象、泛型编程等。面向对象编程允许将数据和操作数据的函数封装在一起,形成类和对象,通过继承、多态等机制提高代码的可复用性和可维护性。

E01. 预处理机制

1. include 指令

心法:预处理器遇到 include 指令时,会将指定文件内容插入当前文件,替换该指令。

C++ 有自己独立的标准库头文件,例如 <iostream><string> 等,这些是 C 语言所没有的。

同时,C++ 为了兼容 C 标准库,也提供了对应的头文件,命名规则是在 C 标准库头文件名前加上 c 并去掉 .h 后缀,如 C 语言中的 <stdio.h> 在 C++ 中可以使用 <cstdio> 进行引入。

其余参考 C 语言笔记: CB1-1-新手村(一)S02E01.1 笔记。

武技:测试 include 指令

// init/Include.h
// Created by 周航宇 on 2025/2/26.
//
#include <cstdio>
#ifndef V1_1_BASIC_ROOKIE_INCLUDE_H
#define V1_1_BASIC_ROOKIE_INCLUDE_Hnamespace Include {void test();
}#endif //V1_1_BASIC_ROOKIE_INCLUDE_H
// init/Include.cpp
// Created by 周航宇 on 2025/2/26.
//
#include "Include.h"void Include::test() {printf("使用 C 语言的 Printf\n");
}

2. define 指令

心法:预处理器遇到 define 指令时,会将参数批量替换到文本中,该操作通常被称为 定义宏 ,该替换操作发生在程序运行之前,属于纯文本替换(字符串中的文本不会被替换)。

C++ 提供了更好的替代方案来定义常量和函数。

对于常量,C++ 推荐使用 const 关键字,它们具有类型检查,比宏常量更安全。

对于函数,C++ 可以使用 inline 内联函数,内联函数是真正的函数,有类型检查和作用域规则,避免了宏函数可能带来的副作用。

其余参考 C 语言笔记: CB1-1-新手村(一)S02E01.2 笔记。

武技:测试 define 指令

// init/Define.h
// Created by 周航宇 on 2025/2/26.
//
#ifndef V1_1_BASIC_ROOKIE_DEFINE_H
#define V1_1_BASIC_ROOKIE_DEFINE_H
#include <iostream>namespace Define {// 使用 const 定义常量const double pi = 3.14;// 使用内联函数替代宏函数inline int square(int x) {return x * x;}void test();
}#endif //V1_1_BASIC_ROOKIE_DEFINE_H
// init/Define.cpp
// Created by 周航宇 on 2025/2/26.
//
#include "Define.h"void Define::test() {std::cout << "PI:" << pi << std::endl;std::cout << "5的平方:" << square(5) << std::endl;
}

3. undef 指令

心法:undef 指令用于取消之前使用 define 定义的宏,当编译器遇到 undef 指令后,后续代码将不再识别该宏。

C:在 C 语言中,宏是定义常量和简单函数的常用手段,因此 undef 指令的使用相对较为常见。例如,在一些大型项目中,可能会根据不同的编译选项定义不同的宏,当某个宏的作用范围结束后,就可以使用 undef 指令取消其定义,避免对后续代码产生影响。

C++:C++ 提供了更安全和更具类型检查的替代方案,如使用 const 关键字定义常量,使用内联函数替代宏函数。因此,在 C++ 中宏的使用相对较少, undef 指令的使用频率也会相应降低。

其余参考 C 语言笔记: CB1-1-新手村(一)S02E01.3 笔记。

4. 预定义宏

心法:C 语言预定义宏由编译器预设,提供编译环境、代码位置和日期等信息。

C++ 中使用 __cplusplus 获取编译器支持的 C++ 标准版本。例如,201103L 表示支持 C++11 标准,201703L 表示支持 C++17 标准等。

其余参考 C 语言笔记: CB1-1-新手村(一)S02E01.4 笔记。

武技:测试预定义宏

// init/SysDefine.h
// Created by 周航宇 on 2025/2/26.
//
#include <iostream>#ifndef V1_1_BASIC_ROOKIE_SYS_DEFINE_H
#define V1_1_BASIC_ROOKIE_SYS_DEFINE_Hnamespace SysDefine {void test();
}#endif //V1_1_BASIC_ROOKIE_SYS_DEFINE_H
// init/SysDefine.cpp
// Created by 周航宇 on 2025/2/26.
//
#include "SysDefine.h"void SysDefine::test() {// 202002 表示支持 C++20 标准std::cout << "当前C++版本" << __cplusplus << std::endl;
}

5. 条件编译

心法:条件编译包含 ifdef, else, endif 等编译指令,且支持嵌套。

参考 C 语言笔记: CB1-1-新手村(一)S02E01.5 笔记。

E03. 程序运行原理

1. GCC

心法:C++ 中使用 G++ 编译器代替 GCC 编译器,所以命令中的 gcc 命令将全部被替换为 g++ 命令。

参考 C 语言笔记: CB1-1-新手村(一)S02E02.1 笔记。

2. Make

心法:C++ 中使用 G++ 编译器代替 GCC 编译器,所以命令中的 gcc 命令将全部被替换为 g++ 命令。

参考 C 语言笔记: CB1-1-新手村(一)S02E02.2 笔记。

3. CMake

参考 C 语言笔记: CB1-1-新手村(一)S02E02.3 笔记。

E04. 代码注释

参考 C 语言笔记: CB1-1-新手村(一)S02E03 笔记。

E05. 输出语句

1. printf

参考 C 语言笔记: CB1-1-新手村(一)S02E04.1 笔记。

2. puts

参考 C 语言笔记: CB1-1-新手村(一)S02E04.2 笔记。

3. cout

心法:std::cout 是 C++ 标准库中 iostream 头文件里定义的一个输出流对象,用于将数据输出到标准输出设备,通常是控制台。

缓冲机制

std::cout 是普通的输出语句,为了提高输出效率,减少对屏幕的频繁访问,该类型的输出语句会使用缓冲机制,这意味着输出的数据不会立即显示在屏幕上,而是先存储在缓冲区中,当缓冲区满或者遇到 std::flush 或 std::endl 时,缓冲区中的数据才会被输出到屏幕上。

std::cerr 用于提示一些异常的,错误的信息,为了确保错误信息能及时反馈出来,该类型的输出不使用缓冲机制,会立刻进行输出显示。

命名空间

std 是一个命名空间,而 cout 是在 std 命名空间中定义的对象,配合 << 插入运算符来输出各种类型的数据:

  • 支持在文件首部使用 using namespace std 添加 std 命名空间,此时可以省略文件中的全部 std:: 前缀,但要注意可能会引起命名冲突。
  • 支持在文件首部使用 using std::out,此时 std::out 的前缀可以省略,其余同理。
  • 无论使用哪种 using 方式,都推荐在源文件中定义,而非在头文件中定义,以免暴露实现细节。

输出语句

C++ 中的输出语句代码是否自动换行是否刷新缓冲区显示效果
std::cout << "内容"不换行不刷新延迟显示
std::cout << "内容\n"换行不刷新延迟显示
std::cout << "内容" << std::flush不换行刷新立刻显示
std::cout << "内容" << std::endl换行刷新立刻显示
std::cerr << "内容"不换行压根不使用缓冲区立刻显示
std::cerr << "内容" << std::endl换行压根不使用缓冲区立刻显示

武技:测试 cout 函数

// basic/COut.h
// Created by 周航宇 on 2025/2/26.
//
#ifndef V1_1_BASIC_ROOKIE_C_OUT_H
#define V1_1_BASIC_ROOKIE_C_OUT_H
#include <iostream>namespace COut {void testCOut();void testCOutWithBuffer();void testCOutWithNoBuffer();void test();
}#endif // V1_1_BASIC_ROOKIE_C_OUT_H
// basic/COut.cpp
// Created by 周航宇 on 2025/2/26.
//
#include "COut.h"
#include <unistd.h>using std::cout, std::cerr, std::flush, std::endl;void COut::testCOut() {// 打印空白行std::cout << std::endl;// 打印空白行:省略 std 前缀(由于文件头部添加了 using namespace std 配置)cout << endl;// 普通输出cout << "换行(普通信息)\n";cout << "换行(普通信息)" << endl;cout << "不";cout << "换";cout << "行";cout << "\n============" << endl;// 错误输出cerr << "换行(错误信息)\n";cerr << "换行(错误信息)" << endl;cerr << "不";cerr << "换";cerr << "行";
}// 测试不刷新缓冲区的输出语句
void COut::testCOutWithBuffer() {cout << "换行,不刷管\n";cout << "不换行,不刷管";sleep(3);
}// 测试刷新缓冲区的输出语句
void COut::testCOutWithNoBuffer() {cout << "换行,刷管" << endl;cout << "不换行,刷管" << flush;cerr << "不换行,刷管" << flush;sleep(3);
}void COut::test() {testCOut();testCOutWithBuffer();testCOutWithNoBuffer();
}

4. 特殊字符

参考 C 语言笔记: CB1-1-新手村(一)S02E04.3 笔记。

E06. 输入语句

1. scanf

参考 C 语言笔记: CB1-1-新手村(一)S02E05.1 笔记。

2. gets

参考 C 语言笔记: CB1-1-新手村(一)S02E05.2 笔记。

3. cin

心法:std::cin 是 C++ 标准库中 iostream 头文件里定义的一个输入流对象,用于从标准输入设备(通常是键盘)读取数据。

std 是一个命名空间,而 cin 是在 std 命名空间中定义的对象,配合 >> 提取运算符来读取各种类型的数据。

武技:测试 cin 函数

// basic/CIn.h
// Created by 周航宇 on 2025/2/26.
//
#ifndef V1_1_BASIC_ROOKIE_C_IN_H
#define V1_1_BASIC_ROOKIE_C_IN_H
#include <iostream>namespace CIn {void test();
}#endif //V1_1_BASIC_ROOKIE_C_IN_H
// basic/CIn.cpp
// Created by 周航宇 on 2025/2/26.
//
#include "CIn.h"using std::cout, std::cin, std::string, std::endl;void CIn::test() {// 接收整形数据(浮点数和字符同理)int a;cout << "输入一个整数:" << endl;cin >> a;cout << a << endl;// 接收字符数据string str;cout << "输入一个字符串:" << endl;cin >> str;cout << str << endl;// 接收布尔数据:非 1 的值都视为 false(0)bool flag;cout << "输入一个布尔值(非1即负):" << endl;cin >> flag;cout << flag << endl;
}

S03. 常量变量

心法:常量就是在整个运行过程中都不可以发生变化的量,分为字面量和后天形成的常量两种。

E01. 定义常量

参考 C 语言笔记: CB1-1-新手村(二)S03E01 笔记。

E02. 定义变量

心法:在 C++ 中,auto 关键字允许编译器根据初始化表达式自动推断变量的类型,从而简化代码编写并提高代码的可读性和可维护性。

必须初始化:auto 变量必须在声明时进行初始化,因为编译器需要根据初始化表达式来推断变量的类型。

其余参考 C 语言笔记:CB1-1-新手村(二)S03E02 笔记。

武技:测试 auto 关键字

// basic/AutoType.h
// Created by 周航宇 on 2025/2/26.
//
#ifndef V1_1_BASIC_ROOKIE_AUTO_TYPE_H
#define V1_1_BASIC_ROOKIE_AUTO_TYPE_H
#include "iostream"namespace AutoType {void test();
};#endif //V1_1_BASIC_ROOKIE_AUTO_TYPE_H
// basic/AutoType.cpp
// Created by 周航宇 on 2025/2/26.
//
#include "AutoType.h"using std::cout, std::endl;void AutoType::test() {// 编译器根据 10 推断 x 的类型为 intauto x = 10;// 编译器根据 3.14 推断 y 的类型为 doubleauto y = 3.14;// 编译器根据 'A' 推断 z 的类型为 charauto z = 'A';cout << "x: " << x << ", y: " << y << ", z: " << z << endl;
}

E01. 数据类型

1. 数字类型

参考 C 语言笔记:CB1-1-新手村(二)S03E03.1 笔记。

2. 字符类型

参考 C 语言笔记:CB1-1-新手村(二)S03E03.2 笔记。

3. 布尔类型

心法:C++ 额外支持直接定义布尔类型变量。

C++ 中使用 bool 作为布尔类型关键字,占 1 个字节,1 表示 true,0 表示 false。

武技:测试 C++ 布尔类型

// basic/Bool.h
// Created by 周航宇 on 2025/2/26.
//
#ifndef V1_1_BASIC_ROOKIE_BOOL_H
#define V1_1_BASIC_ROOKIE_BOOL_H#include <iostream>namespace Bool {void test();
};#endif //V1_1_BASIC_ROOKIE_BOOL_H
// basic/Bool.cpp
// Created by 周航宇 on 2025/2/26.
//
#include "Bool.h"using std::cout, std::endl;void Bool::test() {bool flag = true;cout << "bool 类型的大小:" << sizeof flag << endl;cout << "bool 类型的大小:" << sizeof(bool) << endl;
}

4. 字符串类型

心法:std::string 是 C++ 标准库中 string 头文件里定义的,它封装了字符串的操作并自动管理内存,使用起来更加方便和安全,减少了出错的可能性。

字符串风格转换

  • C 字符串转 C++:const *char c = "hi"; string cpp = string(c);
  • C++ 字符串转 C:string cpp = "hi"; const *char c = cpp.c_str();

头文件

  • std::string 来自 string.h 头文件。
  • std::move() 来自 utility.h 头文件。

字符串拷贝赋值

  • 示例代码string a = "hi"; string b = a;
  • 内存流程:调用字符串的拷贝构造器,首先为 b 分配一块新的空间,然后将 a 中的内容逐字节复制到这块新的空间中,此时 a 和 b 是两个独立无关的对象。
  • 适用场景:适用于需要保留原对象内容的情况,但会有一定的性能开销,因为需要进行内存复制。

字符串转移赋值

  • 示例代码string a = "hi"; string b = std::move(a);
  • 内存流程:调用字符串的移动构造器,直接让 b 接管 a 指向的数据,此时 a 将指向一个空字符串,后续将不应再使用它(除非重新赋值),此时 a 和 b 是两个独立无关的对象。
  • 适用场景:适用于原对象不再需要使用的情况。

常用 API 方法

常用 API 方法描述
str.length()返回字符串长度,可读性更高
str.size()返回字符串长度,通用性更高,推荐使用
str.at(2)返回字符串的 2 号元素
str.front()返回字符串起始字符
str.back()返回字符串结束字符
str.find(str2)返回 str 中第一个 str2 的位置
str.rfind(str2)返回 str 中最后一个 str2 的位置
str.append(str2)追加 str2 到 str 末尾,也可以使用 += 符号进行追加
str.insert(n, str2)在 str 的 n 号位置插入 str2,作用于原字符串
str.erase(n, m)在 str 的 n 号位置删除 n 个元素,作用于原字符串
str.replace(n, m, str2)在 str 的 n 号位置删除 m 个元素,然后插入 str2,作用于原字符串
str.substr(n, m)在 str 的 n 号位置截取 m 个元素,返回新字符串,不作用于原字符串

武技:测试 C++ 字符串类型

// basic/String.h
// Created by 周航宇 on 2025/2/26.
//
#ifndef V1_1_BASIC_ROOKIE_STRING_H
#define V1_1_BASIC_ROOKIE_STRING_H
#include <iostream>
#include <string>namespace String {void testString();void testAssign();void test();
};#endif //V1_1_BASIC_ROOKIE_STRING_H
// basic/String.cpp
// Created by 周航宇 on 2025/2/26.
//
#include <iostream>
#include "String.h"using std::cout, std::endl, std::string;void String::testString() {string str = "hellohello";cout << str << endl;cout << "返回当前字符串的长度:\t" << str.length() << endl;cout << "返回当前字符串的长度:\t" << str.size() << endl;cout << "返回字符串2号元素:\t" << str.at(2) << endl;cout << "返回字符串起始字符:\t" << str.front() << endl;cout << "返回字符串结束字符:\t" << str.back() << endl;cout << "返回第一个llo位置:\t" << str.find("llo") << endl;cout << "返回最后一个llo位置:\t" << str.rfind("llo") << endl;cout << "末尾追加World字符串:\t" << str.append("World") << endl;cout << "在5号位置插入逗号:\t" << str.insert(5, ",") << endl;cout << "删除第2个hello子串:\t" << str.erase(6, 5) << endl;cout << "替换字符串首个h为H:\t" << str.replace(0, 1, "H") << endl;cout << "截取Hello子字符串:\t" << str.substr(0, 5) << endl;cout << str << endl;
}void String::testAssign() {// 拷贝赋值string str1 = "hello";string str2 = str1;cout << "str1: " << str1 << endl;cout << "str2: " << str2 << endl;str2 = "xxxx"; // 重新赋值cout << "str1: " << str1 << endl; // 无影响cout << "str2: " << str2 << endl; // 被影响// 转移赋值string str3 = "hello";string str4 = std::move(str3);cout << "str3: " << str3 << endl; // 输出空字符串cout << "str4: " << str4 << endl;str3 = "xxxx"; // 重新赋值cout << "str3: " << str3 << endl; // 被影响cout << "str4: " << str4 << endl; // 无影响
}void String::test() {testString();testAssign();
}

5. 类型转换

心法:C++ 中推荐使用 static_cast<目标类型>(表达式) 方式进行基本数据类型之间的静态类型转换,对比 C 中的 (目标类型)表达式 方式更加安全,可读性更高。

其余参考 C 语言笔记:CB1-1-新手村(二)S03E03.3 笔记。

武技:测试 C++ 基本数据类型转换

// basic/StaticCast.h
// Created by 周航宇 on 2025/2/26.
//
#ifndef V1_1_BASIC_ROOKIE_STATIC_CAST_H
#define V1_1_BASIC_ROOKIE_STATIC_CAST_H
#include <iostream>namespace StaticCast {void test();
}#endif //V1_1_BASIC_ROOKIE_STATIC_CAST_H
// basic/StaticCast.c
// Created by 周航宇 on 2025/2/26.
//
#include "StaticCast.h"using std::cout, std::endl;void StaticCast::test() {int a = 10;// auto 用来自动推导变量类型,避免重复编写 double 关键字auto b = static_cast<double>(a);cout << b << endl;
}

6. 类型计算

参考 C 语言笔记:CB1-1-新手村(二)S03E03.4 笔记。

7. 结果溢出

参考 C 语言笔记:CB1-1-新手村(二)S03E03.5 笔记。

S04. 运算符号

E01. 基本运算符

1. 数学运算符

参考 C 语言笔记:CB1-1-新手村(二)S04E01.1 笔记。

2. 赋值运算符

参考 C 语言笔记:CB1-1-新手村(二)S04E01.2 笔记。

3. 自运算符

参考 C 语言笔记:CB1-1-新手村(二)S04E01.3 笔记。

4. 关系运算符

参考 C 语言笔记:CB1-1-新手村(二)S04E01.4 笔记。

5. 逻辑运算符

参考 C 语言笔记:CB1-1-新手村(二)S04E01.5 笔记。

6. 位运算符

参考 C 语言笔记:CB1-1-新手村(二)S04E01.6 笔记。

E02. 其他运算符

1. 三目运算符

参考 C 语言笔记:CB1-1-新手村(二)S04E02.1 笔记。

2. 数学工具类

参考 C 语言笔记:CB1-1-新手村(二)S04E02.2 笔记。

S05. 流程控制

无论是选择结构还是循环结构,当 {} 中仅一行代码时,可以省略 {},但是并不建议这么写。

E01. 选择结构

心法:选择结构也叫分支结构,表示多选一。

1. IfElse结构

心法:C++ 允许在 IF 语句的小括号中进行变量声明,声明的变量作用域仅限于所在的 IF 或 ELSE 语句块内。

其他参考 C 语言笔记:CB1-1-新手村(二)S05E01.1 笔记。

武技:测试 IfElse 结构

// structure/Choose.h
// Created by 周航宇 on 2025/2/26.
//
#ifndef V1_1_BASIC_ROOKIE_CHOOSE_H
#define V1_1_BASIC_ROOKIE_CHOOSE_H
#include <iostream>namespace Choose {void test();
}#endif //V1_1_BASIC_ROOKIE_CHOOSE_H
// structure/Choose.cpp
// Created by 周航宇 on 2025/2/26.
//
#include "Choose.h"using std::cout, std::cin, std::endl;void Choose::test() {// 输入一个整数int score;cout << "输入一个分数:";cin >> score;if (auto avgScore = 68.9; score > avgScore) {cout << "超过平均分 68.9" << endl;} else {cout << "低于平均分 68.9" << endl;}// 这里无法访问 num 变量// cout << num << endl; 
}

2. Switch结构

参考 C 语言笔记:CB1-1-新手村(二)S04E01.2 笔记。

E02. 循环结构

心法:循环就是重复性做事,但必须是有逻辑有规律的事情才可使用循环。

无论那种循环,都要满足初判变法则:

  • : 初始化变量(只执行一遍)。
  • : 循环判断条件(只有满足条件的时候才执行循环)。
  • : 每次循环体结束之后将执行变量的变化。

1. While循环

参考 C 语言笔记:CB1-1-新手村(二)S04E02.1 笔记。

2. DoWhile循环

参考 C 语言笔记:CB1-1-新手村(二)S04E02.2 笔记。

3. For循环

参考 C 语言笔记:CB1-1-新手村(二)S04E02.3 笔记。

E03. 流控关键字

1. continue

参考 C 语言笔记:CB1-1-新手村(二)S04E03.1 笔记。

2. break

参考 C 语言笔记:CB1-1-新手村(二)S04E03.2 笔记。

S06. 数组类型

心法:数组是 heap 内存中一段内存连续的线性数据结构,属于引用数据类型。

数组结构特点描述
拥有下标数组的下标从0开始,支持使用 数组[索引] 的格式来操作数组元素
拥有默认值数组中的每个最底层元素都具有默认值,如 0, 0.0, false, \u0000, null 等
增删慢数组长度不可改变,导致增删操作必须使用创新数组,效率低
查询快数组中每个元素的内存地址连续可计算,所以在明确位置的情况下,查询效率更高

E01. 一维数组

心法:一维数组中的每个元素都是一个单一数据类型,且全部元素的数据类型必须一致

1. 一维数组声明

参考 C 语言笔记:CB1-1-新手村(三)S06E01.1 笔记。

2. 一维数组遍历

心法:相比 C 语言,C++ 支持使用范围 for 循环(Range-based for loop),也称增强 for 循环来遍历 std::vector,std::array,std::list 以及普通的 C 风格数组。

范围 for 循环会自动调用容器的 begin() 函数获取指向第一个元素的迭代器,然后依次访问容器中的每个元素,直到到达 end() 迭代器所指向的位置(即容器的末尾),在每次迭代中,将当前元素的值赋给循环变量,然后执行循环体中的代码。

其余参考 C 语言笔记:CB1-1-新手村(三)S06E01.2 笔记。

武技:测试范围 For 循环

// array/OneArray.h
// Created by 周航宇 on 2025/2/26.
//
#include <iostream>
#ifndef V1_1_BASIC_ROOKIE_ONE_ARRAY_H
#define V1_1_BASIC_ROOKIE_ONE_ARRAY_Hnamespace OneArray {void testRangeFor();void test();
};#endif //V1_1_BASIC_ROOKIE_ONE_ARRAY_H
// array/OneArray.cpp
// Created by 周航宇 on 2025/2/26.
//
#include <array>
#include "OneArray.h"using std::cout, std::endl, std::array;void OneArray::testRangeFor() {// 定义一个数组int arr[] = {1, 2, 3, 4, 5};// 使用范围 for 循环遍历数组for (auto num: arr) {cout << num << endl;}
}void OneArray::test() {testRangeFor();
}

E02. 多维数组

心法:二维数组的本质也是一维数组,只不过其中的每个元素都是一个一维数组类型,每个一维数组的类型必须一致,但元素的长度可以不同。

1. 二维数组声明

参考 C 语言笔记:CB1-1-新手村(三)S06E02.1 笔记。

2. 二维数组遍历

参考 C 语言笔记:CB1-1-新手村(三)S06E02.1 笔记。

S07. 高级知识

E01. 函数

心法:函数是完成特定任务的独立代码单元,主要的意义是用于提高代码重用性。

1. 函数创建

心法:不同于 C 语言,C++ 支持函数重载,即创建同名不同参的函数。

其余参考 C 语言笔记:CB1-1-新手村(三)S07E01.1 笔记。

武技:测试函数重载

// senior/Overload.h
// Created by 周航宇 on 2025/2/26.
//
#ifndef V1_1_BASIC_ROOKIE_OVERLOAD_H
#define V1_1_BASIC_ROOKIE_OVERLOAD_H
#include <iostream>namespace Overload {int add(int a, int b);double add(double a, double b);void test();
};#endif //V1_1_BASIC_ROOKIE_OVERLOAD_H
// senior/Overload.cpp
// Created by 周航宇 on 2025/2/26.
//
#include "Overload.h"using std::cout, std::endl;int Overload::add(int a, int b) {return a + b;
}double Overload::add(double a, double b) {return a + b;
}void Overload::test() {int a = 1, b = 2;double c = 1.1, d = 2.2;cout << add(a, b) << endl;cout << add(c, d) << endl;
}

2. 函数传参

心法:不同于 C 语言,C++ 函数支持对参数设置默认值。

其余参考 C 语言笔记:CB1-1-新手村(三)S07E01.2 笔记。

武技:测试函数参数默认值

// senior/DefaultParam.h
// Created by 周航宇 on 2025/2/26.
//
#ifndef V1_1_BASIC_ROOKIE_DEFAULT_PARAM_H
#define V1_1_BASIC_ROOKIE_DEFAULT_PARAM_H
#include <iostream>namespace DefaultParam {int sum(int a, int b);void test();
};#endif //V1_1_BASIC_ROOKIE_DEFAULT_PARAM_H
// senior/DefaultParam.cpp
// Created by 周航宇 on 2025/2/26.
//
#include "DefaultParam.h"using std::cout, std::endl;// 函数定义,为参数 b 指定默认值
int DefaultParam::sum(int a, int b = 0) {return a + b;
}void DefaultParam::test() {int a = 1, b = 2;cout << sum(a) << endl;cout << sum(a, b) << endl;
}

3. 函数递归

参考 C 语言笔记:CB1-1-新手村(三)S07E01.3 笔记。

5. Lambda函数

心法:C++ 中的 Lambda 表达式是 C++11 引入的对象,会被编译器转换为一个匿名的函数闭包对象,常用于在函数内部定义临时函数。

Lambda 表达式可以定义在全局作用域,但此时将不能捕获局部变量,所以一般情况下,Lambda 都推荐定义在函数内部。

Lambda 结构auto Lambda对象 = [捕获列表](形参列表) -> 返回值类型 {函数体}

Lambda 对象:Lambda 表达式的返回值是一个 Lambda 对象,本质是 functional 头文件中提供的 function 类型,具体结构为 std::function<返回类型(参数类型列表)>,但由于编写复杂且具有额外的性能开销(涉及堆内存分配和虚函数调用等),一般使用 auto 自动推导即可。

Lambda 捕获列表:用于捕获外部作用域中的局部变量(全局变量和静态变量不需要捕获,直接在函数体内使用即可)供函数体内部使用,为空时中括号不能省略,捕获方式有如下几种:

捕获方式示例描述
值捕获[a, b]捕获指定外部变量的值并传递给函数体
函数体内使用的是变量的副本
引用捕获[&a, &b]捕获指定外部变量的引用并传递给函数体
函数体内使用的是变量的引用,可修改其值
全部值捕获[=]按值捕获的方式捕获全部外部变量的值并传递给函数体
全部引用捕获[&]按引用捕获的方式捕获全部外部变量的引用并传递给函数体

Lambda 形参列表:当每次调用 Lambda 表达式时需要传入不同的数据进行处理时,推荐使用参数列表,为空时小括号不能省略。

Lambda 返回值类型:若省略返回类型,编译器会根据函数体中的返回语句自动推导返回类型,且返回值类型前,用于表示传递的箭头也必须要一起省略。

武技:测试 Lambda 表达式

// senior/Lambda.h
// Created by 周航宇 on 2025/3/1.
//
#ifndef V1_1_BASIC_ROOKIE_LAMBDA_H
#define V1_1_BASIC_ROOKIE_LAMBDA_H
#include <iostream>
#include <functional>namespace Lambda {void test();
}#endif //V1_1_BASIC_ROOKIE_LAMBDA_H
// senior/Lambda.cpp
// Created by 周航宇 on 2025/3/1.
//
#include "Lambda.h"using std::cout, std::endl, std::flush, std::string;void Lambda::test() {// 最简版 Lambdaauto lambda$01 = []() { cout << "r01" << endl; };// 通过 lambda 对象调用该函数lambda$01();// 自调用:通过末尾的小括号实现自调用[]() { cout << "r02" << endl; }();// 获取返回值:调用 Lambda 之后可以获取其返回值double r03 = []() { return 3.14; }();cout << "r03: " << r03 << endl;// 显示指定返回值string r04 = []() -> string { return "hi"; }();cout << "r04: " << r04 << endl;// 值捕获:内部只能访问,不能修改// 引用捕获:内部可以修改int a = 10;int b = 10;[a, &b]() {b = 20;cout << "r05: a=" << a << flush;}();cout << ",b=" << b << endl;// 形参列表[](int m, int n) {cout << "r06: " << (m + n) << endl;}(3, 4);
}

E02. 指针

心法:变量在内存中创建,每个变量都拥有唯一内存地址,而指针正是用于保存这个内存地址的。

仅声明未赋值的指针变量会存储一个不确定的随机值,可能是上次使用这片内存空间存储的数据遗留下来的值,也可能是编译器初始化的一个随机值,这是一种很危险的操作,所以即使不知道具体的值,也强烈推荐赋值为 空指针,即暂不指向任何内容。

空指针

  • 在 C 中,通常使用 NULL 来表示空指针,它是一个宏,通常被定义为 (void *)0
  • 在 C++ 中,除了 NULL 外,还引入了 nullptr 关键字来专门表示空指针,类型安全,推荐使用。

无类型指针:void 指针是无类型指针,它可以指向任何类型的数据。

![[image/draw/C语言-指针示意图|100%]]

1. 指针创建

参考 C 语言笔记:CB1-1-新手村(三)S07E02.1 笔记。

2. 指针传递

心法:不同于 C 语言,C++ 引入了引用类型(引用是变量的别名),作为函数参数传递时更加简洁易读。

指针参数:如 swap(int *a, int *b){}

  • C 语言没有引用类型,只能通过指针来实现类似的效果。
  • 指针存储的是变量地址,在形参列表和方法体中都需要使用取地址 & 和解地址 * 符号,较麻烦。
  • 指针可以不初始化或初始化为 NULL,操作空指针不会编译报错,所以容易产生系统异常。
  • 指针可以在其生命周期内指向不同的对象,通过重新赋值指针变量来改变其指向。

引用参数:如 swap(int &a, int &b){}

  • C++ 引入了引用类型,函数参数可以使用引用传递,这使得代码更加简洁易读。
  • 对引用的操作实际上就是对所引用对象的操作,不需要额外的取地址或解地址操作,更简单。
  • 引用必须在声明时初始化且不能为 NULL,操作空引用会编译报错,从而避免系统异常。
  • 引用一旦绑定到一个对象,就不能再绑定到其他对象,即引用的目标对象是固定的。

指针和引用均可以作为函数的返回值,但切记不要返回方法体内局部变量的指针或引用,因为局部变量在函数结束后会被销毁,返回的引用或指针将成为悬空引用或悬空指针。

武技:测试指针参数和引用参数

// senior/RefParam.h
// Created by 周航宇 on 2025/2/26.
//
#ifndef V1_1_BASIC_ROOKIE_REF_PARAM_H
#define V1_1_BASIC_ROOKIE_REF_PARAM_H
#include <iostream>namespace RefParam {/** C 风格的交换,需要传递两个变量的取地址,然后在参数中解地址 */void cSwap(int *a, int *b);/** C++ 风格的交换,不需要传递地址,直接传递两个变量,在参数中使用引用 */void cppSwap(int &a, int &b);void test();
}#endif //V1_1_BASIC_ROOKIE_REF_PARAM_H
// senior/RefParam.cpp
// Created by 周航宇 on 2025/2/26.
//
#include "RefParam.h"using std::cout, std::endl;void RefParam::cSwap(int *a, int *b) {int temp = *a;*a = *b;*b = temp;
}void RefParam::cppSwap(int &a, int &b) {int temp = a;a = b;b = temp;
}void RefParam::test() {int a = 1, b = 2;cSwap(&a, &b);cout << "a = " << a << ", b = " << b << endl;int c = 3, d = 4;cppSwap(c, d);cout << "c = " << c << ", d = " << d << endl;
}

3. 常量指针

参考 C 语言笔记:CB1-1-新手村(三)S07E02.3 笔记。

4. 指针数组

参考 C 语言笔记:CB1-1-新手村(三)S07E02.4 笔记。

5. 指针函数

参考 C 语言笔记:CB1-1-新手村(三)S07E02.5 笔记。

E03. 结构体

1. 结构体

C 的结构体

  • 结构体只能包含数据成员,不能包含成员函数。
  • 结构体成员默认都是公共的,没有访问控制的概念,所有代码都可以直接访问结构体的成员。
  • 使用结构体时通常需要加上 struct 关键字,除非使用 typedef 对结构体起了别名。

C++ 的结构体

  • 结构体可以包含成员函数:
    • 函数体可以被 const 修饰,此时函数体中若尝试修改调用者某个非静态成员,编译器会发出警告。
    • 返回值可以被 [[nodiscard]] 修饰,此时若返回值被忽略,编译器会发出警告。
  • 结构体可以用 public,private 和 protected 控制成员的访问权限,默认 public 修饰:
    • 一旦结构体的成员被非 public 修饰,则无法使用列表的方式进行初始化。
  • 使用结构体类型时可以省略 struct 关键字,直接使用结构体名来定义变量。

其余参考 C 语言笔记:CB1-1-新手村(三)S07E03.1 笔记。

武技:测试 C++ 结构体

// senior/Structure.h
// Created by 周航宇 on 2025/2/26.
//
#ifndef V1_1_BASIC_ROOKIE_STRUCTURE_H
#define V1_1_BASIC_ROOKIE_STRUCTURE_H
#include <iostream>
#include <string>namespace Structure {struct User {private:// 主键long long id;public:// 年龄int age;// 体重double weight;// 姓名(字符串)std::string name;// 上级领导(指针)User *manager;void setId(long long newId);// [[nodiscard]] 表示该函数的返回值若被忽略,编译器会发出警告[[nodiscard]] long long getId() const;};void test();
}#endif //V1_1_BASIC_ROOKIE_STRUCTURE_H
// senior/Structure.cpp
// Created by 周航宇 on 2025/2/26.
//
#include "Structure.h"using std::cout, std::endl;void Structure::User::setId(long long int newId) {this->id = newId;
}long long Structure::User::getId() const {return this->id;
}void Structure::test() {// 创建 User 结构体User lucky;lucky.setId(1L);lucky.name = "lucky";lucky.age = 18;lucky.weight = 50.5;lucky.manager = nullptr;User joeZhou;joeZhou.setId(2L);joeZhou.name = "JoeZhou";joeZhou.age = 25;joeZhou.weight = 70.5;joeZhou.manager = &lucky;// 输出 User 结构体信息cout << "Lucky: " << endl;cout << "\tid: " << lucky.getId() << endl;cout << "\tname: " << lucky.name << endl;cout << "\tage: " << lucky.age << endl;cout << "\tweight: " << lucky.weight << endl;cout << "\tmanager: " << (lucky.manager ? lucky.manager->name : "无") << endl;cout << "JoeZhou: " << endl;cout << "\tid: " << joeZhou.getId() << endl;cout << "\tname: " << joeZhou.name << endl;cout << "\tage: " << joeZhou.age << endl;cout << "\tweight: " << joeZhou.weight << endl;cout << "\tmanager: " << (joeZhou.manager ? joeZhou.manager->name : "无") << endl;
}

2. 枚举类

心法:在 C 语言中没有枚举类(enum class)的概念,只有普通枚举(enum),而 C++ 不仅保留了普通枚举,还引入了枚举类(C++11 及以后)。

普通枚举

  • 枚举常量作用域为全局,同一作用域内不能有同名枚举常量,即使来自不同枚举类型。
  • 需要使用 enum Color c = RED; 方式获取枚举常量。

枚举类

  • 枚举常量仅在所在类内有效,不同枚举类可有相同名称的常量,不会冲突。
  • 需要使用 Color c = Color::RED; 方式获取枚举常量。

其余参考 C 语言笔记:CB1-1-新手村(三)S07E03.2 笔记。

武技:测试 C++ 枚举类

// senior/EnumClass.c
// Created by 周航宇 on 2025/2/26.
//
#ifndef V1_1_BASIC_ROOKIE_ENUM_CLASS_H
#define V1_1_BASIC_ROOKIE_ENUM_CLASS_H
#include <iostream>namespace EnumClass {enum class Color {RED = 1,GREEN = 2,BLUE = 3};void test();
}#endif //V1_1_BASIC_ROOKIE_ENUM_CLASS_H
// senior/EnumClass.cpp
// Created by 周航宇 on 2025/2/26.
//
#include "EnumClass.h"using std::cout, std::endl, std::cin;void EnumClass::test() {// 接收一个 int 类型的变量cout << "请输入一个颜色代码 (1:RED, 2:BLUE, 3:GREEN):" << endl;int colorInt;cin >> colorInt;// 将 int 类型的变量转换为 Color 类型的变量auto color = static_cast<Color>(colorInt);switch (color) {case Color::RED:cout << "Color is RED" << endl;break;case Color::BLUE:cout << "Color is BLUE" << endl;break;case Color::GREEN:cout << "Color is GREEN" << endl;break;default:cout << "Color is UNKNOWN" << endl;}
}

E04. 类型别名

1. typedef

参考 C 语言笔记:CB1-1-新手村(三)S07E04.1 笔记。

2. using

心法:在定义普通类型别名时,C++11 引入的 using 别名声明可以替代 typedef,更加直观和灵活。

typedef int Integer:不能定义模板别名。

using Integer = int:可以定义模板别名,但注意 using namespace std 的写法并不是起别名,而是一种引入命名空间中所有名称到当前作用域的方式。

其余参考 C 语言笔记:CB1-1-新手村(三)S07E04 笔记。

武技:测试 using 别名

// senior/Using.h
// Created by 周航宇 on 2025/2/26.
//
#ifndef V1_1_BASIC_ROOKIE_USING_H
#define V1_1_BASIC_ROOKIE_USING_H
#include <iostream>
#include <string>namespace Using {// 定义一个模板结构体template<typename T>struct Point {T x;T y;};// 使用 using 为模板结构体 Point 起别名template<typename T>using P = Point<T>;void test();
}#endif //V1_1_BASIC_ROOKIE_USING_H
// senior/Using.cpp
// Created by 周航宇 on 2025/2/26.
//
#include "Using.h"using std::cout, std::endl;void Using::test() {P<int> p;p.x = 10;p.y = 20;cout << "x = " << p.x << ", y = " << p.y << endl;
}

E05. 内存操作

1. malloc

参考 C 语言笔记:CB1-1-新手村(三)S07E05.1 笔记。

2. calloc

参考 C 语言笔记:CB1-1-新手村(三)S07E05.2 笔记。

3. realloc

参考 C 语言笔记:CB1-1-新手村(三)S07E05.3 笔记。

4. new

心法:C++ 除了可以使用 C 语言的内存分配函数(malloc、calloc、realloc 和 free 等)外,还引入了 newdelete 运算符,用于对象的动态内存分配和释放。

C 语言内存分配(malloc / calloc / realloc)

  • 分配内存时,不会自动调用对象的构造函数,释放内存时,也不会调用对象的析构函数。
  • 分配内存后,返回的是 void* 类型的指针,需要手动进行类型转换,这可能会导致类型不匹配的问题。
  • 内存分配失败时会返回 NULL,需要手动检查返回值来处理内存分配失败的情况。

C++ 语言内存分配(new)

  • 分配内存后,会自动调用对象的构造函数进行初始化,释放内存前,会自动调用对象的析构函数进行资源清理。
  • 分配内存后,自动返回正确类型的指针,不需要手动进行类型转换,提高了类型安全性。
  • 内存分配失败时会抛出 std::bad_alloc 异常,可用 try-catch 捕获并处理异常,可用 e.what() 获取异常信息。
相关 API 方法描述
new动态分配内存并创建对象,返回一个指向该对象的指针
delete释放 new 分配的内存

武技:测试 malloc 申请内存以及 free 释放内存

// senior/Memory.h
// Created by 周航宇 on 2025/2/27.
//
#ifndef V1_1_BASIC_ROOKIE_MEMORY_H
#define V1_1_BASIC_ROOKIE_MEMORY_H
#include <iostream>namespace Memory {void test();
}#endif // V1_1_BASIC_ROOKIE_MEMORY_H
// senior/Memory.cpp
// Created by 周航宇 on 2025/2/27.
//
#include "Memory.h"void Memory::test() {try {// 使用 new 分配内存int *arr = new int[5];// 初始化数组元素for (int i = 0; i < 5; i++) {arr[i] = i;}// 打印数组元素for (int i = 0; i < 5; i++) {std::cout << arr[i] << " ";}std::cout << std::endl;// 释放内存delete[] arr;} catch (const std::bad_alloc &e) {std::cout << "内存分配失败" << e.what() << std::endl;}
}

E06. 文件操作

1. fopen

参考 C 语言笔记:CB1-1-新手村(三)S07E06.1 笔记。

2. fstream

心法:C++ 引入了流的概念,使用标准库中的输入输出流类 iostream 及其派生类来进行文件读写操作,比如ifstream 输入文件流,ofstream 输出文件流和 fstream 读写文件流。

文件操作

相关 API 方法(路径支持相对和绝对两种)描述
ifstream file("文件路径", "读写模式")创建文件输入流,默认 ios::in 模式,可明确
ofstream file("文件路径", "读写模式")创建文件输出流,默认 ios::out 模式,可明确
fstream file("文件路径", "读写模式")创建文件读写流,默认 ios::in | ios::out 模式,更灵活
file.is_open()返回文件流对象是否成功打开了指定的文件
file.get(char c)读取 1 个字符存入 char,可包括空格,制表符和换行符等
getline(file, string str)读取 1 行字符存入 string,直到遇到换行符或指定的行分隔符
file.read(char[] arr, int n)读取 n 个字符存入 char 数组,通常用于处理二进制文件
file.eof()返回输入流是否已经到达文件末尾
file.put(char c)写入 1 个字符到输出流
file.write(char[] arr, int n)写入 n 个字符到输出流,通常用于处理二进制文件
file.flush()在不关闭文件流的情况下,刷新缓冲区,确保数据立即写入文件
file.close()刷新缓冲区,释放文件资源,解除文件关联并重置文件流状态

读写模式:参数可以是以下几种模式的组合(使用按位或 | 运算符):

读写模式描述若文件不存在若文件已存在
ios::in输入(读) 模式打开文件返回 NULL正常读
ios::out输出(写) 模式打开文件自动创建文件先清空文件,然后再写入
ios::app追加(写) 模式打开文件自动创建文件从文件末尾开始追加写入
ios::binary二进制 模式打开文件

武技:测试 fstream 函数

// senior/File.h
// Created by 周航宇 on 2025/2/27.
//
#ifndef V1_1_BASIC_ROOKIE_FILE_H
#define V1_1_BASIC_ROOKIE_FILE_H
#include <iostream>
#include <fstream>namespace File {void testWriteFile();void testReadFile();void test();
}#endif //V1_1_BASIC_ROOKIE_FILE_H
// senior/File.cpp
// Created by 周航宇 on 2025/2/27.
//
#include "File.h"using std::cout, std::endl, std::cerr, std::string;
using std::ios, std::ifstream, std::ofstream;void File::testWriteFile() {// 打开文件用于写入ofstream file("../04_senior/File.txt", ios::app);if (!file.is_open()) {cerr << "打开文件失败" << endl;return;}// 写入文件内容file << "Hello, World!" << endl;file << "Hello, World!" << endl;// 关闭文件file.close();
}void File::testReadFile() {// 打开文件用于读取ifstream file("../04_senior/File.txt");if (!file.is_open()) {cerr << "打开文件失败" << endl;return;}// 读取文件内容string line;while (getline(file, line)) {cout << line << endl;}if (file.eof()) {cout << "文件读取完毕" << endl;}// 关闭文件file.close();
}void File::test() {testWriteFile();testReadFile();
}

C/C++道经第1卷 - 第2阶 - 基础启航

http://www.dtcms.com/a/354004.html

相关文章:

  • 工业 DCS 全面科普:从入门到 AI 赋能的未来
  • 大视码垛机器人:以技术优势撬动工业码垛升级
  • 【datawhale组队学习】RAG技术 -TASK05 向量数据库实践(第三章3、4节)
  • Scala面试题及详细答案100道(21-30)-- 面向对象编程
  • 丝杆支撑座如何助力自动化设备精准定位?
  • 对接连连支付(四)-- 收款查询
  • 在Python中处理GDB、MDB和Shapefile文件转换
  • 滥用Mybatis一级缓存引发OOM问题
  • 如何使用asyncio库
  • 汽车电气系统的发展演进为测试带来了哪些影响?
  • LangChain4J-(3)-模型参数配置
  • AI生成音乐模型发展现状与前景
  • prettier、eslint、stylelint在项目中使用
  • 理解虚拟 DOM:前端开发中的高效渲染利器
  • Linux操作系统——TCP服务端并发模型
  • Java全栈开发面试实战:从基础到复杂场景的深度解析
  • 【51单片机】【protues仿真】基于51单片机点阵屏系统
  • 全域管控,一触可达:复合机器人远程监控方案重塑智能制造
  • Boosting(提升法)详解
  • Spring Boot + Dubbo 实战教程:打造高性能微服务架构
  • 深度学习12 Reinforcement Learning with Human Feedback
  • openwrt ubus 深入分析
  • C# 字符和字符串
  • 怎么解决大模型幻觉问题
  • 【完全二叉树】 P10990 [蓝桥杯 2023 国 Python A] 彩色二叉树|普及+
  • 车辆识别码vin构成
  • python // 和%区别
  • K8S EFK日志收集全流程实战
  • MySQL数据库精研之旅第十二期:探秘视图,数据库中的 “虚拟表” 魔法
  • stm32 hal库spi dma_tx_rx的几个关键函数执行过程jlink trace分析