C++基础入门笔记
目录
一、第一个C++程序
1.1 什么是变量
1.2 什么是常量
1.3 关键字
1.4 标识符命名规则
二、数据类型
2.1 整型
2.2 sizeof关键字
2.3 浮点型(实型)
2.4 字符型
2.5 转义字符
2.6 字符串型
2.7 布尔类型
2.8 数据的输入
三、运算符
3.1 算术运算符
3.2 赋值运算符
3.3 比较运算符
3.4 逻辑运算符
四、程序流程结构
4.1选择结构
4.1.1 if语句
4.1.2 三目运算符
4.1.3 switch语句
4.2 循环结构
4.2.1 while循环
4.2.3 for循环语句
4.2.4嵌套循环
4.3 跳转语句
4.3.1 break语句
4.3.2 continue语句
4.3.3 goto语句
五、数组
5.1 概述
5.2 一维数组
5.2.1 一维数组定义方式
5.2.2 一维数组数组名
5.2.3 冒泡排序
5.3 二维数组
5.3.1 二维数组的定义方式
5.3.2 二维数组的数组名
六、函数
6.1 概述
6.2 函数的定义
6.3 函数的调用
6.4 值传递
6.5 函数的常见样式
6.6 函数的声明
七、指针
7.1 指针的基本概念
7.2 指针变量的定义和使用
7.3 指针所占的内存空间
7.4 空指针和野指针
7.5 const修饰指针
7.6 指针和数组
7.7 指针和函数
7.8 指针、数组、函数
八、结构体
8.1 结构体基本概念
8.2 结构体定义和使用
8.3 结构体数组
8.4 结构体指针
8.5 结构体嵌套结构体
8.6 结构体做函数参数
8.7 结构体中const使用场景
8.8 结构体的案例
8.8.1 案例1
8.8.2 案例2
一、第一个C++程序
输出hello world
#include <iostream>
using namespace std;
int main()
{
cout << "hello world" << endl; //endl 是指end line换行
system("pause");
return 0;
}
main是程序的入口,一个程序有且只有一个main函数
如何使用注释: 单行注释用: //描述信息 多行注释用: /* 描述信息 */
1.1 什么是变量
变量存在的意义:方便管理内存空间
格式:数据类型 变量名 = 变量初始值;
例如: int a = 10;
#include <iostream>
using namespace std;
int main()
{
int a = 10;
cout << "a=" << a << endl;
system("pause");
return 0;
}
1.2 什么是常量
作用:用于记录程序中不可更改的数据
C++定义常量的两种方式:
<1> #define宏常量:#define 常量名 常量值
通常在文件上方定义,表示一个常量
<2> const修饰的变量: const 数据类型 常量名 = 常量值
通常在变量定义前加关键字const,修饰该变量为常量,使其不可修改
#include <iostream>
using namespace std;
#define Day 7
int main()
{
const int month = 12;
cout << "一周总共有:" << Day << "天" << endl;
cout << "一年总共有:" << month << "月" << endl;
system("pause");
return 0;
}
常量修改就会报错。
1.3 关键字

不能用关键字给变量、常量命名
1.4 标识符命名规则
二、数据类型
数据类型存在的意义: 给变量分配合适的内存空间
2.1 整型

短整型 -32768~32767
2.2 sizeof关键字
作用: 利用sizeof关键字可以统计数据类型所占的内存大小
语法: sizeof(数据类型 /变量)
#include <iostream>
using namespace std;
int main()
{
double a = 13.3;
int num1 = sizeof(int);
int num2 = sizeof(a);
cout << "int的数据类型所占的内存大小" << num1 << endl;
cout << "double所占的内存大小" << num2 <<endl;
system("pause");
return 0;
}
2.3 浮点型(实型)
作用:表示小数
1.单精度 float 占用空间为4字节 有效数字范围为7位有效数字
2.双精度 double 占用空间为8字节 有效数字范围为15-16位有效数字
两者区别在于表示的有效数字的范围不同
float f1 = 3.14;
//编辑器把后面的数字默认为双精度的,使用float将其转换为单精度
float f2 = 3.14f;
//这样后面的数字为单精度,就不用在转换一次
float f3 = 3e2; //科学计数法3*10^2
float f4 = 3e-2; //科学计数法3*0.1^2
一般来说在单精度数字后面加上f
输出的小数默认为6位有效数字,想表示更多位有效数字需要进行额外配置
2.4 字符型
作用: 字符型变量用于显示单个字符
语法: char ch = 'a';
字符变量只占1个字节,其本质上是将对应的ASCII编码放入到存储单元之中,而不是把字符本身放到内存中存储
注意: 显示字符型变量的时候,用单引号括起来,不能用双引号
单引号中只能有一个字符,不能是字符串
#include <iostream>
using namespace std;
int main()
{
char ch = 'a';
cout << sizeof(ch) << endl;
cout << (int)ch << endl; //查看a对应的ASCII编码,将字符型强制转换为整型
return 0;
}
a对应的ASCII编码是97 A对应的为65
2.5 转义字符
作用: 用于表示出一些不能显示出来的ASCII字符

C语言中没有endl所以要用\\n进行换行
水平制表符\\t可以整齐输出数据,默认每8个字符一个制表位

2.6 字符串型
作用: 表示一串字符
1.C语言风格: char 变量名[ ] = "字符串值" 注意是双引号
2.C++风格: string 变量名 = "字符串值" 要引用#include <string>
2.7 布尔类型
作用: 代表真或假的值
bool类型只有两个值:
true—–真(本质1) false—–假(本质0)
bool类型占1个字节大小

2.8 数据的输入
作用: 用于从键盘中获取数据
语法: cin >> 变量
#include <iostream>
using namespace std;
#include <string>
int main()
{
int a = 0;
cout << "请给整型变量a赋值:" << endl;
cin >> a;
cout << "a的值为:" << a << endl;
float f = 3.14f;
cout << "请给浮点型变量f赋值:" << endl;
cin >> f;
cout << "f的值为:" << f << endl;
char ch = 'a';
cout << "请给字符变量ch赋值:" << endl;
cin >> ch;
cout << "ch的值为:" << ch << endl;
string str = "abc";
cout << "请给字符串变量str赋值:" << endl;
cin >> str;
cout << "str的值为:" << str << endl;
bool flag = true;
cout << "请给布尔变量flag赋值:" << endl;
cin >> flag; //bool类型只要是非0的数都为真
cout << "flag的值为:" << flag << endl;
return 0;
}
三、运算符
作用: 用于执行代码的运算
3.1 算术运算符
作用: 用于处理四则运算

除法中,两个整数进行相除运算,其结果依旧是整数 例:10/20=0
在C++中,两个int整数相除时执行的是整数除法,结果会自动截断小数部分,因此 10 / 20会得到 0,而不是 0.5 要获得小数结果,需要将至少一个操作数转换为浮点数类型
除法和取余的时候除数不能为0!!!!
在取余的时候,遇到负数时
-
余数的符号与被除数一致,与除数无关。
-
余数的绝对值小于除数的绝对值。
int a = 10, b = 3;
cout << a % b << endl; // 正数 % 正数:10 % 3 = 1
a = -10, b = 3;
cout << a % b << endl; // 负数 % 正数:-10 % 3 = -1(符号同被除数-10)
a = 10, b = -3;
cout << a % b << endl; // 正数 % 负数:10 % (-3) = 1(符号同被除数10)
a = -10, b = -3;
cout << a % b << endl; // 负数 % 负数:-10 % (-3) = -1(符号同被除数-10)
取余和取模之间的区别:取余的商是向零取整,而取模的商是向下取整。
取余的余数的符号与被除数相同;取模的余数的符号与除数相同
C、C++、Java中%是取余,而Python中数取模运算
-
取余(C++/Java 风格):
-
计算商:-10 / 3 = -3.333…,向零取整得 -3。
-
计算余数:余数 = 被除数 – 商 * 除数 = (-10) – (-3)*3 = -10 + 9 = -1。
-
结果:-1 (余数符号同被除数 -10)。
-
-
取模(数学/Python 风格):
-
计算商:-10 / 3 = -3.333…,向下取整得 -4。
-
计算余数:余数 = 被除数 – 商 * 除数 = (-10) – (-4)*3 = -10 + 12 = 2。
-
结果:2 (余数非负,且在 [0, 3)范围内)
-
两个小数之间是不可以做取模运算的
前置后置的常见问题:
前置递增,先让变量+1,再进行表达式运算
后置递增,先进行表达式运算,再让变量+1
int a2 = 10;
int b2 = ++a2 * 10;
cout << "a2 = " << a2 << endl; // a2=10
cout << "b2 = " << b2 << endl; // b2=110
int a3 = 10;
int b3 = a3++ * 10;
cout << "a3 = " << a3 << endl; // a3=11
cout << "b3 = " << b3 << endl; // b3=100
3.2 赋值运算符
作用: 用于将表达式的值赋给变量

3.3 比较运算符
作用: 用于表达式的比较,并返回一个真值或者假值

int a = 10;
int b = 20;
cout << (a == b) << endl;
cout << (a != b) << endl; //加小括号确定优先级
3.4 逻辑运算符
作用: 用于根据表达式的值返回真值或者假值

与 :同真为真,其余为假
或: 同假为假,其余为真
四、程序流程结构
4.1选择结构
4.1.1 if语句
作用: 执行满足条件的语句
单行格式if语句:if(条件) {条件满足执行的语句}
if条件判断后面不要加分号
多行格式if语句:if(条件) {条件满足执行的语句}else{条件不满足执行的语句}
多条件的if语句:if(条件1) {条件1满足执行的语句}else if(条件2){条件2不满足执行的语句}…else{都不满足执行的语句}
#include <iostream>
using namespace std;
int main()
{
int a;
cin >> a;
if(a > 400){
cout << "能上该学校" << endl;
}
else if(a == 400){
cout << "需要考察" << endl;
}
else{
cout << "考不上" << endl;
}
return 0;
}
嵌套if语句:
#include <iostream>
using namespace std;
int main()
{
int score = 0;
cin >> score;
if(score >= 600){
cout << "考上一本" << endl;
if(score < 650){
cout << "考入人大" << endl;
}
else if(score >= 650 && score < 700){
cout << "考入清华" << endl;
}
else if(score >= 700){
cout << "考入北大" << endl;
}
}
else if(score < 600 && score >= 500){
cout << "考上了二本" << endl;
}
else if(score < 500 && score >= 400){
cout << "考上了三本" << endl;
}
else{
cout << "未考上本科" << endl;
}
return 0;
}
案例分析:
//练习案例,判断三只小猪哪个体重最重
#include <iostream>
using namespace std;
int main()
{
int pig1 = 0;
int pig2 = 0;
int pig3 = 0;
cin >> pig1 >> pig2 >> pig3;
if(pig1 >= pig2){
if(pig1 >= pig3){
cout << "猪1最重" << endl;
}
else{
cout << "猪3最重" << endl;
}
}
else{
if(pig2 >= pig3){
cout << "猪2最重" << endl;
}
else{
cout << "猪3最重" << endl;
}
}
return 0;
}
4.1.2 三目运算符
作用: 通过三目运算符实现简单的判断
语法:表达式1 ? 表达式2 : 表达式3
解释:表达式1的值为真,则执行表达式2,并且返回表达式2的结果;如果表达式1的值为假,则执行表达式3,并返回表达式3的结果
//使用三目运算符实现a,b中大的值赋给c
#include <iostream>
using namespace std;
int main(){
int a = 0;
int b = 0;
int c = 0;
cin >> a >> b;
(a > b) ? (c = a) : (c = b);
//c = a > b ? a : b;
cout << c << endl;
return 0;
}
注意:三目运算符(条件运算符)的优先级高于赋值运算符(=),所以要加上括号!!!
在C++中三目运算符返回的是变量,可以继续赋值:
int a = 10;
int b = 20;
(a > b ? a : b) = 100;
cout << "a=" << a << endl; //10
cout << "b=" << b << endl; //100
4.1.3 switch语句
作用:执行多条件分支语句
语法:
switch(表达式)
{
case 结果1: 执行语句;break;
case 结果2: 执行语句;break;
….
default:执行语句;break;
}
switch语句的 case标签必须是整型常量表达式,不能是条件表达式(如 score>=0 && score<5)
// 示例:将 0-10 分数转换为等级(假设分数为整数)
#include <iostream>
using namespace std;
int main() {
int score = 0;
cin >> score;
int level = 0;
if (score >= 0 && score < 5) level = 1; // 差
else if (score >= 5 && score < 8) level = 2; // 良
else if (score >= 8) level = 3; // 优
switch (level) {
case 1: cout << "差" << endl; break;
case 2: cout << "良" << endl; break;
case 3: cout << "优" << endl; break;
default: cout << "无效分数" << endl;
}
return 0;
}
由于case的穿透,不加break后面会一块都输出出来(一般不建议使用穿透去实现)
switch (month) {
case 1: // 1月
case 3: // 3月
case 5: // 5月
case 7: // 7月
case 8: // 8月
case 10: // 10月
case 12: // 12月
cout << "31天" << endl;
break;
case 4: // 4月
case 6: // 6月
case 9: // 9月
case 11: // 11月
cout << "30天" << endl;
break;
case 2: // 2月
cout << "28或29天" << endl;
break;
}
if和switch的区别:
switch的缺点,判断时候只能是整型或者字符型,不可以是一个区间
switch的优点,结构清晰,执行效率高
4.2 循环结构
4.2.1 while循环
作用: 满足循环条件,执行循环语句
语法: while(循环条件){循环语句}
只要循环条件的结果为真,就执行循环语句
#include <iostream>
using namespace std;
int main(){
int num = 0;
while(num<101){
cout << num << endl;
num++;
}
return 0;
}
注意:循环条件不要导致死循环!!!!
案例:猜随机数是否正确
#include <iostream>
using namespace std;
#include <cstdlib>
#include <ctime>
int main(){
srand((unsigned int)time(NULL));
int num = rand() % 100 + 1; //这样是1~100
//int num = rand % 101 是0~100
cout << num << endl;
int a;
while(1){
cin >> a;
if(a>num){
cout << "猜大了" << endl;
}
else if(a==num){
cout << "猜中了" << endl;
break;
}
else{
cout << "猜小了" << endl;
}
}
return 0;
}
4.2.2 do…while循环语句
作用:满足循环条件,执行循环语句
语法: do {循环语句} while (循环条件);
注意:与while的区别在于do…while会先执行一次循环语句,再判断条件
#include <iostream>
using namespace std;
int main(){
int num = 0;
do{
cout << num << endl;
num++;
}
while(num<10);
return 0;
}
案例分析:水仙花数 一个三位数,其各个位数的3次幂之和等于它本身,请利用do…while语句,求出所有三位数中的水仙花数。
#include <iostream>
using namespace std;
int main(){
int num = 100;
int a,b,c;
do{
a = num /100;
b = (num / 10) % 10;
c = num % 10;
if(num == a*a*a + b*b*b + c*c*c){
cout << num << endl;
}
num++;
}
while(num<1000);
return 0;
}
4.2.3 for循环语句
作用: 满足循环条件,执行循环语句
语法: for(起始表达式;条件表达式;末尾循环体) {循环语句;}
for循环的括号里面是分号不是逗号!!!!!!!!
案例分析:敲桌子(从1到100,输出数字个位或者十位为七,或者是七的倍数的数字)
#include<iostream>
using namespace std;
int main(){
for(int i = 0;i < 101;i++){
if(i % 10 == 7||i % 7 == 0||i / 10 == 7){
cout << i << endl;
}
}
return 0;
}
4.2.4嵌套循环
作用: 在循环体中再嵌套一层循环,解决一些实际问题
案例:利用嵌套循环去生成一个*矩阵图
#include<iostream>
using namespace std;
int main(){
for(int i = 0; i < 10;i++){
for(int n = 0;n < 10;n++){
cout << "* ";
}
cout << endl;
}
return 0;
}
案例分析:乘法口诀表
#include<iostream>
using namespace std;
int main(){
for(int i = 1; i < 10;i++){
for(int j = 1;j <= i;j++){
cout << j << "*" << i << "=" << i*j << " ";
}
cout << endl;
}
return 0;
}
4.3 跳转语句
4.3.1 break语句
作用: 用于跳出选择结构或者循环结构
break的使用时机:
- 出现在switch条件语句中,作用是终止case并且跳出switch
- 出现在循环语句中,作用是跳出当前的循环语句
- 出现在嵌套语句中,跳出最近的内层循环语句
4.3.2 continue语句
作用: 在循环语句中,跳过本次循环中余下的尚未执行的语句,继续执行下一次循环
#include<iostream>
using namespace std;
int main(){
for(int i = 0;i < 100; i++){
if(i % 2 == 0){
continue;
}
cout << i << endl;
}
return 0;
}
4.3.3 goto语句
作用: 可以无条件跳转语句
语法: goto 标记;
解释: 如果标记的名称已经存在,执行到goto语句时,会跳转到标记的位置

五、数组
5.1 概述
所谓数组,就是一个集合,里面存放了相同类型的数据元素
特点1: 数组中的每个数据元素都是相同的数据类型
特点2: 数组是由连续的内存位置组成的
5.2 一维数组
5.2.1 一维数组定义方式
三种方式:

数组下标是0从开始的
5.2.2 一维数组数组名
一维数组名称的用途:
#include<iostream>
using namespace std;
int main(){
int arr[6] = {1,2,3,4,5,6};
cout << sizeof(arr) << endl; //统计整个数组占用的内存大小
cout << arr << endl; //输出数组的起始地址
cout << sizeof(arr[0]) << endl; //每个数组元素占的内存大小
cout << sizeof(arr) / sizeof(arr[0]) << endl; //数组中有几个元素
return 0;
}
数组名是个常量,不可以进行赋值操作
案例分析:五个小猪称重,一个数组中记录五只小猪的体重,找出最重的小猪的体重
#include<iostream>
using namespace std;
int main(){
int temp = 0;
int arr[5] = {300,350,200,400,250};
for(int i = 0;i < 5;i++){
if(temp <= arr[i]){
temp = arr[i];
}
}
cout << temp << endl;
return 0;
}
案例分析:数组逆置
#include<iostream>
using namespace std;
int main(){
int arr[5] = {1,3,2,5,4};
int start = 0;
int end = sizeof(arr) / sizeof(arr[0]);
int temp;
for(int i = 0;i < end/2; i++){ //只用换一半即可
temp = arr[i];
arr[i] = arr[end-i-1];
arr[end-i-1] = temp;
}
for(int j = 0;j<5;j++){
cout<<arr[j]<< endl;
}
return 0;
}
用while语句实现
#include<iostream>
using namespace std;
int main(){
int arr[5] = {1,3,2,5,4};
int start = 0;
int end = sizeof(arr) / sizeof(arr[0]);
int temp;
while(start < end){
temp = arr[start];
arr[start] = arr[end-1];
arr[end-1] = temp;
start++;
end–;
}
for(int j = 0;j<5;j++){
cout<<arr[j]<< endl;
}
return 0;
}
5.2.3 冒泡排序
作用:最常用的排序算法,对数组内元素进行排序
#include<iostream>
using namespace std;
int main(){
int arr[9] = {4,2,8,0,5,6,1,3,9};
int temp;
for(int j = 8;j>=0;j–){
for(int i = 0;i < j;i++){
if(arr[i]>arr[i+1]){
temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
}
}
}
for(int n = 0;n < 9;n++){
cout << arr[n] <<endl;
}
return 0;
}
5.3 二维数组
5.3.1 二维数组的定义方式
二维数组的定义的四种方式:
一般建议使用第二种,直观且增加代码的可读性
#include<iostream>
using namespace std;
int main(){
int arr[2][3] =
{
{1,2,3},
{4,5,6}
};
for(int i = 0;i < 2;i++){
for(int j = 0;j < 3;j++){
cout << arr[i][j] << " ";
}
cout << endl;
}
return 0;
}
5.3.2 二维数组的数组名
- 查看二维数组所占的空间
- 获取二维数组的首地址
#include<iostream>
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) / sizeof(arr[0][0]) << endl; //一共有几个元素
cout << sizeof(arr)[0] / sizeof(arr[0][0]) << endl; //一共有几列
cout << (long long)arr << endl; //二维数组首地址
cout << (long long)arr[0] << endl; //二维数组第一行的首地址
cout << (long long)&arr[0][0] << endl; //二维数组第一个元素的首地址
return 0;
}
二维数组的首地址和第一行的首地址和二维数组第一个元素的首地址都是一样的
注意:
- arr[0][0]获取的是二维数组第一个元素的值
- (long long)arr[0][0]只是将元素值强制转换为 long long类型
- 输出的结果是元素的值,不是地址
解决方法是采用指针!!!!!添加取地址符 &
对于二维数组 arr:
-
arr的类型是 int(*)[3](指向包含3个int的数组的指针)
-
arr[0]的类型是 int[3](一维数组),但在大多数表达式中会退化为 int*(指向int的指针)
案例分析: 考试成绩统计
#include<iostream>
using namespace std;
int main(){
int arr[3][3] =
{
{100,100,100},
{90,50,100},
{60,70,80}
};
cout << "1的成绩为" << arr[0][0] + arr[0][1] + arr[0][2] <<endl;
cout << "2的成绩为" << arr[1][0] + arr[1][1] + arr[1][2] <<endl;
cout << "3的成绩为" << arr[2][0] + arr[2][1] + arr[2][2] <<endl;
return 0;
}
#include<iostream>
using namespace std;
#include<string>
int main(){
int arr[3][3] =
{
{100,100,100},
{90,50,100},
{60,70,80}
};
string names[3] = {"张三", "李四", "王五"};
int num = 0;
for(int i = 0;i < 3;i++){
for(int j = 0;j < 3;j++){
num += arr[i][j];
}
cout << names[i] <<"的成绩为" << num << endl;
num = 0;
}
return 0;
}
六、函数
6.1 概述
作用:将一段经常使用的代码封装起来,减少重复的代码
一个较大的程序,一般分为若干个程序块,每个模块实现特定的功能。
6.2 函数的定义
函数定义一般有五个步骤:
语法:
返回值类型 函数名(参数列表)
{
函数体语句
return表达式
}
6.3 函数的调用
功能:使用定义好的函数
语法: 函数名(参数)
#include<iostream>
using namespace std;
int add(int num1,int num2){
int sum = num1+num2;
return sum;
}
int main(){
int n = 5;
int m = 6;
cout << add(n,m);
return 0;
}
函数定义的时候,用的是形参,函数调用的时候,使用的是实参。
6.4 值传递
- 所谓值传递,就是函数调用时实参将数值传入形参
- 值传递时,如果形参发生改变,并不会影响实参
如果函数不需要返回值,声明的时候可以写void
#include<iostream>
using namespace std;
void swap(int& num1,int& num2){
int temp;
temp = num1;
num1 = num2;
num2 = temp;
}
int main(){
int a = 5;
int b = 6;
swap(a,b);
cout << "a和b分别为" << a << " " << b <<endl;
}
在上述这个案例中,如果swap中传入的形参没有使用&,那么将不会修改原始的a和b,仅仅是在函数内部交换两者的副本,只需在函数参数类型后加上&,改为引用传递,让形参成为实参的别名,这样在函数内对形参的修改会直接作用于实参。
即只是swap这个函数内部交换的,并没有作用到外部的实参上面
6.5 函数的常见样式
#include<iostream>
using namespace std;
//无参无返
void test1(){
cout << "this is test1" << endl;
}
//有参无返
void test2(int a){
cout << "this is test2 a = " << a << endl;
}
//无参有返
int test3(){
cout << "this is test3" << endl;
return 100;
}
//有参有返
int test4(int a){
cout << "this is test4 a =" << a <<endl;
return a;
}
int main(){
test1();
test2(2);
int num = test3();
cout << "num = " << num << endl;
int num2 = test4(5);
cout << "num2 = " << num2 << endl;
return 0;
}
6.6 函数的声明
作用: 告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义
函数的声明可以多次,但是函数的定义只能有一次。
代码的执行是从上往下一行一行执行的,所以函数的定义应放在main函数使用之前。可以提起那告诉编译器函数的存在,即利用函数的声明。
#include<iostream>
using namespace std;
int max(int a,int b); //函数的声明
int main(){
int a,b;
int num;
cout << "输入两个数字" << endl;
cin >> a >> b;
num = max(a,b);
cout << "较大的一个是" << num << endl;
return 0;
}
int max(int a,int b){
return a > b ? a : b;
}
6.7 函数的分文件编写
作用: 让代码结构更加清晰
函数分文件编写一般有4个步骤
swap.h :
#include<iostream>
using namespace std;
void swap(int a, int b);
swap.cpp
#include "swap.h"
void swap(int a, int b){
int temp = a;
a = b;
b = temp;
cout << "a =" << endl;
cout << "b =" << endl;
}
swap的使用.cpp
#include "swap.h"
#include<iostream>
using namespace std;
int main(){
int a = 5;
int b = 6;
swap(a,b);
return 0;
}
我是在dev C++中三个文件放到一个项目中,实现的:

七、指针
7.1 指针的基本概念
指针的作用: 可以通过指针间接访问内存
- 内存编号是从0开始记录的,一般用十六进制数字表示
- 可以利用指针变量保存地址
7.2 指针变量的定义和使用
指针变量定义语法: 数据类型 * 变量名;
#include<iostream>
using namespace std;
int main(){
int a = 10;
int * p; //定义指针
p = &a; //取指
cout << "a的地址为" << &a << endl; //0x6ffe34
cout << "指针p为" << p << endl; //0x6ffe34
//使用指针,通过解引用的方式找到指针指向的内存
//指针前加* 代表解引用 ,找到指针指向内存的数据
*p = 1000;
cout << "a=" << a << endl; //a=1000
cout << "*p=" << *p << endl; //*p=1000
return 0;
}
7.3 指针所占的内存空间
指针也是数据类型 例: int *p 在32位操作系统下:占用4个字节,在64位下占用8个字节。
#include<iostream>
using namespace std;
int main(){
int a = 10;
//int * P;
//p = &a;
int *p = &a;
cout << "sizeof(int *) = " << sizeof(int *) << endl; //8
cout << "sizeof(int *) = " << sizeof(p) << endl; //8
return 0;
}
7.4 空指针和野指针
空指针: 指针变量指向内存中编号为0的空间
用途: 初始化指针变量
注意: 空指针指向的内存是不可以访问的
#include<iostream>
using namespace std;
int main(){
int * p = NULL; //空指针给指针变量初始化
//空指针是不可以进行访问的 访问报错
//0~255之间的内存编号是系统占用的,因此不可以访问
cout << *p << endl;
*p = 100;
cout << *p << endl;
return 0;
}
野指针: 指针变量指向非法的内存空间 会报错
#include<iostream>
using namespace std;
int main(){
//野指针 在程序中要尽量避免出现野指针
int * p = (int *)0x1100;
//报错
cout << *p << endl;
return 0;
}
7.5 const修饰指针
const修饰指针有三种情况:
对于常量指针,例如const int * p = &a;
其指针的指向可以改,但是指针指向的值不可以改
- *p = 20; 错误的,因为其指向的值不可以改
- p = &b; 正确的,其指针的指向可以改
对于指针常量,例如int * const p = &a;
其指针的指向不可以改,但是指针指向的值可以改
- *p = 20; 正确的,因为其指向的值可以改
- p = &b; 错误的,其指针的指向不可以改
对于既修饰指针又修饰常量 例如: const int * const p = &a;
其指针的指向和指针指向的值都不可以改
记忆技巧:const —- 常量 * —– 指针
- 指针指的是地址 值是地址中的数据
- 左定值,右定向:const在 *左边,限定值不变;在 *右边,限定指针方向(地址)不变。
- 常量指针:指向可改,值不可改。
- 指针常量:指向不可改,值可改。
修试谁谁能改,都修饰都不改
7.6 指针和数组
作用: 利用指针访问数组中的元素
#include<iostream>
using namespace std;
int main(){
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
cout << "访问第一个元素:" << arr[0] << endl;
int * p = arr; //数组名就是数组的首地址
cout << "利用指针来访问第一个元素:" << * p << endl; //解引用
p++; //p本身是一个int指针,它自增偏移4个字节就是下一个元素
cout << "利用指针来访问第二个元素:" << * p << endl;
//利用指针遍历数组
int * p2 = arr;
for(int i = 0;i < 10;i++){
cout << * p2 << " " << endl;
p2++;
}
return 0;
}
7.7 指针和函数
作用: 利用指针作函数参数,可以修改实参的值
值传递和地址传递的区别:
#include<iostream>
using namespace std;
//值传递
void swap1(int a, int b){
int temp = a;
a = b;
b = temp;
}
//引用传递
void swap2(int &a, int &b){
int temp = a;
a = b;
b = temp;
}
//地址传递
void swap3(int *p1,int *p2){
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
int main(){
int a = 10;
int b = 20;
//swap1(a,b);
//cout << "a = " << a << "b = " << b << endl;//没发生改变
//swap2(a,b);
//cout << "a = " << a << "b = " << b << endl; //发生交换
swap3(&a,&b);
cout << "a = " << a << "b = " << b << endl; //发生交换
return 0;
}
7.8 指针、数组、函数
案例描述:封装一个函数,利用冒泡排序,实现对整型数组的升序排序
例如数组:int arr[10] = {4,3,6,9,1,2,10,8,7,5};
#include<iostream>
using namespace std;
void bubbleSort(int *arr,int n){ //参数一个是传递数组,一个是记录数组中有几个数
for(int j = 0;j < n-1;j++){
int *p1 = arr;
bool swaped = false; // 记录是否发生交换
for(int i = 0;i < n-1-j;i++){
int *p2 = p1 + 1;
if(*p1 > (*p2)){
int temp = *p1;
*p1 = *p2;
*p2 = temp;
swaped = true;
}
p1++;
}
if(!swaped) break;//如果没有交换,说明已有序,提前结束
}
}
int main(){
int arr[10] = {4,3,6,9,1,2,10,8,7,5};
bubbleSort(arr,10);
int *p = arr;
for(int n = 0;n < 10;n++){
cout << *p << " " << endl;
p++;
}
return 0;
}
使用数组下标完成,数组作为函数参数时,不会传递整个数组的拷贝,而是传递数组首元素的地址
#include <iostream>
using namespace std;
void bubbleSort(int arr[],int n){
for(int i = 0;i < n-1;i++){
bool swaped = false;
for(int j = 0;j < n-1-i;j++){
if(arr[j] > arr[j+1]){
int temp = arr[j+1];
arr[j+1] = arr[j];
arr[j] = temp;
swaped = true;
}
}
if(!swaped) break;
}
}
void printfArr(int *arr,int n){
for(int m = 0;m < n;m++){
cout << arr[m] << " ";
}
}
int main(){
int arr[10] = {4,32,65,2,5,678,76,324,546,8};
int len = sizeof(arr) / sizeof(arr[0]);
cout << "输出数组长度" << len << endl;
bubbleSort(arr,len);
printfArr(arr,len);
return 0;
}
- void bubbleSort(int arr[], int n)
- 优点:意图更明确。“这个函数期望接收一个int类型的数组”。这极大地提高了代码的自解释性。
- 在函数内部,你仍然可以像使用数组一样通过下标arr[i]来访问元素,语法上非常自然。
- void bubbleSort(int *arr, int n)
- 优点:更底层、更精确。它明确告诉你,接收到的实际上是一个指针。这对于理解底层机制(比如指针运算 *(arr+i))有帮助。
- 在函数内部,arr[i]的访问方式依然有效,因为下标操作 [i]本质上就是基于指针的偏移。
#include <iostream>
using namespace std;
void show(int arr[],int n){
cout << sizeof(arr) << endl;
cout << sizeof(arr[0]) << endl;
cout << arr << endl;
cout << arr[0] << endl;
}
void show1(int *arr,int n){
cout << sizeof(arr) << endl;
cout << sizeof(arr[0]) << endl;
cout << arr << endl;
cout << arr[0] << endl;
}
int main(){
int arr[10] = {4,32,65,2,5,678,76,324,546,8};
show(arr,10);
cout << "________" << endl;
show1(arr,10);
return 0;
}
/*
8
4
0x6ffe20
4
________
8
4
0x6ffe20
4
*/
八、结构体
8.1 结构体基本概念
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型
8.2 结构体定义和使用
语法: struct 结构体名 { 结构体成员列表 };
通过结构体创建变量的方式有三种:
- struct 结构体名 变量名
- struct 结构体名 变量名 = { 成员1值, 成员2值…..}
- 定义结构体时顺便创建变量
#include<iostream>
using namespace std;
#include<string>
//创建学生数据类型,通过学生类型创建具体学生
struct Student{
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
}s3;
//通过学生类型创建具体学生
//struct Student s1
//struct Student s2 = {…}
//在定义结构体顺便创建一个结构体变量
int main(){
struct Student s1;
s1.name = "张三";
s1.age = 18;
s1.score = 78;
cout << "姓名:" << s1.name << " 年龄:" << s1.age << " 分数:" << s1.score <<endl;
struct Student s2 = {"王五", 17, 100};
cout << "姓名:" << s2.name << " 年龄:" << s2.age << " 分数:" << s2.score <<endl;
s3.name = "李四";
s3.age = 20;
s3.score = 120;
cout << "姓名:" << s3.name << " 年龄:" << s3.age << " 分数:" << s3.score <<endl;
return 0;
}
总结:定义结构体的关键字struct不可以省略,创建结构体变量的关键字struct可以省略,结构体变量利用操作符“.”访问成员。
8.3 结构体数组
作用: 将自定义的结构体放入到数组中方便维护
语法: struct 结构体名 数组名[元素个数] = { { }, { }, ….{ } }
#include<iostream>
using namespace std;
#include<string>
//创建学生数据类型,通过学生类型创建具体学生
struct Student{
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
};
int main(){
Student stuArray[3] =
{
{"张三",17,93},
{"李四",18,99},
{"王五",20,100}
};
stuArray[2].name = "赵六";
stuArray[2].age = 32;
stuArray[2].score = 100;
for(int i = 0;i < 3;i++){
cout << " 姓名:" << stuArray[i].name
<< " 年龄:" << stuArray[i].age
<< " 成绩:" << stuArray[i].score << endl;
}
return 0;
}
可以在创建结构体数组的时候直接给出成员,也可以在后面使用“ . ”进行补充
8.4 结构体指针
作用: 通过指针访问结构体中的成员
利用操作符->可以通过结构体指针访问结构体属性
#include<iostream>
using namespace std;
#include<string>
//创建学生数据类型,通过学生类型创建具体学生
struct Student{
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
};
int main(){
struct Student s = {"张三",17,93};
struct Student * p = &s; //取地址的时候保持等号两边的数据类型一致
cout << " 姓名:" << p -> name
<< " 年龄:" << p -> age
<< " 成绩:" << p -> score << endl;
return 0
}
8.5 结构体嵌套结构体
作用: 结构体中的成员可以是另一个结构体
例如:老师辅导学员,一个老师的结构体中记录一个学生的结构体
#include<iostream>
using namespace std;
#include<string>
struct Student{
string name;
int age;
int score;
};
struct Teacher{
int id;
string name;
int age;
struct Student stu; //要先定义学生的结构体才能使用
};
int main(){
Teacher t;
t.id = 10000;
t.age = 43;
t.name = "王贡献";
t.stu.name = "王小二";
t.stu.age = 20;
t.stu.score = 89;
cout << " 老师姓名:" << t.name
<< " 老师的编号:" << t.id
<< " 老师的年龄:" << t.age << endl
<< " 老师的学生名字:" << t.stu.name
<< " 学生的年龄:" << t.stu.age
<< " 学生的分数:" << t.stu.score << endl;
return 0;
}
将里面嵌套的结构体用结构体数组表示:
#include<iostream>
using namespace std;
#include<string>
struct Student{
string name;
int age;
int score;
};
struct Teacher{
int id;
string name;
int age;
Student stu[3]; // 在C++中,前面的struct可以省略
};
int main(){
// 方法1:定义时直接初始化
Teacher t = {
10000, // id
"王贡献", // name
43, // age
{ // stu数组
{"张三", 17, 93},
{"李四", 18, 99},
{"王五", 20, 100}
}
};
// 方法2:分别赋值(如果不想在定义时初始化)
/*
Teacher t;
t.id = 10000;
t.name = "王贡献";
t.age = 43;
t.stu[0] = {"张三", 17, 93};
t.stu[1] = {"李四", 18, 99};
t.stu[2] = {"王五", 20, 100};
*/
cout << "老师姓名:" << t.name
<< " 老师的编号:" << t.id
<< " 老师的年龄:" << t.age << endl;
for(int i = 0; i < 3; i++){
cout << "学生姓名:" << t.stu[i].name
<< " 学生年龄:" << t.stu[i].age
<< " 学生成绩:" << t.stu[i].score << endl;
}
return 0;
}
8.6 结构体做函数参数
作用: 将结构体作为参数向函数中传递
传递方式有两种:
- 值传递
- 地址传递
#include<iostream>
using namespace std;
struct student{
string name;
int age;
int score;
};
//值传递
void printStudent1(struct student s){
s.age = 100;
cout << "子函数 姓名:" << s.name
<< " 年龄:" << s.age
<< " 分数:" << s.score << endl;
}
//地址传递
void printStudent2(struct student *p){
p->age = 120;
cout << "子函数 姓名:" << p->name
<< " 年龄:" << p->age
<< " 分数:" << p->score << endl;
}
int main(){
struct student s;
s.name = "张三";
s.age = 18;
s.score = 99;
printStudent1(s);
cout << s.age << endl; //18 值传递修改的是形参
printStudent2(&s);
cout << s.age << endl; //120 地址传递修改的是实参
return 0;
}
8.7 结构体中const使用场景
作用: 用const来防止误操作
在使用函数时,如果是值传递,相当于将实参复制了一份给形参,这样会占用大量内存空间,而如果将形参改为指针,使用地址传递,一个指针只占了几个字节,减少了内存空间的占用,而且不会复制出一个新的副本出来

加入const之后,执行修改操作就会报错,这样就可以避免在函数中的误操作,导致实参被改变
#include<iostream>
using namespace std;
struct student{
string name;
int age;
int score;
};
void printStudents(const student *s){
//s->age = 100;
cout << " 姓名:" << s->name
<< " 年龄:" << s->age
<< " 分数:" << s->score << endl;
}
int main(){
student s = {"张三", 15, 70};
printStudents(s);
return 0;
}
8.8 结构体的案例
8.8.1 案例1
学校正在做毕设项目,每名老师带领5个学生,总共有3名老师,需求如下
设计学生和老师的结构体,其中在老师的结构体中,有老师姓名和一个存放5名学生的数组作为成员。学生的成员有姓名、考试分数,创建数组存放3名老师,通过函数给每个老师及所带的学生赋值最终打印出老师数据以及老师所带的学生数据。
#include<iostream>
using namespace std;
#include<cstdlib>
#include <ctime>
struct student{
string name;
int score;
};
struct teacher{
string name;
student sarr[5];
};
void allocateSpace(struct teacher tarr[],int len){
string nameseed = "ABCDE";
for(int i = 0;i < len;i++){
tarr[i].name = "teacher_";
tarr[i].name += nameseed[i];
for(int j = 0;j < 5;j++){
tarr[i].sarr[j].name = "student_";
tarr[i].sarr[j].name += nameseed[j];
int random = rand() % 61 + 40; //40~100
tarr[i].sarr[j].score = random;
}
}
}
void printInfo(struct teacher tarr[],int len){
for(int m = 0;m < len;m++){
cout << "老师名字 :" << tarr[m].name << endl;
cout << "老师带的学生都有:" << endl;
for(int a = 0;a < 5;a++){
cout << "学生姓名:" << tarr[m].sarr[a].name << " 分数为:" << tarr[m].sarr[a].score << endl;
}
}
}
int main(){
//随机数种子
srand((unsigned int)time(NULL));
teacher tarr[3];
//通过函数给三个老师进行信息赋值
allocateSpace(tarr,3);
//打印所有老师学生信息
printInfo(tarr,3);
return 0;
}
运行结果如下:

8.8.2 案例2
案例描述:
设计一个英雄体结构,包括成员姓名,年龄,性别;创建结构体数组,数组中存放5名英雄。通过冒泡排序算法,将数组中的英雄按照年龄进行升序排序,最终打印排序后的结果。
#include<iostream>
using namespace std;
struct hero{
string name;
int age;
string sex;
};
void bubbleSort(struct hero arr[],int len){
for(int i = 0;i < len-1;i++){
bool swaped = false;
for(int j = 0;j < len-1-i;j++){
if(arr[j].age > arr[j+1].age){
string temp1 = arr[j+1].name;
arr[j+1].name = arr[j].name;
arr[j].name = temp1;
string temp2 = arr[j+1].sex;
arr[j+1].sex = arr[j].sex;
arr[j].sex = temp2;
int temp3 = arr[j+1].age;
arr[j+1].age = arr[j].age;
arr[j].age = temp3;
swaped = true;
}
}
if(!swaped) break;
}
}
void printarr(struct hero arr[],int len){
for(int a = 0;a < len;a++){
cout << "英雄名:" << arr[a].name
<< " 英雄年龄:" << arr[a].age
<< " 英雄性别:" << arr[a].sex
<< endl;
}
}
int main(){
hero arr[5] = {
{"刘备",23,"男"},
{"关羽",22,"男"},
{"张飞",20,"男"},
{"赵云",21,"男"},
{"貂蝉",19,"女"},
};
bubbleSort(arr, 5);
printarr(arr, 5);
return 0;
}
但是实际上交换的时候可以直接交换结构体的内容,而不用一个一个的交换
#include<iostream>
using namespace std;
struct hero{
string name;
int age;
string sex;
};
void bubbleSort(struct hero arr[],int len){
for(int i = 0;i < len-1;i++){
bool swaped = false;
for(int j = 0;j < len-1-i;j++){
if(arr[j].age > arr[j+1].age){
struct hero temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
swaped = true;
}
}
if(!swaped) break;
}
}
void printarr(struct hero arr[],int len){
for(int a = 0;a < len;a++){
cout << "英雄名:" << arr[a].name
<< " 英雄年龄:" << arr[a].age
<< " 英雄性别:" << arr[a].sex
<< endl;
}
}
int main(){
hero arr[5] = {
{"刘备",23,"男"},
{"关羽",22,"男"},
{"张飞",20,"男"},
{"赵云",21,"男"},
{"貂蝉",19,"女"},
};
bubbleSort(arr, 5);
printarr(arr, 5);
return 0;
}





