一、C程序的内存结构
C 程序的典型内存表示由以下部分组成。1.文本段(即说明)。2.初始化数据段。3.未初始化的数据段(bss)。4.堆。5.堆栈。
1、文本段
文本段,也称为代码段或简称为文本,是目标文件或内存中的程序段之一,其中包含可执行指令。作为内存区域,可以将文本段放置在堆或堆栈下方,以防止堆和堆栈溢出覆盖它。
通常,文本段是可共享的,因此对于频繁执行的程序(例如文本编辑器、C 编译器、shell 等),只需要在内存中保存一个副本。此外,文本段通常是只读的,以防止程序意外修改其指令。
2、数据段
初始化数据段,通常简称为数据段。数据段是程序虚拟地址空间的一部分,其中包含由程序员初始化的全局变量和静态变量。
请注意,数据段不是只读的,因为变量的值可以在运行时更改。该段可以进一步分为初始化只读区和初始化读写区。
例如,由 C 中的 char s[] = “hello world” 定义的全局字符串和 main 之外的诸如 int debug=1 的 C 语句(即全局)将存储在初始化的读写区域中。而像 const char* string = “hello world” 这样的全局 C 语句使字符串文字“hello world”存储在初始化的只读区域中,而字符指针变量 string 则存储在初始化的读写区域中。
例如:静态 int i = 10 将存储在数据段中,全局 int i = 10 也将存储在数据段中。
3、“ bss ”段
未初始化的数据段通常称为“ bss ”段,以一个古老的汇编运算符命名,代表“由符号开始的块”。该段中的数据在程序开始执行之前由内核初始化为算术 0,未初始化的数据从数据段的末尾开始,包含所有初始化为零或源代码中没有显式初始化的全局变量和静态变量。
例如,一个声明为 static int i 的变量;将包含在 BSS 段中。
例如,声明为 int j 的全局变量;将包含在 BSS 段中。
4、堆栈
堆栈区域传统上与堆区域相邻,并以相反的方向增长;当堆栈指针遇到堆指针时,可用内存已耗尽。(使用现代大地址空间和虚拟内存技术,它们几乎可以放置在任何地方,但它们通常仍以相反的方向增长。)堆栈区域包含程序堆栈,这是一种 LIFO 结构,通常位于内存的较高部分。在标准 PC x86 计算机体系结构上,它向零地址增长;在其他一些架构上,它的增长方向相反。“堆栈指针”寄存器跟踪堆栈的顶部;每次将值“推入”堆栈时都会对其进行调整。为一个函数调用推送的一组值称为“堆栈帧”;堆栈帧至少包含一个返回地址。
堆栈,其中存储自动变量,以及每次调用函数时保存的信息。每次调用函数时,返回的地址和调用者环境的某些信息,例如一些机器寄存器,都保存在堆栈中。然后,新调用的函数在堆栈上为其自动和临时变量分配空间。这就是 C 中递归函数的工作方式。每次递归函数调用自身时,都会使用一个新的堆栈帧,因此一组变量不会干扰来自另一个函数实例的变量。
5、堆
堆是通常发生动态内存分配的段。
堆区域从 BSS 段的末尾开始,并从那里增长到更大的地址。Heap area 由 malloc、realloc、free 管理,可以使用 brk 和 sbrk 系统调用来调整其大小(注意使用 brk/sbrk 和单个“heap area”不是必须履行的契约malloc/realloc/free;它们也可以使用 mmap 来实现,以将虚拟内存的潜在不连续区域保留到进程的虚拟地址空间中)。堆区域由进程中的所有共享库和动态加载的模块共享。
二、new 运算符和delete运算符
C/C++中的动态内存分配是指由程序员手动进行内存分配。动态分配的内存在堆上分配,非静态和局部变量在栈上分配内存。
C 使用malloc() 和 calloc()函数在运行时动态分配内存,并使用 free() 函数释放动态分配的内存。C++ 支持这些函数并且还有两个操作符new和delete以更好和更简单的方式执行分配和释放内存的任务。
很多时候,用户无法提前预知需要多少内存来存储某个定义变量中的特定信息,所需内存的大小需要在运行时才能确定。因此在C++中,可以使用特殊的运算符为给定类型的变量在运行时分配堆内的内存,这会返回所分配的空间地址。这种运算符即new运算符。如果不再需要动态分配的内存空间,可以使用delete运算符,删除之前由new运算符分配的内存。
1、new 运算符
new 运算符表示对 Free Store 的内存分配请求。 如果有足够的内存可用,new 运算符会初始化内存并将新分配和初始化的内存的地址返回给指针变量。
使用 new 运算符的语法:
pointer-variable = new data-type;
这里,指针变量是数据类型类型的指针。 数据类型可以是任何内置数据类型,包括数组或任何用户定义的数据类型,包括结构和类。
(1)例1
int *p = NULL;
p = new int;
int *d = new int;
(2)例2
int *p1 = new int(25);
float *p2 = new float(75.25);
(3)例3
int *p2 = new int[10]
连续动态地为 10 个 int 类型的整数分配内存,并返回指向序列的第一个元素的指针,该指针分配给 p(a 指针)。p[0] 指第一个元素,p[1] 指第二个元素,依此类推。
如果堆中没有足够的内存来分配,会通过抛出std::bad_alloc类型的异常来指示失败。
声明普通数组和使用 new 分配一块内存是有区别的。最重要的区别是,普通数组由编译器释放(如果数组是本地的,则在函数返回或完成时释放)。但是,动态分配的数组始终保留在那里,直到它们被程序员释放或程序终止。
2、delete运算符
由于释放动态分配的内存是程序员的责任,因此 C++ 语言为程序员提供了删除运算符。
delete p1;
delete[] p2;
示例代码
#include <iostream>
using namespace std;
int main ()
{
// Pointer initialization to null
int* p = NULL;
// Request memory for the variable
// using new operator
p = new(nothrow) int;
if (!p)
cout << "allocation of memory failed\n";
else
{
// Store value at allocated address
*p = 29;
cout << "Value of p: " << *p << endl;
}
// Request block of memory
// using new operator
float *r = new float(75.25);
cout << "Value of r: " << *r << endl;
// Request block of memory of size n
int n = 5;
int *q = new(nothrow) int[n];
if (!q)
cout << "allocation of memory failed\n";
else
{
for (int i = 0; i < n; i++)
q[i] = i+1;
cout << "Value store in block of memory: ";
for (int i = 0; i < n; i++)
cout << q[i] << " ";
}
// freed the allocated memory
delete p;
delete r;
// freed the block of allocated memory
delete[] q;
return 0;
}