C++-Part1——入门编程

[TOC]

CLion 环境配置

无 Virtual Studio 方案

参考:

  1. 下载 Cygwin
  2. 安装 - 选择国内镜像
  3. 安装 - 选择 gcc-core、gcc-g++、make、gdb、binutils
  4. 配置工具链

乱码解决方案

根本原因在于移植的编译环境一般只支持 UTF-8,因此要以编译环境为中心,把可能的文件都改成 UTF-8 编码。

否则编码错误会出现在各种地方,包括 Release、ssh。以后使用其他项目,也统一使用 UTF-8 编码。

  1. 设置搜索 UTF-8,修改文件的编码设置。

    image-20220506211715471

  2. 修改 ssh 终端的编码设置

    image-20220506211759176

  3. 项目下所有文件全部转换成 UTF-8 编码

    image-20220506211843056

C++ 初识

Hello, World!

1
2
3
4
5
6
7
#include <iostream>

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

注释

  1. 单行注释:

    • // 描述信息

    • 通常在一行代码的上方或者一条语句的末尾,对该行代码说明

  2. 多行注释:

    • /* 描述信息 */
    • 通常在一段代码的上方,对该段代码做整体说明
  3. 条件编译:

    • #if 0 ... #endif

    • 可以使用条件编译来实现注释,且可以实现嵌套

    • 通过改变参数 0/1 来开启/屏蔽代码

      1
      2
      3
      #if 0
      code1
      #endif

      变量

  • 作用:给一段指定的内存空间起名,方便操作这段内存

  • 语法:数据类型 变量名 = 初始值;

常量

  • 作用:用于记录程序中不可更改的数据
  1. #define 宏常量:
    • #define 常量名 常量值
    • 通常在文件上方定义,表示一个常量
  1. const 修饰的变量
    • const 数据类型 常量名 = 常量值
    • 通常在变量定义前加关键字 const,修饰该变量为常量,不可修改

关键字

asm do if return typedef
auto double inline short typeid
bool dynamic_cast int signed typename
break else long sizeof union
case enum mutable static unsigned
catch explicit namespace static_cast using
char export new struct virtual
class extern operator switch void
const false private template volatile
const_cast float protected this wchar_t
continue for public throw while
default friend register true
delete goto reinterpret_cast try

标识符命名规则

  • 标识符不能是关键字
  • 标识符只能由字母、数字、下划线组成
  • 第一个字符必须为字母或下划线
  • 标识符中字母区分大小写

数据类型

整型

  • 作用:整型变量表示的是整数类型的数据

  • C++ 中能够表示整型的类型有以下几种方式,区别在于所占内存空间不同

数据类型 占用空间 取值范围
short(短整型) 2 字节 -2^15 ~ 2^15-1
int(整型) 4 字节 -2^31 ~ 2^31-1
long(长整形) Windows 为 4 字节,Linux 为 4 字节(32位)8 字节(64位) -2^31 ~ 2^31-1
long long(长长整形) 8 字节 -2^63 ~ 2^63-1

sizeof 关键字

  • 作用:利用 sizeof 关键字可以统计数据类型所占内存大小
  • 语法: sizeof( 数据类型 / 变量)
  • 结论:short < int <= long <= long long
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main() {
cout << "short 类型所占内存空间为: " << sizeof(short) << endl;
cout << "int 类型所占内存空间为: " << sizeof(int) << endl;
cout << "long 类型所占内存空间为: " << sizeof(long) << endl;
cout << "long long 类型所占内存空间为: " << sizeof(long long) << endl;
system("pause");
return 0;
}
/*
* short 类型所占内存空间为: 2
* int 类型所占内存空间为: 4
* long 类型所占内存空间为: 4
* long long 类型所占内存空间为: 8
*/

实型(浮点型)

  • 作用:用于表示小数

  • 浮点型变量分为两种:

    • 单精度 float
    • 双精度 double
  • 有效数字指所有出现的数字个数,浮点数的精度取决于其尾数

数据类型 占用空间 有效数字范围
float 4 字节 7 位有效数字
double 8 字节 15~16 位有效数字
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
int main() {

float f1 = 3.1415926f; // f 指定为 float,否则会经过 double 转型
double d1 = 3.1415926;

cout << f1 << endl; // 默认都只能打印 6 位有效
cout << d1<< endl;
cout << "float sizeof = " << sizeof(f1) << endl;
cout << "double sizeof = " << sizeof(d1) << endl;

//科学计数法
float f2 = 3e2; // 3 * 10 ^ 2
cout << "f2 = " << f2 << endl;
float f3 = 3e-2; // 3 * 0.1 ^ 2
cout << "f3 = " << f3 << endl;

return 0;
}
/*
* 3.14159
* 31.4159
* float sizeof = 4
* double sizeof = 8
* f2 = 300
* f3 = 0.03
*/

字符型

  • 作用:字符型变量用于显示单个字符

  • 语法:char ch = 'a';

  • 注意:

    • 在显示字符型变量时,用单引号将字符括起来,不要用双引号
    • 单引号内只能有一个字符,不可以是字符串
  • C 和 C++ 中字符型变量只占用 1 个字节。

  • 字符型变量是将对应的 ASCII 编码放入到存储单元

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main() {
char ch = 'a';
cout << ch << endl;
cout << sizeof(char) << endl;
cout << (int)ch << endl; //查看字符a对应的ASCII码
ch = 97; //可以直接用ASCII给字符型变量赋值
cout << ch << endl;

return 0;
}
/*
* a
* 1
* 97
* a
*/

ASCII 码大致由以下两部分组成:

  • ASCII 非打印控制字符: ASCII 表上的数字 0-31 分配给了控制字符,用于控制像打印机等一些外围设备。
  • ASCII 打印字符:数字 32-126 分配给了能在键盘上找到的字符,当查看或打印文档时就会出现。
ASCII 控制字符 ASCII 字符 ASCII 字符 ASCII 字符
0 NUT 32 (space) 64 @ 96
1 SOH 33 ! 65 A 97 a
2 STX 34 66 B 98 b
3 ETX 35 # 67 C 99 c
4 EOT 36 $ 68 D 100 d
5 ENQ 37 % 69 E 101 e
6 ACK 38 & 70 F 102 f
7 BEL 39 , 71 G 103 g
8 BS 40 ( 72 H 104 h
9 HT 41 ) 73 I 105 i
10 LF 42 * 74 J 106 j
11 VT 43 + 75 K 107 k
12 FF 44 , 76 L 108 l
13 CR 45 - 77 M 109 m
14 SO 46 . 78 N 110 n
15 SI 47 / 79 O 111 o
16 DLE 48 0 80 P 112 p
17 DCI 49 1 81 Q 113 q
18 DC2 50 2 82 R 114 r
19 DC3 51 3 83 S 115 s
20 DC4 52 4 84 T 116 t
21 NAK 53 5 85 U 117 u
22 SYN 54 6 86 V 118 v
23 TB 55 7 87 W 119 w
24 CAN 56 8 88 X 120 x
25 EM 57 9 89 Y 121 y
26 SUB 58 : 90 Z 122 z
27 ESC 59 ; 91 [ 123 {
28 FS 60 < 92 / 124 |
29 GS 61 = 93 ] 125 }
30 RS 62 > 94 ^ 126 `
31 US 63 ? 95 _ 127 DEL

转义字符

  • 作用:用于表示一些不能显示出来的 ASCII 字符
转义字符 含义 ASCII码值(十进制)
\a 警报 007
\b 退格(BS) ,将当前位置移到前一列 008
\f 换页(FF),将当前位置移到下页开头 012
\n 换行(LF) ,将当前位置移到下一行开头 010
\r 回车(CR) ,将当前位置移到本行开头 013
\t 水平制表(HT) (跳到下一个TAB位置/8个字符位) 009
\v 垂直制表(VT) 011
\\ 代表一个反斜线字符”\“ 092
\' 代表一个单引号(撇号)字符 039
\" 代表一个双引号字符 034
? 代表一个问号 063
\0 数字0 000
\\ddd 8 进制转义字符,d 范围 0~7 3位8进制
\\xhh 16 进制转义字符,h 范围 09, af, A~F 3位16进制

字符串型

  • 作用:用于表示一串字符
  • 两种风格:

    1. C 风格字符串:char 变量名[] = "字符串值"

      1
      2
      3
      4
      5
      6
      7
      8
      9
      #include <iostream>
      using namespace std;

      int main() {
      char str1[] = "hello world";
      cout << str1 << endl;

      return 0;
      }
    2. C++ 风格字符串:string 变量名 = "字符串值"

      • 需要加入 #include <string>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      #include <iostream>
      #include <string>
      using namespace std;

      int main() {
      string str = "hello world";
      cout << str << endl;

      return 0;
      }
  • 注意:string 对象不要和 char 数组混用

布尔类型

  • 作用:布尔数据类型代表真或假的值

  • bool 类型只有两个值:

    • true — 真(本质是1)
    • false — 假(本质是0)
  • bool 类型占 1 个字节大小

数据的输入

  • 作用:用于从键盘获取数据

  • 语法: cin >> 变量

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
#include <iostream>
#include <string>
using namespace std;

int main(){
//整型输入
int a = 0;
cout << "请输入整型变量:" << endl;
cin >> a;
cout << a << endl;

//浮点型输入
double d = 0;
cout << "请输入浮点型变量:" << endl;
cin >> d;
cout << d << endl;

//字符型输入
char ch = 0;
cout << "请输入字符型变量:" << endl;
cin >> ch;
cout << ch << endl;

//字符串型输入
string str;
cout << "请输入字符串型变量:" << endl;
cin >> str;
cout << str << endl;

//布尔类型输入
bool flag = true;
cout << "请输入布尔型变量:" << endl;
cin >> flag;
cout << flag << endl;

return EXIT_SUCCESS;
}

运算符

算术运算符

  • 作用:用于处理四则运算
运算符 术语 示例 结果
+ 正号 +3 3
- 负号 -3 -3
+ 10 + 5 15
- 10 - 5 5
* 10 * 5 50
/ 10 / 5 2
% 取模(取余) 10 % 3 1
++ 前置递增 a=2; b=++a; a=3; b=3;
++ 后置递增 a=2; b=a++; a=3; b=2;
前置递减 a=2; b=–a; a=1; b=1;
后置递减 a=2; b=a–; a=1; b=2;

赋值运算符

  • 作用:用于将表达式的值赋给变量
运算符 术语 示例 结果
= 赋值 a=2; b=3; a=2; b=3;
+= 加等于 a=0; a+=2; a=2;
-= 减等于 a=5; a-=3; a=2;
*= 乘等于 a=2; a*=2; a=4;
/= 除等于 a=4; a/=2; a=2;
%= 模等于 a=3; a%2; a=1;

比较运算符

  • 作用:用于表达式的比较,并返回一个真值或假值
运算符 术语 示例 结果
== 相等于 4 == 3 0
!= 不等于 4 != 3 1
< 小于 4 < 3 0
> 大于 4 > 3 1
<= 小于等于 4 <= 3 0
>= 大于等于 4 >= 1 1

逻辑运算符

  • 作用:用于根据表达式的值返回真值或假值
运算符 术语 示例 结果
! !a 如果a为假,则!a为真; 如果a为真,则!a为假。
&& a && b 如果a和b都为真,则结果为真,否则为假。
|| a || b 如果a和b有一个为真,则结果为真,二者都为假时,结果为假。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
using namespace std;

int main() {
int a = 1;
int b = 0;
int c = 3;
a += b || c;
cout << a << endl;
cout << b << endl;
cout << c << endl;

return EXIT_SUCCESS;
}

程序流程结构

C/C++支持最基本的三种程序运行结构:

  • 顺序结构:程序按顺序执行,不发生跳转
  • 选择结构:依据条件是否满足,有选择的执行相应功能
  • 循环结构:依据条件是否满足,循环多次执行某段代码

选择结构

if 语句

  • 作用:执行满足条件的语句

  • if 语句的三种形式

    • 单行格式 if 语句

      1
      2
      3
      if ( 条件 ) { 
      条件满足执行的语句
      }
    • 多行格式 if 语句

      1
      2
      3
      4
      5
      if ( 条件 ) { 
      条件满足执行的语句
      } else {
      条件不满足执行的语句
      }
    • 多条件的 if 语句

      1
      2
      3
      4
      5
      6
      7
      8
      9
      if ( 条件1 ) { 
      条件1满足执行的语句
      } else if ( 条件2 ) {
      条件2满足执行的语句
      }
      ...
      else {
      都不满足执行的语句
      }

      三目运算符

  • 作用: 通过三目运算符实现简单的判断

  • 语法:表达式1 ? 语句2 :语句3

  • C++ 中三目运算符返回的是变量,可以继续赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;

int main() {
int a = 10;
int b = 20;
int c = 0;

c = a > b ? a : b;
cout << "c = " << c << endl; // c = 20

(a > b ? a : b) = 100;
cout << "a = " << a << endl; // a = 10
cout << "b = " << b << endl; // b = 100
cout << "c = " << c << endl; // c = 20

return 0;
}

switch 语句

  • 作用:执行多条件分支语句

  • 注意:

    • switch 语句中表达式类型只能是整型或者字符型
    • case 里如果没有 break,那么程序会一直向下执行
    • 与 if 语句比,对于多条件判断时,switch 结构清晰,执行效率高,缺点是 switch 不可以直接判断区间
  • 语法:

    1
    2
    3
    4
    5
    6
    switch ( 表达式 ) {
    case 结果1: 执行语句; break;
    case 结果2: 执行语句; break;
    ...
    default: 执行语句; break;
    }
    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
    int main() {
    //请给电影评分
    //10 ~ 9 经典
    // 8 ~ 7 非常好
    // 6 ~ 5 一般
    // 5分以下 烂片

    int score = 0;
    cout << "请给电影打分" << endl;
    cin >> score;

    switch (score) {
    case 10:
    case 9:
    cout << "经典" << endl;
    break;
    case 8:
    cout << "非常好" << endl;
    break;
    case 7:
    case 6:
    cout << "一般" << endl;
    break;
    default:
    cout << "烂片" << endl;
    break;
    }

    return 0;
    }

    循环结构

while 循环语句

  • 作用:满足循环条件,执行循环语句

  • 语法:

    1
    2
    3
    while ( 循环条件 ) { 
    循环语句
    }

    do…while 循环语句

  • 作用: 先执行一次语句,之后满足循环条件,执行循环语句

  • 注意:

    • while 后面要加分号
  • 语法:

    1
    2
    3
    4
    do {
    cout << num << endl;
    num++;
    } while (num < 10);

    for 循环语句

  • 作用: 满足循环条件,执行循环语句

  • 语法:

    1
    2
    3
    for (int i = 0; i < 10; i++) {
    cout << i << endl;
    }

    跳转语句

break 语句

  • 作用:用于跳出选择结构或者循环结构

  • break 使用的时机:

    • 出现在 switch 条件语句中,作用是终止 case 并跳出 switch
    • 出现在循环语句中,作用是跳出当前的循环语句
    • 出现在嵌套循环中,跳出最近的内层循环语句

continue 语句

  • 作用:在循环语句中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环
  • 注意:continue 并没有使整个循环终止,而 break 会跳出循环

goto 语句

  • 作用:可以无条件跳转语句

  • 注意

    • 在程序中不建议使用 goto 语句,以免造成程序流程混乱
  • 语法: goto 标记;

1
2
3
4
5
6
7
8
9
10
11
12
13
int main() {
cout << "1" << endl;

goto FLAG;
cout << "2" << endl;
cout << "3" << endl;
cout << "4" << endl;

FLAG:
cout << "5" << endl;

return 0;
}

数组

一维数组

一维数组定义方式

  • 一维数组定义的三种方式:
    1. 数据类型 数组名[ 数组长度 ];
    2. 数据类型 数组名[ 数组长度 ] = { 值1,值2 ... };
    3. 数据类型 数组名[ ] = { 值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
#include <iostream>
using namespace std;

int main() {
// 1. 第一种定义方式
// 数据类型 数组名[元素个数];
int arr1[10];
arr1[0] = 100; // 利用下标赋值
cout << arr1[0] << endl; // 利用下标输出

// 2. 第二种定义方式
// 数据类型 数组名[元素个数] = {值1,值2 ,值3 ...};
// 如果{}内不足10个数据,剩余数据用0补全
int arr2[10] = {100, 90, 80, 70, 60, 50, 40, 30, 20, 10};
for (int i = 0; i < 10; i++) {
cout << arr2[i] << endl;
}

// 3. 第三种定义方式
// 数据类型 数组名[] = {值1,值2 ,值3 ...};
int arr3[] = {100, 90, 80, 70, 60, 50, 40, 30, 20, 10};
for (int i : arr3) {
cout << i << endl;
}

return 0;
}

一维数组数组名

  • 一维数组名称的用途:

    1. 可以统计整个数组在内存中的长度:sizeof(arr)
    2. 可以获取数组在内存中的首地址:(int) &arr[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
int main() {
//数组名用途
//1、可以获取整个数组占用内存空间大小
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

cout << "整个数组所占内存空间为: " << sizeof(arr) << endl;
cout << "每个元素所占内存空间为: " << sizeof(arr[0]) << endl;
cout << "数组的元素个数为: " << sizeof(arr) / sizeof(arr[0]) << endl;

//2、可以通过数组名获取到数组首地址
cout << "数组首地址为: " << (int) arr << " - " << arr << endl;
cout << "数组中第一个元素地址为: " << (int) &arr[0] << " - " << &arr[0] << endl;
cout << "数组中第二个元素地址为: " << (int) &arr[1] << " - " << &arr[1] << endl;

return 0;
}
/*
* 整个数组所占内存空间为: 40
* 每个元素所占内存空间为: 4
* 数组的元素个数为: 10
* 数组首地址为: 7338512 - 006FFA10
* 数组中第一个元素地址为: 7338512 - 006FFA10
* 数组中第二个元素地址为: 7338516 - 006FFA14
*/

二维数组

二维数组定义方式

  • 二维数组定义的四种方式:
    1. 数据类型 数组名[ 行数 ][ 列数 ];
    2. 数据类型 数组名[ 行数 ][ 列数 ] = { {数据1,数据2 } ,{数据3,数据4 } };(推荐)
    3. 数据类型 数组名[ 行数 ][ 列数 ] = { 数据1,数据2,数据3,数据4};
    4. 数据类型 数组名[ ][ 列数 ] = { 数据1,数据2,数据3,数据4};
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
int main() {
// 1. 方式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;

for (auto &i : arr) {
cout << i << endl;
for (int j : i) {
cout << j << " ";
}
cout << endl;
}

// 2. 方式2
// 数据类型 数组名[行数][列数] = { {数据1,数据2 } ,{数据3,数据4 } };
int arr2[2][3] = {{1, 2, 3},
{4, 5, 6}};

// 3. 方式3
// 数据类型 数组名[行数][列数] = { 数据1,数据2 ,数据3,数据4 };
int arr3[2][3] = {1, 2, 3, 4, 5, 6};

// 4. 方式4
// 数据类型 数组名[][列数] = { 数据1,数据2 ,数据3,数据4 };
int arr4[][3] = {1, 2, 3, 4, 5, 6};

return 0;
}

二维数组数组名

  • 用途
    • 查看二维数组所占内存空间:sizeof(arr)
    • 获取二维数组首地址:&arr[0][0]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int main() {
int arr[2][3] = {{1, 2, 3},
{4, 5, 6}};

// 大小
cout << "二维数组大小:" << sizeof(arr) << endl; // 二维数组大小:24
cout << "二维数组一行大小:" << sizeof(arr[0]) << endl; // 二维数组一行大小:12
cout << "二维数组元素大小:" << sizeof(arr[0][0]) << endl; // 二维数组元素大小:4

cout << "二维数组行数:" << sizeof(arr) / sizeof(arr[0]) << endl; // 二维数组行数:2
cout << "二维数组列数:" << sizeof(arr[0]) / sizeof(arr[0][0]) << endl; // 二维数组列数:3

// 地址
cout << "二维数组首地址:" << arr << endl; // 二维数组首地址:009AFC94
cout << "二维数组第一行地址:" << arr[0] << endl; // 二维数组第一行地址:009AFC94
cout << "二维数组第二行地址:" << arr[1] << endl; // 二维数组第二行地址:009AFCA0

cout << "二维数组第一个元素地址:" << &arr[0][0] << endl; // 二维数组第一个元素地址:009AFC94
cout << "二维数组第二个元素地址:" << &arr[0][1] << endl; // 二维数组第二个元素地址:009AFC98

return 0;
}

函数

函数的定义

  • 函数的定义的 5 个要素:

    • 返回值类型 :一个函数可以返回一个值
    • 函数名:给函数起个名称
    • 参数列表:使用该函数时,传入的数据
    • 函数体语句:花括号内的代码,函数内需要执行的语句
    • return 表达式: 和返回值类型挂钩,函数执行完后,返回相应的数据
  • 函数定义里小括号内称为形参,函数调用时传入的参数称为实参

  • 语法:

    1
    2
    3
    4
    返回值类型 函数名(形参1类型 形参1名[, ...]) {
    函数体
    [return 表达式;]
    }

    函数的调用

  • 功能:使用定义好的函数

  • 语法:函数名(参数)

值传递

  • 值传递,指函数调用时,实参将数值传入给形参(赋值传递)
  • 值传递时,局部变量的改变,不会影响到外部变量

函数的常见样式

  • 常见的函数样式有4种
    1. 无参无返
    2. 有参无返
    3. 无参有返
    4. 有参有返
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//1. 无参无返
void test01() {
cout << "this is test01" << endl;
}

//2. 有参无返
void test02(int a) {
cout << "this is test02" << endl;
cout << "a = " << a << endl;
}

//3. 无参有返
int test03() {
cout << "this is test03 " << endl;
return 10;
}

//4. 有参有返
int test04(int a, int b) {
cout << "this is test04 " << endl;
int sum = a + b;
return sum;
}

函数的声明

  • 作用: 告诉编译器函数名称及如何调用函数。
  • 函数的实际主体可以单独定义。
  • 函数的声明可以多次,但是函数的定义只能有一次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//声明
int max(int a, int b);

int max(int a, int b);

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

int main() {
int a = 100;
int b = 200;
cout << max(a, b) << endl;

return 0;
}

函数的分文件编写

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

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

    1. 创建后缀名为 .h 的头文件
    2. 创建后缀名为 .cpp 的源文件
    3. 在头文件中写函数的声明
    4. 在源文件中写函数的定义
  • 使用 CMakeLists.txt 时,需要编辑:

    1
    2
    3
    4
    5
    6
    cmake_minimum_required(VERSION 3.19)
    project(CLearning)

    set(CMAKE_CXX_STANDARD 20)

    add_executable(CLearning main.cpp swap.cpp swap.h)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // -> swap.h文件
    #ifndef CLEARNING_SWAP_H
    #define CLEARNING_SWAP_H

    #include<iostream>
    using namespace std;

    // 实现两个数字交换的函数声明
    void swap(int a, int b);

    #endif //CLEARNING_SWAP_H
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // -> swap.cpp文件
    #include "swap.h"

    void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;

    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // -> main函数文件
    #include "swap.h"
    int main() {
    int a = 100;
    int b = 200;
    swap(a, b);

    return 0;
    }

    指针

指针变量的定义和使用

  • 指针也是一种数据类型,可以使用 (int *) 强转

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

  • 指针变量和普通变量的区别

    • 普通变量存放的是数据,指针变量存放的是地址
    • 指针变量可以通过解引用操作符 *,操作指针变量指向的内存空间
  • 总结

    • 我们可以通过 & 符号获取变量的地址
    • 利用指针可以记录地址
    • 对指针变量解引用,可以操作指针指向的内存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main() {
// 1. 指针的定义
int a = 10; //定义整型变量a
// 指针定义语法: 数据类型 * 变量名 ;
int *p;
// 指针变量赋值
p = &a; // 指针指向变量a的地址
cout << &a << endl; // 打印数据a的地址
cout << p << endl; // 打印指针变量p

// 2. 指针的使用
// 通过*解引用符操作指针变量指向的内存
cout << "*p = " << *p << endl;

return 0;
}

指针所占内存空间

  • 所有指针类型在 32 位操作系统下是 4 个字节
1
2
3
4
5
6
7
8
9
10
11
12
13
int main() {
int a = 10;
int * p;
p = &a; // 指针指向数据a的地址

cout << *p << endl; // * 解引用
cout << sizeof(p) << endl;
cout << sizeof(char *) << endl;
cout << sizeof(float *) << endl;
cout << sizeof(double *) << endl;

return 0;
}

空指针和野指针

  • 空指针:指针变量指向内存中编号为 0 的空间
1
2
3
4
5
6
7
8
9
10
int main() {
// 指针变量p指向内存地址编号为0的空间
int * p = NULL;

// 访问空指针报错
// 内存编号 0 ~255 为系统占用内存,不允许用户访问
cout << *p << endl;

return 0;
}
  • 野指针:指针变量指向非法的内存空间
1
2
3
4
5
6
7
8
9
int main() {
// 指针变量p指向内存地址编号为0x1100的空间
int * p = (int *)0x1100;

// 访问野指针报错
cout << *p << endl;

return 0;
}

const 修饰指针

  • const 修饰指针的三种情况

    1. 常量指针:const 修饰指针
    2. 指针常量:const 修饰常量
    3. const 即修饰指针,又修饰常量
  • 技巧:看 const 右侧紧跟着的是指针还是常量,是指针就是常量指针,是常量就是指针常量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main() {
int a = 10;
int b = 10;

// 常量指针:const修饰的是指针,指针的值可以改,指针指向的值不可以更改
const int *p1 = &a;
p1 = &b; // 正确
// *p1 = 100; // 报错

// 指针常量:const修饰的是常量,指针的值不可以改,指针指向的值可以更改
int *const p2 = &a;
// p2 = &b; // 错误
*p2 = 100; // 正确

// const既修饰指针又修饰常量
const int *const p3 = &a;
// p3 = &b; // 错误
// *p3 = 100; // 错误

return 0;
}

指针和数组

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

  • 指向数组的指针,进行 +1 操作,其到底移动多少在于指针的类型(若是 int,则为 4 字节)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main() {
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *p = arr; //指向数组的指针

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

for (int i = 0; i < 10; i++) {
cout << *p << endl;
p++; // 此处往后移 4 个字节
}

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
//值传递
void swap1(int a, int b) {
int temp = a;
a = b;
b = temp;
}

//地址传递
void swap2(int *p1, int *p2) {
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}

int main() {
int a = 10;
int b = 20;

swap1(a, b); // 值传递不会改变实参
swap2(&a, &b); //地址传递会改变实参

cout << "a = " << a << endl;
cout << "b = " << b << 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
//冒泡排序函数
void bubbleSort(int *arr, int len) { //int * arr 也可以写为int arr[]
for (int i = 0; i < len - 1; i++) {
for (int j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}

//打印数组函数
void printArray(int arr[], int len) {
for (int i = 0; i < len; i++) {
cout << arr[i] << endl;
}
}

int main() {
int arr[10] = {4, 3, 6, 9, 1, 2, 10, 8, 7, 5};
int len = sizeof(arr) / sizeof(int);

bubbleSort(arr, len);
printArray(arr, len);

return 0;
}

结构体

结构体定义和使用

  • 语法:struct 结构体名 { 结构体成员列表 };
  • 通过结构体创建变量的方式有三种:(建议使用前两种)
    1. [struct] 结构体名 变量名
    2. [struct] 结构体名 变量名 = { 成员1值 , … }
    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
// 结构体定义
struct student {
string name; // 姓名
int age{}; // 年龄
int score{}; // 分数
} stu3;


int main() {
// 结构体变量创建方式1
struct student stu1; // struct 关键字可以省略
stu1.name = "张三";
stu1.age = 18;
stu1.score = 100;
cout << "姓名:" << stu1.name << " 年龄:" << stu1.age << " 分数:" << stu1.score << endl;

// 结构体变量创建方式2
struct student stu2 = {"李四", 19, 60}; // struct 关键字可以省略
cout << "姓名:" << stu2.name << " 年龄:" << stu2.age << " 分数:" << stu2.score << endl;

// 结构体变量创建方式3
stu3.name = "王五";
stu3.age = 18;
stu3.score = 80;
cout << "姓名:" << stu3.name << " 年龄:" << stu3.age << " 分数:" << stu3.score << endl;

return 0;
}

结构体数组

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 结构体定义
struct student {
string name; // 姓名
int age{}; // 年龄
int score{}; // 分数
}

int main() {
// 结构体数组
struct student arr[3] = {{"张三", 18, 80},
{"李四", 19, 60},
{"王五", 20, 70}};

for (auto & i : arr) {
cout << "姓名:" << i.name << " 年龄:" << i.age << " 分数:" << i.score << endl;
}

return 0;
}

结构体指针

  • 作用:通过指针访问结构体中的成员
  • 利用 -> 可以通过结构体指针访问结构体属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 结构体定义
struct student {
string name; // 姓名
int age{}; // 年龄
int score{}; // 分数
};


int main() {
struct student stu = {"张三", 18, 100,};
struct student *p = &stu;

p->name = "张三"; // 指针通过 -> 操作符可以访问成员
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
// 学生结构体定义
struct student {
string name; // 姓名
int age{}; // 年龄
int score{}; // 分数
};

// 教师结构体定义
struct teacher {
int id{}; // 职工编号
string name; // 教师姓名
int age{}; // 教师年龄
student stu; // 子结构体 学生
};


int main() {
teacher t1;
t1.id = 10000;
t1.name = "老王";
t1.age = 40;

t1.stu.name = "张三";
t1.stu.age = 18;
t1.stu.score = 100;

cout << "教师 职工编号: " << t1.id << " 姓名: " << t1.name << " 年龄: " << t1.age << endl;
cout << "辅导学员 姓名: " << t1.stu.name << " 年龄:" << t1.stu.age << " 考试分数: " << t1.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
// 学生结构体定义
struct student {
string name; // 姓名
int age{}; // 年龄
int score{}; // 分数
};

// 值传递
void printStudent(student stu) {
stu.age = 28;
cout << "子函数中 姓名:" << stu.name << " 年龄: " << stu.age << " 分数:" << stu.score << endl;
}

// 地址传递
void printStudent2(student *stu) {
stu->age = 28;
cout << "子函数中 姓名:" << stu->name << " 年龄: " << stu->age << " 分数:" << stu->score << endl;
}

int main() {
student stu = {"张三", 18, 100};
// 值传递
printStudent(stu);
cout << "主函数中 姓名:" << stu.name << " 年龄: " << stu.age << " 分数:" << stu.score << endl << endl;

// 地址传递
printStudent2(&stu);
cout << "主函数中 姓名:" << stu.name << " 年龄: " << stu.age << " 分数:" << stu.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
// 学生结构体定义
struct student {
string name; // 姓名
int age; // 年龄
int score; // 分数
};

// const使用场景
void printStudent(const student *stu) { // 加const防止函数体中的误操作
// stu->age = 100; // 操作失败,因为加了const修饰
cout << "姓名:" << stu->name << " 年龄:" << stu->age << " 分数:" << stu->score << endl;
}

int main() {
student stu = {"张三", 18, 100};
printStudent(&stu);

return 0;
}