内存四区模型

内存由四大区域组成
分别是

  • 堆区
  • 栈区
  • 代码区
  • 数据区

这里可以参考内存分区模型 -> 内存分区模型
程序内存.svg

栈区

对于栈区来讲,它是先进后出的。
程序使用前,编译器会根据代码来分配合适的栈空间,程序中的局部变量,返回值,参数等等都会放在栈空间中,编译器也会在程序结束时进行回收,如果栈空间的内存不足,就会导致内存溢出 overflow。
但是栈空间是完全由编译器管理的。
栈区.svg

堆区

堆区是程序员自己申请分配和管理的一块内存区域,要分配就要释放。例如一个两个节点的动态链表:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <malloc.h>
  4. using namespace std;
  5. struct LinkedNode {
  6. int data;
  7. LinkedNode* next;
  8. };
  9. typedef LinkedNode Node;
  10. int main() {
  11. Node* p1 = NULL, * p2 = NULL;
  12. p1 = (Node*)malloc(sizeof(Node));
  13. p2 = (Node*)malloc(sizeof(Node));
  14. //Consider p1 and p2 allocate success
  15. p1->next = p2;
  16. p2->next = NULL;
  17. printf("%p\n%p", p1, p2);
  18. return 0;
  19. }

它的示意图如下:
堆区.svg

代码区

代码区涉及汇编,暂不讨论

数据区

数据区中存放着静态变量,全局变量等。
例如下面这段代码

  1. #include <iostream>
  2. #include <cstdio>
  3. using namespace std;
  4. int g_var = 20;
  5. int main() {
  6. int a = 1;
  7. static int b = 2;
  8. const int c = 10;
  9. printf("int a = %d, \tadd = %p\n", a,&a);
  10. printf("const int c = %d, \tadd = %p\n", c, &c);
  11. printf("static int b = %d, \tadd = %p\n", b,&b);
  12. printf("globe int g_var = %d,\tadd = %p\n", g_var,&g_var);
  13. return 0;
  14. }

他的输出结果如下:

  1. int a = 1, add = 00F8F82C
  2. const int c = 10, add = 00F8F820
  3. static int b = 2, add = 0076C008
  4. globe int g_var = 20, add = 0076C004

我们发现变量a和变量c是连续的
但是静态变量和局部变量和前者并不连续,因为他们储存在数据区
而数据区分为
Data Segment (数据区)
存放已初始化的全局和静态变量, 常量数据(如字符串常量)。
BSS(Block started by symbol)
存放未初始化的全局和静态变量。(默认设为0)

内存对齐

内存占位

首先以整型和字符型举例
我们通过如下代码知晓了两个数据类型的大小:

  1. int main() {
  2. int _intVar;
  3. char _charVar;
  4. printf("int takes \t%d\n", sizeof(_intVar));
  5. printf("char takes \t%d\n", sizeof(_charVar));
  6. return 0;
  7. }
  1. int takes 4
  2. char takes 1

内存对齐

那么有两个结构体定义如下:

  1. struct MemoryAligned {
  2. int _intVar01;
  3. char _charVar01;
  4. char _charVar02;
  5. char _charVar03;
  6. char _charVar04;
  7. };
  8. struct MemoryNotAligned {
  9. char _charVar01;
  10. int _intVar01;
  11. char _charVar02;
  12. };

在第一个结构体中,定义了5个变量,第二个结构体中只定义了3个,那么按照内存分配来说,第一个占据的内存空间应该比第二个大。

  1. memory aligned takes 8
  2. memory not aligned takes 12

但结果却是第二个内存占据了比第一个内存还小的空间。
之所以出现这种现象是因为内存没有对齐
下面我们看这个对齐的示意图:
内存对齐.svg

内存操作

内存的开辟

通过new或者其他内存开辟函数和方法来开辟内存,内存大小可以指定。
例如这个函数可以指定特定的类型和空间大小。

  1. /*被注释掉的部分是C语言风格的,但是C语言并不支持模板和decltype表达式,所以不建议使用*/
  2. #include <iostream>
  3. #include <malloc.h>
  4. using std::cout;
  5. using std::endl;
  6. template <typename T>
  7. T* AllocateBlock(int number){
  8. /*
  9. T *p = NULL;
  10. p = (T*)malloc(sizeof(T)*number);
  11. return p;
  12. */
  13. T *pArray = new T(number);
  14. return pArray;
  15. }
  16. int main(){
  17. int test_type;
  18. decltype(test_type) *p = NULL;
  19. p = AllocateBlock<decltype(test_type)>(10);//开辟10个test_type类型的内存空间
  20. //free(p);
  21. delete p;
  22. return 0;
  23. }

内存的精确写入

如果要在一个块中写入特定的值,并将其通用化。可以使用malloc函数,这是一个底层函数。

  1. /**
  2. 2021/2/8
  3. Phil616@163.com
  4. */
  5. #include <iostream>
  6. #include <typeinfo>
  7. #include <malloc.h>
  8. using std::cout;
  9. using std::endl;
  10. //提供了在特定内存块写入特定值的函数
  11. template<typename T>
  12. T *TestBlock(T data){
  13. T *p = (T*)malloc(sizeof(T));
  14. cout << "size of " << typeid(T).name() << " is " << sizeof(T) << endl;
  15. *p = data;
  16. cout << "block storge " << (int)*p << " at " << &p << endl;
  17. return p;
  18. }
  19. int main(){
  20. char test_type;
  21. decltype(test_type)*p =NULL;//指定类型
  22. p = TestBlock<decltype(test_type)>(0b01000100);//写值
  23. free(p);
  24. return 0;
  25. }

以上函数在调用时的参数是二进制,在内存块中申请了一个字节,这个字节是八个位,八个位写的值非常精确。

内存排布

  1. void showMemoryBytes(void *startADD,int len) {
  2. const char *header = "|Address |0x|0b |\n|--------|--|---------|\n";
  3. char HexList[16][5] = {
  4. "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111",
  5. "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"
  6. };
  7. unsigned char *pStart = (unsigned char *)startADD;
  8. printf(header);
  9. for (int i = 0; i < len; i++)
  10. {
  11. printf("|%p|%.2x|%s %s|\n", pStart, *pStart,HexList[*pStart/16], HexList[*pStart%16]);
  12. pStart++;
  13. }
  14. }

调用上面的函数可以查看内存的排布情况