6、运算符
- 算数运算符
**+ 、 -、 *、 /、 %、 ++、 – **
+:加法、正号
-:减法 、负号
- 赋值运算符
=、 /= 、+=、-=、*=、%=
- 比较运算符
** >、 <、 == 、!=、<=、>= **
- 逻辑运算符
!、 &&、 ||
7.选择结构
8.循环结构
- while、dowhile、for
do{ }while( );
for(int i=0;i<10;i++){}
9.跳转语句
- break、continue、goto
goto:可以无条件跳转语句,一般不推荐使用,因为你不知道你程序跳过去跳过来,不知道程序跳到哪里了,影响逻辑结构。不方便阅读。
语法:goto FLAG;去这个标记
如果标记的名称存在,执行到goto语句时,会跳转到标记的位置。
int main(){
cout << "a"<<endl;
goto FLAG;
cout<<"b"<<endl;
cout<<"c"<<endl;
cout<<"d"<<endl;
FLAG:
cout<<"e"<<endl;
}
9.一维数组
int score[3];
score[0]=0;
score[1]=10;
score[2]=20;
- 数据类型 数组名[长度]={值1,值2,…};
//如果在初始化的时候,没有全部的填写,会用0来填补剩余的数字
int arr[3]={0,10};
cout<<arr[3]<<endl;
int arr[ ]={0,10,20,30,40};
10.二维数组
矩阵方式,一板鸡蛋
int arr[2][3];
arr[0][0]=1;
arr[0][1]=2;
arr[0][2]=3;
arr[1][0]=4;
arr[1][1]=5;
arr[1][2]=6;
for(int i=0;i<2;i++){
for(int j=0;j<3;j++){l
cout<<arr[i][j]<<endl
}
}
- 数据类型 数组名[ 行数][列数]={{数据1,数据2},{数据3,数据4},{数据5,数据6}};
int arr[2][3]=
{
{1,2,3},
{4,5,6}
};
- 数据类型 数组名[ 行数][列数]={数据1,数据2,数据3,数据4,数据5,数据6};
int arr[2][3]={1,2,3,4,5,6}
- 数据类型 数组名[ ][列数 ]={数据1,数据2,数据3,数据4,数据5,数据6};
int arr[][3]={1,2,3,4,5,6}
11.函数
int sum(int a,int b){
return a+b;
}
int main(){
sum(1,2);
}
- 值传递
概念:函数调用时实参将数值传递给形参
当我们做值传递的时候,如果形参发生改变,并不会影响实参
因为形参和实参不是同一个内存空间。
void swap(int num1,int num2){
cout<<"交换前:"<<end1;
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;
}
int main(){
cout<<"交换前:"<<endl;
cout<<"num1="<<num1<<endl;
cout<<"num2="<<num2<<endl;
int a =10;
int b=20;
swap(10,20);
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
}
- 函数声明
提前告诉编译器函数的存在,可以利用函数的声明。因为代码时一行一行执行的,如果方法放在main()方法后面,编译器就会找不到这个函数,所以需要提前声明。
int max(int a,int b);
int main(){
cout<<max(1,2) <<end1
}
int max(int a ,int b){
return a>b?a:b;
}
函数的分文件编写
作用:让代码结构更加清晰
函数分文件一般有四个步骤:
- 创建后缀名为.h的头文件
- 创建后缀名为.cpp的源文件
- 在头文件中写函数的声明
- 在源文件中写函数的定义
#include
using namespace std
void swap(int a,int b);
#include "swap.h"
void swap(int a,int b){
cout<<"交换前:"<<end1;
cout<<"num1="<<num1<<endl;
cout<<"num2="<<num2<<endl;
int temp =num1;
num1=num2;
num2=temp;
cout<<"交换后:"<<end1;
cout<<"num1="<<num1<<endl;
cout<<"num2="<<num2<<endl;
}
#include
using namespace std
#include "swap.h"
int main(){
cout<<swap(10,20)<<endl;
}
指针
指针就是一个地址
基本概念:可以通过指针间接访问内存。
内存编号是从0开始记录的,一般用16进制数字表示
可以利用指针变量保存地址
1.指针变量的定义和使用
指针就是记录这个地址的编号
int main(){
int a =10;
int * p;
p=&a;
cout<<"a的地址:"<< &a<<end1;
cout<<"p:"<< p<<end1;
*p=1000;
cout<<"a="<< a<<end1;
cout<<"*p:"<< *p<<end1;
}
2.简单点
- int a=10;
- int * p=&a;
- *p=1000
- a的值就是1000
- 通过修改地址,改变a的值;
解释:定义a这个变量,将a的地址保存到指针变量中,找到指针p指向的地址重新修改值。
3.指针所占内存空间
问:指针也是种数据类型,那么这种数据类型占用多少内存空间呢?
32位系统占4个字节。64位系统占8个字节
int main(){
int a =10;
int * p =&a;
cout<<"sizeof(p)"<< sizeof(p)<<end1;
cout<<"sizeof(int *)"<< sizeof(int *)<<end1;
}
3.空指针和野指针
空指针:指针变量指向内存中编号位0的空间
用途:初始化指针变量
注意:空指针指向的内存是不可以访问的
野指针:指针变量指向非法的内存空间
int * p =null;
int * p =(int *) 0x110011;
总结: 空指针和野指针都不是我们申请的空间,因此不能访问
4.const修饰指针的三种情况:
- 修饰指针 :常量指针 值不可以更改
- 修饰常量 :指针常量 指针指向不可以改
- 既修饰指针,又修饰常量 都不可以改
int main() {
int a = 10;
int b = 10;
const int * p1 = &a;
p1 = &b;
int * const p2 = &a;
*p2=200;
const int * const p=&a;
}
5.指针和数组
利用指针访问数组中的元素
int main() {
int arr[10] = { 1,2,3,4,5,6,7,8,9 };
int * p = arr;
cout <<"利用指指针访问第一个元素:"<< *p << endl;
p++;
cout << "利用指指针访问第一个元素:" << *p << endl;
}
6.指针和函数
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
void swap2(int * p1, int * p2) {
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
int main() {
int num1 = 10;
int num2 = 20;
swap(num1, num2);
cout << "num1=" << num1 << endl;
cout << "num2=" << num2 << endl;
swap2(&num1, &num2);
cout << "num1=" << num1 << endl;
cout << "num2=" << num2 << endl;
system("pause");
return 0;
}
结构体
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型
1.结构体定义和使用
struct 结构体名 { 结构体成员列表 };
通过结构体创建变量的方式有3种:
- struct 结构体名 变量名 //可以省略struct
- struct 结构体名 变量名={成员1值,成员2值……}
- 定义结构体时顺便创建变量 //不建议使用
struct Student
{
string name;
int age;
} student;
int main(){
struct Student stu;
stu.name="zhangsan";
stu.age=13;
struct Student stu2={"zhangsan",33};
student.name="lisi";
student.age=22;
}
总结:
- 创建结构体变量时,关键字struct可以省略,结构体变量利用操作符“.”访问成员
2.结构体数组
将自定义的结构体放入数组中方便维护
语法: struct 结构体名 数组名[元素个数]={{},{},……}
- 定义结构体
- 创建结构体数组
- 给结构体数组中的元素赋值
struct Student
{
string name;
int age;
}
int main(){
struct Student arr[3]=
{
{"zhangsan",22},
{"lisi",33},
{"wangwu",44}
}
arr[1].name="zhaoliu";
arr[1].age=30;
}
}
3.结构体指针
利用操作符 -> 可以通过结构体指针访问结构体属性
- 创建结构体变量
- 通过指针指向结构体变量
- 通过指针访问结构体变量中的数据
Student stu={"zhangsan",22};
Student * p =&stu;
cout<<p->name<<endl
4.结构体嵌套结构体
一个老师辅导一个学生
struct Teacher{
string teacherName;
int teacherAge;
Student stu;
}
struct Student{
studentName;
studentAge;
}
5.结构体做函数参数
void printStudent(Student stu){
stu.name="lisi";
cout<<"name="<<stu.name<<endl;
cout<<"age="<<stu.age<<endl;
}
void printStudent2(Student * p){
cout<<"name="<<stu->name<<endl;
cout<<"age="<<stu->age<<endl;
}
int main(){
Student student ;
student.name="zhangsan";
student.age=22;
printStudent(student);
printStudent2(&student);
}
总结:
- 如果不想修改主函数中的数据,用值传递,反之用地址传递。
个人理解:
- 值传递,将实参的数据拷贝到形参,然后进行处理,非常占用内存空间。形参和实参不是同一个对象,只是他们的值时一样的。
- 地址传递,将函数中的形参改为指针,可以减少内存空间,不会赋值新的副本出来。
6.结构体中const使用场景
作用:用const来防止误操作。
比如:将函数里面的结构体进行修改了。
struct Student
{
string name;
int age;
};
void printStudent( const Student *stu){
cout<<"name="<<stu.name<<"age="<<stu.age<<endl;
}
int main(){
Student student ={"zhangsan",22};
printStudent(&student);
}
7.案例1
需求:
struct Student {
string stuName;
int age;
};
struct Teacher {
string teacherName;
struct Student stu[5];
};
void printTeacher(struct Teacher teacher[], int length) {
for (int i = 0; i < length; i++)
{
cout << "老师姓名" << teacher[i].teacherName << endl;
int length2 = sizeof(teacher[i].stu) / sizeof(teacher[i].stu[0]);
for (int j = 0; j < length2; j++)
{
cout << "学生姓名" << teacher[i].stu[j].stuName << endl;
}
}
}
int main() {
Teacher teacher[3] = {
{"wang",{{"wang1",11},{"wang2",12},{"wang3",13},{"wang4",14},{"wang5",15},}},
{"wang11",{{"wang1",11},{"wang2",12},{"wang3",13},{"wang4",14},{"wang5",15},}},
{"wang22",{{"wang1",11},{"wang2",12},{"wang3",13},{"wang4",14},{"wang5",15},}}
};
int length = sizeof(teacher) / sizeof(teacher[0]);
printTeacher(teacher,length);
}
7.案例2
给五个英雄进行年龄排序,打印排序后的结果
struct Hero
{
string name;
int age;
};
void bubbleSort(struct Hero heroArray[], int len) {
for (int i = 0; i < len-1; i++) {
for (int j = 0; j < len - i - 1; j++) {
if (heroArray[j].age > heroArray[j + 1].age) {
struct Hero temp = heroArray[j];
heroArray[j] = heroArray[j + 1];
heroArray[j + 1] = temp;
}
}
}
}
void printHero(struct Hero heroArray[],int len) {
for (int i = 0; i < len; i++)
{
cout << "英雄姓名" << heroArray[i].name <<"年龄:"<<heroArray[i].age << endl;
}
}
int main() {
Hero heroArray[] = {
{"刘备",33},
{"关羽",22},
{"张飞",32},
{"貂蝉",27}
};
int len = sizeof(heroArray) / sizeof(heroArray[0]);
bubbleSort(heroArray, len);
printHero(heroArray, len);
}
通讯录管理系统
需求:
- 添加联系人: 最多添加1000人
- 显示联系疼:
- 删除联系人:
- 查找联系人:
- 修改联系人:
- 清空联系人:
- 退出通讯录:
struct Person {
string name;
int age;
};
struct Addressbooks {
struct Person personArray[MAX];
int m_Size;
};
void addPerson(Addressbooks * abs) {
if (abs->m_Size<MAX) {
cout << "请输入姓名:" << endl;
string name = "";
cin >> name;
cout << "请输入年龄:" << endl;
int age = 0;
cin >> age;
abs->personArray[abs->m_Size] = { name,age };
abs->m_Size++;
cout << "添加用户成功" << endl;
system("pause");
system("cls");
}
else {
cout << "通讯录已满,不能再添加了" << endl;
}
}
void showPerson(Addressbooks * abs) {
if (abs->m_Size == 0) {
cout << "没有联系人" << endl;
}
else {
for (int i = 0; i < abs->m_Size; i++)
{
cout << "姓名:" << abs->personArray[i].name<<"\t\t年龄:"<<abs->personArray[i].age << endl;
}
}
system("pause");
system("cls");
}
int isExist(Addressbooks * abs,string name) {
for (int i = 0; i < abs->m_Size; i++) {
if (name == abs->personArray[i].name) {
return i;
}
}
return -1;
}
void delPerson(Addressbooks * abs) {
cout << "请输入你要删除的联系人姓名:" << endl;
string name = "";
cin >> name;
int res = isExist(abs, name);
if (res == -1) {
cout << "查无此人" << endl;
}
else {
for (int i = res; i < abs->m_Size; i++) {
abs->personArray[i] = abs->personArray[i + 1];
}
abs->m_Size--;
cout << "删除成功" << endl;
}
system("pause");
system("cls");
}
void findPerson(Addressbooks * abs) {
cout << "请输入你要查找的联系人姓名:" << endl;
string name = "";
cin >> name;
int res = isExist(abs, name);
if (res == -1) {
cout << "查无此人" << endl;
}
else {
cout << "姓名:" << abs->personArray[res].name << "\t\t年龄:" << abs->personArray[res].age << endl;
}
system("pause");
system("cls");
}
void updatePerson(Addressbooks * abs) {
cout << "请输入你要修改的联系人姓名:" << endl;
string name = "";
cin >> name;
int res = isExist(abs, name);
if (res == -1) {
cout << "查无此人" << endl;
}
else {
cout << "请输入修改的姓名:" << endl;
string name = "";
cin >> name;
cout << "请输入修改的年龄:" << endl;
int age = 0;
cin >> age;
abs->personArray[res] = { name,age };
cout << "修改成功" << endl;
}
system("pause");
system("cls");
}
void clearPerson(Addressbooks* abs) {
abs->m_Size = 0;
cout << "通讯录已经清空了" << endl;
system("pause");
system("cls");
}
int main() {
Addressbooks abs;
abs.m_Size = 0;
while (true) {
showMenu();
int num = 0;
cout << "请选择你的操作:";
cin >> num;
switch (num)
{
case 1:
addPerson(&abs);
break;
case 2:
showPerson(&abs);
break;
case 3:
delPerson(&abs);
break;
case 4:
findPerson(&abs);
break;
case 5:
updatePerson(&abs);
break;
case 6:
clearPerson(&abs);
break;
case 0:
cout << "欢迎下次使用:" << endl;
system("pause");
return 0;
break;
default:
break;
}
}
二、C++核心编程
C++面向对象编程
内存分区模型
代码区、全局区、栈区、堆区
意义:不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程。
代码执行前,分两个区:代码区和全局区
1.代码区
存放函数体的二进制代码,由操作系统进行管理
特点:共享、只读。exe可执行文件,只有一份代码,共享的,不可以修改。
2.全局区
存放全局变量和静态变量以及常量
由操作系统进行回收和释放;
同一个区,他们的内存地址很近
常量:
- 字符串常量 :“hello world”
- const 修饰的全局常量: const int a =10;
局部常量不存放在全局区;
总结:
- 全局区:全局变量、静态变量 、常量(字符串常量、const 修饰的全局常量)
- 不在全局区:局部变量、const修饰的局部变量(局部常量)
3.栈区
由编译器自动分配释放,存放函数的参数值,局部变量等
注意:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放。
int * func(){
int a =10;
return &a;
}
int main(){
int * p = func();
cout<<*p<<endl;
cout<<*p<<endl;
}
4.堆区
由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
主要是利用new在堆区开辟内存空间
int * func(){
int * p=new int(10);
return p;
}
int main(){
int * p = func();
cout<<*p<<endl;
}
new操作符:
- 利用new创建的数据,会返回该数据对应类型的指针;
- 释放堆区的数据,利用关键字 delete; eg: delete p; delete[] pArray;
- 数组返回的是数组的首地址,因为数组的内存空间是连续的。eg: int * pArray=new int[2];
void func1(){
double * p = new double(10);
cout<<*p<<endl;
delete p;
引用 ( 给变量取别名:int &b = a )
1.基本语法
作用:给变量取别名。
语法:数据类型 &别名 = 原名
int a = 10 ;
int &b=a;
b=20;
int c =20;
b=c;
2.引用做函数参数
作用:函数传参时,可以利用引用的技术让形参修饰实参
优点:可以简化指针修改实参
void swap(int a,int b){
int temp =a;
a= b ;
b=temp;
}
void swap2(int * a,int * b){
int temp =*a;
*a= *b ;
*b=temp;
}
int main(){
int a = 10;
int b = 20;
swap(a,b);
cout << "a="<< a << endl;
cout << "b="<< b << endl;
swap2(&a,&b);
cout << "a="<< a << endl;
cout << "b="<< b << endl;
}
----- 引用传递
void swap3(int &a,int &b){
int temp =a;
a= b ;
b=temp;
}
int main(){
swap3(a,b);
cout << "a="<< a << endl;
cout << "b="<< b << endl;
}
3.引用做函数返回值
作用:引用是可以作为函数的返回值存在的
用法:函数调用作为左值
in& test(){
int a =10;
return a;
}
in& test2(){
static int a =10;
return a;
}
int main(){
int &ref = test();
cout << "ref="<< ref << endl;
cout << "ref="<< ref << endl;
int &ref2 = test2();
cout << "ref2 ="<< ref2 << endl;
cout << "ref2 ="<< ref2 << endl;
test2()=1000;
cout << "ref2 ="<< ref2 << endl;
}
4.引用的本质
本质:引用的本质在c++内部实现时一个指针常量
引用一旦初始化后,就不可以发生该改变。
void func(int &ref){
ref=100;
}
int main(){
int a =10;
int &ref =a;
ref=20;
cout << "a="<< a<< endl;
cout << "ref="<< ref << endl;
func(a);
return 0;
}
5.常量引用
作用:主要是用来修饰形参,防止误操作
在函数形参列表中,可以加const修饰形参,防止形参改变实参
int a =10;
const int & ref =10;
void show(int &val){
val=1000;
cout << "val="<< val<< endl;
}
void show2(const int &val){
cout << "val="<< val<< endl;
}
int mian(){
int a =100;
show(a);
show(a);
}
函数
1.默认参数
语法:返回值类型 函数名(参数=默认值){ }
- 1、如果某个位置参数有默认值,那么从这个位置往后,即从左往右,必须要有默认值
- 2、如果函数声明有默认值,函数实现的时候就不能有默认值
int func(int a,int b=10,int c=20){return a+b+c;}
int func2(int a=10,int b=10);
int func2(int a=10,int b=10)(return a+b;)
int mian(){
func(10);
func(10,20);
func2(10,10);
}
2.函数的占位参数
占位参数:返回值类型 函数名 (数据类型)
void func(int a ,int){}
void func2(int a ,int=10){}
int main(){
fun(10,20);
fun2(30);
}
3.函数重载
作用:函数名可以相同,提高复用性
满足条件:
- 同一个作用域
- 函数名相同
- 函数参数类型不同或者个数不同或者顺序不同
注意:函数的返回值不可以作为函数重载的条件。
原因:函数调用的时候无法通过返回值确定调用的是哪个函数
int show();
int show(int a);
int show(int a ,string b);
int show(string a,int b);
void show(int a,int b);
4.函数重载注意事项
void func(int &a){ }
void func(const int &a){}
int main(){
int a =10;
func(a);
func(10);
}
void func2(int a){ }
void func2(int a,int b=20){ }
int main(){
}
知识点
1、获取数组长度
int arr[] = { 1,5,9,10,9,2 };
int len = sizeof(a)/sizeof(a[0])
原理:sizeof()函数可以返回数组所占的内存,而sizeof(a[0])返回的是数组第一个元素所占的内存。
2、随机数种子
#include
strand(unsigned int)time(NULL);
int rand = rand()%61;
int rand2 = rand()%61+40;