目录
1 内存分区模型
1.1 内存四区---不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编辑。
C++程序在执行时,将内存大方向划分为4个区域:
1、代码区:函数体的二进制代码,注释、由操作系统进行管理的;
2、全局区:全局变量、静态变量、常量;
3、栈区:编译器自动分配释放,存放函数的参数值,局部变量等;
4、堆区:程序员分配和释放,若程序员不释放,程序结束时由操作系统回收;
1.2 程序运行前---编译后,生成.exe可执行程序,未执行该程序前分为以下:
1、代码区:存放CPU执行的机器指令,
代码区共享---对于频繁被执行的程序,只需要内存中有一份代码;
代码区只读---防止程序意外地修改指令
2、全局区---全局变量、静态变量
全局区包含常量区,字符串常量和其他const修饰的常量;该区域数据程序结束后由操作系统释放
c-const;g-global-全局;l-local-局部
1.3 staic和const区别
1、static---规定作用域和存储方式
(1)局部变量,static规定其为静态存储方式,每次调用的初始值为上一次调用的值,调用结束后存储空间不释放;
(2)全局变量,如果以文件划分作用域的话,此变量只在当前文件可见;对于static函数也是在当前模块内函数可见.
2、const---只读,只在声明中使用;
(1)const放在变量类型名前,说明这个变量的值是保持不变的,该变量必须在定义时初始化,初始化后对它进行的任何赋值都是非法的。
(2)假如变量是一个非常量变量,而指针或者引用的类型名前使用了const,那么,可以直接修改变量,不能通过指针或者引用修改变量。
1.4 程序运行后
1、栈区:由编译器自动分配释放,存放函数的参数值,局部变量等,
注意:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放
2、堆区:程序员分配释放,程序结束时,由操作系统回收
在C++中主要利用new关键字在堆区开辟内存
int *p=new int(10);//利用new关键字,将数据开辟到堆区;指针:本质是局部变量,放在栈上,指针保存的数据是放在堆区。
1.5 new操作符
C++中new操作符在堆区开辟数据,堆区开辟的数据,程序员手动开辟,释放利用操作符delete
语法:new 数据类型;利用new创建的数据,会返回改数据对应的类型的指针
2 C++的引用---变量起别名
语法:数据类型 &别名=原名;
//练习1---new关键词的用法
//1、new操作符在堆区开辟数据,程序员手动开辟,释放利用操作符delete;
//2、利用new创建的数据,会返回改数据对应的类型的指针
int* func(){
int *a=new int(10);
return a;
}
int* test()//在堆区利用new开辟数据{
int *arr=new int[10];//创建10整型数据的数据,在堆区,10代表数据由10个数据,int代表数据元素类型
for(int i=0;i<10;i++){
arr[i]=i+100;
}
for(int i=0;i<10;i++){
cout<<arr[i]<<endl;
}
return arr;
}
int main(){
int *p=test();
cout<<*p<<endl;
//delete p;//delete释放在堆区创建的指针
delete []p;//释放数组时,需要加[]
return 0;
}
2.1 引用的注意事项
(1)引用必须初始化;(2)引用在初始化后,不可以改变再变成其他变量的别名了
//练习2---C++引用
//作用:给变量起别名
//语法:数据类型 &别名=原名
//注意:引用必须初始化;初始化引用后,不可以更改
int main(){
int a=10;
int &b=a;
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
b=20;
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
int c=40;
b=c;//赋值操作,没有更改引用
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
cout<<"c="<<c<<endl;
return 0;
}
2.2 引用做函数参数
(1)作用:函数传参时,可以利用引用的技术,让形参修饰实参;
(2)优点:可以简化指针修改实参
//引用作为函数参数,让形参修饰实参,可以简化指针修改实参
//值传递
void MySwap_0(int a,int b){
int temp=a;
a=b;
b=temp;
cout<<"MySwap_0_a="<<a<<endl;
cout<<"MySwap_0_b="<<b<<endl;
return;
}
//地址传递
void MySwap_1(int *a,int *b){
int temp=*a;
*a=*b;
*b=temp;
cout<<"MySwap_1_a="<<*a<<endl;
cout<<"MySwap_1_b="<<*b<<endl;
return;
}
//引用传递
void MySwap_2(int &a,int &b){
int temp=a;
a=b;
b=temp;
cout<<"MySwap_2_a="<<a<<endl;
cout<<"MySwap_2_b="<<b<<endl;
return;
}
int main(){
int a=10;
int b=20;
//MySwap_0(a,b);
//MySwap_1(&a,&b);
MySwap_2(a,b);
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
return 0;
}
2.3 引用做函数返回值
1、不要返回局部变量的引用;2、函数的调用作为左值
//引用做函数的返回值
//1、不要返回局部变量的引用
int& test01(){
int a=1;//局部变量保存在四区中的栈区
return a;
}
//2、函数的调用可以作为左值
int& test02(){
static int a=10;//静态变量,存放在全局区,全局区的数据在程序结束系统进行释放
return a;
}
//引用作为函数返回值
//注意:不要返回局部变量的引用
//用法:函数的调用作为左值
int main(){
//int& ref=test01();
//cout<<"ref="<<ref<<endl;//第一次输出ref=1,只是因为编译器作了保留
//cout<<"ref="<<ref<<endl;//第二次输出ref=2039399392,a的内存已经释放
int& ref=test02();
cout<<"ref="<<ref<<endl;//ref=10;
cout<<"ref="<<ref<<endl;//ref=10;
//如果函数的返回值是引用,这个函数调用可以作为左值
test02()=100;
cout<<"ref="<<ref<<endl;//ref=100;
cout<<"ref="<<ref<<endl;//ref=100;
return 0;
}
2.4 引用的本质---C++内部实现是一个指针常量
//引用的本质---指针常量
int& ref=a;
//自动转换为int* const ref=&a;
//指针常量是指针指向不可改,也说明为什么引用不可更改
//发现是引用,转换为int* const ref=&a;
void func(int& ref){
ref=100;//内部发现ref是引用,转换为*ref=100
}
2.5 常量引用---修饰形参,防止误操作
在函数形参列表中,可以加const修饰形参,防止形参改变实参
void ShowValue(const int& val){
//val=100;//非法操作了,const规定只读,引用状态下可以修改实参,
//通过const就可以防止修改产生误操作
cout<<"val="<,val<<endl;
}
3 函数高级---默认参数,占位参数,函数重载
3.1 函数的默认参数
(1)如果自己传入数据,就用自己的数据,如果没有,用默认值;
(2)语法:返回值类型 函数名(形参=默认值){};
(3)如果某个位置有了默认值,从左至右位置都必须由默认值;
(4)如果函数声明有默认参数,函数实现就不能有默认参数;
int func(int a,int b=20,int c=30){
return a+b+c;
}
int main(){
cout<<func(10)<<endl;//输出60
cout<<func(10,30)<<endl;//输出70
return 0;
}
3.2 函数占位参数
C++中的函数的形参列表里可以有占位参数,调用函数时必须填补该位置;
占位参数也可以由默认参数
语法:返回值类型 函数名 (数据类型){}
void func(int a,int =10){
cout<<"func"<<endl;
}
3.3 函数重载---函数名可以相同,提高复用性
1、函数重载满足条件
(1)同一作用域下;(2)函数名称相同;(3)函数参数类型不同||个数不同||顺序不同
注意:函数的返回值不可以作为函数重载的条件
//(3)函数重载
void func(){
cout<<"func的调用"<<endl;
}
void func(int a){
cout<<"func(int a)的调用"<<endl;
}
void func(int a,double b){
cout<<"func(int a,double b)的调用"<<endl;
}
void func(double b,int a){
cout<<"func(double b,int a)的调用"<<endl;
}
int main(){
func();
func(20);
func(20,3.14);
func(3.14,20);
return 0;
}
//注意!!函数的返回值不可以作为函数重载的条件
//以下函数不可以函数重载,会报错
//int func(double b,int a){
// cout<<"func(double b,int a)的调用"<<endl;
// return 0;
//}
2、函数重载的注意事项
1、引用作为重载条件;2、函数重载碰到函数默认参数---出现二义性,尽量避免
//1、引用作为函数重载
void func(int& a){//如果Int& a =10;引用的合法的空间,在栈上或者堆区,这个是不合法的
cout<<"func(int& a)的调用"<<endl;
}
void func(const int& a){//只读,开辟在全局区
cout<<"func(const)的调用"<<endl;
}
int main(){
//int a=10;//局部变量,开辟在栈区
//func(a);//此时调用的是func(int& a)
func(10);//此时调用的是func(const int& a)
return 0;
}
//2、函数重载碰到默认参数
void func(int a){
cout<<"func(int a)的调用"<<endl;
}
void func(int a,int b=10){
cout<<"func(int a,int b=10)的调用"<<endl;
}
//此时调用func(10)会引起二义性,应该尽量避免这种情况