序言
总结内容来自b站“黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难”
视频链接:C++| 匠心之作 从0到1入门学编程
如有错误,望纠正,内容加了个人理解与自己在编程时犯的错误。
目录
01第一个C++基础程序
#include <iostream>
using namespace std;
int main() {
//单行注释:main为程序入口,有且仅有一个
cout << "Hello c++" << endl;
/*
多行注释:变量类型 + 变量名 = 变量值
*/
int a = 10;
cout << "a = " << a << endl;
system("pause");
return 0;
}
02变量和常量
常量的作用:用于记录程序中不可更改的数据
c++定义常量两种方式
1. #define宏常量:#define 常量名 常量值
通常在文件上方定义,表示一个常量
2. const修饰的变量 const 数据类型 常量名 = 常量值
通常在变量定义前加关键字const,修饰该变量为常量
//1.宏变量
#define day 7
#include <iostream>
using namespace std;
int main() {
/*
day = 9;
报错:day是常量,已经被define修饰,一旦修改就会报错
*/
cout << "一周共有:" << day << "天" << endl;
//2.const修饰的变量
const int month = 12;
/*
* month = 24;
报错:month是变量,const修饰的变量也称为常量
*/
cout << "一年总共有:" << month << "个月份" << endl;
system("pause");
}
03数据类型
1.标识符命名规则
1)标识符只能以字母、数字、下划线命名
2)标识符命名开头只能用字母、下划线(数字不允许)
3)标识符不能是关键字
4)标识符大小写严格区分
2.sizeof()关键字
sizeof关键字可以统计数据类型所占内存大小
sizeof(数据类型/变量)
3.整型
类型[字节](取值范围)
short[2](-2^e15~2^e15-1)
int[4](-2^e31~2e^31-1)
long[Windows四字节 Linux四字节(32位) 八字节(64位)]
long long[8](-2^e63~2^*63-1) C++有long long类型
超出数据范围,数据与实际不符
short num = 1;
cout << "short(短整型)内存大小为:" << sizeof(num) << "字节"<< endl;
//sizeof()里面可以写num也可以写short 因为num定义为short类型的,占的字节也跟short一样
cout << "int(整型)内存大小为:" << sizeof(int) << "字节" <<endl;
cout << "long(长整型)内存大小为:" << sizeof(long) << "字节" << endl;
cout << "long long(长长整型)内存大小为:" << sizeof(long long) << "字节"<<endl;
4.浮点型(实型)
float(单精度) 4字节 7位有效数字
double(双精度)8字节 15~16位有效数字
有效数字也包括小数点前的数字 eg.3.14三位有效数字
默认情况下,输出一位小数,最多只能输出六位有效数字!!!
float f = 3.1415926f;//如果不加 f,系统默认转化为double类型
double d = 178.17888;//四舍五入出六位有效数字
cout << f << " " << d << endl;
5.科学计数法
float f1 = 3e2;//(3*10^2) 3e2中间没有任何符号
float f2 = 3e-2;//(3*10^-2)
cout << f1 << " " << f2 << endl;
6.字符型
作用:用于显示单个!!!字符
字符型变量只占一个字节 字符型变量不是将字符本身放入内存中存储,而是将对应的ASCII编码放入存储单元
char c = 'acd';//只会显示出最后一个字母d 也就是说只能显示单个字符
char c1 = 'a';
cout << "错误形式表达后输出:" <<c << " 正确形式输出:" << c1 << endl;
7.ASCII码
ASCII码大致由以下两部分组成:
ASCII非打印控制字符:ASCII表上的数字0-31分配给了控制字符,
用于控制像打印机等一些外围设备
ASCII打印字符:数字32-126分配给了能在键盘上找到的字符
当查看或打印文档时就会出现
//字符型变量对应的ASCII码 a-97 A-65
cout << "字母a对应的ASCII码:" << int(c1) << endl;
8.转义字符
作用:用于表示一些不能显示出来的ASCII字符
\b:退格
\n:换行(将当前位置移到下一行开头)
\r:回车(将当前位置移到下一行开头)
\v:垂直制表符
//换行符 \n
cout << "樂y\n";//不用endl
//反斜杠 \\
cout << "\\" << endl;//只加一个反斜杠报错!需要用两个反斜杠生成一个反斜杠
//
//水平制表符 \t 作用:可以整齐输出数据
cout << "0718\t樂y" << endl;
cout << "718\t樂y" << endl;
cout << "8\t樂y" << endl;
//\t加上前面的数字补足八字节 使在同一水平上
输出结果如图
9.字符串型
作用:用于表示一串字符
两种风格:
1.C风格字符串:char 变量名[] = "字符串值"
2.C++风格字符串:string 变量名 = "字符串值"
运用sting时,需要引入头文件#include<string>
//1.C风格字符串
char str[] = "樂yyyy";
cout << str << endl;
//2.C++风格字符串
string s = "樂";
cout << s << endl;
10.布尔类型bool
(跟Java不一样的写法)
作用:布尔数据类型代表真或假的值
bool类型占一个字节
//创建bool类型
bool flag = true;
cout << flag << endl;
11.数据输入
作用:用于键盘获取数据
关键字:cin
语法 cin >> 变量
//浮点数输入
double d1 = 0;
cout << "请输入浮点数的值:" << endl;
cin >> d1;
cout << "键盘输入:" <<d1 << endl;
//布尔类型输入
bool b = false;
cout << "请输入布尔类型的值:" << endl;
cin >> b;//即使输true,输出也是0,除了0,其他都为1
cout << "键盘输入:" << b << endl;//键盘只输出0/1
04算术_比较_逻辑运算符
1.算术运算符
作用:用于处理四则运算
+(正数、加运算)
-(负数、减运算)
*(乘)
/(除)
%(取模)
++(前后置递增)
--(前、后置递减)
在除法运算中,除数不能为0,故也做不了取模运算
两个小数不可以做取模运算!!!作用:两数相除取余数
double d1 = 3.14;
double d2 = 1.1;
//cout << d1 % d2 << endl; 报错!取模运算使用于整数与未区分范围的枚举类型
//前置递增
int a = 10;
a++;//a = a + 1;
cout << "a++ = " << a << endl;
//前置递增与后置递增的区别
//前置递增先对变量进行++,再计算表达式
int a1 = 12;
int b1 = ++a1 * 10;
cout << "前置递增的结果为:" << b1 << endl;
//后置递增先对计算表达式,再对变量进行++
int a2 = 12;
int b2 = a2++ * 10;
cout << "后置递增的结果为:" << b2 << endl;
//总结:递增/递减前置先对变量进行++/--,再计算表达式,后置递增相反
2.赋值运算符
作用:用于将表达式的值赋给变量
= += -= *= /= %=
3.逻辑运算符
作用:用于将表达式的值返回真值或假值
!非 &&与 ||或
非:真变假 假变真
与:两真才为真,其余为假
或:两假才为假,其余为真
cout << !a << endl;//a是12 非真即假 0
cout << !!a << endl;//1
cout << (a1 && a2) << endl;//&& ||表达式要记得用小括号
C++支持最基本的三种程序运行结构:顺序结构、选择结构、循环结构
顺序结构:程序按顺序执行,不发生跳转
选择结构:依据条件是否满足,有选择的执行相应的功能
循环结构:依据条件是否满足,循环多次执行某段代码
05选择结构_小猪称重案例
1.选择结构
if语句的三种形式:
单行格式if语句 if(条件){条件满足执行的语句};
多行格式if语句 if(条件){条件满足执行的语句}else{条件不足执行的语句};
多条件if语句 if(条件1){条件1满足执行的语句}else if(条件2){条件2满足执行的语句}... else{都不满足执行的语句}
嵌套if语句:在if语句中,可以嵌套使用if语句,达到更精确的条件判断
//有三只小猪ABC,请分别输入三只小猪的体重,并且判断哪知小猪最重?
//1.创建三只小猪的体重变量
int num1 = 0;
int num2 = 0;
int num3 = 0;
//2.让用户输入三只小猪重量
cout << "请输入小猪A、B、C的体重:" << endl;
cin >> num1;
cin >> num2;
cin >> num3;
//3.判断哪知最重
//先判断A和B谁重
if (num1 > num2)//A比B重
{
if (num1 > num3)//A比C重
{
cout << "小猪A最重" << endl;
}
else //C比A重
{
cout << "小猪C最重" << endl;
}
}
else //B比A重
{
if (num2 > num3) //B比C重
{
cout << "小猪B最重" << endl;
}
else //C比B重
{
cout << "小猪C最重" << endl;
}
}
2.三目运算符
作用:通过三目运算符实现简单的判断
语法:表达式1?表达式2:表达式3
解释:
如果表达式1的值为真,执行表达式2,并返回表达式2的结果
如果表达式2的值为假,执行表达式3,并返回表达式3的结果
int a = 10;
int b = 20;
int c = 0;
c = a > b ? a : b;
cout << c << endl;
//c++中三目运算府返回的是变量,可以继续赋值
(a > b ? a : b) = 100;
cout << "b = " << b << endl;
3.switch语句
作用:执行多条件分支语句
switch(表达式)
{
case 结果1:执行语句;break;
...
default:执行语句;break;
}
//1.提示用户给电影评分
cout << "请给电影进行评分" << endl;
//2.用户开始进行打分
int score = 0;
cin >> score;
cout << "您为电影打的分数为:" << score << endl;
//3.根据用户输入的分数来提示用户最后的结果
switch (score)
{
case 10:
cout << "您认为是经典电影" << endl;
break;//如果没有这个break,则会继续往下执行
default :
cout << "您认为它没有满分" << endl;
break;
}
4.if和switch区别
switch缺点:判读的时候只能是整型或者字符型,不可以是一个区间
switch优点:结构清晰,执行效率高
06循环结构
1.while循环语句
作用:满足循环条件,执行循环语句
语法:while(循环条件){循环语句}
解释:只要循环条件结果为真,就执行循环语句
int num = 0;
while (num < 10)
{
cout << num++ << " ";
}
//注意:在执行循环语句时候,程序必须提供循环的出口,否则出现死循环
1.1.猜数字
#include <iostream>
//time系统时间头文件包含
#include<ctime>
using namespace std;
//添加随机数种子 作用:利用当前系统时间生成随机数,防止每次随机数一样
srand((unsigned int)time(NULL));
//不加这个的话,随机数一直都是42 使用time需要包含头文件 #include<ctime>
//1.系统生成随机数
int num1 = rand() % 100 + 1;//rand()%100 + 1生成0 + 1~99 + 1的随机数
cout << "\n猜测正确的数字为:" << num1 << endl;
//2.玩家猜测
int val = 0;
//3.判断玩家的猜测
int limit = 0;//限制猜测的次数
while (limit < 3) {//控制只能猜3次
cout << "请输入猜测的数字为:";
cin >> val;
if (val > num1) {
cout << "猜的数大了" << endl;
limit++;
if (limit == 3)
{
cout << "次数已到,退出游戏";
}
}
else if (val < num1)
{
cout << "猜的数小了" << endl;
limit++;
if (limit == 3)
{
cout << "次数已到,退出游戏"<< endl;
}
}
else
{
cout << "猜对了" << endl;
break;
}
}
/*
2.do while 语句
作用:满足循环条件,执行循环语句
语句:do{循环语句}while(循环条件);
注意:与while的区别在于do……while会执行一次循环语句,再判断循环条件
int num2 = 0;
do {
cout << num2 << " ";
num2++;
}
2.1水仙花数
水仙花数是指一个 3 位数,它的每个位上的数字的3次幂之和等于它本身
(例如:1^3 + 5^3+ 3^3 = 153)。
水仙花数153
获取个位 153%10
获取十位 153/10 = 15 15%10 = 5
获取百位 153/100 = 1
判断 个位^3 + 十位^3 + 百位^3 = 本身
int i = 100;
cout << "\ndo-while循环水仙花数有:" << endl;
do
{
int i1 = i % 10;//个位
int i2 = i / 10 % 10;//十位
int i3 = i / 100;//百位
if (i1 * i1 * i1 + i2 * i2 * i2 + i3 * i3 * i3 == i)
{
cout << i << endl;
}
i++;
} while (i < 1000);
3.for循环
语法:for(起始表达式;条件表达式;末尾循环体){循环语句;}
for (int num = 0; num < 10; num++)
{
cout << num << " ";
//1.int num = =0;2.cout << num << " ";3.num < 10;4.num++;
//接着就是124124124...直到不满足3
}
3.1敲桌子案例
在1-100里面,如果是7的倍数、个位有7、十位有7;打印敲桌子
7的倍数:%7 = 0;
个位有7:%10 = 7;
十位有7:/10 = 7;
for (int num1 = 0; num1 <= 100; num1++)
{
if (num1 % 7 == 0 || num1 % 10 == 7 || num1 / 10 ==7)
{
cout << "敲桌子" << endl;
}
else
{
cout << num1 << endl;
}
}
4.嵌套循环
作用:在循环体中再嵌套一层循环,解决一些实际问题
4.1星图
//打印一个10*10的星图
for (int x = 0; x < 10; x++)
{
for (int y = 0; y < 10; y++)
{
cout << "* " ;
}
cout << endl;
}
4.2九九乘法表
//九九乘法表
//行数 <= 列数 行数*列数 = 计算结果
for (int x = 1; x <= 9; x++) //行
{
for (int y = 1; y <= x; y++)//列
{
cout << y << "×" << x << "=" << x * y << " ";
}
cout << endl;
}
07跳转语句
1.break语句
作用:用于跳出选择结构或者循环结构
break使用的时机:
出现在switch条件语句中,作用是终止case并跳出switch
出现在循环语句中,作用是跳出当前的循环语句
出现在嵌套语句中,跳出最近的内层循环语句
//再嵌套循环语句中使用break,退出内层循环
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (j == 5)
{
break;//一行中列的星到了5就退出循环了
}
cout << "* ";
}
cout << endl;
}
2.continue语句
作用:在循环语句中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环
注意:continue并没有使整个循环终止,而break会跳出循环
for (int i1 = 0; i1 < 10; i1++)
{
if (i1 % 2 == 0)
{
continue;//奇数输出,偶数不输出
}
cout << i1 << " ";
}
3.goto语句
作用:可以无条件跳转语句
语法:goto 标记;
解释:如果标记的名称存在,执行到goto语句时,会跳转到标记的位置
cout << "\n7" ;
cout << "1" ;
goto flag;
cout << "3";
cout << "4";
cout << "5";
flag:
cout << "8\n" ;
08一维数组
数组
特点1:数组中的每个数据元素都是相同的数据类型
特点2:数组是由连续的内存位置组成的
一维数组定义方式:
1.数据类型 数组名 [数组长度];
2.数据类型 数组名[数组长度] = {值1,值2...};
3.数据类型 数组名[] = {值1,值2...};
//定义方式1
int score[10];
//利用下标赋值
score[0] = 100;
//定义方式2
int score1[10] = { 100,90,80 };
//定义方式3
int score2[] = { 100,90,80,70 };
for (int i = 0; i < 4; i++)
{
cout << score2[i] << endl;
}
//数组中下标是从0开始索引
1一维数组的用途
1.可以统计整个数组在内存中的长度
2.可以获取数组在内存中的首地址
//1.可以获取整个数组占用内存空间大小
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
cout << "每个地址/首地址所占的内存空间为:" << sizeof(arr[0]) << endl;
cout << "整个数组所占用的内存空间为:" << sizeof(arr) << endl;
cout << "数组的元素个数为:" << (sizeof(arr) / sizeof(arr[0])) << endl;
//2.可以通过数组名获取到数组首地址
cout << "数据首地址为:" << arr << endl;
//首地址输出都是一个16进制数 强转为10进制数,强转为int类型就行
cout << "数据第一个元素的地址为:" << &arr[0] << endl;
//首地址跟第一个元素的地址是一样的
cout << "数据第二个元素的地址为:" << &arr[1] << endl;/
/每个地址间隔4个字节 int类型占四个字节
//取址要加&
2.五只小猪称体重
在一个数组中记录了五只小猪的体重,如:int array[5] = {300,350,200,400,250}
找出并打印最重的小猪体重
int array[5] = { 300,500,200,400,250 };
int max = 0;
for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++)
{
if (array[i] > max)
{
max = array[i];
}
}//这块我用冒泡排序写 可以运行 但是运行后出现异常 数组会越界好像 不要想太复杂
cout << "最重小猪体重为:" <<max << endl;
3.数组元素逆置
元素组为:1,3,2,5,4;逆置后输出结果为:4,5,2,3,1
int ar[] = { 1,3,2,5,4 };
int start = 0;
int end = sizeof(ar) / sizeof(ar[0]) -1;
while (start < end)
{
int temp = ar[start];
ar[start] = ar[end];
ar[end] = temp;
start++;
end--;
}
cout << "逆置数组为:" << endl;
for (int j = 0; j < sizeof(ar) / sizeof(ar[0]); j++)
{
cout << ar[j] << " ";
}
cout << endl;
4.冒泡排序
作用:最常用的排序算法,对数组内元素进行排序
1.比较相邻的元素,如果第一个比第二个大,就交换他们两个
2.对每一对相邻元素做同样的工作,执行完毕之后,找到第一个最大值
3.重复以上的步骤,每次比较次数-1,直到不需要比较
int arra[9] = {4,2,8,0,5,7,1,3,9};
//总共排序轮数为 元素个数-1
for (int i = 0; i < sizeof(arra) / sizeof(arra[0]) - 1; i++)
{
//内层循环对比 次数 = 元素个数 - 当前轮数 - 1
for (int j = 0; j < sizeof(arra) / sizeof(arra[0]) - i - 1; j++)
{
if (arra[j] > arra[j+1])
{
int temp = arra[j];
arra[j] = arra[j + 1];;
arra[j + 1] = temp;
}
}
}
cout << "冒泡排序后的数组:" << endl;
for (int z = 0; z < sizeof(arra) / sizeof(arra[0]); z++)
{
cout << arra[z] << " ";
}
09二维数组
二维数组定义的四种方式
1.数据类型 数组名[行数][列数];
2.数据类型 数组名[行数][列数] = {{数据1,数据2},{数据3,数据4}};
3.数据类型 数据名[行数][列数] = {数据1,数据2,数据3,数据4};
4.数据类型 数据名[ ][列数] = {数据1,数据2,数据3,数据4};
建议:以上四种定义方式,利用第二种更为直观,提高代码的可读性
//数据类型 数组名[行数][列数];
int arr[2][2];
arr[0][0] = 1;
arr[0][1] = 2;
arr[1][0] = 3;
arr[1][1] = 4;
//数据类型 数组名[行数][列数] = {{数据1,数据2},{数据3,数据4}};
int arr1[2][2] = { {1,2} ,{3,4} };
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 2; j++)
{
cout << arr1[i][j] << " ";
}
cout << endl;
}
//数据类型 数据名[行数][列数] = {数据1,数据2,数据3,数据4};
int arr2[2][2] = { 1,2,3,4, };
//数据类型 数据名[ ][列数] = {数据1,数据2,数据3,数据4};
int arr3[][2] = {1,2,3,4};//不能不写行数,否则无法判断行列
//在定义二维数组时,如果初始化了数据,可以省略行数
1.二维数组用途
查看二维数组所占内存空间
获取二维数组首地
int arr4[2][3] =
{
{7,1,8},
{8,2,6}
};
cout << "二维数组所占用内存空间为:" << sizeof(arr4) << endl;
cout << "二维数组第一行所占用内存空间为:" << sizeof(arr4[0]) << endl;
cout << "二维数组第一个元素所占用内存空间为:" << sizeof(arr4[0][0]) << endl;
cout << "二维数组的行数:" << sizeof(arr4) / sizeof(arr4[0]) << endl;
cout << "二维数组的列数:" << sizeof(arr4[0]) / sizeof(arr4[0][0]) << endl;
//查看首地址
cout << "二维数组的首地址:" << arr4 << endl;
cout << "二位地址第一个元素/第一行的地址:" << int(&arr4[0]) << endl;
cout << "二位地址第二行的地址:" << int(&arr4[1]) << endl;
2.考试成绩统计
有三名同学(张三,李四,王五),分别给出其在三科成绩分数,请分别输入三名同学的总成绩
int score[3][3] =
{
{100,100,100}, // 每个人各科的分数为一行,其列为该科目
{70,80,90},
{50,60,80}
};
string s[3] = { "张三","李四","王五" };
for (int i = 0; i < 3; i++)
{
int sum = 0;//统计每个人的总分
for (int j = 0; j < 3; j++)
{
sum += score[i][j];
}
cout << s[i] << "的总分为:" << sum << endl;
}
10函数的定义_调用_声明_分文件编写
1.函数的定义
函数
作用:将一段经常使用的代码封装起来,减少重复代码
一个较大的程序,一般分为若干个程序块,每个模块实现特定的功能
函数的定义一般主要有五个步骤:
1.返回值类型:一个函数可以返回一个值,在函数定义中
2.函数名:给函数起个名称
3.参数列表:使用该函数时,传入的数据
4.函数体语句:花括号内的代码,函数内需要执行的语句
5.return表达式:和返回值类型挂钩,函数执行完后,返回相应的数据
语法:
返回值类型 函数名(参数列表)
{
函数体语句
return 表达式
}
int add(int num1,int num2) {
//定义中的num1,num2成为形式参数,简称形参
int sum = num1 + num2;
return sum;
}
int main(){
int a = 10;
int b = 10;
//main函数中调用add函数
//函数调用语法:函数名称(参数)
//当调用函数时,实参的值会传给形参
int sum = add(a, b);//调用时的a,b成为实际参数,简称实参
cout << "sum = " << sum << endl;
}
2.函数的常见样式(4种)
* 1.无参无返
* 2.有参无返
* 3.无参有返
* 4.有参有返
//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;
}
int main(){
//无参无返函数调用
test01();
//有参有返函数调用
test02(100);
//无参有返函数调用
int n = test03();
cout << "n = " << n << endl;
//有参有返函数调用
int n1 = test04(10,20);
cout << "n1 = " << n1 << endl;
}
3.函数的声明
* 作用:告诉编译器函数名称及如何调用函数,函数的实际主体可以单独定义
* 提前告诉编译器函数的存在,可以利用函数的声明
* 函数的声明可以多次,但是函数的定义只能有一次
void swap(int i1, int i2);
//void swap(int i1, int i2);//声明可以多次
int main(){
int c = 10;
int d = 20;
//当我们做值传递时候,函数的形参改变,实参并不会受影响
swap(c,d);
cout << "main中的c = " << c << endl;
cout << "main中的d = " << d << endl;
//值传递时,形参是修饰不了实参的
}
4.分文件编写
作用:让代码结构更加清晰
函数分文件编写一般有四个步骤
1.创建后缀名为.h的头文件
2.创建后缀为.cpp的源文件
3.在头文件中写函数的声明
4.在源文件中写函数的定义
头文件创建→swap1.h
#include<iostream>;
using namespace std;
//实现两个数字交换的函数声明
void swap1(int a, int b);
源文件创建→swap1.cpp
#include "swap1.h";
//函数的定义
void swap1(int a, int b) {
int temp = a;
a = b;
b = temp;
cout << " a = " << a << endl;
cout << " b = " << b << endl;
}
#include <iostream>;
#include "swap1.h"//在另一个.cpp文件里面要包含头文件
using namespace std;
int main(){
//分文件编写
cout << "分文件编写的结果:" << endl;
int s1 = 718;
int s2 = 826;
swap1(s1,s2);
}
5.值传递
所谓值传递,就是函数调用时实参将数值传入给形参
值传递时,如果形参发生,并不会影响实参
如果函数不需要返回值,声明的时候可以写void
void swap(int i1, int i2)
{
cout << "交换前:" << endl;
cout << "i1=" << i1 << endl;
cout << "i2=" << i2 << endl;
int temp = i1;
i1 = i2;
i2 = temp;
cout << "交换后:" << endl;
cout << "i1=" << i1 << endl;
cout << "i2=" << i2 << endl;
//return;当函数声明的时候,不需要返回值,可以不写return
}
11指针_空_野指针_const_指针与函数与数组
1.指针的定义和使用
指针
作用:可以通过指针间接访问内存
内存编号是从0开始记录的,一般用十六进制数字表示
可以利用指针变量保存地址
指针变量定义语句:数据类型 * 变量名;
#include <iostream>
using namespace std;
int main() {
//1.指针的定义
int a = 10;
//指针定义的语法:数据类型 * 指针变量名
int * p;
//让指针跟变量联系起来→也就是说让指针记录变量a的地址 指针本质就是地址
p = &a;
cout << "a的地址为:" << &a << endl;
cout << "指针p为:" << p << endl;
//2.使用指针
//可以通过解引用的方式来找到指针指向的内存
*p = 1000;
cout << "a = " << a << endl;
cout << "*p = " << *p << endl;
system("pause");
return 0;
}
2.指针所占内存空间
指针也是一种数据类型,它的数据类型占的内存空间是怎么样的?
在32位操作系统下(x86),指针是占4个字节空间大小,不管什么数据类型
在64位操作系统下,指针是占8个字节空间大小
#include <iostream>
using namespace std;
int main() {
int a = 10;
int * p = &a;//其实就相当于int * p1; p1 = &a;
cout << "sizeof(int *) = " << sizeof(int *) << endl;
cout << "sizeof(int *) = " << sizeof(double*) << endl;
cout << "sizeof(int *) = " << sizeof(float*) << endl;
cout << "sizeof(int *) = " << sizeof(char*) << endl;
//数据类型虽然不同,但是指针占用的内存空间还是一样的
system("pause");
return 0;
}
3.空指针
空指针:指针变量指向内存中编号为0的空间
用途:初始化指针变量
注意:空指针指向的内存是不可以访问的
#include <iostream>;
using namespace std;
int main() {
int* p = NULL;
//0~255之间的内存编号是系统占用的,因此不可以访问
//*p = 100;//报异常
system("pause");
return 0;
}
4.野指针
野指针:指针变量指向非法的内存空间
//指针变量p指向内存地址编号为0x1100的空间
int* p = (int * )0x1100;
//访问指针
//cout << *p << endl; 访问野指针报错
//总结:空指针和野指针都不是我们申请的空间,因此不要访问
system("pause");
return 0;
}
5.const修饰指针
const修饰指针有三种情况:
1.const修饰指针---常量指针
2.const修饰常量---指针常量
3.const既修饰指针,又修饰常量
int main() {
int a = 10;
int b = 10;
//const修饰的是指针,指针指向可以改,指针指向的值不可以更改
const int* p = &a;
p = &a;
//*p = 100; 报错
//const修饰的是常量,指针指向不可以改,指针指向的值可以更改
int* const p1 = &a;
//p1 = &b;//报错
*p1 = 100;
//const修饰指针和常量,指针指向不可以改,指针指向的值不可以更改
const int* const p2 = &a;
//p2 = &b;//报错
//*p2 = 100;//报错
//看const右侧紧跟的是指针还是常量,是指针就是常量指针,是常量就是指针常量
system("pause");
return 0;
}
6.指针和数组
#include <iostream>
using namespace std;
int main() {
//利用指针访问数组中元素
int arr[] = { 1,2,3,4,5,6,7,8 };
int* p = arr;//将指针指向数组的指针
cout << "第一个元素的为" << arr[0] << endl;
cout << "指针指向的第一个元素:" << *p << endl;
for (int i = 0; i < 8; i++)
{
//利用指针遍历数组
cout << *p << endl;
p++;//让指针向后偏移4个字节
}
system("pause");
return 0;
}
7.指针和函数_地址传递
作用:利用指针作函数参数,可以修改实参的值
#include <iostream>
using namespace std;
void swap(int a,int b) {
int temp = a;
a = b;
b = temp;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
void swap1(int* a,int* b) {
int temp = *a;
*a = *b;
*b = temp;
cout << " swap1 a = " << a << endl;
cout << " swap1 b = " << b << endl;
}
int main() {
int a = 718;
int b = 816;
swap(a,b);//值传递不会改变实参
swap1(&a,&b);//地址传递会改变实参
cout << "main a =" << a << endl;
cout << "main b =" << b << endl;
}
8.指针、数组、函数_整型数组的升序排序
案例描述:封装一个函数,利用冒泡排序,实现对整型数组的升序排序
int arr[10] = {4,3,6,9,1,2,10,8,7,5};
#include <iostream>
using namespace std;
//冒泡排序函数 参数1 数组的首地址 参数2 数组长度
void bubbleSort(int* arr,int length) //int * arr也可以写成int arr[]
{
for (int i = 0; i < length - 1; i++)
{
for (int j = 0; j < length - i -1; j++)
{
//如果j > j + 1 交换数字
if (arr[j] > arr[j+1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
void printArr(int arr[],int length) {//int arr[]也可以写成int * arr
for (int i = 0; i < length; i++)
{
cout << arr[i] << endl;
}
}
int main() {
//1.先创建数组
int arr[10] = { 4,3,6,9,1,2,10,8,7,5 };
//数组长度
int length = sizeof(arr) / sizeof(arr[0]);
//2.创建函数,实现冒泡排序
bubbleSort(arr,length);
//3.打印排列后的数组
printArr(arr, length);
system("pause");
return 0;
}
12结构体
结构体基本概念
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型
1.结构体定义和使用
语法:struct 结构体名{ 结构体成员列表 };
通过结构体创建变量的方式有三种:
struct 结构体名 变量名
struct 结构体名 变量名 = {成员1值,成员2值...}
定义结构体时顺便创建变量
定义结构体的关键字是struct,不可以省略
创建结构体变量时,关键字struct可以省略
结构体变量利用操作符“.”访问成员
#include <iostream>
#include <string>
using namespace std;
struct Student //struct定义结构体的时候不可省略
{
//成员列表
//姓名
string name;
//年龄
int age;
//分数
int score;
}s3;
int main() {
//创建变量的方式
//第一种 struct Student s1;
//struct关键字可以省略
struct Student s1;
//给S1属性复制,通过“.”访问结构体变量中的属性
s1.name = "张三";
s1.age = 18;
s1.score = 100;
cout << "姓名:" << s1.name << "年龄:" << s1.age << "分数:" << s1.score;
//第二种 struct Student s2 = { };
struct Student s2 = {"李四",19,88};
cout << "姓名:" << s2.name << "年龄:" << s2.age << "分数:" << s2.score;
//在定义结构体时顺便创建结构体变量
s3.name = "王五";
s3.age = 20;
s3.score = 60;
cout << "姓名:" << s3.name << "年龄:" << s3.age << "分数:" << s3.score;
system("pause");
return 0;
}
2.结构体数组
作用:将自定义的结构放入到数组中方便维护
语法:struct 结构体名 数组名[元素个数] = {{},{},..{}};
#include <iostream>
#include <string>
using namespace std;
//结构体定义
struct Student {
string name;
int age;
int score;
};
int main() {
//结构体数组
struct Student arr[3] =
{
{"张三",18,100},
{"李四",28,99},
{"王五",38,66}
};
//给结构体数组的元素赋值
arr[2].name = "赵六";
arr[2].age = 80;
arr[2].score = 60;
for (int i = 0; i < 3; i++)
{
cout << "姓名为:" << arr[i].name << " 年龄为:"
<< arr[i].age << " 分数为:" << arr[i].score << endl;
}
system("pause");
return 0;
}
3.结构体指针
作用:通过指针访问结构体中的成员
利用操作符->可以通过结构体指针访问结构体属性
#include <string>
#include <iostream>
using namespace std;
//结构体定义
struct Student
{
string name;
int age;
int score;
};
int main() {
//1.创建学生结构体变量
struct Student stu = { "张三",18,100 };
//2.通过指针指向结构体变量
struct Student* p = &stu;
//3.通过指针访问结构体变量的数据
p->name = "李四";
p->age = 20;
cout << "姓名:" << p->name << " 年龄:" << p->age
<< " 分数:" << p->score << endl;
system("pause");
return 0;
}
4.结构体嵌套结构体
作用:结构体中的成员可以是另一个结构体
例如:每个老师辅导一个学院,一个老师的结构体中,记录一个学生的结构体
#include <iostream>
#include <string>
using namespace std;
struct Student
{
string name;
int age;
int score;
};
struct Teacher
{
int id;
string name;
int age;
struct Student stu;
};
int main() {
struct Teacher t;
t.id = 718;
t.name = "老王";
t.age = 18;
t.stu.name = "小王";
t.stu.age = 8;
t.stu.score = 90;
cout << "老师名字:" << t.name << " 老师编号:" << t.id << " 老师年龄:" << t.age
<< " 老师辅导学生的名字:" + t.stu.name << " 学生年龄:" << t.stu.age
<< " 学生考试分数:" + t.stu.score << endl;
//总结:在结构体中可以定义另一个结构体作为成员,用来解决实际问题
system("pause");
return 0;
}
5.结构体做函数参数
作用:将结构体作为参数向函数中传递
传递方式有两种:
值传递
地址传递
#include<iostream>
#include <string>
using namespace std;
struct Student
{
string name;
int age;
int score;
};
void printStudent(Student s)
{
s.age = 100;
cout << "子函数中的姓名:" << s.name << " 年龄:" << s.age
<< "分数:" << s.score << endl;
}
void printStudent01(Student *p)
{
p->age = 200;
cout << "子函数中的姓名:" << p->name << " 年龄:" << p->age
<< "分数:" << p->score << endl;
}
int main() {
struct Student s;
s.age = 18;
s.name = "小李";
s.score = 99;
//printStudent(s);
printStudent01(&s);
cout << "主函数中的姓名:" << s.name << " 年龄:" << s.age
<< "分数:" << s.score << endl;
system("pause");
return 0;
//总结:如果不修改主函数的数据,用值传递,反之用地址传递
}
6.结构体中const的作用
结构体中const的作用:防止误操作
#include <string>
#include <iostream>
using namespace std;
struct Student
{
string name;
int age;
int score;
};
//将函数中的形参改为指针, 可以减少内存空间,而且不会复制新的副本
void printStudent(const Student *s) {
//s->age = 150; 报错,加入cons之后,一旦有修改的操作就会报错,防止我们的误操作
cout << "名字:" << s->name << " 年龄为:" << s->age << " 分数为:" << s->score;
}
int main() {
//创建结构体变量
struct Student s = { "张三",15,70 };
//通过函数打印结构变量信息
printStudent(&s);
}
7.结构体案例
案例描述:
学校正在做毕设项目,每名老师带领五个学生,总共三名老师,需求如下
设计学生和老师的结构体,其中在老师的结构中,有老师的姓名和一个存放五名同学的数组作为成员
学生的成员有姓名、考试分数、创建数组存放三名老师,通过函数给每个老师及所带的学生赋值
最终打印老师数据以及老师所带的学生数据
#include <iostream>
#include <string>
#include<time.h>
using namespace std;
struct Student
{
string name;
int score;
};
struct Teacher
{
string name;
struct Student stu[5];
};
//给老师和学生赋值的函数
void allocate(Teacher t[], int len) {
string s = "ABCDE";
for (int i = 0; i < len; i++)
{
t[i].name = "Teacher_";
t[i].name += s[i];
for (int j = 0; j < 3; j++)
{
t[i].stu[j].name = "Student_";
t[i].stu[j].name += s[j];
int random = rand() % 61 + 40;//40-100
t[i].stu[j].score = random;
}
}
}
//打印全部信息
void printInfo(Teacher t[], int len) {
for (int i = 0; i < len; i++)
{
cout << "老师姓名:" << t[i].name << endl;
for (int j = 0; j < len; j++)
{
cout << "\t学生姓名:" << t[i].stu[j].name
<< " 学生成绩:" << t[i].stu[j].score << endl;
}
}
}
int main() {
//随机数种子
srand((unsigned int)time(NULL));
//1.创建三名老师的数组
Teacher t[3];
//2.通过函数给3名老师的信息赋值,并给老师所带的学生信息赋值
int len = sizeof(t) / sizeof(t[0]);
allocate(t, len);
//3.打印所有老师及所带的学生信息
printInfo(t, len);
}
8.结构体案例
设计一个英雄的结构体,包括成员姓名,年龄,性别,创建结构体数组,数组中存放5名英雄。
通过冒泡排序的算法,将数组中的英雄按照年龄进行升序排序,最终打印排序后的结果
#include <string>
#include <iostream>
using namespace std;
struct Hero
{
string name;
int age;
string sex;
};
void bubbleSort(Hero h[],int len) {
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - i -1; j++)
{
if (h[j+1].age > h[j].age) { //把大于号改成小于号 则变成降序排列
Hero temp = h[j + 1];//注意这里的数据类型
h[j + 1] = h[j];
h[j] = temp;
}
}
}
}
void print(Hero h[],int len) {
for (int j = 0; j < len; j++)
{
cout << "英雄的名字:" << h[j].name << " 年龄:"
<< h[j].age << " 性别:" << h[j].sex << endl;
}
}
int main() {
//1.创建英雄的数组
Hero h[5] = {
{"w",20,"男"},
{"s",28,"男"},
{"a",23,"男"},
{"d",24,"男"},
{"f",19,"女"},
};
//2.将英雄进行排序
int len = sizeof(h) / sizeof(h[0]);
bubbleSort(h, len);
//3.打印结果;
print(h, len);
}
13通讯录管理系统
通讯录管理系统
系统需求
实现功能如下:
添加联系人:向通讯录中增加新人,信息包括(姓名、性别、年龄、联系电话、家庭住址)最多记录1000人
显示联系人:显示通讯录中所有联系人信息
删除联系人:按照姓名进行删除指定联系人
查找联系人:按照姓名查看指定联系人信息
修改联系人:按照姓名重新修改指定联系人
清空联系人:清空通讯录中所有信息
退出通讯录:退出当前使用的通讯录
#include <iostream>;
#include <string>;
using namespace std;
#define MaxNumber 1000//最大人数 注意:不能加分号!!!
//1.显示菜单
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;
}
/*
添加联系人实现步骤:
设计联系人结构体
设计通讯录结构体
main函数创建通讯录
封装添加联系人函数
测试添加联系人功能
*/
//联系人结构体
struct Person
{
string name;
int sex;//1.男 2.女
int age;
string phone;
string address;
};
//通讯录结构体
struct AddressBooks
{
struct Person personArrays[MaxNumber];//通讯录中保存的联系人数组
int size;//通讯录中当前记录联系人的个数
};
//2.添加联系人函数
void addPerson(struct AddressBooks *abs) {
//判断人数是否已满
if (abs->size == MaxNumber) {
cout << "通讯录人数已满,无法进行添加" << endl;
return;
}
else
{
//添加具体的联系人
//姓名
string name;
cout << "请输入名字:" << endl;
cin >> name;
abs->personArrays[abs->size].name = name;
//性别 1.男 2.女
int sex;
cout << "请输入性别(1.男 2.女):" << endl;
while (true)
{
cin >> sex;
if (sex == 1 || sex == 2) {
abs->personArrays[abs->size].sex = sex;
break;
}
else
{
cout << "输入有误,请重新输入" << endl;
}
}
//年龄
int age;
cout << "请输入年龄:" << endl;
cin >> age;
abs->personArrays[abs->size].age = age;
//电话
string phone;
cout << "请输入电话:" << endl;
cin >> phone;
abs->personArrays[abs->size].phone = phone;
//住址
string address;
cout << "请输入住址:" << endl;
cin >> address;
abs->personArrays[abs->size].address = address;
//更新通讯录的人数
abs->size++;
system("pasuse");//请按任意键继续
system("cls");//清屏操作
}
}
//3.显示联系人函数
void showPerson(struct AddressBooks *abs) {
if (abs->size == 0)
{
cout << "联系人列表为空" << endl;
}
else
{
for (int i = 0; i < abs->size; i++)
//要记得用循环把所有输出 当时没有考虑到用循环
{
cout << "姓名为:" << abs->personArrays[i].name << "\t";
cout << "性别为:" << (abs->personArrays[i].sex == 1 ? "男" : "女") << "\t";
//注意双目运算符括号括的位置
cout << "年龄为:" << abs->personArrays[i].age << "\t";
cout << "电话为:" << abs->personArrays[i].phone << "\t";
cout << "地址为:" << abs->personArrays[i].address << endl;
}
}
system("pause");
system("cls");
}
/*
删除联系人:按照姓名进行删除指定联系人
实现步骤:
封装检测联系人是否存在(删除、查找、修改都需要检测,所以把它封装为一个函数)
封装删除联系人函数
测试删除联系人函数
*/
//4.检测联系人是否存在
//参数一 同学录 参数二 对比姓名
int isExist(struct AddressBooks* abs, string name) {
//数据是int 最后要返回在的地方的下标
for (int i = 0; i < abs->size; i++)
{
if (abs->personArrays[i].name == name)
{
return i;
}//不可以在这里加else return -1;!!!!
/*解释:修改第一个是没有问题的,但是你修改第二个(甚至之后)的话
* 直接进来 下标为0 走if 也就是修改的第一个数值 name是一样的 会返回i = 0 没问题
* 如果你输入的是2,下标0的值跟你下标1的值name是不一样,走else,直接返回-1
* 也就是为什么这里加else return -1 修改数据第二个值(甚至之后)都报查不到
*/
}
return -1;//如果遍历结束都没有找到,返回-1
//想好return -1放在哪里!!
}
//5.删除联系人函数
void deletePerson(AddressBooks* abs) {
cout << "请输入要删除联系人的姓名:" << endl;
string name;
cin >> name;
//查找是否存在 result != -1;存在 result == -1 不存在
int result = isExist(abs, name);
if (result != -1)//找到人了2
{
for (int i = 0; i < abs->size; i++)
{
//删除的方式:将后一位数据盖到前一位,即数据前移
abs->personArrays[i] = abs->personArrays[i + 1];
}
abs->size--;//更新通讯录的人员数
cout << "删除成功" << endl;
}
else
{
cout << "删除失败,无此人" << endl;
}
system("pause");
system("cls");
}
//6.查找联系人:按照姓名查看联系人信息
void findPerson(struct AddressBooks *abs) {
string name;
cout << "请输入你要找的姓名:" << endl;
cin >> name;
int result = isExist(abs, name);
//查有没有这个人
if (result != -1)
{
cout << "姓名为:" << abs->personArrays[result].name << "\t";
cout << "性别为:" << (abs->personArrays[result].sex == 1 ? "男" : "女")
<< "\t";
cout << "年龄为:" << abs->personArrays[result].age << "\t";
cout << "电话为:" << abs->personArrays[result].phone << "\t";
cout << "地址为:" << abs->personArrays[result].address << endl;
}
else
{
cout << "查无此人" << endl;
}
system("pause");
system("cls");
}
//7.修改联系人
void modifyPerson(struct AddressBooks * abs) {
cout << "请输入你要修改联系人的姓名:" << endl;
string name;
cin >> name;
int result = isExist(abs, name);
if (result != -1)
{
string name;
cout << "请输入修改后的名字为:" << endl;
cin >> name;
abs->personArrays[result].name = name;
int sex;
cout << "请输入性别(1.男 2.女):" << endl;
while (true)
{
cin >> sex;
if (sex == 1 || sex == 2) {
abs->personArrays[result].sex = sex;
//一定要区分好括号里是加result还是abs->size
break;
}
else
{
cout << "输入有误,请重新输入" << endl;
}
}
//年龄
int age;
cout << "请输入年龄:" << endl;
cin >> age;
abs->personArrays[result].age = age;
//电话
string phone;
cout << "请输入电话:" << endl;
cin >> phone;
abs->personArrays[result].phone = phone;
//住址
string address;
cout << "请输入住址:" << endl;
cin >> address;
abs->personArrays[result].address = address;
}
else
{
cout << "查无此人" << endl;
}
system("pause");
system("cls");
}
//8.清空联系人
void clearPerson(struct AddressBooks* abs) {
abs->size = 0;
cout << "通讯录已清空" << endl;
system("pause");
system("cls");
}
int main() {
int chose = 0;
//创建通讯录结构体变量
struct AddressBooks abs;
//初始化通讯录的当前人数的数量
abs.size = 0;
while (true)
{
showMenu();
cin >> chose;
switch (chose)
{
case 1://1.添加联系人
addPerson(&abs);//;利用地址传递,可以修饰实参
break;
case 2://2.显示联系人
showPerson(&abs);
break;
case 3://3.删除联系人
//{
// 查找是否存在这个人
// string name;
// cout << "请输入名字:" << endl;
// cin >> name;
// if(isExist(&abs, name) == -1)
// {
// cout << "查无此人" << endl;
//}else
// {
// cout << "找到此人" << endl;
// }
//case里代码过多,需要加{}!!
deletePerson(&abs);
break;
case 4://4.查找联系人
findPerson(&abs);
break;
case 5://5.修改联系人
modifyPerson(&abs);
break;
case 6://6.清空联系人
clearPerson(&abs);
break;
case 0://0.退出通讯录
cout << "欢迎下次使用" << endl;
system("pause");
return 0;
break;
default:
break;
}
}
system("pause");
return 0;
}