进程环境之C程序的存储空间布局、共享库、存储空间分配以及环境变量

C程序的存储空间布局

历史一直沿袭至今,C程序有下列几部分组成:

  • 正文段 。这是由CPU执行的机器指令的部分。通常,正文段是可共享的,所以即使是频繁执行的程序在存储器中也只需有一个副本,另外,正文段常常是只读的,以防止程序由于意外而修改其指令。
  • 初始化数据段。通常将此段称为数据段,它包含了程序中需明确地赋初值的变量。
  • 未初始化数据段。通常称之为BSS段,在程序开始执行之前,内核将此段中的数据初始化为0或空指针。
  • 。自动变量以及每次函数调用时所需保存的信息都存放在此段中。每次函数调用时,其返回地址以及调用者的环境信息(如某些机器寄存器的值)都存放在栈中。然后,最近被调用的函数在栈上为其自动和临时变量分配存储空间。通过以这种方式使用栈,C递归函数可以工作。递归函数每次调用自身时,就用一个新的栈帧,因此一次函数调用实例中的变量集不会影响另一次函数调用实例中的变量。
  • 。通常在堆中进行动态存储分配。由于历史原因,堆位于未初始化数据段和栈之间。

典型的存储空间布局如下图所示:

由存储空间布局图可知,栈的增长方向是从低地址向高地址方向。在堆顶和栈顶之间未用的地址空间很大。未初始化数据段的内容并不存放在磁盘程序文件中。其原因是内核在程序开始运行前将它们都设置为0。需要存放在磁盘程序文件中的段只有正文段和初始化数据段。
size命令报告正文段、数据段和BSS段的长度。

共享库

如今,大多数UNIX系统都已支持共享库。共享库使得可执行文件中不再需要包含公用的库函数,而只需在所有进程都可引用的存储区中保存这种库例程的一个副本。程序第一次执行或者第一次调用某个库函数时,用动态链接方法将程序与共享库函数相链接。这减少了每个可执行文件的长度,但增加了一些运行时间的开销。这种时间开销发生在该例程第一次被执行时,或者每个共享库函数第一次被调用时。共享库的另一个优点是可以用库函数的新版本替代老版本而无需对使用该库的程序重新连接编辑。
不同系统中,可以使用不同的方法说明是否使用共享库。在Linux下,运行结果如下:

由图可知,使用共享库的方式下,可执行文件的正文和数据段的长度都显著减小。

存储空间分配

ISO C说明了3个用于存储空间动态分配的函数。

  1. malloc,分配指定字节数的存储区。此存储区中的初始值不确定。
  2. calloc,为指定数量指定长度的对象分配存储空间。该空间中的每一位都初始化为0。
  3. realloc,增加或减少以前的分配区的长度。
#include <stdlib.h>
void *malloc(size_t size);
void *calloc(size_t nobj, size_t size);
void *realloc(void *ptr, size_t newsize);
3个函数返回值:若成功,返回非空指针;若出错,返回NULLvoid free(void *ptr);

注意: 1. realloc函数使我们可以增加、减少之前分配的存储区的长度。当存储区后有足够的空间可供扩充,则可在原存储区位置上向高地址方向扩充,无需移动任何原先的内容。如果原存储区后没有足够的空间,则realloc分配另一个足够大的存储区,将原存储区中的内容复制到新分配的存储区中。然后释放原存储区,返回指向新存储区的指针。2. 大多数实现所分配的空间要大于所要求的空间,因为需要额外的空间来存储分配块长度、指向下一个分配块的指针等。这也就意味着,如果超过一个已分配区的尾端或者在已分配区的起始位置之前进行写操作,则会改写另一块的管理记录信息。这种错误是灾难性的,因为这种错误不会很快暴露出来,极难发现问题所在。3. 常见错误:(1)释放一个已经释放了的块 。(2)调用free函数时所用的指针不是3个内存分配函数返回的指针(3)使用malloc函数分配空间后未使用free函数对其分配的空间进行释放,这种行为被称之为内存泄漏。

环境变量

环境字符串形式: name = value
ISO C定义了一个函数getenv,可以用其取环境变量值。

#include <stdlib.h>
char *getenv(const char *name);
返回值:指向与name关联的value的指针;若未找到,返回NULL

各种环境变量请查看《UNIX环境高级编程》中进程环境章节。
其他与操作环境变量的函数如下:

#include <stdlib.h>
int putenv(char *str);
函数返回值:若成功,返回0;若出错,返回非0int setenv(const char *name, const char *value, int rewrite);
int unsetenv(const char *name);
两个函数返回值:若成功,返回0;若出错,返回-1

putenv取形式为name = value的字符串,将其放到环境表中。如果name已经存在,则先删除其原来的定义。
setenvname设置为value。如果在环境中name已经存在,那么(a)若rewrite非0,则先删除其现有的定义;(b)若rewrite为0,则不删除其现有定义。
unsetenv删除name的定义。即使不存在这种定义也不算出错。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值