1、在C++程序执行时,将内存大方向可以划分为4个区域。
1)代码区:存放函数的二进制代码,由操作系统进行管理。
2)全局区:存放全局变量、静态变量以及常量。
3)堆区:由工程师分配和释放、若不释放,程序结束时由操作系统回收。
4)栈区:由编译器自动分配释放、存放着函数参数值,局部变量等。
2、内存四区意义?
不同区域存放不同的数据,赋予不同的生命周期,给我们更大的灵活编程。
在程序编译后,生成一个可执行程序,未执行该程序前分为两个区域:
1、代码区:
存放CPU执行的机器指令。 -> 其实指的就是0101这种数据。
代码区是共享的,共享的目的是对于频繁被执行的代码,只需要在内存中有一份代码即可。
无论执行了多少次程序,都是使用同一个代码。
代码区是只读的,使其只读的原因是防止程序意外地修改它的指令。
2、全局区:
全局变量与静态变量存放在此。
全局区还包含了常量区、字符串常量、其他常量(const修饰的变量)在存放在此。
该区域的数据在程序结束之后由操作系统来释放
#includeusing namespace std; //2. 创建全局变量 int g_a = 10; int g_b = 20; //全局变量: 全局区 //5. const修饰全局变量 -> 全局常量 const int c_g_a = 10; const int c_g_b = 10; int main(int argc,char *argv[]) { //1. 创建局部变量 int a = 10; int b = 20; cout << "&a = " << &a << endl; cout << "&b = " << &b << endl; //局部变量:栈区 cout << "&g_a = " << &g_a << endl; cout << "&g_b = " << &g_b << endl; //3. 静态变量 static int s_a = 10; static int s_b = 20; cout << "&s_a = " << &s_a << endl; cout << "&s_b = " << &s_b << endl; //静态变量: 全局区 //4. 常量区 //字符串常量 cout << "string addr = " << &"helloworld" << endl; //字符串常量: 全局区 //const修饰的变量 //const修饰全局变量、const修饰局部变量。 cout << "&c_g_a = " << &c_g_a << endl; cout << "&c_g_b = " << &c_g_b << endl; const int c_l_a = 10; //局部常量 const int c_l_b = 20; cout << "&c_l_a = " << &c_l_a << endl; cout << "&c_l_b = " << &c_l_b << endl; //局部常量:栈区 return 0; }
运行结果:
wzf@ubuntu:/mnt/hgfs/C++$ ./all &a = 0x7ffd6293a768 &b = 0x7ffd6293a76c &g_a = 0x602070 &g_b = 0x602074 &s_a = 0x602078 &s_b = 0x60207c string addr = 0x400c57 &c_g_a = 0x400c14 &c_g_b = 0x400c18 &c_l_a = 0x7ffd6293a770 &c_l_b = 0x7ffd6293a774
结论:
1.不在全局区:
局部变量 0x7ffd6293a768
const修饰的局部变量(局部常量) 0x7ffd6293a770
2.在全局区:
全局变量 0x602070
静态变量 使用static修饰 0x602078
常量
字符串常量 0x400c57
使用const修饰的全局变量(全局常量) 0x400c14
栈区: 由编译器自动分配释放,存放函数的参数值,局部变量等。
注意: 不要返回局部变量的地址,栈区开辟的数据由编译器自动释放。
#includeusing namespace std; //栈区:存放函数的参数值,局部变量 //不要返回局部变量的地址 int *fun(int b) //b是形式参数,是存放在栈区 { int a = 10; //a是局部变量,是存放在栈区 -> 开始申请a的空间 return &a; //因为返回时,局部变量的空间会释放掉。 -> 开始释放a的空间 } int main(int argc,char *argv[]) { int *p = fun(1); //返回到主函数时,a的空间已经释放掉了。 cout << "p = " << p << endl; //0 -> NULL cout << "*p = " << *p << endl; // *NULL 就是段错误 return 0; }
结果:
wzf@ubuntu:/mnt/hgfs/C++$ ./stack p = 0 -> 正常来讲,应该返回a的地址,但是由于a的空间释放掉,为了防止用户去访问该区域,所以返回安全指针NULL给函数调用的地方。 Segmentation fault (core dumped)
堆区: 由工程师分配和释放,若不释放,程序结束时由操作系统来回收。
在C++中主要利用new在堆区开辟空间。
#includeusing namespace std; int *func() { //利用new关键词,可以将数据开辟到堆区 int *a = new int(10); return a; //函数返回时,堆空间不会释放。 } int main(int argc,char *argv[]) { int *p = func(); cout << "p = " << p << endl; cout << "*p = " << *p << endl; return 0; }
结果:
wzf@ubuntu:/mnt/hgfs/C++$ ./heap p = 0xd19c20 *p = 10十四、new操作符。
C++中利用new操作符在堆区开辟数据。
堆区中开辟的数据,由工程师手动开辟,手动释放,释放利用操作符delete
语法: new 数据类型
利用new创建的数据,会返回该数据对应的类型的指针。
1、堆区的基本语法示例。
#includeusing namespace std; //语法: new 数据类型 //返回该数据类型对应的类型的指针 int *func() { //在堆区创建整型数据 int *p = new int(10); return p; } int main(int argc,char *argv[]) { int *p = func(); cout << "*p = " << *p << endl; //10 //堆区的数据由用户自己来释放。 delete p; p = NULL; cout << "*p = " << *p << endl; //0 return 0; }
2、在堆区利用new开辟数组。
#include十五、引用的基本使用。using namespace std; int main(int argc,char *argv[]) { //创建10个整型的数据在堆区。 int *arr = new int[10]; //10代表数组有10个元素 int i; for(i=0;i<10;i++) { arr[i] = i + 100; //给10个元素赋值 100~109 } for(i=0;i<10;i++) { cout << arr[i] << endl; } //释放数组时,需要加[]才可以。 delete[] arr; return 0; }
1、引用作用:
给变量起别名。
2、语法。
数据类型 &别名 = 原名
#include十六、引用注意事项。using namespace std; //引用作用:给变量起别名 //引用语法: 数据类型 &别名 = 原名 int main(int argc,char *argv[]) { int a = 10; //创建引用 int &b = a; cout << "a = " << a << endl; //10 cout << "b = " << b << endl; //10 b = 100; cout << "a = " << a << endl; //100 cout << "b = " << b << endl; //100 return 0; }
1、引用必须初始化。
2、引用在初始化后,不可以改变。
#include十七、引用做函数参数。using namespace std; int main(int argc,char *argv[]) { //1、引用必须初始化。 //int &c; //编译出错,引用必须初始化,必须要引用一块合法并且存在的空间。 //2、引用在初始化后,不可以改变。 int a = 10; int b = 20; //创建引用 int &c = a; //一旦初始化了,就不可以更改。(c永远都是a的别名) c = b; //这是赋值操作 //不是让c去做b的别名 cout << "a = " << a << endl; //20 cout << "b = " << b << endl; //20 cout << "c = " << c << endl; //20 return 0; }
作用:函数传参时,可以利用引用的技术让形参修饰实参。
优点:可以简化指针修饰实参的方法。
#include十八、引用做函数返回值。using namespace std; //1. 值传递 void mySwap01(int a,int b) //a = 10 b = 20 { int temp = a; a = b; b = temp; } //2. 地址传递 void mySwap02(int *a,int *b) { int temp = *a; *a = *b; *b = temp; } //3. 引用传递 void mySwap03(int &a,int &b) //int &a = a int &b = b //修改形参a,就等于修改实参a { int temp = a; a = b; b = temp; } int main(int argc,char *argv[]) { int a = 10; int b = 20; //mySwap01(a,b); //mySwap02(&a,&b); mySwap03(a,b); cout << "a = " << a << endl; cout << "b = " << b << endl; return 0; }
作用:引用是可以作为函数返回值存在的。
注意事项:
1、不要返回局部变量的引用。
2、函数调用作为左值的存在。
#includeusing namespace std; //int -> 返回的是10(本体的值) //int test01() //int& -> 返回的是a(本体) int &test01() { //1、不要返回局部变量的引用。 //int a = 10; //局部变量 static int a = 10; return a; } int main(int argc,char *argv[]) { int &ret = test01(); cout << "ret = " << ret << endl; //10 //2、函数调用作为左值的存在。 //test01(); // -> 返回是a的本体 //test01() = 1000; //cout << "ret = " << ret << endl; //1000 return 0; }



