c语言 - 程序内存分布

c语言 - 程序内存分布,了解一个c程序在运行过程中,各个部分的内存是怎么分布的,都有什么特点

内存分布

C语言程序内存分布图

  • text代码段,存放程序的二进制代码、字面量(整数常量浮点常量字符常量字符串字面值枚举常量),该区域的大小在编译期间即可确定,在运行期间通常是只读的;
  • data存放已初始化且初始值非零的静态变量的全局数据段,静态变量在进程启动时被依次赋值,在进程结束时被系统释放,该区域的大小在编译期间即可确定,在运行期间是可读写的,但是整个 data 段的大小是固定的;
  • bss存放未初始化或初始值为零的静态变量的全局数据段,进程启动时会将该区域全部赋予 0 值,在进程结束时被系统释放,该区域的大小在编译期间即可确定,在运行期间是可读写的,但是整个 bss 段的大小是固定的;
  • heap堆区存放进程在运行期间动态申请的内存段,大小是不固定的,可动态扩张和缩减,使用 malloc 等函数分配的内存就在堆上,使用完后可以使用 free 函数来主动释放申请的内存,如果不释放,则在进程结束时被系统自动释放;
  • stack栈区存放函数的局部变量、函数的参数等值,每个函数栈帧的大小在编译期间是确定的,但是整个栈区的大小是不定的,不过操作系统对于每个栈的大小是有限制的,在 Linux 中一般为 8192 KB,也就是 8 MB,如果需要可以适当调高(使用 ulimit 命令)。

在多线程环境中:

  • textdatabssheap都是线程间共享的,如果对该区域的数据的访问顺序敏感,则需要采取相应的措施,如使用互斥锁,来避免出现脏数据问题;
  • stack是每个线程私有的,每个线程都会有一个独立的 stack 区域,每个 stack 区域都拥有一样的大小限制(即操作系统对栈的大小限制)。

值类型、指针类型
无论是 C、C++ 还是 Java,都只存在两大类数据类型,即值类型(基本类型)指针类型(引用类型),但是在内存中,它们都是一样的,都是二进制数值,也就是一个整数。

比如,int就是一个 4 字节的数字、char就是一个 1 字节的数字,而指针类型int *则是一个 8 字节的数字,只不过这个数字是一个内存地址,我们得到这个地址后,会从该地址所指的内存区域中读取 4 个字节的数据,这个数据(int 整型)才是我们要的真正数据。

对数据类型的理解
在 C 语言中,定义一个变量必须指明数据类型,比如:short短整型、int整型、long长整型、float单精度浮点型、double双精度浮点型、int *整型指针、char *字符指针、void *裸指针(无具体类型)。

但是,这些所谓的数据类型都是写给编译器看的,在内存中,并不存在所谓的数据类型,比如一个 int 变量,在 64 位环境中占用 4 个字节的内存,这四个字节存储的只是这个变量的值(二进制)。

那为什么还要指明数据类型呢?很简单,为了标明给定变量所占有的内存大小(即长度)。举个例子,假如没有指明数据类型,你在读取一个整型变量时就不知道该读取多长了,究竟是 4 个字节还是 2 个字节,都是不确定的。

而指明了数据类型就不一样了,给你一个 int 变量,你就知道要读取 4 个字节,给你一个 double 变量,你就知道要读取 8 个字节,等等;不过对于指针类型,指明数据类型有两层意义:第一,我可以从数据类型中知道这是一个指针(实际就是一个长整型数值,只不过这个值的意义有点特殊,它代表的是一个内存地址);第二,我可以知道要从这个指针所指的内存区域中读取的数据长度。举个例子,对于数据类型int *,我可以知道它是一个指针,而当前的环境是 64 位的,因此一个指针变量的长度是 64 bit 即 8 字节,我读取了这 8 个字节的数据后,就可以得知我真正要读取的数据的内存地址,而接下来要读取的数据长度就是由 int 指明的长度,即 4 个字节。对于特殊指针void *来说,它只有一层意义,即表示这是一个指针,但是具体数据的长度是不知道的。

实例说明