1.main函数
C函数总是从执行一个名为main的函数开始。main函数的原型为
int main(int argc, char *argv[]);
其中 argc是命令行参数的数量而,argv是参数指针的数组。
2.进程终止
有8种方法终止一个进程。普通终止有5种:
1).从main函数中返回;
2).调用exit;
3).调用_exit或_Exit;
4).最后线程从启动例程(eg:start函数)返回;
5).从最后线程里调用pthread_exit
异常终止有3种:
6).调用abort
7).收到一个信号
8).最后线程回应一个取消请求
Exit 函数
三个普通终止程序的函数:_exit和_Exit从内核立即返回;eixt则先执行特定清理处理然后从内核返回
#include <stdlib.h> void exit(int status); void _Exit(int status); #include <unistd.h> void _exit(int status);
atexit函数
根据ISO C,一个进程可以最多注册32个函数,这些函数由exit函数调用的函数。这些被称为exit处理器,并通过调用atexit函数来登记这些函数。
#include <stdlib.h> int atexit(void (*func)(void)); //成功返回0,错误返回非0值。
声明说我们传递一个函数地址作为atexit的参数。当这个函数被调用时,不传入任何参数也不返回任何值。exit函数以它们注册的顺序的相反顺序调用这些函数。每个函数都被调用和它被注册的一样多的次数。
3.命令行参数
当一个程序被执行时,使用exec的进程可以传递命令行参数给这个新的程序。这是UNIX系统shell的普通操作的一部分
4.环境表
每个程序还被传入一个环境列表。就像参数列表那样,环境列表是一个字符指针的数组,每个指针包含一个以null终止的C字符串的地址。这个指针数组的地址包含在全局变量environ里:
extern char **environ;
例如,如果环境由5个字符串组成,用environ指向一个长度为5的指针数组。数组里的每个地址都指向一个如“HOME=/home/tommy\0”形式的字符串。每个字符串的结尾处显示的有null字符。我们称environ为环境指针,称指针数组为环境列表 ,称它们指向的字符串为环境字符串
5.C程序的存储空间布局
1).代码段(text segment 又称正文段),CPU执行的机器指令。通过,代码段是可共享的,以便经常执行的程序只需在内存里单个拷贝,比如文本编辑器,C编译器,外壳,等等。还有代码段通常是只读的,为了阻止一个程序偶然修改了它的指令。
2).初始化的数据段(Initialized data segment),通常简称为数据段,包括在程序里特别初始化的变量。例如,C出现在任何函数外的声明int maxcount = 99;会导致这个变量以其初始值存储在初始数据段里。
3).未初始化的数据段(Uninitialized data segment),经常被称为“bss”段,在代表“block started by symbol”的古老的汇编操作之后命令。在这个段的数据被内核在程序开始执行前初始化为数字0或null指针。出现在任何函数外的C声明long sum[1000];导致这个变量被存储在未初始化的数据段里。
4).栈,存储自动变量和每次一个函数调用时保存信息的地方。每次一个函数被调用时,它要返回到的地址和关于调用者环境的特定信息,比如一些机器寄存器,被保存在栈里。新调用的函数然后在栈上为自动和临时变量开辟空间。这是在C里的递归函数如何工作的。每次一个递归函数调用它自身时,一个新的栈框架被使用,所以一堆变量不会和这个函数的其它实例的变量冲突。
5).堆,动态内存分配通常发生的地方。历史上,堆一直放在未初始化数据和栈之间。
这些段的典型布局是:最低地址是代码段,其上是初始化数据,再上是未初始化数据,最高地址是命令行参数和环境变量,其下是栈,在栈和bss段之间是堆。
6.存储器分配
ISO C为内存分配规定了三个函数:
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); //三者成功都返回非空指针,错误返回NULL。 void free(void *ptr);
7.环境变量
如同前述,环境字符串的形式通常是这样的格式:
name=value
1).ISO C定义了一个函数getenv,可以用其环境变量值,但是该标准又称环境的内容是由实现定义的
#include <stdlib.h> char *getenv(const char *name); //返回和name相关的值的指针,没有找到则返回NULL。
注意这个函数返回一个name=value的字符串的指针。我们应该使用getenv来从环境得到指定的值,而不是直接访问environ。
2).
include <stdlib.h> int putenv(char *str); int setenv(const char *name, const char *value, int rewrite); int unsetenv(const char *name); //三者成功返回0,错误返回非0.
a.putenv函数取形式为name=value的字符串,并把它放在环境列表中。如果name已经存在,它的旧的定义会首先被移除。
b.setenv将name设置为value,如果name存在于环境中,那么1、如果rewrite为非0,则存在的name的定义首先被移除;2、如果rewrite为0,name的已存在的定义不被删除,name不会被设置为新的value,也没有错误发生。
c.unsetenv函数删除任何name的定义。
注意putenv和setenv的区别。setenv必须开辟内存来创建它参数的name=value的字符串,putenv可以把字符串直接传给环境。
8.setjmp和longjmp函数
setjmp 与 longjmp的结合使用,却可以实现在不同程序之间的跳转
#include <setjmp.h> int setjmp(jmp_buf env); //如果直接调用返回0,如果从longjmp调用返回则返回非0。 void longjmp(jmp_buf env, int val);
我们从我们想回到的地点里调用setjmp,在下面例子里是main函数。这种情况下, setjmp返回0因为我们直接调用它。在这个setjmp的调用里,env参数是一个特殊的类型jmp_buf。这个数据类型是某种格式的数组,能够存储所有所需的信息,当我们调用longjmp时用来恢复栈的状态。通常,env变量是一个全局变量,因为我们将需要从另一个函数里引用它。
#include <setjmp.h> jmp_buf jmpbuffer; int main(void) { char line[MAXLINE]; if (setjmp(jmpbuffer) != 0) printf("error"); while (fgets(line, MAXLINE, stdin) != NULL) do_line(line); exit(0); } void cmd_add(void) { int token; token = get_token(); if (token < 0) longjmp(jmpbuffer, 1); /* rest of processing for this command */ } /* 当main被执行时,我们调用setjmp,它在变量jmpbuffer里记录任何它需要信息并返回0。我们然后调用do_line,它会用 cmd_add,并假定察觉到某种形式的一个错误。
在cmd_add里调用longjmp之前,栈里有main、do_line和cmd_add函数的框架。但是longjmp导致栈直接回到main函数,把cmd_add和do_line的栈框架给丢弃了。调用
longjmp导致main里的 setjmp返回,但这次它返回的值为1(longjmp的第二个参数。) */
9.getrlimit和setrlimit函数
每个进程都有一堆资源限制,其中一些可以用getrlimit和setrlimit函数查询和更改。
#include <sys/resource.h> int getrlimit(int resource, struct rlimit *rlptr); int setrlimit(int resource, const struct rlimit *rlptr); //两者成功都返回0,错误都返回非0。
对于两个函数的每次调用都指单个资源和一个指向以下结构体的指针:
struct rlimit { rlim_t rlim_cur; /* soft limit: current limit */ rlim_t rlim_max; /* hard limit: maximum value for rlim_cur */ };