C++程序在执行时,将内存大方向划分为4个区域
代码区:存放函数体的二进制代码,由操作系统进行管理的
全局区:存放全局变量和静态变量以及常量
栈区:由编译器自动分配释放, 存放函数的参数值,局部变量等
堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
1.0 c++程序执行
C++程序编译运行的整个过程分为以下四个阶段:预处理、编译、汇编、连接。
预处理:预处理器(cpp)对源程序中的伪指令和特殊符号进行展开,例如头文件的展开、宏定义展开、条件编译命令(#include、#define、#ifdef-#else-#endif等),得到展开后的C程序通常以 .i 为扩展名
编译:编译器(ccl)进行词法分析、语法分析、语义分析等,将文本文件hello.i翻译成汇编文本文件hello.s
汇编:汇编器(as)将汇编语言转换为机器语言,生成二进制文本hello.o
连接:链接器(id)把所有的目标文件链接到一起,并生成最终的可执行文件hello.exe
1.1 程序运行前
程序编译时是不分配内存的,此时只是根据声明时的类型进行占位,到以后程序执行时分配内存。也就是说程序没有被加载到内存前,可执行程序内部已经分好3段信息,分别是代码区、数据区(data)和未初始化数据区(bss)三个部分。其中,数据区与未初始化数据区统称为全局区。在程序编译后,生成exe可执行文件。未执行该程序前程序分为两个区域
代码区:存放待执行的机器指令
代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码 即可
代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令
全局区:存放全局变量、常量、静态变量
全局区包含了常量区,字符串常量和其他常量也存放在此,该区域的数据在程序结束后由操作系统释放。
1.2 程序运行后
运行可执行程序,系统把程序加载到内存,除了根据可执行程序的信息分出代码区、数据区和未初始化数据区之外,还额外增加了栈区和堆区。
栈区:栈是一种先进后出的内存结构,由编译器自动分配释放, 存放函数的参数值,局部变量等。
堆区:堆是一个大容器,它的容量要远远大于栈,但没有栈那样先进后出的顺序。由程序员分配释放,若程序员不释放,程序结束时由操作系统回收。
示例:
#include <iostream>
using namespace std;
//全局变量--全局区
int global_a = 10;
int global_b = 10;
//全局常量--全局区
const int c_g_a = 10;
const int c_g_b = 10;
void test01()
{
//局部变量--栈区
int a = 10;
int b = 10;
//局部常量--栈区
const int c_a = 10;
const int c_b = 10;
//静态变量--全局区
static int s_a = 10;
static int s_b = 10;
cout << "全局变量 global_a 的地址为:" << &global_a << endl;
cout << "全局变量 global_b 的地址为:" << &global_b << endl;
cout << "全局常量 c_g_a 的地址为:" << &c_g_a << endl;
cout << "全局常量 c_g_b 的地址为:" << &c_g_b << endl;
cout << "局部变量 a 的地址为:" << &a << endl;
cout << "局部变量 b 的地址为:" << &b << endl;
cout << "局部常量 c_a 的地址为:" << &c_a << endl;
cout << "局部常量 c_b 的地址为:" << &c_b << endl;
cout << "静态变量 s_a 的地址为:" << &s_a << endl;
cout << "静态变量 s_b 的地址为:" << &s_b << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
输出结果
全局变量 global_a 的地址为:00EBC000
全局变量 global_b 的地址为:00EBC004
全局常量 c_g_a 的地址为:00EB9B30
全局常量 c_g_b 的地址为:00EB9B34
局部变量 a 的地址为:00FCFA10
局部变量 b 的地址为:00FCFA04
局部常量 c_a 的地址为:00FCF9F8
局部常量 c_b 的地址为:00FCF9EC
静态变量 s_a 的地址为:00EBC008
静态变量 s_b 的地址为:00EBC00C
1.3 new操作符
==new== 在堆区开辟数据存储空间
==delete== 释放堆区开辟的空间
语法:new 数据类型
**说明:**new开辟的空间在堆上,因此当在局部函数中new出一段新的空间,该段空间在局部函数调用结束后仍然能够使用,可以用来向主函数传递参数。
利用new创建的数据,会返回该数据对应的类型的指针
示例1:基本语法
new DataType( ) 分配这种类型的一个大小的内存空间,并以括号中的值来初始化这个变量;
int* test02()
{
int* a = new int(10);
return a;
}
int main()
{
int* p = test02();
cout << "堆区开辟变量的地址为:" << p << endl;
cout << "堆区开辟变量的值为:" << *p << endl;
delete p; //释放堆数据
system("pause");
return 0;
}
输出结果
堆区开辟变量的地址为:0132C6E8
堆区开辟变量的值为:10
示例2:创建数组
new DataType[ ] 分配该类型n个大小的内存空间,并用默认构造函数来初始化这些变量;当使用new运算符定义一个多维数组变量或数组对象时,它产生一个指向数组第一个元素的指针。
int main()
{
int* arr = new int[10]; //堆区开辟数组
for (int i = 0; i < 10; ++i)
{
arr[i] = i;
cout << arr[i] << " ";
}
delete[] arr; //释放数组 delete 后加 []
system("pause");
return 0;
}
输出结果
0 1 2 3 4 5 6 7 8 9
示例3:创建二维数组
在C++中,数组可以被视为一种类型——但是,不存在‘二维数组’这种类型。二维数组本身会被解释成一个一维数组:这个数组的元素类型为另一种一维数组。比如int [2][3]这个二维数组,它会被编译器视作一个元素类型为‘int[3]’的一维数组。并且,‘int[3]’和'int[4]'会被当成不同的数据类型。
假设a, b为两个int型变量,如果你希望这样生成一个二维数组:new int[a][b],是不会得到编译器允许的——因为你没有指定这个数组的元素类型。如果将b指定为一个常量,例如new int[a][5],其实质与new int[a]创建一个动态数组并无多大区别——只是元素类型由int变为了'int[5]'而已。
void test03(unsigned int n)
{
int(*arr)[4] = new int[n][4];
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < 4; ++j)
{
arr[i][j] = 4 * i + j;
cout << *(*(arr + i) + j) << " ";
}
cout << endl;
}
delete[] arr; // 回收方法和普通动态数组相同,使用'delete[]'即可
}
int main()
{
test03(5);
system("pause");
return 0;
}
输出结果
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
16 17 18 19
用这个方法来创建二维数组,比较直观、易用,但它最大的限制在于:你必须在编译时确定b的大小。更为实用的二维数组创建方法见参考博客 4 。