C程序内存布局详解

C程序内存布局详解

1. 内存布局概述

C程序在内存中分为以下几个主要区域(从低地址到高地址):

  • 代码段(.text)
  • 只读数据段(.rodata)
  • 初始化数据段(.data)
  • 未初始化数据段(.bss)
  • 堆(Heap)
  • 栈(Stack)
  • 内核空间(Kernel space)(用户程序不可访问)
    注意:堆和栈之间是未分配的内存空间,两者相向生长。
    在这里插入图片描述

2. 各区域详解

2.1 代码段(Text Segment)

  • 存储内容:程序的可执行指令(机器代码)。
  • 特点:只读、固定大小。
  • 生命周期:程序整个运行期间。
  • 示例
    int main() {return 0;   // main函数的指令存储在此
    }
    

2.2 只读数据段(Read-Only Data Segment)

  • 存储内容:字符串常量、const修饰的全局变量。
  • 特点:只读,任何修改操作会导致段错误(Segmentation Fault)。
  • 生命周期:程序整个运行期间。
  • 示例
    const int max = 100;   // 存储于.rodata
    char *str = "hello";   // 字符串字面量"hello"存储于.rodata
    void foo() {// 错误示例:尝试修改只读数据// str[0] = 'H';   // 运行时错误:段错误
    }
    

2.3 初始化数据段(Initialized Data Segment)

  • 存储内容:已初始化的全局变量和静态变量(非零初始化)。
  • 特点:可读写,程序加载时从可执行文件中读取初始值。
  • 生命周期:程序整个运行期间。
  • 示例
    int global_init = 42;           // 存储于.data
    static int static_init = 10;    // 存储于.data
    int main() {// ...
    }
    

2.4 未初始化数据段(Uninitialized Data Segment / .bss)

  • 存储内容:未初始化或初始化为0的全局变量和静态变量。
  • 特点:可读写,程序加载时由系统初始化为0。
  • 生命周期:程序整个运行期间。
  • 示例
    int global_uninit;         // 存储于.bss,初始化为0
    static int static_uninit;  // 存储于.bss,初始化为0
    int main() {// ...
    }
    

2.5 堆(Heap)

  • 存储内容:动态分配的内存。
  • 特点
    • 手动管理(通过malloccallocrealloc分配,free释放)。
    • 从低地址向高地址增长。
    • 分配速度相对较慢。
  • 生命周期:从分配成功到显式释放。
  • 示例
    int main() {int *arr = (int*)malloc(10 * sizeof(int));  // 在堆上分配if (arr) {arr[0] = 1;   // 合法访问free(arr);    // 显式释放}return 0;
    }
    

2.6 栈(Stack)

  • 存储内容
    • 函数调用时的返回地址
    • 函数参数
    • 局部变量(非静态)
    • 函数调用的上下文
  • 特点
    • 由编译器自动管理(入栈/出栈)。
    • 从高地址向低地址增长。
    • 分配速度快。
    • 大小有限(Linux默认约8MB,可通过ulimit -s查看)。
  • 生命周期:函数调用期间。
  • 示例
    void func(int param) {   // 参数param在栈上int local_var = 10;  // 局部变量在栈上// ...
    }   // 函数结束时,param和local_var自动释放
    int main() {func(5);return 0;
    }
    

3. 内存布局图示

高地址
+-----------------------+
|       内核空间         |
+-----------------------+
|         栈            | <- 由高地址向低地址增长
| (局部变量、函数参数等)  |
+-----------------------+
|          ...          |
+-----------------------+
|         堆            | <- 由低地址向高地址增长
|   (动态分配的内存)     |
+-----------------------+
|         .bss          | (未初始化全局/静态变量)
+-----------------------+
|         .data         | (已初始化全局/静态变量)
+-----------------------+
|        .rodata        | (只读数据)
+-----------------------+
|         .text         | (程序代码)
低地址

在这里插入图片描述
在这里插入图片描述
空洞地址(Hole Address) 通常指虚拟地址空间中未被映射到任何物理内存或存储介质(如磁盘交换区)的地址范围。这些地址属于进程可见的虚拟地址空间,但由于未被操作系统分配或关联到实际的物理资源,无法被进程正常访问。

核心特点:
1,未映射性:空洞地址没有对应的物理内存页或磁盘块,操作系统的内存管理单元(MMU)无法将其转换为物理地址。
2,访问受限:进程若尝试读取或写入空洞地址,会触发内存访问错误(如 Linux 中的SIGSEGV信号),导致进程终止(俗称 “段错误”)。
3,存在的合理性:,空洞地址并非 “浪费”,而是操作系统设计中用于隔离内存区域的常见手段。例如:
(1)进程的代码段、数据段、堆、栈等区域之间通常存在空洞,防止不同区域的越界访问相互干扰;
(2)32 位系统中,用户空间与内核空间之间可能保留大量未映射的地址作为隔离带;
(3)动态内存分配(如malloc)后,堆的增长可能与栈的扩展之间形成临时空洞。

4. 关键注意事项

4.1 静态局部变量的存储位置

静态局部变量虽然作用域在函数内,但存储在.data.bss段(根据是否初始化):

void counter() {static int count = 0;  // 存储在.data(因为初始化了)count++;
}

4.2 指针与内存区域

指针变量本身存储在:

  • 全局指针:.data.bss
  • 静态指针:.data.bss
  • 局部指针:栈上
  • 动态分配指针:堆上(指针变量在栈,指向的内容在堆)

4.3 常见错误

  1. 栈溢出(Stack Overflow)
    void recursion() {int arr[10000];  // 大数组占用栈空间recursion();     // 无限递归
    }
    
  2. 堆内存泄漏(Memory Leak)
    void leak() {malloc(100);   // 分配后未释放,且丢失了指针
    }
    
  3. 野指针(Dangling Pointer)
    int* dang() {int local = 10;return &local;   // 返回局部变量地址(函数结束即失效)
    }
    
  4. 重复释放(Double Free)
    int *p = malloc(sizeof(int));
    free(p);
    free(p);   // 错误:重复释放
    

5. 验证内存布局的示例程序

#include <stdio.h>
#include <stdlib.h>
const int const_global = 10;      // .rodata
int init_global = 20;             // .data
int uninit_global;                // .bss
int main() {static int static_init = 30;   // .datastatic int static_uninit;      // .bssint local_var;                 // 栈int *heap_var = malloc(10);    // 堆printf("代码段(.text):   %p\n", main);printf("只读段(.rodata): %p\n", &const_global);printf("数据段(.data):   %p (init_global)\n", &init_global);printf("数据段(.data):   %p (static_init)\n", &static_init);printf("BSS段(.bss):     %p (uninit_global)\n", &uninit_global);printf("BSS段(.bss):     %p (static_uninit)\n", &static_uninit);printf("堆(Heap):        %p\n", heap_var);printf("栈(Stack):       %p\n", &local_var);free(heap_var);return 0;
}

运行此程序可以观察到各变量的地址分布,验证内存布局。

6. 总结

理解C程序内存布局对于:

  • 避免内存错误(泄漏、越界、野指针)
  • 优化程序性能(缓存友好性)
  • 理解程序运行机制
    至关重要。务必掌握各区域的特点及生命周期,并在编程中谨慎处理动态内存和指针。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/90695.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/90695.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

新手向:Git下载全攻略

Git 的安装与重要性在现代软件开发中&#xff0c;版本控制是必不可少的工具&#xff0c;而 Git 是目前最流行的分布式版本控制系统。无论是个人开发者还是大型团队&#xff0c;Git 都能高效管理代码变更&#xff0c;确保项目历史清晰可追溯。安装 Git 是开发者入门的第一步&…

linux中如何清除history命令

写在前面 使用ssh远程连接客户端连接上linux后操作的命令多了&#xff0c;有时候需要清除对应的历史命令记录&#xff0c;可以通过下面几种方式实现。第一种方法 通过修改.bash_history文件 这是最简单直接的方法&#xff0c;但是只会影响当前用户的历史记录。执行以下命令即可…

PHP插件开发中的一个错误:JSON直接输出导致网站首页异常

问题描述 最近在使用步数统计插件&#xff08;WeFootStep&#xff09;时&#xff0c;发现网站首页完全变成了一段JSON数据&#xff0c;而不是正常的HTML页面。具体表现为首页显示如下内容&#xff1a; {"results":"<li><a href\"https:\/\/blog…

落霞归雁的思维框架:十大经典思维工具的源头活水

在当今复杂多变的世界中&#xff0c;思维框架成为了解决问题、优化决策和提升效率的重要工具。提到思维框架&#xff0c;人们往往会想到那些被广泛认可和应用的十大经典思维工具&#xff1a;金字塔原理、黄金圈法则、5W1H分析法、SWOT分析、SCQA模型、STAR法则、PDCA循环、六顶…

spring Could 高频面试题

一、基础概念Spring Cloud 的核心组件有哪些&#xff1f; 答案&#xff1a;Eureka/Nacos&#xff08;服务注册发现&#xff09;、Ribbon/LoadBalancer&#xff08;负载均衡&#xff09;、Feign/OpenFeign&#xff08;声明式HTTP客户端&#xff09;、Hystrix/Sentinel&#xff0…

从零开始的云计算生活——番外6,使用zabbix对中间件监控

目录 一.网络设备监控 1、GNS模拟器的使用 创建路由 创建交换机 2.构建网络 3.添加Cisco路由器的监控 二.中间件监控 1、MySQL数据库监控 1.1、拷贝自定义的监控脚本到指定目录 1.2、添加监控用户 1.3、重启zabbix-agent服务 1.4、在zabbix-server服务端测试数据 1…

haproxy七层均衡

一.haproxy的安装和服务信息1.1实验环境ip实验设备172.25.254.100haproxy172.25.254.10RS1172.25.254.20RS2172.25.254.111client1.2软件安装及配置haproxy主机上配置#下载#进入此文件进行编辑#关闭防火墙RS1主机上配置#下载#生成默认文件#重启#关闭防火墙RS2主机上配置#下载#生…

分类预测 | MATLAB实现CPO-SVM冠豪猪算法优化支持向量机分类预测

分类预测 | MATLAB实现CPO-SVM冠豪猪算法优化支持向量机分类预测 目录 分类预测 | MATLAB实现CPO-SVM冠豪猪算法优化支持向量机分类预测 分类效果 基本介绍 算法步骤 参数设定 运行环境 应用场景 程序设计 参考资料 分类效果 基本介绍 该MATLAB代码实现了基于冠豪猪优化算法(…

【MySQL 数据库】MySQL基本查询(第二节)

文章目录&#x1f4dd;Update&#x1f309; 将孙悟空同学的数学成绩变更为 80 分&#x1f309; 将曹孟德同学的数学成绩变更为60分&#xff0c;语文成绩变更为70分&#x1f309; 将总成绩倒数前三的3位同学的数学成绩加上30分&#x1f309;将所有同学的语文成绩更新为原来的2倍…

Axios 响应拦截器

1.定义&#xff1a;响应拦截器&#xff08;Response Interceptor&#xff09;是一个可以在 axios 接收到服务器响应后&#xff0c;响应数据交给 .then() 处理之前执行的函数。你可以用它来统一处理响应数据&#xff0c;进行错误处理&#xff0c;或者对返回的数据做格式化和转换…

k8s的nodeport和ingress

1.流量转发图targerport转发到实际的容器端口containerPort&#xff08;后端端口&#xff09;nodeportingress2.配置场景总结字段作用对象必填示例值何时配置containerPort容器否80需明确记录容器端口时&#xff08;推荐&#xff09;targetPortPod是80定义 Service 转发规则时p…

VLA:自动驾驶的“新大脑”?

&#x1f525; 什么是 VLA&#xff1f;为什么突然火了&#xff1f;在自动驾驶圈子里&#xff0c;最近一个词特别火&#xff1a;VLA。它不是某个新车的型号&#xff0c;也不是某家公司的新品牌&#xff0c;而是一种全新的智能架构&#xff0c;被称为“自动驾驶的大脑2.0”。&…

Linux操作系统之线程(八):信号量sem

前言&#xff1a;大家好啊&#xff0c;我们上一篇文章已经讲解了关于线程同步的一种办法&#xff1a;运用条件变量cond。今天&#xff0c;我们就来学习一下线程同步的另外一种方法&#xff0c;信号量&#xff01;&#xff01;信号量呢有System V 信号量与POSIX 信号量&#xff…

【RocketMQ】一分钟了解RocketMQ

MQ是什么 MQ全称为Message Queue&#xff0c;即消息队列 &#xff0c;是一种提供消息队列服务的中间件&#xff0c;也称为消息中间件&#xff0c;是一套提供了消息生 产、存储、消费全过程的软件系统&#xff0c;遵循FIFO原则。 MQ的好处有哪些 异步解耦 最常见的一个场景是…

01 01 01 第一部分 C++编程知识 C++入门 第一个C++程序

第一部分 C编程知识第一章 C入门 —— 第一个C程序一、第一个C程序代码展示//写一个C程序&#xff0c;实现在屏幕上打印 “hello world” #include <iostream> using namespace std; int main() {cout << "hello world" << endl;return 0; }二、…

进制定义与转换详解

文章目录&#x1f4d8; 进制定义与转换详解一、进制的含义二、常见进制介绍1. 十进制&#xff08;Decimal&#xff0c;Base-10&#xff09;2. 二进制&#xff08;Binary&#xff0c;Base-2&#xff09;3. 八进制&#xff08;Octal&#xff0c;Base-8&#xff09;4. 十六进制&am…

【安卓笔记】用MVC、MVP、MVVM来实现井字棋案例

0. 环境&#xff1a;电脑&#xff1a;Windows10Android Studio: 2024.3.2编程语言: JavaGradle version&#xff1a;8.11.1Compile Sdk Version&#xff1a;35Java 版本&#xff1a;Java111. 首先、简单实现井字棋的功能。功能拆解&#xff1a;1. 棋盘为3x32. 点击棋盘button&a…

【洛谷】单向链表、队列安排、约瑟夫问题(list相关算法题)

文章目录单向链表题目描述题目解析代码队列安排题目描述题目解析代码约瑟夫问题题目描述题目解析代码单向链表 题目描述 题目解析 这道题因为有大量的任意位置插入删除&#xff0c;所以肯定不能用数组&#xff0c;用链表是最合适的&#xff0c;而在算法竞赛通常都用静态链表&a…

当人机交互迈向新纪元:脑机接口与AR/VR/MR的狂飙之路

从手机到 “头盔”&#xff1a;交互终端的变革猜想​​在当今数字化时代&#xff0c;智能手机无疑是我们生活中不可或缺的一部分。它集通讯、娱乐、办公等多种功能于一身&#xff0c;成为了人们与外界交互的主要窗口。然而&#xff0c;随着科技的飞速发展&#xff0c;智能手机作…

InfluxDB HTTP API 接口调用详解(二)

实际应用案例演示 1. 数据写入案例 假设在一个物联网设备数据采集场景中&#xff0c;有多个传感器设备持续采集环境的温度和湿度数据。我们以 Python 语言为例&#xff0c;使用requests库来调用 InfluxDB 的 Write 接口将数据写入 InfluxDB。 首先&#xff0c;确保已经安装了…