1、变量
1个字节 | 2个字节 | 4个字节 | 8个字节 |
char | short | int | long(linux64位) |
float | long long | ||
long(win/linux32位) | double |
switch(只能整形或字符){}
生成随机数0~99:rand()%100;
//随机数种子生成随机数每次不同:
#include <ctime>
srand((unsigned int)time(NULL));
-
水仙花数:1个3位数每位3次方之和等于它本身
-
跳转语句
-
break:终止整个循环
-
continue:跳过之下语句执行下一次循环
-
goto(后纯大写):无条件跳
-
2、数组
2.1、一维数组
数组的末尾元素下标:sizeof(arr)/sizeof(arr[0]-1)
//冒泡
int arr[9] = {5,8,2,3,4,1,6,2,7};
//总共排列的轮数
for(int i = 0 ; i < 9 - 1 ; i++)
{
//总共排列的次数
for(int j = 0 ; j < 9 - i - 1 ; j++)
{
if(arr[j]<arr[j+1])
{
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
for(int i = 0 ; i < 9 ; i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
2.2、二维数组
二维数组4种定义方式:
1、数据类型 数组名[行数][列数];
2、(推荐用)数据类型 数组名[行数][列数] =
{
//直观看出有2行2列
{数据1,数据2},
{数据3,数据4}
};
3、数据类型 数组名[行数][列数] = {数据1,数据2,数据3,数据4};//根据列数自动划分行数
4、数据类型 数组名[ ][列数] = {数据1,数据2,数据3,数据4};//少行数,也是根据列数自动划分行数
//计算二维数组的行数
sizeof(arr)/sizeof(arr[0])
//计算二维数组的列数
sizeof(arr[0])/sizeof(arr[0][0])
访问首地址:arr
访问第一行首地址:arr[0]
访问第二行首地址:arr[1]
访问第一个元素首地址:&arr[0][0] // 要有取地址符
//二维数组案例-考试成绩统计
#include <iostream>
using namespace std;
#include <string>
int main()
{
//1、列出成绩
int scores[3][3] =
{
{100,100,100},
{90,80,70},
{60,50,40}
};
string names[3] = {"张三","李四","狗五"};
//2、统计每人的总分
//控制行数
for(int i=0 ; i<3 ; i++)
{
int sum = 0;//统计分数总和变量
//控制列数
for(int j=0 ; j<3 ; j++)
{
sum += scores[i][j];
}
cout<<names[i]<<"的总分为:"<<sum<<endl;
}
return 0;
}
3、函数
3.1、函数基本形式:无参无反、无参有反、有参无反、有参有反
语法:
返回值类型 函数名 (参数列表)
{
声明和语句;
return x;
}
//若返回值类型为:void ,则最后写成 return;
3.2、函数声明可以写多次,定义只能写一次。
3.3、函数的分文件编写
//1、在“头文件”新建.h后缀名的头文件
//2、在“源文件”新建.cpp后缀名的源文件
//3、在头文件中写函数的声明
#include <iostream>
using namespace std;
//写函数声明
//4、在源文件中写函数的定义
#include "函数名.h"
//写函数定义
4、指针
4.1、指针的定义
int a = 10;
//定义指针:数据类型 * 指针变量名 或者 数据类型 * 指针变量名 = &地址变量名
int * p;
//引用:&放在一个变量声明或者是函数的形参声明前
//取地址:&放在一个已经定义的变量前
p = &a;
//两者打印结果一样
cout<<"a的地址为:"<<&a<<endl;
cout<<"指针p为:"<<p<<endl;
//指针前加 * 代表解引用,找到指针指向内存中的数据,即 a 的值
*p = 1000;
//两者打印结果一样
cout<<"a="<<a<<endl;
cout<<"*p="<<*p<<endl;
4.2、空指针(指针变量指向内存中编号为0的空间)
//用途:初始化指针变量
int * p = NULL;
//注意:空指针指向的内存是不可以访问的
//0~255之间的内存编号是系统占用的,不可访问
*p = 100;//运行报错
4.3、野指针(指针变量指向非法的内存空间)
//在程序中,尽量避免出现野指针
int * p = (int *)0x1100;//未声明就访问
cout<<*p<<endl;//报错
空指针和野指针都不是我们申请的空间,因此不要访问
4.4、const修饰指针或者常量(原理:const在谁前谁不可变)
int a=10;
int b=10;
//1、const 修饰指针:常量指针(把const读成常量,把*读成指针)
const int * p = &a;
//指针指向的值不可以改,指针的指向可以改
*p = 20;//报错
p = &b;//正确
//2、const 修饰常量:指针常量
int * const p2 = &a;
*p2 = 100;//正确
p2 = &b;//报错
//3、const 修饰指针和常量
const int * const p3 = &a;
*p3 = 100;//报错
p3 = &b;//报错
4.5、指针访问数组
int arr[3] = {1,2,3};
int * p = arr;//指向数组的指针,arr就是数组首地址
cout<<"指针访问第一个元素:"<<*p<<endl;
//利用指针遍历数组
for(int i=0 ; i<3 ; i++)
{
cout<<*p<<endl;
p++;//让指针向后偏移4个字节
}
4.6、地址传递(值传递不改实参,地址传递改变实参)
void swap(int * p1,int * p2)
{
//函数体
}
int main()
{
int a = 10;
int b = 20;
swap(&a,&b);//地址传递会改变实参
return 0;
}
4.7、指针数组函数实现冒泡
//指针数组函数实现冒泡排序
//冒泡排序函数
void bubbleSort(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;
}
}
}
}
void printArray(int * arr , int len)
{
for(int i=0 ; i<len ; i++)
{
cout<<arr[i]<<endl;
}
}
int main()
{
//1、创建数组
int arr[10] = {3,2,9,8,3,9,5,7,4,5};
//数组长度(关键:可灵活处理数组长度)
int len = sizeof(arr)/sizeof(arr[0]);// 40/4=10
//2、创建函数,实现冒泡排序
bubbleSort(arr,len);
//3、打印排序后的数组
printArray(arr,len);
return 0;
}
5、结构体
5.1、概念:属于用户自定义的数据类型,允许用户存储不同的数据类型
/*
语法:(定义结构体时的关键字struct不可省)
struct 结构体名
{
数据类型 变量名1;
数据类型 变量名2;
};
*/
struct Student
{
string name;
int age;
};
//3种通过结构体创建变量的方式
//1、struct 结构体名 变量名
struct Student s1;//(创建结构体变量时的关键字struct可省)
//可直接写成:Student s1;
s1.name = "张三";//用操作符 . 访问成员
s1.age = 10;
//2、struct 结构体名 变量名 = { 成员1值 , 成员2值...}
struct Student s2 = {"李四",19};//(此struct可省略)
//3、定义结构体时顺便创建变量(不建议用)
struct Student
{
string name;
int age;
}s3;//尾括号加变量名就顺便创建结构体变量
s3.name = "张三";
s3.age = 10;
5.2、结构体数组
作用:将自定义的结构体放入到数组中方便维护
//语法:struct 结构体名 数组名[元素个数] = {{},{},{}};
struct Student
{
string name;
int age;
};
int main()
{
struct Student arr[2] =
{
{"张三",10},
{"李四",20}
};
//访问数组中的元素
cout<<arr[0].name<<endl;
}
5.3、结构体指针
作用:通过指针访问结构体中的成员
struct Student
{
string name;
int age;
};
//1、创建学生结构体变量
struct Student s = {"小明",19};
//2、通过指针指向结构体变量
struct Student * p = &s;
//3、利用操作符 -> 访问结构体中的成员
cout<<p->name<<endl;
5.4、结构体嵌套
#include <iostream>
#include <string>
using namespace std;
struct Student
{
string name;
int age;
};
struct Teacher
{
string name;
int id;
struct Student stu;//结构体中再嵌套结构体
};
int main()
{
//创建结构体
struct Teacher t1;
t1.name = "老师";
t1.id = 10;
t1.stu.name = "学生";//对结构体中的结构体赋值操作
t1.stu.age = 18;
cout<<t1.name<<endl;
cout<<t1.id<<endl;
cout<<t1.stu.name<<endl;
cout<<t1.stu.age<<endl;
return 0;
}
5.5、结构体做函数参数(值传递不改主函数中的数据,地址传递会改)
#include <iostream>
#include <string>
using namespace std;
struct Student
{
int age;
};
//代码检测时把函数printStudent1或者printStudent2注释其中之一
//值传递
void printStudent1(struct Student s)
{
s.age = 19;
cout<<"值传递值:"<<s.age<<endl;//值传递输出为19,主函数输出仍为18,不改主函数的值
}
void printStudent2(struct Student * p)
{
p->age = 20;
cout<<"地址传递值:"<<p->age<<endl;//地址传递输出为20,主函数输出也为20,改变主函数的值
}
int main()
{
//创建结构体
struct Student s;
s.age = 18;
printStudent1(s);
//printStudent2(&s);
cout<<"主函数值:"<<s.age<<endl;
return 0;
}
5.6、结构体const的使用
作用:用const来防止误操作
#include <iostream>
using namespace std;
struct Student
{
int age;
};
//将函数中的形参改为指针,可以减少内存空间,而且不会复制新的副本出来
void printStudent1(const struct Student *s)//形参改为指针
{
//s->age = 19;//报错
cout<<s->age<<endl;
}
int main()
{
//创建结构体
struct Student s;
s.age = 18;
printStudent1(&s);//加个取地址符
return 0;
}
5.7、结构体案例
/*
案例描述1 :学校正在做毕设项目,每名老师带领5个学生,总共有3名老师,需求如下。
设计学生和老师的结构体,其中在老师的结构体中,有老师姓名和一个存放5名学生的数组作为成员。
学生的成员有姓名、考试分数。
创建数组存放3名老师,通过函数给每个老师及所带的学生赋值。
最终打印出老师数据以及老师所带的学生数据。
*/
#include <iostream>
using namespace std;
#include <string>
#include <ctime>
//1、创建老师和学生的结构体
//学生的结构体
struct Student
{
//学生姓名
string sName;
//学生分数
int score;
};
//老师的结构体
struct Teacher
{
//老师姓名
string tName;
//每名老师所带学生的数组
struct Student sArray[5];
};
//tips:赋值函数和打印函数有许多相同的地方
//3、给老师和学生赋值的函数
void allocateSpace(struct Teacher tArray[],int len)
{
string nameSeed = "ABCDE";
//循环给老师赋值
for(int i=0 ; i<len ; i++)
{
tArray[i].tName = "Teacher_";
tArray[i].tName += nameSeed[i];
//循环给每名老师所带的学生赋值
for(int j=0 ; j<5 ; j++)
{
tArray[i].sArray[j].sName = "Student_";
tArray[i].sArray[j].sName += nameSeed[j];
int random = rand()% 61+40; //40~100
tArray[i].sArray[j].score = random;
}
}
}
//4、打印所有老师及所带学生信息
void printInof(struct Teacher tArray[],int len)
{
//循环打印老师的信息
for(int i=0 ; i<len ; i++)
{
cout<<"老师姓名:"<<tArray[i].tName<<endl;
//循环打印每名老师所带的学生信息
for(int j=0 ; j<5 ; j++)
{
cout<<"\t学生姓名:"<<tArray[i].sArray[j].sName
<<" 考试分数:"<<tArray[i].sArray[j].score
<<endl;
}
}
}
int main()
{
//随机数种子
srand((unsigned int)time(NULL));
//2、创建3名老师的数组
struct Teacher tArray[3];
//tips:赋值函数和打印函数有许多相同的地方
//3、通过函数给3名老师的信息赋值,并给老师带的学生信息赋值
int len = sizeof(tArray)/sizeof(tArray[0]);
allocateSpace(tArray,len);
//4、打印所有老师及所带学生信息
printInof(tArray,len);
return 0;
}
/*
案例描述2 :设计一个英雄的结构体,包括成员姓名,年龄,性别;
创建结构体数组,数组中存放5名英雄。
通过冒泡排序的算法,将数组中的英雄按照年龄进行升序排序,
最终打印排序后的结果。
*/
#include <iostream>
using namespace std;
#include <string>
//1、设计英雄结构体
struct Hero
{
//姓名
string name;
//年龄
int age;
//性别
string sex;
};
//冒泡排序:实现年龄升序排序
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
<<"性别:"<<heroArray[i].sex
<<endl;
}
}
int main()
{
//2、创建数组存放5名英雄
struct Hero heroArray[5] =
{
{"刘备",23,"男"},
{"关羽",22,"男"},
{"张飞",20,"男"},
{"赵云",21,"男"},
{"貂蝉",19,"女"}
};
int len = sizeof(heroArray)/sizeof(heroArray[0]);
cout<<"排序前的打印:"<<endl;
//可作为测试代码
for(int i=0 ; i<len ; i++)
{
cout<<"姓名:"<<heroArray[i].name
<<"年龄:"<<heroArray[i].age
<<"性别:"<<heroArray[i].sex
<<endl;
}
//3、对数组进行排序,按照年龄进行升序排序
bubbleSort(heroArray,len);
cout<<"排序后的打印:"<<endl;
//4、将排序后结果打印输出
printHero(heroArray,len);
return 0;
}
6、小项目:通讯录管理系统
//
// Created by mac on 2021/2/8.
//
//封装函数显示该界面 如 void showMenu()
//在 main 函数中调用封装好的函数
#include <iostream>
using namespace std;
#include <string>
#define MAX 1000
#include <unistd.h>
//3、设计联系人和通讯录的结构体
//设计联系人的结构体
struct Person
{
//姓名
string m_Name;
//性别:男1 女2
int m_Sex;
//年龄
int m_Age;
//电话
string m_Phone;
//住址
string m_Addr;
};
//通讯录的结构体
struct Addressbook
{
//通讯录中保存的联系人数组
struct Person personArray[MAX];
//通讯录中当前记录联系人个数
int m_Size;
};
//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;
}
//5、利用地址传递,可以修饰实参
void addPerson(struct Addressbook * 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--男;2--女"<<endl;
int sex = 0;
while(true)
{
cin>>sex;
//输入1或者2正确退出循环,输入其他则重新输入
if (sex == 1 || sex == 2)
{
abs->personArray[abs->m_Size].m_Sex = sex;
break;
}
cout<<"输入有误,请从新输入!"<<endl;
}
//添加年龄
cout<<"请输入年龄:"<<endl;
int age = 0;
cin>>age;
abs->personArray[abs->m_Size].m_Age = age;
//添加电话
cout<<"请输入联系电话:"<<endl;
string phone;
cin>>phone;
abs->personArray[abs->m_Size].m_Phone = phone;
//添加住址
cout<<"请输入家庭住址:"<<endl;
string addr;
cin>>addr;
abs->personArray[abs->m_Size].m_Addr = addr;
//更新通讯录人数(关键)
abs->m_Size++;
cout<<"添加成功"<<endl;
//pause();//请按任意键继续 mac下似乎不能用这两个函数,此问题暂时还为解决
//system("clear");//清屏操作
}
}
//6、显示所有的联系人
void showPerson(struct Addressbook * abs)
{
//判断通讯录中人数是否为0,如果为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_Sex ==1 ? "男":"女")<<"\t";//关键
cout<<"年龄:"<<abs->personArray[i].m_Age<<"\t";
cout<<"电话:"<<abs->personArray[i].m_Phone<<"\t";
cout<<"住址:"<<abs->personArray[i].m_Addr<<endl;
}
}
//pause();//按任意键继续
//system("clear");//请屏
}
//7、检测联系人是否存在,如果存在,返回联系人所在数组中的具体位置,不存在返回-1
//参数1 通讯录 参数2 对比姓名
int isExist(struct Addressbook * abs , string name)
{
for(int i=0 ; i<abs->m_Size ; i++)
{
//找到用户输入的姓名了
if(abs->personArray[i].m_Name == name)
{
return i;//找到了,返回这个人在数组中的下标编号
}
return -1;//如果遍历结束都没有找到,返回-1
}
}
//8、删除指定联系人
void deletePerson(struct Addressbook *abs)
{
cout<<"请输入您要删除的联系人"<<endl;
string name;
cin>>name;
//ret == -1 未查到
//ret != -1 查到了
int ret = isExist(abs,name);
if(ret != -1)
{
for(int i=0 ; i<abs->m_Size ; i++)
{
//数据前移
abs->personArray[i] = abs->personArray[i+1];//只能按顺序删除,把后一个数据覆盖调前一个数据,此代码有缺陷
}
abs->m_Size--;//更新通讯录中的人员数
cout<<"删除成功"<<endl;
}
else
{
cout<<"查无此人"<<endl;
}
//pause();
//system("clear");
}
//9、查找指定联系人:此代码有缺陷,必须先创建一个联系人信息才能执行完整代码
void findPerson(struct Addressbook * abs)
{
cout<<"请输入您要查找的联系人"<<endl;
string name;
cin>>name;
//判断指定的联系人是否存在通讯录
int ret = isExist(abs,name);
if(ret != -1)//找到联系人
{
cout<<"姓名:"<<abs->personArray[ret].m_Name<<"\t";
cout<<"性别:"<<abs->personArray[ret].m_Sex<<"\t";
cout<<"年龄:"<<abs->personArray[ret].m_Age<<"\t";
cout<<"电话:"<<abs->personArray[ret].m_Phone<<"\t";
cout<<"住址:"<<abs->personArray[ret].m_Addr<<endl;
}
else
{
cout<<"查无此人"<<endl;
}
//pause();
//system("clear");
}
//10、修改指定联系人的信息
void modifyPerson(struct Addressbook * abs)
{
cout<<"请输入您要修改的联系人"<<endl;
string name;
cin>>name;
int ret = isExist(abs,name);
if(ret != 1) {
//修改姓名
cout << "请输入姓名:" << endl;
string name;
cin >> name;
abs->personArray[ret].m_Name = name;
//修改性别
cout << "请输入性别:" << endl;
cout << "1--男" << endl;
cout << "2--女" << endl;
int sex = 0;
while (true)
{
cin >> sex;
if (sex == 1 || sex == 2)
{
abs->personArray[ret].m_Sex = sex;
break;
}
cout<<"输入有误,请重新输入"<<endl;
}
//修改年龄
cout<<"请输入年龄:"<<endl;
int age = 0;
cin>>age;
abs->personArray[ret].m_Age = age;
//修改电话
cout<<"请输入联系电话:"<<endl;
string phone;
cin>>phone;
abs->personArray[ret].m_Phone = phone;
//修改住址
cout<<"请输入家庭住址:"<<endl;
string addr;
cin>>addr;
abs->personArray[ret].m_Addr = addr;
}
else
{
cout<<"查无此人"<<endl;
}
}
//11、清空所有的联系人
void cleanPerson(struct Addressbook * abs)
{
cout<<"是否确定清空?"<<endl;
cout<<"1 -- 是"<<endl;
cout<<"2 -- 否"<<endl;
int num = 0;
while(true)
{
cin >> num;
if (num == 1)
{
abs->m_Size = 0;//将当前记录联系人的数量置为0,做逻辑清空操作
cout << "通讯录已清空" << endl;
break;
}
else if (num == 2)
{
cout << "停止清空" << endl;
break;
}
cout<<"输入有误,请重新输入"<<endl;
}
//pause();
//system("clear");
}
int main()
{
//4、创建通讯录结构体变量
struct Addressbook abs;
//初始化通讯录中当前人员个数
abs.m_Size = 0;
int select = 0;//2、创建用户选择输入的变量
while(true)
{
//1、菜单调用
showMenu();
//2、创建用户选择输入的变量
cin>>select;
//只有选择0的时候才退出,其他选择则继续循环
switch (select) {
case 1: //1、添加联系人
addPerson(&abs);//5、增加联系人。利用地址传递,可以修饰实参
break;
case 2://2、显示联系人
showPerson(&abs);//6、显示所有的联系人
break;
case 3://3、删除联系人
/*
* 测试代码
{
cout << "请输入需要删除的联系人姓名:" << endl;
string name;
cin >> name;
if (isExist(&abs, name) == -1) {
cout << "查无此人" << endl;
} else {
cout << "找到此人" << endl;
}
}
*/
deletePerson(&abs);//7和8、删除指定联系人
break;
case 4://4、查找联系人
findPerson(&abs);//9、查找指定联系人
break;
case 5://5、修改联系人
modifyPerson(&abs);//10、修改指定联系人的信息
break;
case 6://6、清空联系人
cleanPerson(&abs);//11、清空所有的联系人
break;
case 0://0、退出通讯录
cout << "欢迎下次使用" << endl;
pause();
return 0;
default:
break;
}
}
}
7、程序的内存模型
7.1、内存四区
7.1.1、概念
C++程序在执行时,将内存大方向划分为4个区域
- 代码区(二进制):存放函数体的二进制代码,由操作系统进行管理的
- 全局区(存放量):存放全局变量、静态变量和常量(常量区存放const修饰的全局变量和字符串常量)
- 栈区(函参、局部):由编译器自动分配释放, 存放函数的参数值,局部变量等
- 堆区(码农分配):由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
7.1.2、意义(区域不同寿命不同)
不同区域存放的数据,赋予不同的生命周期, 给我们更大的灵活编程
7.2、程序运行前
在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域
7.2.1、代码区
存放 CPU 执行的机器指令
代码区是共享的,频繁被执行的程序只需在内存中有一份代码即可
代码区是只读的,防止程序意外地修改了它的指令
7.2.2、全局区
该区数据在程序结束后由操作系统释放
#include <iostream>
using namespace std;
//全局变量(凡函数体外的变量称为全局变量)
int g_a = 10;
//const修饰的全局变量称为全局常量
const int c_g_a = 10;
int main()
{
//全局区:全局变量、静态变量、常量
//创建普通局部变量(在函数体内的变量称为局部变量)
int a = 10;
cout<<"局部变量a的地址为:"<<&a<<endl;
cout<<"全局变量g_a的地址为:"<<&g_a<<endl;
//静态变量(在普通变量前加static,属于静态变量)
static int s_a = 10;
cout<<"静态变量s_a的地址为:"<<&s_a<<endl;
//常量
//字符串常量
cout<<"字符串常量的地址为:"<<&"字符串常量地址"<<endl;
//const修饰的变量
//const修饰的全局变量
cout<<"全局常量 c_g_a的地址为:"<<&c_g_a<<endl;
//const修饰的局部变量称为局部常量
const int c_l_a = 10;//c代表const l代表local g代表global
cout<<"局部常量 c_l_a的地址为:"<<&c_l_a<<endl;
return 0;
}
7.3、程序运行后
7.3.1、栈区
//由编译器自动分配释放, 存放函数的参数值,局部变量等
//注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放
#include <iostream>
using namespace std;
int* func(int b)//形参数据也会放在栈区
{
b = 100;
int a = 10;//局部变量,存放在栈区,栈区的数据在函数执行完成后自动释放
return &a;//不能返回局部变量的地址
}
int main()
{
int * p = func(1);
cout<<*p<<endl;//第一次可以打印正确的数字是因为编译器做了保留
cout<<*p<<endl;//第二次这个数据就不再保留了
return 0;
}
7.3.2、堆区
//由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
//在C++中主要利用new在堆区开辟内存
#include <iostream>
using namespace std;
int * func()//形参数据也会放在栈区
{
//利用new关键字可以将数据开辟到堆区
//指针,本质也是局部变量,放在栈上;指针保存的数据是放在堆区
int * p = new int(10);
return p;
}
void test01()
{
int * p = func();
cout<<*p<<endl;
cout<<*p<<endl;
cout<<*p<<endl;
cout<<"---------"<<endl;
//1、堆区开辟的数据,由程序员手动开辟,手动释放,释放利用关键字 ==delete==
delete p;
cout<<*p<<endl;//内存已经被释放,再次访问就是非法操作,会报错
}
//2、在堆区利用new开辟数组
void test02()
{
//创建10整形数据的数组,在堆区
int * arr = new int[10];
for(int i=0 ; i<10 ; i++)
{
arr[i] = i+100;//给10个元素赋值 100~109
}
for(int i=0 ; i<10 ; i++)
{
cout<<arr[i]<<endl;
}
//释放堆区数组
//释放数组的时候要加[]才可以
delete[] arr;
}
int main()
{
//在堆区开辟数据
//int * p = func();
test01();
test02();
//
return 0;
}
8、引用
作用: 给变量起别名
8.1、引用的基本语法与注意事项
#include <iostream>
using namespace std;
int main()
{
//语法: 数据类型 &别名 = 原名
int a = 10;
//创建引用
int &b = a;
//int &b;//1、错误,必须初始化
//2、引用在初始化后不可以改变
int c = 30;
b = c;//赋值操作,而不是更改引用
cout<<"原名:"<<a<<endl;
cout<<"别名:"<<b<<endl;
cout<<"c的值:"<<c<<endl;
cout<<"--------"<<endl;
b = 20;
cout<<"原名:"<<a<<endl;
cout<<"别名:"<<b<<endl;
return 0;
}
8.2、引用做函数参数
作用:函数传参时,可以利用引用的技术让形参修饰实参
优点:可以简化指针修改实参
#include <iostream>
using namespace std;
void Swap(int &a,int &b)//其实 &a 是 a 的别名,&b 是 b 的别名
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int a = 1;
int b = 2;
Swap(a,b);//引用传递和地址传递一样,都可以形参修饰实参
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
return 0;
}
8.3、引用做函数的返回值
作用:引用是可以作为函数的返回值存在的
用法:函数调用作为左值
#include <iostream>
using namespace std;
//引用做函数的返回值
//1、注意:不要返回局部变量的引用
int& test01()
{
int a = 10;//局部变量存放在四区中的 栈区
return a;
}
//2、用法:函数的调用可以作为左值
int& test02()
{
static int a = 10;//静态变量,存放在全局区,全局区上的数据在程序结束后系统释放
return a;
}
int main()
{
int &ref1 = test01();
cout<<"ref1 = "<<ref1<<endl;//第一次结果正确,是因为编译器做了保留
cout<<"ref1 = "<<ref1<<endl;//第二次结果错误,是因为a的内存已经释放
cout<<"------"<<endl;
int &ref2 = test02();
cout<<"ref2 = "<<ref2<<endl;
cout<<"ref2 = "<<ref2<<endl;
test02() = 1000;//如果函数的返回值是引用,这个函数调用可以作为左值(左边的值)
cout<<"-------"<<endl;
cout<<"ref2 = "<<ref2<<endl;
cout<<"ref2 = "<<ref2<<endl;
return 0;
}
8.4、引用的本质
本质:在c++内部实现是一个指针常量
结论:C++推荐用引用技术,因为语法方便,引用本质是指针常量,但是所有的指针操作编译器都帮我们做了
8.5、常量引用
#include <iostream>
using namespace std;
void showValue(const int &val)//const修饰形参,防止误操作
{
//val = 1000;//加了const防止形参改变实参 assignment:分配 ,reference:引用
cout<<"val="<<val<<endl;
}
int main()
{
//作用:常量引用主要用来修饰形参,防止误操作
int a = 10;
//int &ref = 10;//10是在常量区,引用必须引一块合法的内存空间
//加上const之后编译器将代码修改 int temp = 10;const int & ref = temp;
//const int &ref = 10;
//ref = 20;//加入const之后变为只读,不可以修改
showValue(a);
cout<<"a = "<<a<<endl;
return 0;
}