学习的过程之前,先来了解下过程中的操作环境。
main功能
过程总是开始main功能开始执行,我们编程,程序从main功能进行,它是原型例如,下面的:
int main(int argc, char *[]argv);
argc是命令行參数的数目。argv是指镇数组。即指向指针的指针,能够写代码測试一下:
#include<stdio.h>
int main(int argc, char *argv[])
{
int i;
for(i=0; i<argc; i++)
printf("argv[%d]:%s\n",i,argv[i]);
exit(0);
}
输出:
linux:/home # ./test this is a test program
argv[0]:./test
argv[1]:this
argv[2]:is
argv[3]:a
argv[4]:test
argv[5]:program
在调用main函数之前,内核先调用一个特殊的启动例程,它从内核取得命令行參数和环境变量的值,可运行程序文件将此启动例程指定为程序的起始地址。
内核使用exec函数来启动C程序。
进程终止
有5种方式能够终止进程
(1)正常终止
(a)从main函数返回
(b)调用exit
(c)调用_exit
(2)异常终止
(a)调用abort
(b)由一个信号终止
在上面的正常终止中有exit和_exit,差别在于:_exit调用后马上进入内核。而exit则会先运行一些清除工作(包含调用运行各种终止处理程序,关闭全部标准I/O流等),然后在进入内核。它们定义例如以下:
#include<stdlib.h>
void exit(int status);
#include<unistd.h>
void _exit(int status);
使用了不同的头文件,由于exit是右ANSI C说明的。而_exit是由POSIX.1说明的。
atexit函数
我们能够在进程中登记一些函数(最多32个),这些函数由exit调用。这种函数叫做终止处理程序(exit handler),有atexit函数来登记。
这点看起来有点像类的析构函数。
#include<stdlib.h>
int atexit(void (*func)(void));
其參数是一个函数地址。
exit调用顺序与登记顺序相反,先登记后调用(像栈),同一个函数能够被登记多次。
环境表
进程的执行都有一个环境,环境的信息存储在环境表里面。
环境表是一个字符指针数组。当中每一个指针包括一个以NULL结束的字符串的地址。全局变量environ包括了该指针数组的地址:
extern char **environ;
程序的存储空间布局
C程序由下面几部分组成:
正文段:也叫代码段,是CPU运行的机器指令。正文段一般能够共享的。且是仅仅读的。
初始化数据段:也叫做数据段,包括程序中已经初始化的全局变量和静态变量。
非初始化数据段:也叫做bss段,包括未初始化的全局变量和静态变量,在程序运行之前有内核初始化为0。
不在代码中存储器初始值,能够减小代码的大小。
栈:自己主动存储变量以及每次函数调用时所需保存的信息都放在此段中。
每次函数调用时,其返回地址、以及寄存器中的变量都放在栈中。新被调的函数在栈上为其自己主动和暂时变量分配存储空间。
堆:进行动态分配的变量。
须要手动释放。堆位于未初始化数据段的顶和栈底之间。
存储器的分配:
能够參考这里
setjmp和longjmp
在C语言,可以用于goto语言(虽然不建议)。但goto声明不能跳功能。当你运行这个功能,搜索功能setjmp和longjmp。这两个函数用于处理发生在非常深的嵌套调用中的错误是非常容易使用。
递归调用返回唯一能够恢复到以前的功能,这两个功能就可以使它递归调用的。