内存四区模型
内存由四大区域组成
分别是
- 堆区
- 栈区
- 代码区
- 数据区
这里可以参考内存分区模型 -> 内存分区模型
栈区
对于栈区来讲,它是先进后出的。
程序使用前,编译器会根据代码来分配合适的栈空间,程序中的局部变量,返回值,参数等等都会放在栈空间中,编译器也会在程序结束时进行回收,如果栈空间的内存不足,就会导致内存溢出 overflow。
但是栈空间是完全由编译器管理的。
堆区
堆区是程序员自己申请分配和管理的一块内存区域,要分配就要释放。例如一个两个节点的动态链表:
#include <iostream>#include <cstdio>#include <malloc.h>using namespace std;struct LinkedNode {int data;LinkedNode* next;};typedef LinkedNode Node;int main() {Node* p1 = NULL, * p2 = NULL;p1 = (Node*)malloc(sizeof(Node));p2 = (Node*)malloc(sizeof(Node));//Consider p1 and p2 allocate successp1->next = p2;p2->next = NULL;printf("%p\n%p", p1, p2);return 0;}
代码区
数据区
数据区中存放着静态变量,全局变量等。
例如下面这段代码
#include <iostream>#include <cstdio>using namespace std;int g_var = 20;int main() {int a = 1;static int b = 2;const int c = 10;printf("int a = %d, \tadd = %p\n", a,&a);printf("const int c = %d, \tadd = %p\n", c, &c);printf("static int b = %d, \tadd = %p\n", b,&b);printf("globe int g_var = %d,\tadd = %p\n", g_var,&g_var);return 0;}
他的输出结果如下:
int a = 1, add = 00F8F82Cconst int c = 10, add = 00F8F820static int b = 2, add = 0076C008globe int g_var = 20, add = 0076C004
我们发现变量a和变量c是连续的
但是静态变量和局部变量和前者并不连续,因为他们储存在数据区
而数据区分为
Data Segment (数据区)
存放已初始化的全局和静态变量, 常量数据(如字符串常量)。
BSS(Block started by symbol)
存放未初始化的全局和静态变量。(默认设为0)
内存对齐
内存占位
首先以整型和字符型举例
我们通过如下代码知晓了两个数据类型的大小:
int main() {int _intVar;char _charVar;printf("int takes \t%d\n", sizeof(_intVar));printf("char takes \t%d\n", sizeof(_charVar));return 0;}
int takes 4char takes 1
内存对齐
那么有两个结构体定义如下:
struct MemoryAligned {int _intVar01;char _charVar01;char _charVar02;char _charVar03;char _charVar04;};struct MemoryNotAligned {char _charVar01;int _intVar01;char _charVar02;};
在第一个结构体中,定义了5个变量,第二个结构体中只定义了3个,那么按照内存分配来说,第一个占据的内存空间应该比第二个大。
memory aligned takes 8memory not aligned takes 12
但结果却是第二个内存占据了比第一个内存还小的空间。
之所以出现这种现象是因为内存没有对齐
下面我们看这个对齐的示意图:
内存操作
内存的开辟
通过new或者其他内存开辟函数和方法来开辟内存,内存大小可以指定。
例如这个函数可以指定特定的类型和空间大小。
/*被注释掉的部分是C语言风格的,但是C语言并不支持模板和decltype表达式,所以不建议使用*/#include <iostream>#include <malloc.h>using std::cout;using std::endl;template <typename T>T* AllocateBlock(int number){/*T *p = NULL;p = (T*)malloc(sizeof(T)*number);return p;*/T *pArray = new T(number);return pArray;}int main(){int test_type;decltype(test_type) *p = NULL;p = AllocateBlock<decltype(test_type)>(10);//开辟10个test_type类型的内存空间//free(p);delete p;return 0;}
内存的精确写入
如果要在一个块中写入特定的值,并将其通用化。可以使用malloc函数,这是一个底层函数。
/**2021/2/8Phil616@163.com*/#include <iostream>#include <typeinfo>#include <malloc.h>using std::cout;using std::endl;//提供了在特定内存块写入特定值的函数template<typename T>T *TestBlock(T data){T *p = (T*)malloc(sizeof(T));cout << "size of " << typeid(T).name() << " is " << sizeof(T) << endl;*p = data;cout << "block storge " << (int)*p << " at " << &p << endl;return p;}int main(){char test_type;decltype(test_type)*p =NULL;//指定类型p = TestBlock<decltype(test_type)>(0b01000100);//写值free(p);return 0;}
以上函数在调用时的参数是二进制,在内存块中申请了一个字节,这个字节是八个位,八个位写的值非常精确。
内存排布
void showMemoryBytes(void *startADD,int len) {const char *header = "|Address |0x|0b |\n|--------|--|---------|\n";char HexList[16][5] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111","1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"};unsigned char *pStart = (unsigned char *)startADD;printf(header);for (int i = 0; i < len; i++){printf("|%p|%.2x|%s %s|\n", pStart, *pStart,HexList[*pStart/16], HexList[*pStart%16]);pStart++;}}
调用上面的函数可以查看内存的排布情况
