C++学习
- 前言
- using namespace std; 使用标准命名空间
- 一、helloworld-输出表示
- 二、变量
- 三、sizeof---统计数据类型所占的内存大小
- 四、小数表示
- 五、字符表示
- 六、转义字符
- 七、字符串
- 八、数据输入-cin
- 九、随机数小游戏
- 十、数组
- 十一、元素排序
- 十二、二维数组
- 十三、形参函数--值传递
- 十四、🌷重难点:指针
- 十五、空指针
- 十六、常量指针
- 十七、指针数组
- 十八、指针与函数
- 十九、冒泡排序升序排列(指针版)
- 二十、实训项目-通讯录系统
- 二十一、 结构体
- 二十二、 结构体数组
- 二十三、结构体指针
- 二十四、结构体嵌套结构体(套娃)
- 二十五、结构体做函数参数
- 二十七、C++核心
- 二十八、引用
- 总结
前言
学校的C++课程好无聊,干脆自己一下之前在B站学习黑马的C++课程写的代码,找回一点记忆吧。
编译器:Visual Studio 2022
using namespace std; 使用标准命名空间
什么是命名空间?
命名空间是C++中用于组织代码和避免命名冲突的一种机制。它允许在全局作用域中创建一个或多个独立的命名空间,以便在其中定义变量、函数、类和其他命名实体。
命名空间可以被视为一个容器,其中包含了相关的标识符,防止它们与其他代码中的标识符产生冲突。通过在命名空间中定义的标识符可以使用命名空间作用域解析运算符 ::
来访问。
在C++中,命名空间的语法如下所示:
namespace namespace_name { // 在此定义变量、函数、类等 }
在定义的命名空间内部,可以包含各种声明和定义,例如:
变量
函数
类
结构体
枚举
其他命名空间
命名空间的使用可以帮助程序员编写更清晰、更模块化的代码,避免全局命名冲突,并且可以更好地组织和管理大型项目。例如,标准库中的所有标识符都位于 std 命名空间中,以避免与用户代码中的标识符发生冲突。
一、helloworld-输出表示
1.1代码
#include <iostream>
using namespace std;
//黑马程序员的C++教程
int main()
{
cout << "hello world" << endl;
//printf_s("hello");
system("pause");
return 0;
}
并没有什么好解释的,endl属于换行符,可有可无啦。
🌷注意cout别写成计数的了,经常做错。
1.2 运行结果
二、变量
2.1.1 普通变量代码
#include <iostream>
using namespace std;
int main()
{
int a = 10;
cout << "a = " << a << endl;
system("pause");
return 0;
}
唯一值得关注的是 'a='这类的文字表述怎么写的。
2.1.2 运行结果
2.2.1 常量和变量代码
#include <iostream>
using namespace std;
//宏定义是一个常量了,不能作为变量赋值
#define Day 7
int main()
{
cout << "一周一共有:" << Day << "天" << endl;
//const修饰的是变量 = 常量---不可修改
const int month = 12;
//month = 24; 会报错
cout << "一年一共有:" << month << "月份" << endl;
system("pause");
return 0;
}
如果给常量month定义,报错现象如图
2.2.2 运行结果
三、sizeof—统计数据类型所占的内存大小
3.1 代码
/*sizeof---统计数据类型所占的内存大小*/
#include <iostream>
using namespace std;
int main()
{
int num1 = 1;
cout << "num1类型所占的内存空间为:" << sizeof(num1) << endl;
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;
}
在C++中,short 是一种整数数据类型,用于存储较小范围的整数值。它通常表示为16位带符号整数,其范围通常为 -32768 到 32767。short 的大小和范围可能会因编译器和操作系统的不同而有所变化,但通常它是一个较小的整数类型。
我是使用x86的操作系统的。
3.2 运行结果
四、小数表示
#include <iostream>
using namespace std;
int main()
{
//数字后面加f---告诉编译器这个数据是单精度,不然会默认是双精度
//鼠标悬停展示数据类型
//float f1 = 3.14;
float f1 = 3.14f;
cout << "f1 = " << f1 << endl;
double d1 = 3.1415927;
cout << "d1 = " << d1 << endl;
cout << "float 类型所占的内存空间为:" << sizeof(float) << endl; //4字节
cout << "double 类型所占的内存空间为:" << sizeof(double) << endl; //8字节
//科学计数法
float f2 = 3e2; //3*10^2
cout << "f2 = " << f2 << endl;
system("pause");
return 0;
}
- 鼠标悬停展示数据类型
4.2 运行结果
数字后面加f—告诉编译器这个数据是单精度,不然会默认是双精度
五、字符表示
5.1 代码表示-站是一个字符
注意单个字符是单引号''
值得注意的还有获取单个字符的大小的办法-需要进行类型转换。
#include <iostream>
using namespace std;
int main()
{
//字符型变量只是占一个字节
//字符型变量不是字符本身放在内存中存储,而是把对应的ASCII码放入存储单元
char ch = 'a'; //注意是单引号
cout << ch << endl;
cout << "char字符型变量所占内存 :" << sizeof(char) << endl;
cout << (int)ch << endl;
system("pause");
return 0;
}
5.2 运行结果
六、转义字符
6.1 代码
直观感受制表符的魅力。
#include <iostream>
using namespace std;
int main()
{
//换行符
cout << "helloworld \n";
//反斜杠 第一个反斜杠相当于提示要输出特殊字节,第二个表示所要输出的
cout << "\\" << endl;
//水平制表符 \t
cout << "The world is wonderful\t because of me" << endl;
cout << "The world\t is wonderful because of me" << endl;
cout << "The world is\t wonderful because of me" << endl;
system("pause");
return 0;
}
6.2 运行结果
这个得对应着看才能理解。
七、字符串
字符串有两种表述方法,一种是C语音风格,需要[]来表示的。
另一种是C++风格,直接定义string类型的变量。
—注意有些情况需要包含头文件<String.h>。
#include <iostream>
using namespace std;
int main()
{
//C风格字符串
char str[] = "hello world";
cout << str << endl;
//C++风格字符串
//在旧版本需要包含<String.h>
string str2 = "susocool";
cout << str2 << endl;
system("pause");
return 0;
}
7.2 运行结果
八、数据输入-cin
仔细回想一下,C++的输入 C in;输出C out,确实很好记忆。
相对于C语音来说逻辑上会清晰点
8.1 代码
#include <iostream>
using namespace std;
int main()
{
int a = 0;
cout << "请给整型变量a赋值:" ;
cin >> a;
cout << "整型变量a = " << a << endl;
float f = 3.14f;
cout << "请给浮点型变量f赋值:";
cin >> f;
cout << "浮点型变量f = " << f << endl;
char ch = 'a';
cout << "请给字符型变量ch赋值:";
cin >> ch;
cout << "字符型变量ch = " << ch << endl;
string str = "hello";
cout << "请给字符串变量ch赋值:";
cin >> str;
cout << "字符串变量ch = " << str << endl;
system("pause");
return 0;
}
注意一下符号的方向就行了。
8.2 运行结果
九、随机数小游戏
这个没啥好说的,值得注意的只有随机数生成的办法。
1、需要包含头文件
2、rand的用法
9.1 代码
#include <iostream>
#include <ctime>
using namespace std;
int main()
{
//添加随机数种子,利用当前系统实践生成随机数,防止每次随机数都一样
srand((unsigned int)time(NULL));
int num = rand() % 100 + 1; //伪随机数,生成0~100随机数
int val = 0;
// cout << num << endl; //显示随机数的数值
while (1)
{
cin >> val;
if (val > num)
{
cout << "猜错了,数字偏大" << endl;
}
else if (val < num)
{
cout << "猜错了,数字偏小" << endl;
}
else
{
cout << "恭喜您,猜对了" << endl;
break;
}
}
system("pause");
return 0;
}
服气了。还好这串代码也没什么好评价的,不运行了。
十、数组
10.1 一维数组
包括数组的内存空间地址、大小、个数获取。以及首地址、第一个元素获取。
sizeof --> 对数组的使用。
#include <iostream>
using namespace std;
//数组是指放在一块连续的内存空间中,数组中每个元素都是相同的数据类型
//数组名是个常量不可以重新命名
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9 }; //4*10
cout << "整个数组占用的内存空间是 : " << sizeof(arr) << endl;
cout << "每个数组占用的内存空间是 : " << sizeof(arr[0]) << endl;
cout << "数组中元素的个数为 : " << sizeof(arr)/sizeof(arr[0]) << endl;
cout << "数组的首地址是 : " <<(int)arr << endl; //16进制地址转换成整数的
cout << "数组的第一个元素的地址是 : " << (int) & arr[0] << endl;
system("pause");
return 0;
}
10.2 运行结果
十一、元素排序
11.1 代码
要点就是如何获取 数组的上下标
#include <iostream>
using namespace std;
int main()
{
int arr[5] = { 1,3,2,5,4 };
cout << "数组逆置前 : "<< endl;
for (int i = 0; i < 5; i++)
{
cout << arr[i] << endl;
}
int start = 0; //起始下标
int end = sizeof(arr) / sizeof(arr[0]) - 1;//结束下标
while (start < end)
{
//实现元素互换
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
//下标更新
start++;
end--;
}
cout << "数组元素逆置后:" << endl;
for (int i = 0; i < 5; i++)
{
cout << arr[i] << endl;
}
system("pause");
return 0;
}
11.2 运行结果
经典–冒泡排序
我认为冒泡排序最重要的就是判断的嵌套+元素交换。
这张还有一个值得学习的要点就是每个输出之后换行的操作。
#include <iostream>
using namespace std;
/*
1、比较相邻的元素,如果第一个比第二个大,就交换他们两个
2、对每一组元素做同样的工作,执行完毕后找到一个最大值
3、重复以上步骤,每次比较次数-1,直到不需要比较
*/
int main13()
{
int arr[9] = { 4, 2, 8, 0, 5, 7, 1, 3, 9 };
cout << "排序前 : " << endl;
for (int i = 0; i < 9; i++)
{
cout << arr[i] << " ";
}
cout << endl;
//开始冒泡排序
//总共排序的轮数等于元素个数 - 1
for (int i = 0; i < 9 - 1; i++)
{
//内层循环
//循环次数=元素个数 - 当前的轮数 - 1
for (int j = 0; j < 9 - i - 1; j++)
{
if (arr[j] > arr[i])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
cout << "排序后 : " << endl;
for (int i = 0; i < 9; i++)
{
cout << arr[i] << " ";
}
cout << endl;
system("pause");
return 0;
}
运行结果
可以实现对一组数组的数据从小到大排序。
十二、二维数组
- 注意二维数组的表示。
复习一下嵌套for循环。
- 简单说就是外层循环走一次,内层循环就走了一整遍。
- 二维数组的基本操作。对一些相应数据的获取。
12.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;
}
//4字节*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[0]) / sizeof(arr[0][0]) << endl;
cout << "二维数组首地址为: " << (int)arr << endl;
cout << "二维数组的第一种表示 "<< endl;
int arr1[2][3] = { 1,2,3,4,5,6 };
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
cout << arr1[i][j] << " ";
}
cout << endl;
}
cout << "二维数组的第二种表示 " << endl << "知道列数会自己知道是2*3的数组" << endl;
//知道列数会自己知道是2*3的数组
int arr2[][3] = { 1,2,3,4,5,6 };
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
cout << arr2[i][j] << " ";
}
cout << endl;
}
system("pause");
return 0;
}
12.2 运行结果
二维数组实例–考试成绩统计
#include <iostream>
using namespace std;
int main()
{
// 姓名
string name[3] = { "张三","李四","王五" };
// 创建二维数组
int scores[3][3] =
{
{100,100,100},
{90,50,100},
{60,70,80}
};
// 统计每个同学的总分
for (int i = 0; i < 3; i++)
{
int sum = 0;
for (int j = 0; j < 3; j++)
{
sum += scores[i][j];
// cout << scores[i][j] << " ";
}
cout << name[i] << "个人总得分为: " << sum << endl;
}
system("pause");
return 0;
}
运行结果
十三、形参函数–值传递
值传递,就是函数调用时实参的值传递给形参。
13.1 代码
#include <iostream>
using namespace std;
//值传递,就是函数调用时实参的值传递给形参
void swap(int num1, int num2)
{
cout << "交换前:" << endl;
cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl;
int temp = num1;
num1 = num2;
num2 = temp;
cout << "交换后:" << endl;
cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl;
//return; //返回值不需要的时候甚至可以没有return
}
int main16()
{
int a = 10;
int b = 20;
//值传递时,如果新参发生改变,并不会影响实参
cout << "a = " << a << endl;
cout << "b = " << b << endl;
swap(10, 20);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
system("pause");
return 0;
}
13.2 运行结果
简单来说,函数内的变量属于局部变量,只能在这个函数内闹腾,函数运行结束就会回到原本的数值那里。
十四、🌷重难点:指针
简单说
指针就是一个地址
下面两种表述是等价的
int a = 10;
int* p;
p = &a; //int *p = &a;
可以通过解引用【指针前加* 】的方法来招待指针指向的内存。
可以理解为获取数值
解引用是指使用指针访问其指向的内存地址上存储的值。解引用操作符 * 用于访问指针指向的对象。
也可以用来修改指针的数值,示例如下:
int x = 10;
int *ptr = &x; // ptr指向x的内存地址
// 使用解引用操作符访问指针指向的值
cout << "The value of x: " << *ptr << endl; // 输出:The value of x: 10
// 修改指针指向的值
*ptr = 20;
cout << "The new value of x: " << x << endl; // 输出:The new value of x: 20
14.1 代码
//指针就是一个地址
//在32位操作系统下(X86),一个指针占4个字节;64位操作系统(X64)占8个字节
#include <iostream>
using namespace std;
int main()
{
int a = 10;
//定义一个指针
int* p;
//让指针记录变量a的地址
p = &a; //int *p = &a;
cout << "a的地址:" << &a << endl;
cout << "p的地址:" << p << endl;
//内存
cout << "sizeof(int *) = " << sizeof(int*) << "字节" << endl;
cout << "sizeof(float*) = " << sizeof(float*) << "字节" << endl;
cout << "sizeof(double*) = " << sizeof(double*) << "字节" << endl;
cout << "sizeof(char*) = " << sizeof(char*) << "字节" << endl;
//可以通过解引用的方法来招待指针指向的内存
//指针前加* 就是解引用
*p = 100;
cout << "a = " << a << endl;
cout << "*p = " << *p << endl;
system("pause");
return 0;
}
14.2 运行结果
- 指针就是地址
十五、空指针
~ O ~ 其实我看不懂这一段的表述。大概了解一下什么是空指针、什么是野指针吧。
空指针
:指针变量指向内存中编号为0的空间
用途:初始化指针变量
注意:空指针指向的内存是不可以访问的
野指针:指针变量指向非法的内存空间
#include <iostream>
using namespace std;
//空指针:指针变量指向内存中编号为0的空间
//用途:初始化指针变量
//注意:空指针指向的内存是不可以访问的
//野指针:指针变量指向非法的内存空间
int main()
{
//初始化空指针
int* p = NULL;
//空指针不可以被访问,这段会报错
//0~255之间的内存表号是系统占用内存,不允许用户访问
*p = 100;
cout << *p << endl;
int* s = (int*)0x1100; //0x1100被强制转换成地址
cout << *s << endl;
system("pause");
return 0;
}
//空指针和野指针都不是我们申请的内存空间,因此不要访问
十六、常量指针
我愿称之为C++文字游戏。
常量指针—>指针的指向可以修改但是指针指向的值不可以修改。const int *
指针常量—> 指针的指向不可以改,指针指向的值可以改。int* const
#include <iostream>
using namespace std;
int main()
{
//常量指针,特点,指针的指向可以修改但是指针指向的值不可以修改
int a = 10;
int b = 20;
const int *p = &a;
//*p = 20; 错误
p = &b;
//指针常量
//指针的指向不可以改,指针指向的值可以改
int c = 9;
int* const f = &a;
*f = 20;
//f = &c; 错误
system("pause");
return 0;
}
十七、指针数组
我觉得要义就是指向数组首地址,引用指针的时候需要解引用
17.1代码
#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; //arr就是数组的首地址
cout << "利用指针访问第一个元素: " << *p << endl; //*p就是解引用
p++;
cout << "利用指针访问第二个元素: " << *p << endl; //指针向后移动4个字节
cout << "利用指针遍历数组: " << endl;
//遍历数组
int* p2 = arr;
for (int i = 0; i < 9; i++)
{
cout << *p2 << endl;
p2++;
}
system("pause");
return 0;
}
17.2 运行结果
十八、指针与函数
18.1 代码
#include<iostream>
using namespace std;
//值传递,实参的值并没有发生改变,只改变形参
void swap01(int a, int b)
{
int temp = 0;
a = b;
b = temp;
cout << "temp a = " << a;
cout << " temp b= " << b << endl;
}
//指针传递
void swap02(int *p1, int *p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
int main()
{
int a = 10, b = 20;
//swap01(a, b);
swap02(&a, &b);
cout << "a = " << a;
cout << " b= " << b << endl;
system("pause");
return 0;
}
18.2 运行结果
指针传递改变形参的数值
十九、冒泡排序升序排列(指针版)
19.1 代码
#include <iostream>
using namespace std;
void bulleSort(int *arr,int len)
{
for (int i = 0; i < len - 1; i++)
{
for(int j= 0;j< len - i - 1;j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
//函数接受两个参数,分别是指向整型数组的指针arr和数组的长度len。
void printArray(int *arr, int len)
{
for (int i = 0; i < len; i++)
{
cout << arr[i] << " ";
}
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//数组名就是数组的首地址
int len = sizeof(arr) / sizeof(arr[0]);
bulleSort(arr, len);
printArray(arr, len);
system("pause");
return 0;
}
19.1.1 解释一下冒泡排序
意思是一直比直到不交换位置为止!
根据代码分析,应该就是(n-1)次了,一共4个数字,需要进行3(4-1)轮。
19.2运行结果
二十、实训项目-通讯录系统
20.1 代码
#include <iostream>
//#include <String> //这个头文件放在这里,会导致报错 error C4430: 缺少类型说明符 - 假定为 int。注意: C++ 不支持默认 int
using namespace std;
#include <String>
#define MAX 1000
//设计联系人结构体
struct Person
{
string m_Name;
int m_Sex; //1男2女
int m_Age;
string m_Phone;
string m_Addr;
};
struct Addressbooks
{
// 通讯录中保存的联系人数组
struct Person personArray[MAX];
// 通讯录中当前记录联系人个数
int m_Size;
};
//通讯录结构体
//菜单界面
void showMenu()
{
cout << "*************************" << endl;
cout << "***** 1、添加联系人 *****" << endl;
cout << "***** 2、显示联系人 *****" << endl;
cout << "***** 3、删除联系人 *****" << endl;
cout << "***** 4、查找联系人 *****" << endl;
cout << "***** 5、修改联系人 *****" << endl;
cout << "***** 6、清空联系人 *****" << endl;
cout << "***** 0、退出通讯录 *****" << endl;
cout << "*************************" << endl;
}
// 添加联系人
void addPerson(Addressbooks* abs)
{
// 判断通讯录是否已满,如果满了就不再添加
if (abs->m_Size == MAX)
{
cout << "通讯录已满,无法再添加!" << endl;
return;
}
else
{
// 添加具体联系人 姓名、性别、年龄、性别
string name;
cout << "请输入姓名" << endl;
cin >> name;
abs->personArray[abs->m_Size].m_Name = name;
//cout << "请输入性别" << endl;
//cout << "1---男" << endl;
//cout << "2---女" << endl;
int sex = 0;
cout << "请输入性别" << endl << "1---男" << endl << "2---女" << endl;
while (true) //输入正确退出循环
{
cin >> sex;
if (sex == 1 || sex == 2)
{
abs->personArray[abs->m_Size].m_Sex = sex;
break;
}
cout << "输入有误,请重新输入" << endl;
}
cout << "请输入年龄 " << endl << "0~100岁" << endl;
int age = 0;
while (true)
{
cin >> age;
if (age > 0 && age < 100)
{
abs->personArray[abs->m_Size].m_Age = age;
break;
}
cout << "输入有误,请重新输入!" << endl;
}
cout << "请输入联系电话:" << endl;
string phone;
cin >> phone;
abs->personArray[abs->m_Size].m_Phone = phone;
cout << "请输入通讯地址:" << endl;
string address;
cin >> address;
abs->personArray[abs->m_Size].m_Addr = address;
// 更新通讯录人数
abs->m_Size++;
cout << "添加成功" << endl;
system("pause"); // 请按任意键继续
system("cls");
}
}
//显示联系人
void showPerson(Addressbooks* abs)
{
// 判断通讯录中人数是否为0,如果为0提示记录为空
if (abs->m_Size == 0)
{
cout << "记录为空" << endl;
}
else
{
for (int i = 0; i < abs->m_Size; i++)
{
cout << "姓名: " << abs->personArray[i].m_Name << "\t";
cout << "年龄: " << abs->personArray[i].m_Age << "\t";
cout << "性别: " << (abs->personArray[i].m_Sex == 1 ?"男":"女" )<< "\t";
cout << "联系电话: " << abs->personArray[i].m_Phone << "\t";
cout << "家庭地址: " << abs->personArray[i].m_Addr << endl;
}
}
system("pause"); // 请按任意键继续
system("cls");
}
//监测联系人是否存在,如果存在,返回联系人所在数组中的具体位置,不存在返回-1
//参数1 通讯录,参数2 对比姓名
int isExist(Addressbooks *abs,string name)
{
for (int i = 0; i < abs->m_Size; i++)
{
if (abs->personArray[i].m_Name == name)
{
return i; // 找到返回下标
}
}
return -1;
}
void findPerson(Addressbooks* abs)
{
cout << "请输入要查询联系人的姓名" << endl;
string name;
cin >> name;
//判断联系人是否存在
//这里的abs是外层函数findPerson传入的指针,所以不用取地址
int ret = isExist(abs, name);
if (ret != -1)
{
cout << "姓名: " << abs->personArray[ret].m_Name << "\t";
cout << "年龄: " << abs->personArray[ret].m_Age << "\t";
cout << "性别: " << (abs->personArray[ret].m_Sex == 1 ? "男" : "女") << "\t";
cout << "联系电话: " << abs->personArray[ret].m_Phone << "\t";
cout << "家庭地址: " << abs->personArray[ret].m_Addr << endl;
}
else
{
cout << "查无此人!" << endl;
}
system("pause"); // 请按任意键继续
system("cls");
}
// 删除联系人
void deletePerson(Addressbooks* abs)
{
cout << "请输入你要删除的联系人的名字" << endl;
string name;
cin >> name;
// ret == -1 未查到
// ret != -1 查到了
int ret = isExist(abs, name);
if (ret != -1)
{
//数组中的删除元素操作
//i=ret,就是查找对象数组下标
for (int i = ret; i < abs->m_Size; i++)
{
//数据前移,后面的数据赋值给前面
abs->personArray[i] = abs->personArray[i + 1];
}
abs->m_Size--; //更新通讯录中的人数
cout << "删除成功!" << endl;
}
else
{
cout << "查无此人" << endl;
}
system("pause"); // 请按任意键继续
system("cls");
}
//修改指定的联系人信息
void modifyPerson(Addressbooks* abs)
{
cout << "请输入你要修改的联系人" << endl;
string name;
cin >> name;
int ret = isExist(abs, name);
if (ret != -1)
{
cout << "找到指定的联系人!" << endl;
// 添加具体联系人 姓名、性别、年龄、性别
string name;
cout << "请输入姓名" << endl;
cin >> name;
abs->personArray[abs->m_Size].m_Name = name;
int sex = 0;
cout << "请输入性别" << endl << "1---男" << endl << "2---女" << endl;
while (true) //输入正确退出循环
{
cin >> sex;
if (sex == 1 || sex == 2)
{
abs->personArray[abs->m_Size].m_Sex = sex;
break;
}
cout << "输入有误,请重新输入" << endl;
}
cout << "请输入年龄 " << endl << "0~100岁" << endl;
int age = 0;
while (true)
{
cin >> age;
if (age > 0 && age < 100)
{
abs->personArray[abs->m_Size].m_Age = age;
break;
}
cout << "输入有误,请重新输入!" << endl;
}
cout << "请输入联系电话:" << endl;
string phone;
cin >> phone;
abs->personArray[abs->m_Size].m_Phone = phone;
cout << "请输入通讯地址:" << endl;
string address;
cin >> address;
abs->personArray[abs->m_Size].m_Addr = address;
// 更新通讯录人数
abs->m_Size++;
cout << "添加成功" << endl;
}
else
{
cout << "查无此人" << endl;
}
system("pause");
system("cls");
}
void cleanPerson(Addressbooks* abs)
{
abs->m_Size = 0;
cout << "通讯录已经清空" << endl;
system("pause");
system("cls");
}
int main()
{
// 创建通讯录结构体变量
Addressbooks abs;
abs.m_Size = 0; // 初始化通讯录中的当前人员个数
int select = 0; // 用户选择界面
while (true)
{
showMenu();
cin >> select;
switch (select)
{
case 1: //添加联系人
addPerson(&abs); // 利用地址传递,可以在形参里面修饰实参
break;
case 2: //显示联系人
showPerson(&abs);
break;
case 3: //删除联系人
deletePerson(&abs);
break;
case 4: //查找联系人
findPerson(&abs);
break;
case 5: //修改联系人
modifyPerson(&abs);
break;
case 6: //清空联系人
cleanPerson(&abs);
break;
case 0: //退出通讯录
cout << "欢迎下次使用~O~" << endl;
system("pause");
break;
}
}
system("pause");
return 0;
}
20.2 运行结果
界面展示
添加联系人
输入错误会提示重新输入【但是错误3次左右会卡死在请重新输入哪里,但是我懒得去寻找原因了】
显示联系人
删除联系人
备注:为了展示删除后面才随便加入的新联系人名字随便起的0
也许可以继续完善加入,确定删除某某某吗的选项,but我还是没有加入
查找联系人
找不到的
找到的
找到的展示信息
清空联系人
二十一、 结构体
21.1 知识点补货
营养全在注释里面了,主要是学会怎么创建结构体。
深入学习可以看这个⇨C语言结构体(struct)最全的讲解(万字干货)
- 关于语句说明
- 关于加不加后缀名字
比较1和3,可以看出在结构体后面加名称的作用
- 多种使用方法
2是介绍了其他方法的应用结构体变量的方法,引用的同时直接赋值了。
21.2 代码
#include <iostream>
using namespace std;
//创建学生数据,包括:姓名、年龄、分数
//struct(关键字,不可省略)+ 名称
struct Student
{
string name;
int age;
int score;
}s3;
//通过学生的类型创建具体的学生
/*
1、struct Student s1
2、struct Student s2 =
3、在定义结构体时顺便创建结构体变量
*/
int main()
{
//1
//这里的关键字可以省略
struct Student s1;
s1.name = "张三";
s1.age = 18;
s1.score = 100;
cout << "姓名:" << s1.name << " 年龄:" << s1.age << " 分数:" << s1.score << endl;
//2
struct Student s2 = { "李四",19,80 };
cout << "姓名:" << s2.name << " 年龄:" << s2.age << " 分数:" << s2.score << endl;
//3
s3.name = "刘六";
s3.age = 18;
s3.score = 12;
cout << "姓名:" << s3.name << " 年龄:" << s3.age << " 分数:" << s3.score << endl;
system("pause");
return 0;
}
21.3 运行结果
二十二、 结构体数组
22.1 代码
#include <iostream>
using namespace std;
struct Student
{
string name;
int age;
int score;
};
int main()
{
struct Student stuArray[3]
{
{"艾米",18,100},
{"爱腾",20,99},
{"爱搞",18,16}
};
//对内容进行修改
stuArray[2].name = "张二狗";
for (int i = 0; i < 3; i++)
{
cout << "姓名:" << stuArray[i].name
<< " 年龄:" << stuArray[i].age
<< " 成绩:" << stuArray[i].score << endl;
}
system("pause");
return 0;
}
22.2 分析
命名方法
修改变量的值
22.3 运行结果
加点分析
二十三、结构体指针
23.1 代码
#include <iostream>
using namespace std;
struct Student
{
string name;
int age;
int score;
};
int main()
{
Student s = { "张三",18,100 };
//通过指针指向结构体变量
Student* p = &s;
//通过指针访问结构体变量的数据
cout << "姓名:" << p->name
<< " 年龄" << p->age
<< " 成绩:" << p->score << endl;
system("pause");
return 0;
}
23.2 分析
23.3 运行结果
二十四、结构体嵌套结构体(套娃)
24.1 代码
#include <iostream>
using namespace std;
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 = 1000;
t.name = "老苏";
t.age = 50;
t.stu.name = "小小酥";
t.stu.age = 20;
t.stu.score = 100;
cout << "教师姓名:: " << t.name << " 教师编号:" << t.id << " 教师年龄:" << t.age<< endl
<< "教师辅导的学生姓名:" << t.stu.name << " 学生年龄:" << t.stu.age << " 学生成绩: " << t.stu.score << endl;
system("pause");
return 0;
}
24.2 分析
内容套娃
使用起来超级套娃
输出的时候
24.3 运行结果
二十五、结构体做函数参数
25.1 代码
#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 = 200; //地址传递改变实参的值
cout << "地址传递 姓名:" << p->name << " 年龄:" << p->age << " 分数:" << p->score << endl;
}
int main()
{
student s;
s.name = "张三";
s.age = 20;
s.score = 100;
printStudent1(s);
printStudent2(&s);
cout << "主函数 姓名:" << s.name << " 年龄:" << s.age << " 分数:" << s.score << endl;
system("pause");
return 0;
}
25.2 分析
两种方式进行参数的传入
地址传递改变实参的值
值传递不改变实参的值
25.3 运行结果
二十七、C++核心
27.1 内存的分区模型-四区
C++程序在执行时候,将内存大方向化为4个区域
- 1、代码区:存放函数体的二进制代码,由操作系统进行管理的。
- 2、全局区:存放全局变量和静态变量以及常量。
- 3、栈区:由编译器自动分配释放,存放函数的参数值、局部变量等。
- 4、堆区:由程序员分配释放,若程序员不释放,程序结束时由操作系统回收。
27.2 四区的意义:
不同的区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程。【谁可以决定生死】
27.3.1 代码区
在程序运行前
在程序编译后,生成了exe可执行程序,未执行程序前分为两个区域
- 代码区:
存放CPU执行的机器指令
代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。
代码区是可读的,使其可读的原因是为了防止程序呗意外地修改了它的指令 - 全局区:
全局变量和静态变量存放于此
全局区还包括了长量区,字符串常量和其他常量页放在此。
该区域的数据在程序结束后由操作系统释放。
27.3.2 全局区
概念介绍
全局变量和静态变量存放于此
全局区还包括常量区,字符串常量和其他常量也存放于此
该区域的数据结束后由操作系统释放。
例子
#include<iostream>
using namespace std;
int main()
{
// 全局区包括 全局变量、静态变量、常量
// 创建局部变量【不在全局区】
int a = 10;
int b = 10;
cout << "局部变量a的地址为: " << (int)&a << endl;
cout << "局部变量b的地址为: " << (int)&b << endl;
system("pause");
}
细节分析
运行结果
加入全局变量进行对比
(这里有个很奇怪的现象就是加入全局变量之后,我的局部变量就搬家了哈哈哈很奇怪)
#include<iostream>
using namespace std;
int g_a = 10;
int g_b = 10;
int main()
{
// 全局区包括全局变量、静态变量、常量
int a = 10;
int b = 10;
cout << "局部变量a的地址为: " << (int)&a << endl;
cout << "局部变量b的地址为: " << (int)&b << endl;
cout << "全局变量a的地址为: " << (int)&g_a << endl;
cout << "全局变量b的地址为: " << (int)&g_b << endl;
system("pause");
}
加入静态变量
#include<iostream>
using namespace std;
int g_a = 10;
int g_b = 10;
int main()
{
// 全局区包括全局变量、静态变量、常量
int a = 10;
int b = 10;
cout << "局部变量a的地址为: " << (int)&a << endl;
cout << "局部变量b的地址为: " << (int)&b << endl;
cout << "全局变量a的地址为: " << (int)&g_a << endl;
cout << "全局变量b的地址为: " << (int)&g_b << endl;
// 静态变量
static int s_a = 10;
static int s_b = 10;
cout << "静态变量a的地址为: " << (int)&s_a << endl;
cout << "静态变量b的地址为: " << (int)&s_b << endl;
system("pause");
}
常量
1、字符串常量
2、const修饰的变量
#include<iostream>
using namespace std;
int g_a = 10;
int g_b = 10;
//const 修饰的全局变量
const int c_g_a = 10;
int main()
{
// 全局区包括全局变量、静态变量、常量
int a = 10;
int b = 10;
cout << "局部变量a的地址为: " << (int)&a << endl;
cout << "局部变量b的地址为: " << (int)&b << endl;
cout << "全局变量a的地址为: " << (int)&g_a << endl;
cout << "全局变量b的地址为: " << (int)&g_b << endl;
// 静态变量
static int s_a = 10;
static int s_b = 10;
cout << "静态变量a的地址为: " << (int)&s_a << endl;
cout << "静态变量b的地址为: " << (int)&s_b << endl;
// 常量
// 1、字符串常量
cout << "字符串常量的地址为:" << (int)&"hello world" << endl;
// 2、const修饰的变量(全局和局部)
//const 修饰的全局变量
const int c_c_b = 10;
cout << "全局常量量c_g_a的地址为: " << (int)&c_g_a << endl;
cout << "局部常量量c_c_b的地址为: " << (int)&c_c_b << endl;
system("pause");
}
这里少一个地方没有标注出来就是绿色框 81、82的区别,体现的就是它俩距离很近。
结论
27.3.3 栈区
由编译器自动分配释放,存放函数的参数值,局部变量等
🔔注意:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放
#include<iostream>
using namespace std;
// 不要返回局部变量的地址,战区开辟的数据由编译器自动释放
//该函数的返回值类型是int型的指针
int* func()
{
int a = 10;
return &a;//返回局部变量的地址
}
int main()
{
//接收func的返回值
int *p = func();
//解引用
cout << *p << endl; //func执行完局部变量a就被清空,但第一次执行的时候编译器进行了一次保留
cout << *p << endl; //第二次这个数据就不被保留了,再次引用就属于非法操作了
cout << *p << endl;
cout << *p << endl;
system("pause");
return 0;
}
这里一个很奇怪的事情
就是老师说,不要返回局部变量的地址,栈区开辟的数据由编译器自动释放,意思就是第二次引用的时候理论上,就不该是最初局部变量里面的10了,但是,我很多次都是10.。。。害,编译器你人还怪好的嘞~
形参数据也会存在栈区
【得不到一点验证🗑】
27.3.4 堆区
1、由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
2、在C++中主要利用new在堆区开辟内存
#include<iostream>
using namespace std;
//指针本质也是局部变量,放在栈上,指针保存的数据是放在堆区
int* func()
{
//建立在栈区数据不能顺利利用的何况,要把数据移动到堆区
//利用new关键字操作
//int a = 10;
//return &a;
int* p = new int(20); //new开辟了一个20(数据的初始值)的地址,然后用指针去取值,并且返回指针
return p;
}
int main()
{
int* p = func();
cout << *p << endl;
system("pause");
return 0;
}
示意图大概这样
意味着,只要我不让这组数据消失,他就会一直存在。
释放堆区数据
1、C++中利用new操作符在堆区开辟数据
2、堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符delete
3、语法: new数据类型
4、利用new创建的数据,会返回该数据对应的类型的指针
new基本用法
#include<iostream>
using namespace std;
// 1、new基本用法
int* func()
{
//堆区创建整型数据
//返回是该数据的指针
int* pp = new int(20);
return pp;
}
void test01()
{
int* p = func();
cout << *p << endl;
cout << *p << endl;
cout << *p << endl; //堆区的数据由程序员管理开辟,释放
delete p;
cout << *p << endl; //内存已经被释放,再次访问就是非法操作,会报错
}
// 2、堆区开辟数组
void test02()
{
//一组数据用小括号,一堆数据用方括号
//创建10整型的数组,在堆区
int* arr = new int[10];//10表示数组有10个元素
for (int i = 0; i < 10; i++)
{
arr[i] = i + 100; //给是个元素赋值100~109
cout << arr[i] << endl;
}
//释放数组,记得要加中括号(代表释放大量数据)
delete[]arr;
for (int i = 0; i < 10; i++)
{
cout<< "二次引用" << endl << arr[i] << endl;
}
}
int main()
{
test01();
//test02();
system("pause");
return 0;
}
堆区开辟数组
#include<iostream>
using namespace std;
// 1、new基本用法
int* func()
{
//堆区创建整型数据
//返回是该数据的指针
int* pp = new int(20);
return pp;
}
void test01()
{
int* p = func();
cout << *p << endl;
cout << *p << endl;
cout << *p << endl; //堆区的数据由程序员管理开辟,释放
delete p;
cout << *p << endl; //内存已经被释放,再次访问就是非法操作,会报错
}
// 2、堆区开辟数组
void test02()
{
//一组数据用小括号,一堆数据用方括号
//创建10整型的数组,在堆区
int* arr = new int[10];//10表示数组有10个元素
for (int i = 0; i < 10; i++)
{
arr[i] = i + 100; //给是个元素赋值100~109
cout << arr[i] << endl;
}
//释放数组,记得要加中括号(代表释放大量数据)
delete[]arr;
for (int i = 0; i < 10; i++)
{
cout<< "二次引用" << endl << arr[i] << endl;
}
}
int main()
{
//test01();
test02();
system("pause");
return 0;
}
二十八、引用
28.1 基本使用(无代码)
作用:给变量起别名
语法:数据类型 &别名=原名
🔥例子。
int a = 10;
就相当于4字节大小(int)地址存放个10的数据,a去操作这块内存。也就是说,a代表它。
现在换个别名b去代表a(小名)
如果b对数据进行修改,打印a的数值也会是修改后的数值
28.2 注意事项
· 引用必须初始化
· 引用在初始化后,不可以改变
总结
鄙人的一些逼逼赖赖
今天从这内存开始续起来,但是按照黑马的教程应该是还有四节的。我想了想,先不学,但要记录起来。—2024/6/9