当前位置: 首页 > news >正文

C语言内存讲解-详说内存分布和heap空间

目录

内存分布

变量

内存4区模型

开辟释放 heap 空间

使用 heap 空间

二级指针对应的 heap空间


内存分布

程序没有加载到内存前,可执行程序内部已经分好3段信息,分别为代码区(text)、数据区(data)和未初始化数据区(bss)3 个部分(有些人直接把data和bss合起来叫做静态区或全局区)。

代码区

存放 CPU 执行的机器指令。通常代码区是可共享的(即另外的执行程序可以调用它),使其可共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。代码区通常是只读的,使其只读的原因是防止程序意外地修改了它的指令。另外,代码区还规划了局部变量的相关信息。

全局初始化数据区/静态数据区(data段)

该区包含了在程序中明确被初始化的全局变量、已经初始化的静态变量(包括全局静态变量和局部静态变量)和常量数据(如字符串常量)。

未初始化数据区(又叫 bss 区)

存入的是全局未初始化变量和未初始化静态变量。未初始化数据区的数据在程序开始执行之前被内核初始化为 0 或者空(NULL)。

程序在加载到内存前,代码区和全局区(data和bss)的大小就是固定的,程序运行期间不能改变。然后,运行可执行程序,系统把程序加载到内存,除了根据可执行程序的信息分出代码区(text)、数据区(data)和未初始化数据区(bss)之外,还额外增加了栈区、堆区

代码区(text segment)

加载的是可执行文件代码段,所有的可执行代码都加载到代码区,这块内存是不可以在运行期间修改的。

未初始化数据区(BSS)

加载的是可执行文件BSS段,位置可以分开亦可以紧靠数据段,存储于数据段的数据(全局未初始化,静态未初始化数据)的生存周期为整个程序运行过程。

全局初始化数据区/静态数据区(data segment)

加载的是可执行文件数据段,存储于数据段(全局初始化,静态初始化数据,文字常量(只读))的数据的生存周期为整个程序运行过程。

栈区(stack)

栈是一种先进后出的内存结构,由编译器自动分配释放,存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放,因此,局部变量的生存周期为申请到释放该段栈空间。

堆区(heap)

堆是一个大容器,它的容量要远远大于栈,但没有栈那样先进后出的顺序。用于动态内存分配。堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。

变量

局部变量:

 概念:定义在函数内部的变量。

 作用域:从定义位置开始,到包裹该变量的第一个右大括号结束。

 生命周期:局部变量:从变量定义开始,函数调用完成。 --- 函数内部。

全局变量:

概念:定义在函数 外 部的变量。

作用域:从定义位置开始,默认到本文件内部。 其他文件如果想使用,可以通过声明方式将作用域导出。

生命周期: 程序启动开始,程序终止结束。  --- 程序执行期间。


static全局变量:

定义语法: 在全局变量定义之前添加 static 关键字。        static int a = 10;

作用域:被限制在本文件内部,不允许通过声明导出到其他文件。

生命周期:程序启动开始,程序终止结束。  --- 程序执行期间。


static局部变量:

 定义语法: 在局部变量定义之前添加 static 关键字。

 特性: 静态局部变量只定义一次。在全局位置。 通常用来做计数器。

 作用域:从定义位置开始,到包裹该变量的第一个右大括号结束。

 生命周期:程序启动开始,程序终止结束。  --- 程序执行期间

全局函数:  函数

    定义语法: 函数原型 + 函数体

    生命周期:程序启动开始,程序终止结束。  --- 程序执行期间。

static函数:

   定义语法:static + 函数原型 + 函数体

   static 函数 只能在 本文件内部使用。 其他文件即使声明也无效。

  生命周期:程序启动开始,程序终止结束。  --- 程序执行期间。
 

内存4区模型

代码段:.text段。 程序源代码(二进制形式)。

数据段:只读数据段 .rodata段。初始化数据段 .data段。 未初始化数据段 .bss 段。

 stack:栈。 在其之上开辟 栈帧。    windows 1M --- 10M    Linux: 8M --- 16M

  

 heap:堆。 给用户自定义数据提供空间。 约 1.3G+
 

当全局变量与局部变量命名冲突时采用就近原则

开辟释放 heap 空间

void *malloc(size_t size);  申请 size 大小的空间

        返回实际申请到的内存空间首地址。 【我们通常拿来当数组用】

 void free(void *ptr);    释放申请的空间

        参数: malloc返回的地址值。


使用 heap 空间

    空间时连续。 当成数组使用。

    free后的空间,不会立即失效。 通常将free后的 地址置为NULL。

    free 地址必须 是 malloc申请地址。否则出错。

    如果malloc之后的地址一定会变化,那么使用临时变量tmp 保存。
 

代码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

int main()
{
	//int arr[1000000] = {10, 20, 40};
	int *p = (int *)malloc(sizeof(int) * 10);
	//char *str = (char *)malloc(sizeof(char)*10);
	if (p == NULL)
	{
		printf("malloc error\n");
		return -1;
	}
	char *tmp = p;  // 记录malloc返回的地址值。用于free

	// 写数据到 malloc 空间。
	for (size_t i = 0; i < 10; i++)
	{
		p[i] = i + 10;
	}
	// 读出malloc空间中的数据
	//for (size_t i = 0; i < 10; i++)
	//{
	//	printf("%d ", *(p+i));
	//}
	for (size_t i = 0; i < 10; i++)
	{
		printf("%d ", *p);
		p++;
	}

	// 释放申请的内存。
	free(tmp);
	p = NULL;

	system("pause");
	return EXIT_SUCCESS;
}

二级指针对应的 heap空间

 申请外层指针: char **p = (char **)malloc(sizeof(char *) * 5);

 申请内层指针:

 for(i = 0; i < 5; i++)
 {
     p[i] = (char *)malloc(sizeof(char) *10);
 }

    使用: 不能修改 p 的值。

 for(i = 0; i < 5; i++)
 {
     strcpy(p[i], "helloheap");
 }

    释放内层:

 for(i = 0; i < 5; i++)
 {
     free(p[i]);
 }

    释放外层:

        free(p);

相关文章:

  • SpringBoot电商项目前后端界面搭建
  • Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife
  • 【C++学习】string的使用
  • WeMos Mini ESP32-S2FN4R2介绍
  • Halcon图像分割总结
  • 5 h0255. 迷宫问题,6 h0253. 鸣人和佐助(广度优先搜索)
  • 《数据结构》堆栈(铁路、洗牌、汉诺塔、走迷宫)全解析
  • 基于时序行为的协同过滤推荐算法(Python)
  • Vue--》计算属性与监视(侦听)属性的使用
  • 【状语从句练习题】because / because of / although / in spite of
  • Web前端开发基础教程二
  • MyBatis-写分页的几种方法,怎么写分页最简单
  • 狗厂员工来面试本想难为一下,结果被虐得连console.log也不敢写了
  • Pytorch(Tensor)-Numpy(ndarrays) API对照表
  • RK3399驱动开发 | 14 - AP6255 SDIO WiFi 调试(基于linux5.4.32内核)
  • 【附node操作实例】redis简明入门系列—字符串类型
  • android高仿小视频、应用锁、3种存储库、QQ小红点动画、仿支付宝图表等源码...
  • extract-text-webpack-plugin用法
  • laravel 用artisan创建自己的模板
  • miaov-React 最佳入门
  • Mysql数据库的条件查询语句
  • Otto开发初探——微服务依赖管理新利器
  • TCP拥塞控制
  • 从零开始在ubuntu上搭建node开发环境
  • 分布式任务队列Celery
  • 基于axios的vue插件,让http请求更简单
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用(一)
  • 如何合理的规划jvm性能调优
  • 如何选择开源的机器学习框架?
  • 世界上最简单的无等待算法(getAndIncrement)
  • 写代码的正确姿势
  • Spring第一个helloWorld
  • 带你开发类似Pokemon Go的AR游戏
  • ​决定德拉瓦州地区版图的关键历史事件
  • ​人工智能书单(数学基础篇)
  • ###C语言程序设计-----C语言学习(6)#
  • #include到底该写在哪
  • #mysql 8.0 踩坑日记
  • #NOIP 2014# day.1 生活大爆炸版 石头剪刀布
  • ${ }的特别功能
  • $NOIp2018$劝退记
  • (¥1011)-(一千零一拾一元整)输出
  • (1)安装hadoop之虚拟机准备(配置IP与主机名)
  • (20)目标检测算法之YOLOv5计算预选框、详解anchor计算
  • (C++17) optional的使用
  • (zhuan) 一些RL的文献(及笔记)
  • (草履虫都可以看懂的)PyQt子窗口向主窗口传递参数,主窗口接收子窗口信号、参数。
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (理论篇)httpmoudle和httphandler一览
  • (一)插入排序
  • (转载)CentOS查看系统信息|CentOS查看命令
  • (轉貼) VS2005 快捷键 (初級) (.NET) (Visual Studio)
  • *上位机的定义
  • .bat批处理(一):@echo off