C++核心编程--内存分区模型、函数重载详解

C++核心编程--内存分区模型、函数重载详解 C核心编程1 内存分区模型C程序在执行时将内存大方向划分为4个区域代码区存放函数体的二进制代码由操作系统进行管理的全局区存放全局变量和静态变量以及常量栈区由编译器自动分配释放存放函数的参数值局部变量等堆区由程序员分配和释放若程序员不释放程序结束时由操作系统回收内存四区意义不同区域存放的数据赋予不同的生命周期给我们更大的灵活编程1.1 程序运行前在程序编译后会生成exe可执行程序程序运行前分为两个区域代码区存放CPU执行的机器指令(二进制01)代码区是共享的共享的目的是对于频繁执行的程序只需要在内存中有一份代码即可代码区是只读的原因是为了防止程序意外修改它的指令全局区全局变量和静态变量存放在这里全局区还包含了常量区字符串常量和其他常量(const修饰的全局常量)存放在这里这个区域的数据在程序结束后由操作系统释放#includeiostream using namespace std; //全局变量没有写在函数体中的变量 int g_a 10; int g_b 10; //const修饰的全局变量全局常量 const int c_g_a 10; const int c_g_b 10; int main() { //全局区 //全局变量、静态变量、常量 //创建普通的局部变量:函数体内的变量都叫局部变量 int a 10; int b 10; cout 局部变量a的地址 (int)a endl; cout 局部变量b的地址 (int)b endl; cout 全局变量g_a的地址 (int)g_a endl; cout 全局变量g_b的地址 (int)g_b endl; //静态变量在普通变量前加一个static static int s_a 10; static int s_b 10; cout 静态变量s_a的地址 (int)s_a endl; cout 静态变量s_b的地址 (int)s_b endl; //常量 //字符串常量 cout 字符串常量的地址 (int)hello world endl; //const修饰的变量 //const修饰的全局变量const修饰的局部变量 cout 全局常量c_g_a的地址 (int)c_g_a endl; cout 全局常量c_g_b的地址 (int)c_g_b endl; int c_l_a 10;//c-const g-global全局 l-local局部 int c_l_b 10; cout 局部常量c_l_a的地址 (int)c_l_a endl; cout 局部常量c_l_b的地址 (int)c_l_b endl; system(pause); return 0; }输出结果为1.2 程序运行后栈区由编译器自动分配释放存放函数的参数值局部变量等注意不要返回局部变量的地址栈区开辟的数据由编译器自动释放#includeiostream using namespace std; //栈区数据注意事项--不要返回局部变量的地址 //栈区的数据由编译器管理开辟和释放 //函数会返回一个int类型的指针 int* func(int b) {//形参数据也会放在栈区 b 100; int a 10;//局部变量存放在栈区栈区的数据在函数执行完后自动释放 return a;//返回局部变量的地址 } int main() { //接收func函数返回的地址也就是栈区变量a的值 int* pfunc(1); cout *p endl;//第一次可以打印正确的数字是因为编译器做了保留 cout *p endl;//第二次这个数据不再保留已经被释放乱码 system(pause); return 0; }输出结果为堆区由程序员分配释放若程序员不释放程序结束时由操作系统回收在C中主要利用new在堆中开辟内存#includeiostream using namespace std; int* func() { //利用new关键字可以将数据开辟到堆区 //指针本质也是局部变量放在栈上指针保存的数据是放在堆区 //p接收new分配的堆内存地址 int *pnew int(10);//在堆上开辟一个内存空间里面存的值是10把地址交给指针p return p; } int main() { //在堆区开辟数据 int* p func();//接收了func()函数返回的堆内存地址 cout *p endl;//10 system(pause); return 0; }知识点回顾int*p 定义指针int*pa 定义一个指针p来接收a的地址将a的地址直接赋值给pp变量a的地址*p变量a的具体值int*parr 将数组首元素的地址赋值给p等价于int *parr[0]p-age表示通过结构体指针访问结构体中的属性需要利用-(int)p表示将p的地址强转为int整数直接求指针地址直接写p1.3 new操作符C中利用new操作符在堆区开辟数据堆区开辟的数据由程序员手动开辟手动释放释放利用操作符delete语法new 数据类型利用new创建的数据会返回该数据对应类型的指针#includeiostream using namespace std; //1、new的基本语法 int* func() { //在堆区创建整型数据 //new返回的是该数据类型的指针 int*pnew int(10); return p; } void test01(){ int* p func(); cout *p endl;//10 //堆区的数据由程序员开辟程序员管理释放 //如果想释放堆区的数据利用关键字delete delete p; //cout *p endl;//内存已经被释放再次访问就是非法操作会报错 } //2、在堆中利用new开辟数组 void test02() { //创建10个整型数据的数组在堆区 int* arrnew int[10];//10代表数组有10个元素 for (int i 0; i 10; i) { //赋值操作不赋值只默认里面存了10个数不知道具体数值只能输出10个乱码 arr[i] i 100;//给10个元素赋值 100~109 // arr[i] i;输出0-9 } for (int i 0; i 10;i) { cout arr[i] endl; } //释放堆区数组 //释放数组的时候要加[] //delete[] arr; } int main() { //test01(); test02(); system(pause); return 0; }输出结果为2 引用2.1 引用的基本使用作用给变量起别名语法数据类型 别名原名2.2 引用注意事项引用必须初始化 (告诉我b是谁的别名)引用在初始化后不可以改变2.3 引用做函数参数作用函数传参时可以利用引用的技术让形参修饰实参优点可以简化指针修改实参#includeiostream using namespace std; //交换函数 //1、值传递 void mySwap01(int a, int b) { int temp a; a b; b temp; //cout mySwap01 中的a a endl; //cout mySwap01 中的b b endl; } //2、地址传递 void mySwap02(int *a,int*b) { int temp *a; *a *b; *b temp; } //3、引用传递 void mySwap03(inta,intb) { int temp a; a b; b temp; } int main() { int a 10; int b 20; //mySwap01(a, b);//值传递,形参不会修饰实参也就是说改变了形参实参没有改变 //mySwap02(a, b);//地址传递形参会修饰实参 mySwap03(a, b);//引用传递形参也会修饰实参 cout a a endl;//20 cout b b endl;//10 system(pause); return 0; }2.4 引用做函数返回值作用引用是可以作为函数返回值存在的注意不要返回局部变量引用用法函数调用作为左值#includeiostream using namespace std; //引用做函数的返回值 //1、不要返回局部变量的引用 int test01() { int a 10;//局部变量存放在四区中的栈区 return a; } //2、函数的调用可以作为左值 int test02() { static int a 10;//静态变量存放在全局区全局区的数据在程序结束后系统释放 return a; } int main() { //int ref test01();//ref相当于a的别名 //cout ref ref endl;//10第一次是对的是因为编译器做了保留 //cout ref ref endl;//乱码第二次错误是因为a的内存已经被释放 int ref2 test02(); cout ref2 ref2 endl;//10 cout ref2 ref2 endl;//10 test02() 100;//如果函数的返回值是引用这个函数的调用可以作为左值 cout ref2 ref2 endl;//100 cout ref2 ref2 endl;//100 system(pause); return 0; }2.5 引用的本质本质引用的本质是在C内部实现一个指针常量2.6 常量引用作用常量引用主要用来修饰形参防止误操作#includeiostream using namespace std; //打印数据函数 void showValue(const int val) { //val 1000; cout val val endl; } int main() { //常量引用 //使用场景用来修饰形参防止误操作 //int a 10; //加上const之后编译器将代码修改为int temp10;const intreftemp; //加上const之后ref原名是temp别名是ref只能用别名操作 //const int ref 10;//引用必须引一块合法的内存空间 //ref 20;//加入const之后变为只读不可修改 int a 100; showValue(a);//修改了别名val原名也会被修改添加了const防止误操作 cout a a endl; system(pause); return 0; }3 函数提高3.1 函数的默认参数在C中函数的形参列表中的形参是可以有默认值的。语法返回值类型 函数名(参数默认值){}#includeiostream using namespace std; //函数默认参数 //如果我们自己传入数据就用自己的数据如果没有就用默认值 //语法返回值类型 函数名形参默认值{} int func(int a,int b20,int c30){ return a b c; } //注意事项 //1、如果某个位置已经有了默认参数那么从这个位置往后,从左到右都必须有默认值 // int func2(int a, int b20,int c) {//会报错 // return a b c; //} //2、如果函数声明有默认参数函数实现就不能有默认参数 //声明和实现只能有一个有默认参数 int func2(int a10, int b70);//声明 int func2(int a, int b) {//实现 return a b; } int main() { //coutfunc(10,30)endl;//70 coutfunc2(20, 80)endl; system(pause); return 0; }3.2 函数占位参数C函数的形参列表里可以有占位参数用来做占位调用函数时必须填补该位置语法返回值类型 函数名(数据类型){}3.3 函数重载3.3.1 函数重载概述作用函数名可以相同提高复用性函数重载满足以下几个条件同一个作用域下函数名称相同函数参数类型不同或者个数不同或者顺序不同注意函数的返回值不可以作为函数重载的条件#includeiostream using namespace std; //函数重载 //可以让函数名相同提高复用性 //函数重载的满足条件 //1、同一个作用域下(全局作用域不在main函数里,写在所有函数外面的变量) //2、函数名称相同 //3、函数参数类型不同或者个数不同或者顺序不同 void func() { cout func的调用 endl; } void func(int a) { cout func(int a)的调用 endl; } void func(double a) { cout func(double a)的调用 endl; } void func(int a,double b) { cout func(int a,double b)的调用 endl; } void func(double a, int b) { cout func(double a,int b)的调用 endl; } //注意事项 //函数返回值不可以作为函数重载的条件,会报错 //int func(double a, int b) { // cout func(double a,int b)的调用 endl; //} int main() { //func(); //func(10); //func(3.8); func(88,3.8); func(6.6,66); system(pause); return 0; }3.3.2 函数重载注意事项引用作为重载条件函数重载碰到函数默认参数#includeiostream using namespace std; //函数重载的注意事项 //1、引用作为重载的条件参数类型不同可以实现重载 void func(inta) {//int a10;不合法 cout func(int a)调用 endl; } void func(const int a) {//const inta10;合法 cout func(const int a)调用 endl; } //2、函数重载碰到默认参数 void func2(int a,int b10) { cout func2(int a,int b)的调用 endl; } void func2(int a) { cout func2(int a)的调用 endl; } int main() { //int a 10;//本身a是一个变量可读可写而const只读不能写 //func(a);//输出没有const的 //func(10);//输出有const的 //func2(10);//当函数重载碰到默认参数出现二义性,报错 system(pause); return 0; }知识点回顾int a10不合法会报错引用必须引用一块合法的内存空间也就是右边必须是一个有内存有名字的变量10是临时值没有名字没有固定内存地址int ab意思是a是b的别名const inta10;合法语法编译器将代码优化为int temp10;const intatemp;a原名是temp别名是a