C/C++程序内存区域划分以及各区域的介绍

简介: C/C++程序内存区域划分以及各区域的介绍

C/C++程序内存区域划分

直接上图:

在这里插入图片描述

注:以下的说明均已VS2019为例

栈区(stack)

在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元会自动释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存空间有限。栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等

在栈空间存储数据时地址的变化

栈空间遵循“先入后出”原则。

栈区的数据是从高地址向低地址存储的,最新压入栈的数据位于栈底(高地址),而最早压入栈的数据位于栈顶(低地址)。

我们以一个具体的例子来进行说明:

#include<stdio.h>
#include<stdlib.h>
int main()
{
  char a = 10;
  char b = 10;
  printf("%p\n", &a);
  printf("%p\n", &b);
  return 0;
}

output

008FFA3B
008FFA2F
  • 局部变量a先于变量b存入栈区,因此a的地址要高于b的地址

在栈空间读取数据时地址的变化

当读取栈区的数据时,是从低地址向高地址访问,也就是从栈顶向栈底读取数据。

首先我们要明白,在VS2019中,栈区中的数据是以小端字节序存储的,还不清楚的小伙伴看这里👉整数在内存中的存储

我们以一个具体的例子来进行说明:

#include<stdio.h>
#include<stdlib.h>
int main()
{
  int num = 0x11223344;
  char* pc = &num;
  printf("%x\n", *pc);
  return 0;
}

output

44
  • int型num占4个字节,由于采用小端存储,低位数据44要放在低地址:

  • 而由于栈区的数据是由低地址向高地址访问,因此指针pc指向的应该是变量num的低位字节,管理一个字节,因此打印44

数组在栈区的存储

#include<stdio.h>
#include<stdlib.h>
int main()
{
  int num1[5] = { 0 };
  for (int i = 0; i < 5; i++)
    printf("%p\n", &num1[i]);
  printf("\n");
  int num2[5] = { 0 };
  for (int i = 0; i < 5; i++)
    printf("%p\n", &num2[i]);
  return 0;
}

output:

0113FE08
0113FE0C
0113FE10
0113FE14
0113FE18
0113FDE0
0113FDE4
0113FDE8
0113FDEC
0113FDF0

可以画出示意图:

可以得出结论:

随着下标的增长,数组元素的地址逐渐加大

堆区(heap)

堆区一般存储的是动态内存如由malloc,calloc,realloc动态开辟的空间。这些空间一般由程序员分配释放,若不释放,程序结束时可能由OS回收,但若程序没有结束,则会造成内存泄漏,因此动态开辟的空间一定要记得free释放。内存分配的方式类似于链表

在堆区存储数据时地址的变化

通常情况下,堆区的地址分配是从低地址到高地址进行的,即在堆区中分配的内存空间的地址是逐渐增大的。这是因为堆区是通过动态内存分配来实现的,通常使用链表或类似的数据结构来管理已分配和未分配的内存块。新分配的内存块会被添加到链表的末尾,因此地址是递增的。

例如:

#include<stdio.h>
#include<stdlib.h>
int main()
{
  int* arr1 = (int*)malloc(sizeof(int) * 5);
  if (NULL == arr1)
  {
    perror("malloc");
    return 1;
  }
  int* arr2 = (int*)malloc(sizeof(int) * 5);
  if (NULL == arr2)
  {
    perror("malloc");
    return 1;
  }
  printf("%p\n",arr1);
    printf("%p\n",arr2);
  free(arr1);
  free(arr2);
  arr1 = NULL;
  arr2 = NULL;
  return 0;
}

output

00ED1E28
00ED1E68

可以看到后存入的数据地址更大

在堆区读取数据时地址的变化

在堆区中读取数据时,并没有固定的规定要求从高地址到低地址读取。可以通过指针来访问堆区中的数据,可以根据需要进行读取和修改操作,而不是受地址增长方向的限制。

数组在堆区的存储

#include<stdio.h>
#include<stdlib.h>
int main()
{
  int* num1 = (int*)malloc(sizeof(int) * 5);
  if (NULL == num1)
  {
    perror("malloc");
    return 1;
  }
  for (int i = 0; i < 5; i++)
    printf("%p\n", &num1[i]);
  printf("\n");
  int* num2 = (int*)malloc(sizeof(int) * 5);
  if (NULL == num2)
  {
    perror("malloc");
    return 1;
  }
  for (int i = 0; i < 5; i++)
    printf("%p\n", &num2[i]);
  free(num1);
  free(num2);
  num1 = NULL;
  num2 = NULL;
  return 0;
}

output

00ED1E28
00ED1E2C
00ED1E30
00ED1E34
00ED1E38
00ED1E68
00ED1E6C
00ED1E70
00ED1E74
00ED1E78

可以画出示意图(注意比较和栈区的区别):

数据段(静态区)

(static)存放全局变量、静态数据。程序结束后(不是函数结束)由系统释放

代码段

存放函数体(类成员函数和全局函数)的二进制代码以及只读常量。

关于数据的读取

在计算机系统中,数据的存储和读取通常是以字节为单位进行的。对于单个字节的数据,无论是在堆区、栈空间还是其他存储区域,数据的存储和读取都是从低位(低地址)向高位(高地址)进行的

在C语言中,数据类型的存储和读取也遵循这个原则。例如,对于整型数据类型(如int、long等),它们通常占用多个字节的内存空间,其中每个字节都按照从低位到高位的顺序存储数据。

当我们读取一个整型变量时,计算机会从最低有效字节开始,按照地址逐字节读取数据,然后根据数据类型的大小将其合并为一个完整的值。这种方式被称为"小端字节序"(Little-Endian),它是目前主流的处理器架构所采用的方式。

需要注意的是,在特定的硬件平台或编译器中,也可能存在"大端字节序"(Big-Endian)的方式。在大端字节序中,数据的存储和读取是从高位到低位进行的。但是,大端字节序相对较少见,小端字节序是C语言中的常见规范。

综上所述,一般情况下,在计算机系统中,数据的存储和读取是从低位向高位进行的,即从低地址到高地址进行。这是C语言和许多常见的计算机系统所采用的数据存储方式。

相关文章
|
24天前
|
存储 缓存 编译器
【硬核】C++11并发:内存模型和原子类型
本文从C++11并发编程中的关键概念——内存模型与原子类型入手,结合详尽的代码示例,抽丝剥茧地介绍了如何实现无锁化并发的性能优化。
|
5天前
|
存储 程序员 编译器
什么是内存泄漏?C++中如何检测和解决?
大家好,我是V哥。内存泄露是编程中的常见问题,可能导致程序崩溃。特别是在金三银四跳槽季,面试官常问此问题。本文将探讨内存泄露的定义、危害、检测方法及解决策略,帮助你掌握这一关键知识点。通过学习如何正确管理内存、使用智能指针和RAII原则,避免内存泄露,提升代码健壮性。同时,了解常见的内存泄露场景,如忘记释放内存、异常处理不当等,确保在面试中不被秒杀。最后,预祝大家新的一年工作顺利,涨薪多多!关注威哥爱编程,一起成为更好的程序员。
|
3月前
|
NoSQL 测试技术
内存程序崩溃
【10月更文挑战第13天】
154 62
|
2月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
80 1
|
2月前
|
存储 缓存 C语言
【c++】动态内存管理
本文介绍了C++中动态内存管理的新方式——`new`和`delete`操作符,详细探讨了它们的使用方法及与C语言中`malloc`/`free`的区别。文章首先回顾了C语言中的动态内存管理,接着通过代码实例展示了`new`和`delete`的基本用法,包括对内置类型和自定义类型的动态内存分配与释放。此外,文章还深入解析了`operator new`和`operator delete`的底层实现,以及定位new表达式的应用,最后总结了`malloc`/`free`与`new`/`delete`的主要差异。
65 3
|
2月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
199 4
|
2月前
|
存储 缓存 Java
结构体和类在内存管理方面的差异对程序性能有何影响?
【10月更文挑战第30天】结构体和类在内存管理方面的差异对程序性能有着重要的影响。在实际编程中,需要根据具体的应用场景和性能要求,合理地选择使用结构体或类,以优化程序的性能和内存使用效率。
|
3月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
3月前
|
存储 C语言 C++
【C++打怪之路Lv6】-- 内存管理
【C++打怪之路Lv6】-- 内存管理
63 0
【C++打怪之路Lv6】-- 内存管理
|
3月前
|
C++
C/C++内存管理(下)
C/C++内存管理(下)
57 0