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要放在低地址:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5L80Hfd6-1689750718686)(C:\Users\HUASHUO\AppData\Roaming\Typora\typora-user-images\image-20230715102005060.png)]

  • 而由于栈区的数据是由低地址向高地址访问,因此指针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

可以画出示意图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KVfxM1O3-1689750718686)(C:\Users\HUASHUO\AppData\Roaming\Typora\typora-user-images\image-20230715103849782.png)]

可以得出结论:

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

堆区(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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iq0yxWvD-1689750718687)(C:\Users\HUASHUO\AppData\Roaming\Typora\typora-user-images\image-20230715110940508.png)]

数据段(静态区)

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

代码段

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

关于数据的读取

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

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

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

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

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C++程序内存区域划分主要包括栈区、堆区、全局/静态存储区和常量存储区。 1. 栈区(Stack): - 栈区用于存储局部变量和函数调用时的参数。 - 栈区的内存分配和释放是由系统自动完成的,速度较快。 - 栈区的大小是有限的,一般较小,通常在几MB到几十MB之间。 - 栈区的内存分配是按照“先进后出”的原则进行的,即后进入的变量先释放。 2. 堆区(Heap): - 堆区用于存储动态分配的内存,例如使用new或malloc函数分配的内存。 - 堆区的内存分配和释放需要程序员手动控制。 - 堆区的内存分配速度相对较慢,而且容易产生内存碎片。 - 堆区的大小一般比栈区大得多,可以达到几GB甚至更大。 3. 全局/静态存储区: - 全局/静态存储区用于存储全局变量和静态变量。 - 全局变量在程序的整个生命周期内都存在,静态变量在定义时分配内存,直到程序结束才释放。 - 全局/静态存储区的内存分配和释放由系统自动完成。 4. 常量存储区: - 常量存储区用于存储常量数据,例如字符串常量。 - 常量存储区的内存分配和释放由系统自动完成。 下面是一个C++程序内存区域划分的示例: ```cpp #include <iostream> int globalVariable; // 全局变量 int main() { int localVariable; // 局部变量 int* dynamicVariable = new int; // 动态分配的变量 std::cout << "地址:全局变量 - " << &globalVariable << std::endl; std::cout << "地址:局部变量 - " << &localVariable << std::endl; std::cout << "地址:动态变量 - " << dynamicVariable << std::endl; delete dynamicVariable; // 释放动态分配的内存 return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Forward♞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值