数据类型有很多种,有临时变量,也有静态类型,还有const修饰的常变量类型,等等。这么多钟不同的数据类型,每种数据类型想要完成的功能也有所不同,计算机该如何对于这些不同的数据类型进行管理呢?
如果一股脑将这些数据都放在一块,必然会引起很多问题,存储数据确实很轻松,但想用到其中某个数据呢,从里面找数据会非常麻烦
对此,计算机会根据不同的数据类型将他们存在不同的空间里
首先是临时变量(非静态局部变量),我们都熟知它是存在栈区的,另外,存在于栈区的还有函数的参数以及函数的返回值。
栈区地址的存储规则是,向下增长,所以这里可以看到,先定义的a变量的地址比后定义的b的地址更大。
并且,存储在栈区上的数据一旦出了作用域会立马销毁,如下代码是说明这条规则的有力证据:
当我们不希望某个数据被修改时,可能会在前面加上const修饰,将它定义为常变量,那这种数据又是存在哪里
常变量,难道真的存在于常量区?
错,尽管常变量确实具有常属性,但它的本质还是变量,因为常变量虽然不能直接被修改,但还是能够间接被修改,所以它应该存在于栈区。
接下来探讨静态变量的存储。静态变量顾名思义存在于静态区(数据段)。
此外,全局变量也是存在于静态区,其实也不难理解,静态变量本身就具有全局属性
下面来看这段代码:
int _a = 1;
void Test3()
{
static int a = 1;
static int b = 0;
cout << &_a << endl;
cout << &a << endl;
cout << &b << endl;
}
int _b = 1;
int main()
{
Test3();
cout << &_a << endl;
cout << &_b << endl;
return 0;
}
果然,他们的地址如此相近,虽然不能直接得出他们就一定存在一块空间,但这也是一种体现.
当我们向内存主动申请空间的时候,动态开辟的内存无疑是在堆这个区域内申请的,与栈区不同,堆里面的空间不会自动清理,需要我们主动释放,否则将会导致内存泄漏。(动态内存的管理详见动态内存管理)。
堆区的地址是可以向上增长的。
接着来看下面这个问题:
void Test()
{
int* ptr = (int*)malloc(4*sizeof(int));
}
这里的ptr存在哪里?*ptr又存在哪里?
毋庸置疑,*ptr是ptr指向的内容,是malloc申请出来的空间,*ptr肯定是在堆区。
而ptr只是一个指针变量,它不是申请出来的空间,当然就存储在栈区。
最后就是常量区,主要的存储对象有可执行的代码/只读常量,所有不能通过直接或者间接被修改的,都是常量,存在于常量区。
比如,1,'a' ,1.1 ,"abcd" 等等都是常量。
如下nums,ch1,以及ptr都存在哪里呢?
void Test4()
{
int nums[5] = { 1, 2, 3, 4};
char ch1[] = "hello world";
char *ptr = "abcd";
}
数组名表示首元素地址,nums和ch1分别表示的是1和'h'的地址,而ptr也是指向了"abcd"的首元素地址,可是打印出来的ptr地址却截然不同?
其实这里的nums和ch1并没有直接指向1和'h'的地址,直接用数组+方括号初始化,中间会先做一次拷贝,在栈区创建一个临时数组后,将常量区上的内容拷贝到这个临时数组里面,最后让nums和ch1指向这个数组,所以nums和ch1一定存在栈区上。哪怕就算是*nums和*ch1,他们也都存在于栈区。而ptr就没有那些步骤,直接就指向了静态区里面的"abcd",所以ptr里面存的地址就一定是一个静态区的地址。
另外,还有一个叫做内核空间的区域和一个叫做内存映射段的区域
内核空间定义:内核空间是操作系统内核运行的区域,它包括了操作系统内核代码、数据结构和设备驱动程序等。内核空间通常是操作系统中的一块保护内存区域,只有操作系统内核才能够访问这个区域。
关于内核空间及内存映射段,牵涉到操作系统,当前不做过多说明。