1.简介
在C/C++程序运行时,所使用的内存分为代码区(Code)、堆区(Heap)、栈区(Stack)、全局/静态存储区(static)、常量存储区(const) 五个分区。注意此处的堆和栈并不是指数据结构
2.详解
(1)代码区
这个区域存放程序包含的所有函数体的二进制代码
(2)堆区
由用户代码分配/释放,下面的语句将会分配堆上的存储空间:
char *p = (char *)malloc(10); //分配一段10字节的存储空间
(3)栈区
由系统自动分配的空间,用户代码无法控制,用于存放局部变量,下面的代码片段创建了三个存储在栈上的不同类型的变量(或数组),对应三块大小不同的存储空间:
void main()
{
int a=10;
float c[5];
char *b;
}
栈区是向下存储的,每次分配的地址越来越小,堆区是向上存储的(顾名思义,堆起来),每次分配的地址越来越大
(4)全局/静态存储区
在所有函数外部定义的全局变量,以及通过static修饰的任意位置的变量都存储在该区域,下面的示例创建了3个存储在全局/静态存储区的变量
int a=4;
void punc() {
static int b=5;
}
void main() {
punc();
static char c=‘c’;
}
上面的例子中a,b,c三个变量都存储在全局/静态存储区
下面的示例有助于我们理解全局/静态存储区和栈区变量的区别,以及 static关键字的作用
#include <stdio.h>
void test1()
{static int b=1;printf("static b = %d\n",b++);
}
void test2()
{int b=1;printf("b = %d\n",b++);
}
void main()
{for(int i=1;i<=3;i++){ test1();test2();}
}
运行结果:
static b = 1
b = 1
static b = 2
b = 1
static b = 3
b = 1
从上面的例子我们不难发现:
1.static关键字修饰的静态局部变量的声明语句只会在第一次执行的时候初始化,之后即使反复执行也不会重新初始化
2.普通的存储在栈上的局部变量每次执行声明/赋值语句都会进行一次初始化
3. static关键字修饰的变量仍然是局部变量,无法从外部调用,也不会和其他作用域的同名变量起冲突
(5)常量存储区
通过#defineconst定义的常量, 源代码中以指针形式定义的字符串,都存储在常量存储区。和主程序的生命周期相同,而且他们的值在编译时已经确定,一旦创建就无法更改(非正常手段除外)。
下面的例子展示了哪些数据会存储在常量存储区
#define PI 3.14
const int P=3
void main()
{
char *s=“hello”;
}
上面声明的变量都存储在常量存储区,其中char指针s本身存储在栈上,他指向的值存放在常量存储区。一个很简单的证据就是*s不可被二次赋值:
附:#define和const的区别:
(1)就起作用的阶段而言: #define是在编译的预处理阶段起作用,而const是在 编译、运行的时候起作用。
(2)就起作用的方式而言:
#define只是简单的字符串替换,没有类型检查。而const有对应的数据类型,是要进行判断的,可以避免一些低级的错误。
(3)就存储方式而言:#define只是进行展开,有多少地方使用,就替换多少次,它定义的宏常量在内存中有若干个备份;const定义的只读变量在程序运行过程中只有一份备份。
(4)从代码调试的方便程度而言: const常量可以进行调试的,define是不能进行调试的,因为在预编译阶段就已经替换掉了。
const优点: (1)const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
(2)有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。
(3)const可节省空间,避免不必要的内存分配,提高效率
引用自: const与#define相比,区别和优点超详解总结!