学习资料见:https://www.bilibili.com/video/BV1et411b73Z
1 初识C++
1.1 第一个C++程序
#include<iostream>
using namespace std;
int main()
{
cout << "Hello World!" << endl;
system("pause");
return 0;
}
1.2 变量
变量的定义和赋值和C语言一样
格式为:变量类型 变量名=数据
#include<iostream>
using namespace std;
int main()
{
int a=10; //定义变量语句
cout << "a="<< a << endl;
system("pause");
return 0;
}
运行结果为:
1.3 常量
常量定义的两种方式
- 宏定义 格式为:
#define 常量名 常量数值
- 用 const 格式为:
const 常量类型 常量名称=常量数值
#include<iostream>
using namespace std;
#define Day 7 //宏定义常量格式
int main()
{
int a = 10;
cout << "一周有" << Day << "天" << endl; //验证定义结果
const int month = 12; //第二种定义常量的方式
cout << "一年总共有" << month << "个月份" << endl; //验证定义结果
system("pause");
return 0;
}
程序运行结果:
2 数据类型
数据类型的存在意义就是可以给变量一个合理的内存空间。
2.1 整型(integer)
C++中能够表示整型的类型有一下几种:
数据类型 | 占用空间 | 取值范围 |
---|---|---|
short(短整型) | 2字节 | (-215 ~ 215-1) |
int(整型) | 4字节 | (-231 ~ 231-1) |
long(长整型) | Windows为4字节,Linux为4字节(32位),8字节(64位) | (-231 ~ 231-1) |
long long(长长整型) | 8字节 | (-263 ~ 263-1) |
2.2 sizeof关键字
作用:利用sizeof关键字可以统计数据类型所占的内存空间大小。
语法:sizeof(数据类型/变量)
示例:
#include<iostream>
using namespace std;
int main()
{
int num1 = 10;
cout << "num1占用的内存空间为" << sizeof(num1) << "个字节" << endl;
cout << "int占用的内存空间为" << sizeof(int) << "个字节" << endl;
//注意:sizeof(),括号里的可以是变量名,也可以是变量类型
system("pause");
return 0;
}
运行结果:
2.3 实型(浮点型)
作用:用于表示小数
浮点型变量分为两种:
- 单精度浮点型float
- 双精度浮点型double
数据类型 | 占用空间 | 有效数字范围 |
---|---|---|
float | 4字节 | 7位有效数字 |
double | 8字节 | 15~16位有效数字 |
示例:
#include<iostream>
using namespace std;
int main()
{
float f1 = 3.14f; //注意:在float的数据类型上最好加一个f,要不然系统容易出问题
double d1 = 3.14;
cout << "f1的值为" << f1 << endl;
cout << "d1的值为" << d1 << endl;
float f2 = 3.1415926;
double d2 = 3.1415926;
cout << "f2的值为" << f2 << endl; //注意:编译器默认保留6位有效数字
cout << "d2的值为" << d2 << endl;
//统计占用内存空间
cout << "f1占用的内存空间为" << sizeof(f1) << "个字节" << endl;
cout << "d1占用的内存空间为" << sizeof(d1) << "个字节" << endl;
//注意:sizeof(),括号里的可以是变量名,也可以是变量类型
//科学计数法
float f3 = 2e3; //2*10^3
float f4 = 2e-3; //2*10^-3
cout << "f3的值为" << f3 << endl;
cout << "f4的值为" << f4 << endl;
system("pause");
return 0;
}
程序运行结果:
2.4 字符型
- 字符型数据类型创建方式
语法:char 变量名='字符'
例如:char ch = 'a';
注意:只能用单引号。 - 字符型变量所占内存空间大小
char型变量只占用1个字节。 - 字符型变量常见错误
- 不能用双引号;
- 单引号里面只能放一个字符
- 字符型变量对应的ASCII码值
示例:
#include<iostream>
using namespace std;
int main()
{
char ch1 = 'a';
char ch2 = 'A';
cout << "a的ASCII码值为:" << (int)ch1 << endl; //(int)ch1 为强制转换类型,将ch1转换为整型
cout << "A的ASCII码值为:" << (int)ch2 << endl;
system("pause");
return 0;
}
程序运行结果:
2.5 转义字符
作用:用于表示一些不能显示出来的ASCII字符。
转义字符 | 含义 | ASCII码值(十进制) |
---|---|---|
\a | 警报 | 007 |
\b | 退格(BS),将当前位置移到前一列 | 008 |
\f | 换页(FF),将当前位置移到下页开头 | 012 |
\n | 换行(LF),将当前位置移到下行开头 | 010 |
\r | 回车(CR),将当前位置移到本行开头 | 013 |
\t | 水平制表(HT)(跳到下一个TAB位置) | 009 |
\v | 垂直制表(VT) | 011 |
\\ | 代表一个反斜线字符 | 092 |
\’ | 代表一个单引号字符 | 039 |
\" | 代表一个双引号字符 | 034 |
\? | 代表一个问号字符 | 063 |
示例:
#include<iostream>
using namespace std;
int main()
{
//在c++中有endl表示换行,但是在C语言中没有,就用\n表示
cout << "hello world\n";
//想要打印一个\,只有一个\是不行的,需要两个
cout << "\\" << endl;
//\t水平制表,一个制表符是八个字母
cout << "aaaa\thelloworld" << endl;
cout << "aaa\thelloworld" << endl;
cout << "aaaaaa\thelloworld" << endl;
system("pause");
return 0;
}
程序运行结果:
2.6 字符串
作用:用于表示一串字符
两种风格:
- C语言风格字符串:
char 变量名[]="字符串值"
例如:char str[]="hello world";
- C++语言风格字符串:
string 变量名="字符串值"
例如:string str1="hello world";
注意:在使用string时,需要前面加头文件
#include<string>
2.7 布尔类型 bool
作用:布尔数据类型代表真的或假的值。
bool类型只占一个字节的大小
示例:
#include<iostream>
using namespace std;
int main()
{
//创建布尔运算类型
bool flag = true;
cout << flag << endl;
flag =false;
cout << flag << endl;
//计算布尔运算类型的大小
cout << "布尔运算类型的大小" << sizeof(flag) << endl;
system("pause");
return 0;
}
程序运行结果:
2.8 数据的输入
作用:从键盘中获取数据。
关键字:cin。
语法:cin>>变量;
示例:
#include<iostream>
using namespace std;
#include<string>
int main()
{
//整型
int a;
cout << "请输入一个整型值:" << endl;
cin >> a;
cout << "输入的值为:" << a << endl;
//浮点型
float f;
cout << "请输入一个浮点型的数据:" << endl;
cin >> f;
cout << "输入的值为:" << f << endl;
//字符型
char ch;
cout << "请输入一个字符型的数据:" << endl;
cin >> ch;
cout << "输入的值为:" << ch << endl;
//字符串
string str;
cout << "请输入一个字符串的数据:" << endl;
cin >> str;
cout << "输入的值为:" << str << endl;
system("pause");
return 0;
}
程序运行结果:
3 运算符
3.1 算数运算符
作用:用于四则运算。
运算符 | 术语 | 示例 | 结果 |
---|---|---|---|
+ | 正号 | +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=3 |
- - | 前置递减 | a=2;b=- -a | a=1;b=1 |
- - | 后置递减 | a=2;b=a- - | a=1;b=2 |
除法注意:
- 两个整型相除,结果还是整型。
- 两个数只要有一个是浮点型,除法结果就是浮点型。
- 0不可以当作被除数
取模注意:
- 取模运算是基于除法,因为除数不能为0。
- 两个小数不能进行取模运算。
3.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 |
3.3 比较运算符
作用:用于表达式的比较,并返回一个真值或假值。
运算符 | 术语 | 示例 | 结果 |
---|---|---|---|
== | 相等于 | 4==3 | 0 |
!= | 不等于 | 4!=3 | 1 |
< | 小于 | 4<3 | 0 |
> | 大于 | 4>3 | 1 |
<= | 小于等于 | 4<=3 | 0 |
>= | 大于等于 | 4>=3 | 1 |
3.4 逻辑运算符
作用:用于根据表达式的值返回真值或假值。
运算符 | 术语 | 示例 | 结果 |
---|---|---|---|
! | 非 | !a | 若a为假,则!a为真 |
&& | 与 | a&&b | 都为真,则是真(可理解为乘法) |
|| | 或 | a||b | 一个是真,则是真(可理解为加法) |
4 程序流程结构
C/C++支持最基本的三种结构:
- 顺序结构
- 选择结构
- 循环结构
4.1 选择结构
4.1.1 if 语句
if语句的三种形式
- 单行格式if语句
- 多行格式if语句
- 多条件的if语句
- 单行格式的if语句:
if(条件){被执行的语句}
- 多行格式的if语句:
if(条件){被执行的语句1}else{被执行的语句2}
- 多条件的if语句
if(条件1){被执行的语句1}else if(条件2){被执行的语句2}…else{被执行的语句n}
嵌套if语句
if中嵌套一个if
4.1.2 三目运算符
作用:通过三目运算实现简单的判断。
语法:表达式1 ? 表达式2 : 表达式3
解释:
如果表达式1为真,则执行表达式2的语句
如果表达式1为假,则执行表达式3的语句
示例:
#include<iostream>
using namespace std;
int main()
{
int a = 10;
int b = 20;
int c = 0;
//以下代码的作用:将a,b中数值较大的赋给c
c = (a > b ? a : b);
cout << "c=" << c << endl;
//注意:三目运算输出的是变量,下面代码说明
(a > b ? a : b) = 100;
cout << "a=" << a << endl;
cout << "b=" << b << endl;
system("pause");
return 0;
}
程序运行结果:
4.1.3 switch语句
swith优缺点分析:
- 优点:结构清晰,执行效率高(跟if比)。
- 缺点:判断的值只能是整型和字符型,不能表示区间。
4.2 循环结构
4.2.1 while循环语句
语法:while(循环条件){循环语句}
示例:目的:依次打印0~20
#include<iostream>
using namespace std;
int main()
{
//目的:依次输出0~20
int num = 0;
while (num < 21)
{
cout << num << endl;
num++;
}
system("pause");
return 0;
}
程序运行结果:
小游戏:猜数字
程序示例:
#include<iostream>
using namespace std;
#include<ctime> //使用随机种子必备头文件
int main()
{
srand((unsigned int)time(NULL));//添加随机数种子,利用当前系统时间生成随机数
int num = rand() % 100 + 1; //生成随机数,%100代表生成0~99的数字,加一生成1~100
int time = 0;
cout << "您给自己几次机会?" << endl;
cin >> time;
cout << "请您从0—100中猜一个数字" << endl;
int val = 0;
while (time--)
{
cin >> val;
if (val > num)
{
cout << "大了!" << endl;
}
else if(val<num)
{
cout << "小了!" << endl;
}
else
{
cout << "恭喜您,猜对了!" << endl;
break;
}
}
if (time <= 0)
{
cout << "很遗憾,您的额度已用完!" << endl;
}
system("pause");
return 0;
}
程序运行结果:
4.2.2 do…while循环语句
语法:do{执行语句}while(循环条件)
注意:与while的区别是先执行一次循环,再判断是否满足条件。
小游戏:水仙花数
解释水仙花数:每一位的三次方之和还等于数字本身。
比如:153=1^3+5^3+3^3。
问题:用do while 求出所有三位数中的水仙花数。
示例:
#include<iostream>
using namespace std;
int main()
{
int num = 100;
do
{
int a = 0; //个位
int b = 0; //十位
int c = 0; //百位
a = num % 10;
b = num / 10 % 10;
c = num / 100 % 10;
if (a*a*a+b*b*b+c*c*c==num)
{
cout << num << endl;
}
num++;
} while (num < 1000);
system("pause");
return 0;
}
程序运行结果:
4.2.3 for循环语句
语法:for(起始表达式;条件表达式;末尾循环体){循环语句;}
示例:用for循环输出0~9。
#include<iostream>
using namespace std;
int main()
{
for (int i = 0; i < 10; i++)
{
cout << i << endl;
}
system("pause");
return 0;
}
程序运行结果
小游戏:敲桌子
解释:从1数到100,遇到个位有7,十位有7或者是7的倍数的数时,输出:敲桌子。
程序示例:
#include<iostream>
using namespace std;
int main()
{
int i ;
for (i = 1; i < 101; i++)
{
if (i % 10 == 7 || i / 10 == 7 || i % 7 == 0)
{
cout << "敲桌子" << endl;
}
else
{
cout << i << endl;
}
}
system("pause");
return 0;
}
程序运行结果:
4.2.4 嵌套循环
用嵌套循环打印一个10*10 的星图
示例:
#include<iostream>
using namespace std;
int main()
{
int i,j;
for (i = 0 ; i < 10; i++)
{
for (j = 0; j < 10; j++)
{
cout << "* ";
}
cout << endl;
}
system("pause");
return 0;
}
程序运行结果:
小游戏:九九乘法表
利用嵌套循环,实现打印九九乘法表。
示例:
#include<iostream>
using namespace std;
int main()
{
int i,j;
for (i = 0 ; i < 9; i++)
{
for (j = 0; j < i+1; j++)
{
cout << j + 1 << "*" << i + 1 << "=" << (j + 1)*(i + 1)<<"\t";
}
cout << endl;
}
system("pause");
return 0;
}
程序运行结果:
4.3 跳转语句
4.3.1 break语句
作用:用于跳出选择结构或循环结构
break使用的时机:
- 出现在switch语句中,作用是终止case并跳出switch
- 出现在循环语句中,作用是跳出当前循环语句
- 出现在嵌套循环中,作用是跳出最近的内层循环语句
4.3.2 continue语句
作用:再循环语句中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环。
示例:
#include<iostream>
using namespace std;
int main()
{
//目的:只输出1~100中的奇数
int i;
for (i = 1; i <= 100; i++)
{
if (i % 2 == 0)
{
continue; //跳过输出偶数的语句
}
cout << i << endl;
}
system("pause");
return 0;
}
程序运行结果:
注意:continue不会使循环停止,而break会跳出整个循环。
4.3.3 goto语句
作用:可以无条件跳转语句。
语法:goto 标记
示例:
#include<iostream>
using namespace std;
int main()
{
cout << "1.XXXXX" << endl;
cout << "2.XXXXX" << endl;
goto FLAG;
cout << "3.XXXXX" << endl;
cout << "4.XXXXX" << endl;
FLAG: //注意:FLAG后面要加冒号
cout << "5.XXXXX" << endl;
system("pause");
return 0;
}
程序运行结果:
5 数组
5.1 概述
特点1:数组中的每个数据元素都是相同的数据类型。
特点2:数组是由连续的内存位置组成的。
5.2 一维数组
5.2.1 一维数组的定义方式
数据类型 数组名[数组长度];
数据类型 数组名[数组长度]={值1,值2,…};
如果在初始化时没有全部赋值,会用0来填补剩余的数据。数据类型 数组名[ ]={值1,值2,…};
注意1:数组元素的下标是从0开始索引的。
注意2:定义数组的时候,不管采用什么方法,都需要知道数组的具体长度。
5.2.2 一维数组数组名
一名数组名称的用途:
- 可以统计整个数组在内存中的长度。
- 可以获取数组在内存中的首地址。
示例:
#include<iostream>
using namespace std;
int main()
{
//数组名的作用:
//1、可以知道数组所占内存空间的大小:
int arr[] = { 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 << endl << "数组在内存中的首地址为" << (int)arr << endl; //给出地址默认为十六进制,这里强制转换为十进制
cout << "第一个元素在内存中的首地址为" << (int)&arr[0] << endl;//&为取址符
cout << "第二个元素在内存中的首地址为" << (int)&arr[1] << endl;//&为取址符
system("pause");
return 0;
}
程序运行结果:
一维数组练习
- 练习案例1:五只小猪称体重
案例描述:在一个一维数组中记录了五只小猪的体重,找出并打印最终的小猪的体重。
示例:
#include<iostream>
using namespace std;
int main()
{
//1.得到五只小猪的体重
int arr[] = { 200,350,430,250,340 };
//2.依次进行比较
int max = 0;
int i;
for (i = 0; i < 5; i++)
{
max = arr[i] > max ? arr[i] : max;
}
cout << "最重的小猪的体重为" << max << endl;
system("pause");
return 0;
}
程序运行结果:
- 练习案例2:数组元素逆置
案例描述:声明一个五个元素的数组,并且将元素逆置。
示例:
#include<iostream>
using namespace std;
int main()
{
//1、声明一个数组并且打印出来
int arr[] = { 2,5,1,3,4 };
cout << "原数组为:" << endl;
for (int i = 0; i < 5; i++)
{
cout << arr[i] << endl;
}
//2、对数组进行逆置
//2.1 创建第一个元素和最后一个元素的下标
int start = 0;
int end = sizeof(arr) / sizeof(arr[0]) - 1;//这里可以直接使end=4,但是这写以后不管数组多么长都可以适用
//2.2 创建一个中间变量
int temp;
//2.3 开始换
while (start < end)
{
temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++; end--;
}
//3 打印逆置之后的数组
cout << "逆置之后的数组为" << endl;
for (int i = 0; i < 5; i++)
{
cout << arr[i] << endl;
}
system("pause");
return 0;
}
程序运行结果:
5.2.3 冒泡排序
作用:最常用的排序算法,对数组内元素进行排序。
- 比较相邻的元素,如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素做同样的工作,执行完毕后,找到第一个最大值。
- 重复以上的步骤,每次比较次数-1,直到不需要比较。
示例:用冒泡排序将数组中的数据升序排列
#include<iostream>
using namespace std;
int main()
{
//目的:对一组数据进行升序排序——冒泡排序
//1.定义数组并计算长度
int arr[] = { 4,2,8,0,5,7,1,3,9 };
int length = sizeof(arr) / sizeof(arr[0]);
//2.打印原数组
cout << "原数组为:" << endl;
for (int i = 0; i < length; i++)
{
cout << arr[i] << endl;
}
//3.用冒泡排序法进行排序
for (int j = 0; j < length-1; j++) //比较的轮数是元素个数减1
{
for (int k = 0; k < length - j - 1; k++) //每一轮的比较论述是(元素个数-1)-轮数(第一轮是0)
{
if (arr[k] > arr[k + 1])
{
int temp = arr[k];
arr[k] = arr[k + 1];
arr[k + 1] = temp;
}
}
}
//4.打印排序完之后的数组
cout << "排序完成之后的数组为:" << endl;
for (int i = 0; i < length; i++)
{
cout << arr[i] << endl;
}
system("pause");
return 0;
}
程序运行结果:
5.3 二维数组
5.3.1 二维数组的定义方式
二维数组有四种定义方式:
数据类型 数组名[ 行数 ][ 列数 ];
数据类型 数组名[ 行数 ][ 列数 ]={{数据1,数据2},{数据3,数据4}};
数据类型 数组名[ 行数 ][ 列数 ]={数据1,数据2,数据3,数据4};
数据类型 数组名[ ][ 列数 ]={数据1,数据2,数据3,数据4};
注意:更推荐第2种定义方式,更加直观。
5.3.2 二维数组命名
- 查看二维数组所占空间
- 获取二维数组首地址
示例:
#include<iostream>
using namespace std;
int main()
{
//1.二维数组名称可以查看数组所占内存空间大小
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 << endl<< "二维数组总元素个数为" << sizeof(arr) / sizeof(arr[0][0]) << endl;
cout << "二维数组行数为:" << sizeof(arr) / sizeof(arr[0]) << endl;
cout << "二维数组列数为:" << sizeof(arr[0]) / sizeof(arr[0][0]) << endl;
//2.二维数组首地址
cout << endl << "二维数组首地址为:" << (int)arr << endl;
cout << "二维数组第一行首地址为:" << (int)arr[0] << endl;
cout << "二维数组第二行首地址为:" << (int)arr[1] << endl;
cout << "二维数组第一个元素的首地址为:" << (int)&arr[0][0] << endl;
system("pause");
return 0;
}
程序运行结果:
5.3.3 二维数组应用案例
考试成绩统计:
案例描述:输出三名同学的总成绩
姓名 | 语文 | 数学 | 英语 |
---|---|---|---|
张三 | 100 | 100 | 100 |
李四 | 90 | 50 | 100 |
王五 | 60 | 70 | 80 |
示例:
#include<iostream>
using namespace std;
#include<string>
int main()
{
int score[3][3]=
{
{100,100,100},
{90.50,100},
{60,70,80}
};
string name[3] = { "张三","李四","王五" };
int sum = 0;
for (int i = 0; i < 3; i++)
{
sum = 0;
for (int j = 0; j < 3; j++)
{
sum += score[i][j];
}
cout <<name[i]<< "的总成绩为:" << sum << endl;
}
system("pause");
return 0;
}
程序运行结果:
6 函数
6.1 概述
作用:将一段经常使用的代码封装起来,减少重复代码。
6.2 函数的定义
函数的定义主要包括一下5个步骤:
- 返回值类型
- 函数名
- 参数表列
- 函数体语句
- return表达式
语法:
函数类型 函数名(参数列表)
{
函数体语句;
return表达式;
}
6.3 函数的调用
功能:使用已经定义好的函数。
语法:函数名(参数)
示例:
#include<iostream>
using namespace std;
//函数的定义
int add(int num1, int num2)
{
int sum = num1 + num2;
return sum;
}
int main()
{
//函数的调用
int a = 10;
int b = 20;
int c = add(a, b);
cout << c << endl;
system("pause");
return 0;
}
6.4 值传递
- 值传递:函数调用时实参将数值传给形参。
- 值传递时,如果形参发生改变,不会影响实参。
6.5 函数的常见样式
函数的常见样式有4种
- 有参无返
- 无参无返
- 有参有返
- 无参有返
6.6 函数的声明
作用:当函数写在main函数后面的时候,需要提前告诉编译器函数的存在。
注意:声明可以写多次,但是函数定义只能有一次。
6.7 函数的分文件编写
作用:让代码结构更加清晰。
函数分文件编写一般有四个步骤:
- 创建后缀名为.h的头文件
- 创建后缀名为.cpp的源文件
- 在头文件中写函数的声明
- 在源文件中写函数的定义
下面分步讲解:以一个交换两个数的函数swap为例
-
1创建.h的头文件
-
2头文件中写声明
-
3创建.cpp的源文件,源文件中写函数定义
-
4主程序中调用头文件,即可使用该函数
7 指针
7.1 指针的基本概念
作用:可以通过指针,间接访问内存。
7.2 指针变量的定义和使用
- 指针变量定义语法:
数据类型 * 变量名;
- 指针的内容是地址
- 可以通过解引用的办法,得到指针指向地址的内容,解引用的语法:
*p
示例:
#include<iostream>
using namespace std;
int main()
{
//1.指针的定义
int a = 10;
int *p;
//2.指针的使用
//让指针指向a的地址。
p = &a;
cout << "变量a的地址为:" << &a << endl;
cout << "指针p= " << p << endl;
//通过解引用的方式来获得指针所指位置的内容
*p = 1000;
cout << "a的值为:" << a << endl;
system("pause");
return 0;
}
程序运行结果:
7.3 指针所占内存空间
注意:在32位的编译环境下,指针(不管是 int *还是double * 还是char *等)都占4个字节。
在64位时,占8个字节。
7.4 空指针和野指针
空指针:指针变量中指向编号为0的空间。
用途:初始化指针变量。
注意:空指针指向的内存是不可访问的。
如:int * p = NULL;
野指针:指针变量指向非法的内存空间。
野指针是不经申请,直接让指针指向了一块内存地址。
空指针和野指针都不是我们申请的内存空间,因此不要访问。
7.5 const修饰指针
const修饰指针有三种情况
- const修饰指针——常量指针
- const修饰常量——指针常量
- const既修饰指针,又修饰常量
- 常量指针:
如:const int * p = &a;
特点:指针的指向可以修改,但是指针指向的值不可以改。 - 指针常量
如:int * const p = &a;
特点:指针的指向不可以改,但是指针指向的值可以改。 - 既修饰指针,又修饰常量
如:const int * const p = &a;
特点:指针的指向和指针指向的值都不可以改。
7.6 指针和数组
作用:利用指针访问数组中的元素
示例:
#include<iostream>
using namespace std;
int main()
{
//利用指针遍历数组
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int * p = arr; //指针指向数组的首地址
for (int i = 0; i < 10; i++)
{
cout << *p << endl;
p++;
}
system("pause");
return 0;
}
程序运行结果:
7.7 指针和函数(地址传递)
作用:利用指针作为函数的形参,可以修改实参的值。
示例:
#include<iostream>
using namespace std;
void swap01(int * p1, int * p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
int main()
{
//利用指针作为函数的形参(地址传递)
int a = 10;
int b = 20;
swap01(&a, &b); //因为参数是指针,所以应该给的是地址
cout << "a= " << a << endl;
cout << "b= " << b << endl;
system("pause");
return 0;
}
程序运行结果:
可以对比之前的6.4 值传递,比较二者的不同。
总结:如果不想改变实参,就用值传递;如果想改变实参,就用地址传递。
7.8 指针 数组 函数
案例描述:封装一个函数,利用冒泡排序,实现对整型数组的升序排序。
示例:
#include<iostream>
using namespace std;
void bubbleSort(int * p, int len)
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - 1 - i; j++)
{
if (p[j] > p[j + 1])
{
int temp = p[j];
p[j] = p[j + 1];
p[j + 1] = temp;
}
}
}
}
int main()
{
//封装一个函数,实现冒泡排序。
int arr[10] = { 4,3,6,9,1,2,10,8,7,5 };
int len = sizeof(arr) / sizeof(arr[0]);
bubbleSort(arr, len);
for (int k = 0; k < len; k++)
{
cout << arr[k] << endl;
}
system("pause");
return 0;
}
总结:当数组名传入到函数作为参数时,被退化为指向首元素的指针
8 结构体
8.1 结构体基本概念
结构体用于用户自定义的数据类型,允许用户存储不同的数据类型。
8.2 结构体定义和使用
语法:struct 结构体名 { 结构体成员列表 };
通过结构体创建变量的方式有三种:
- struct 结构体名 变量名
- struct 结构体名 变量名={成员1值,成员2值…}
- 定义结构体是顺便创建变量
示例:
//结构体的定义及应用
#include<iostream>
#include<string>
using namespace std;
//1.结构体的定义
struct Student //定义一个学生结构体,包括 姓名 年龄 分数
{
string name;
int age;
int score;
}s3; //顺便定义一个变量
int main()
{
//用结构体来定义一个变量并访问
//2.1 创建方式1 结构体名+变量名
Student s1;
//赋值
s1.name = "张三"; //用.来访问结构体的每个元素
s1.age = 18;
s1.score = 100;
//输出验证
cout << "姓名:" << s1.name << " 年龄:" << s1.age << " 分数:" << s1.score << endl;
//2.2 创建方式2 直接赋值
Student s2 = { "李四",19,80 };//变量要按照定义时的顺序
//输出验证
cout << "姓名:" << s2.name << " 年龄:" << s2.age << " 分数:" << s2.score << endl;
//2.3 创建方式3,在定义结构体的时候顺便创建,见上面
Student s3 = { "王五",17,70 };
cout << "姓名:" << s3.name << " 年龄:" << s3.age << " 分数:" << s3.score << endl;
system("pause");
return 0;
}
程序运行结果
8.3 结构体数组
作用:将自定义的结构体放到数组中方便维护。
语法:struct 结构体名 数组名[ ]={ { }, { }, … { } };
把结构体理解成一个数据类型,就是定义一个数据类型为 “结构体” 的数组。
示例:
/**************************************************************
名称:结构体数组的定义及应用
***************************************************************/
#include<iostream>
#include<string>
using namespace std;
//1.结构体的定义
struct Student //定义一个学生结构体,包括 姓名 年龄 分数
{
string name;
int age;
int score;
};
int main()
{
//2.结构体数组的定义,把结构体看做成一个数据类型
Student arr[3]=
{
{"张三",20,100},
{"李四",18,90},
{"王五",19,80}
};
//3.给数组赋值,比如说想改变李四的姓名
arr[1].name = "赵四"; //就是按照数组操作来
//4.遍历结构体数组
for (int i = 0; i < 3; i++)
{
cout << " name= " << arr[i].name
<< " age= " << arr[i].age
<< " score= " << arr[i].score << endl;
}
system("pause");
return 0;
}
程序运行结果
8.4 结构体指针
作用:通过指针访问结构体中的数据。
- 利用操作符
->
可以通过结构体指针访问结构体属性
示例:
/**************************************************************
名称:结构体指针的定义及应用
***************************************************************/
#include<iostream>
#include<string>
using namespace std;
//1.结构体的定义
struct Student //定义一个学生结构体,包括 姓名 年龄 分数
{
string name;
int age;
int score;
};
int main()
{
//2.创建一个结构体变量
Student s={"张三",20,100};
//3.创建一个指针指向结构体变量
Student * p = &s;
//4.通过指针访问变量里的数据
//指针放访问结构体时,通过->访问
cout << " name= " << p->name << " age= " << p->age << " score= " << p->score << endl;
system("pause");
return 0;
}
程序运行结果:
注意:如果一个指针指向了一个结构体,那么用
p->age
和(*p).age
的效果是一样的。
8.5 结构体嵌套结构体
作用:结构体中的成员可以是另一个结构体。
例如:每个老师辅导一个学员,每个老师的结构体中,都有一个学生的结构体。
示例:
/**************************************************************
名称:结构体嵌套结构体
***************************************************************/
#include<iostream>
#include<string>
using namespace std;
//1.定义一个学生结构体
struct Student //定义一个学生结构体,包括 姓名 年龄 分数
{
string name;
int age;
int score;
};
//2.定义一个老师的结构体并创建一个老师变量
struct Teacher
{
int id;
string name;
int age;
Student stu; //这里在老师结构体中嵌套了学生结构体
};
int main()
{
//3.创建一个老师结构体变量
Teacher t1;
//4.给老师结构体变量赋值
t1.id = 20180605;
t1.name = "老王";
t1.age = 50;
t1.stu.name = "小王"; //依然是通过.来访问结构体中的数据
t1.stu.age = 18;
t1.stu.score = 100;
//5.通过结构体中的各个成员
cout << " techer's id= " << t1.name << " teacher's age= " << t1.age << " teacher's name= " << t1.name
<<" 老师辅导的学生的名字="<<t1.stu.name<<" student's age= "<<t1.stu.age<<" student's score= "<<t1.stu.score<< endl;
system("pause");
return 0;
}
程序运行结果:
8.6 结构体函数做参数
作用:将结构体作为参数向函数中传递
传递方式有两种:
- 值传递
- 地址传递
示例:
/**************************************************************
名称:结构体变量作为函数的参数
***************************************************************/
#include<iostream>
#include<string>
using namespace std;
//创建结构体
struct Student
{
string name;
int age;
int score;
};
void PrintStudent1(Student var) //通过值传递打印,接受变量的是形参
{
var.age = 80; //改变年龄
cout << "值传递子函数:姓名: " << var.name << " 年龄: " << var.age
<< " 分数: " << var.score << endl;
}
void PrintStudent2(Student * p) //通过地址传递打印,接受变量的是一个指针
{
(*p).age = 80; //改变年龄
cout << "值传递子函数:姓名: " << (*p).name << " 年龄: " << (*p).age
<< " 分数: " << (*p).score << endl;
}
int main()
{
//创建一个结构体并且赋值
Student s;
s.age = 18;
s.name = "张三";
s.score = 100;
PrintStudent1(s); //执行打印函数1——值传递
cout<<"经过值传递后,main中变量:姓名: " << s.name << " 年龄: " << s.age
<< " 分数: " << s.score << endl;
PrintStudent2(&s); //执行打印函数2——地址传递
cout << "经过地址传递后,main中变量:姓名: " << s.name << " 年龄: " << s.age
<< " 分数: " << s.score << endl;
system("pause");
return 0;
}
程序运行结果:
8.7 结构体中const使用场景
背景:在通过定义函数来完成某个操作时,如果通过值传递,那么就相当于把实参复制了一份给形参,当实参的数据量特别大的时候,就很占用内存空间,这时候推荐使用地址传递。
而使用地址传递,又会容易出现在子函数中把原来变量的值修改了的误操作,这时候,在定义函数变量时加一个const,就可以解决这一问题。
8.8 结构体案例
8.8.1 结构体案例1
案例描述:
学校正在做毕设项目,每名老师带领5个学生,总共有3名老师,需求如下
设计学生和老师的结构体,其中在老师的结构体中,有老师姓名和一个存放5名学生的数组作为成员
学生的成员有姓名、考试分数,创建数组存放3名老师,通过函数给每个老师及所带的学生赋值
最终打印出老师数据以及老师所带的学生数据。
示例:
/**************************************************************
名称:结构体案例1
***************************************************************/
#include<iostream>
#include<string>
using namespace std;
#include<ctime>
//1.创建学生结构体
struct Student
{
string name;
int score;
};
//2.创建老师的结构体
struct Teacher
{
string name;
Student sArray[5];
};
void DefineTeacher(Teacher tArr[], int len)
{
for (int i = 0; i < len; i++)
{
string nameSeed = "ABCDE";
tArr[i].name = "Teacher_";
tArr[i].name += nameSeed[i];//给老师的名字为Teacher_A的形式
for (int j = 0; j < 5; j++)
{
tArr[i].sArray[j].name = "Student_";
tArr[i].sArray[j].name += nameSeed[j];
tArr[i].sArray[j].score = rand() % 61 + 40; //给每个学生一个随机值
}
}
}
void PrintInformation(Teacher tAr[],int len)
{
for (int i = 0; i < len; i++)
{
cout << tAr[i].name << endl;
for (int j = 0; j < 5; j++)
{
cout << "\t" << tAr[i].sArray[j].name << " " << tAr[i].sArray[j].score << endl;
}
}
}
int main()
{
//3.创建数组有三个老师
Teacher tArray[3];
int len = sizeof(tArray) / sizeof(tArray[0]);
//4.通过定义一个函数来给老师数组来赋值,函数定义见上↑
//保证产生随机数,添加一个随机数种子
srand((unsigned int)time(NULL));
DefineTeacher(tArray, len);
//5.通过定义一个函数来实现将以上所有信息打印,函数定义见上↑
PrintInformation(tArray, len);
system("pause");
return 0;
}
程序运行结果:
8.8.2 结构体案例2
案例描述:
设计一个英雄的结构体,包括成员姓名,年龄,性别;创建结构体数组,数组中存放5名英雄。
通过冒泡排序的算法,将数组中的英雄按照年龄进行升序排序,最终打印排序后的结果。
{"刘备",23,"男"},
{"关羽",22,"男"},
{"张飞",20,"男"},
{"赵云",21,"男"},
{"貂蝉",19,"女"},
示例:
/**************************************************************
名称:结构体案例2
***************************************************************/
#include<iostream>
#include<string>
using namespace std;
//1.创建结构体
struct Hero
{
string name;
int age;
string gender;
};
//3.冒泡排序的子函数
void bubbleSort(Hero ar[], int len)
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
if (ar[j].age > ar[j + 1].age)
{
Hero temp = ar[j];
ar[j] = ar[j + 1];
ar[j + 1] = temp;
}
}
}
}
//4.打印结果的子函数
void printResult(Hero ar[], int len)
{
for (int i = 0; i < len; i++)
{
cout << ar[i].name << " " << ar[i].age << " " << ar[i].gender << endl;
}
}
int main()
{
//2.创建结构体数组存放数据
Hero arr[]=
{
{"刘备",23,"男"},
{"关羽",22,"男"},
{"张飞",20,"男"},
{"赵云",21,"男"},
{"貂蝉",19,"女"}
};
int len = sizeof(arr) / sizeof(arr[0]);
cout << "排序前:" << endl;
printResult(arr, len);
//3. 冒泡排序
bubbleSort(arr, len);
//4.打印输出拍完序后的值
cout << "排序后:" << endl;
printResult(arr, len);
system("pause");
return 0;
}
程序运行结果: