8种机械键盘轴体对比
本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?
C语言的入口函数是main函数,程序通过找到main函数,作为整个程序的入口,顺序执行,执行完main函数之后结束程序。
但是我们想,全局变量、对象的构造在什么时候执行呢?肯定是在main函数之前就被构造了,但是这个过程又不会发生在编译期间,由此分析,在main函数之前有一段代码先执行了。
首先是由操作系统为这个程序创建进程,然后把控制权移交给程序入口(这个入口通常是程序运行库中的某个入口函数)。
入口函数对运行库和程序运行环境进行初始化,包括堆、I/O、线程、全局变量的构造等等。
入口函数执行完毕,调用main函数,正式开始执行函数主体部分。
main函数执行完毕之后,返回到入口函数,入口函数进行清理工作,包括全局变量析构、堆销毁、关闭I/O等,然后进行系统调用结束进程。
main函数执行之前
对静态变量、全局变量和全局对象来说的分配是早在main()函数之前就完成的
举个小例子:1
2
3
4
5
6
7
8
9
10
11int ();
int i = foo();
int (){
printf("Before mainn");
return 0;
}
int main(){
printf("Mainn");
return 0;
}
main函数执行之后
全局对象的析构函数会在main函数之后执行
用atexit注册的函数也会在main之后执行
通过对上面main函数执行之前的分析,我们可以理解全局对象的析构会发生在main函数之后。现在重点介绍atexit函数。
exit函数
有些程序会在一些语句中做判断,如果遇到异常情况会退出程序,通常会使用会exit语句。根据exit()函数中的参数可以分析出程序大致出错原因。函数原型为:
void exit(int state);
顺便介绍exit与return的区别
参考return is an instruction of the language that returns from a function call
exit is a system call(not a language statement) that terminates the current process.
return是在函数中用作返回使用的,而exit是终止当前进程的一个系统调用。
只有在main函数中他们的行为才比较相像。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include
void f1(){
printf("f1n");
return ;
}
void fun2(){
printf("f2n");
exit(0); // 整个程序被终止
}
int main(){
f1();
printf("back from f1n");
f2();
printf("back from f2n"); // 不会执行到这一句,程序在fun2中就被终止
return 0;
}
main函数结束时也会隐式地调用exit()函数。
exit()函数运行时首先会执行由atexit()函数登记的函数,然后会做一些自身的清理工作,同时刷新所有输出流、关闭所有打开的流并且关闭通过标准I/O函数tmpfile()创建的临时文件。
atexit函数
atexit()函数用于注册终止函数(即main执行结束后调用的函数),其原型为:
int atexit(void (*function)(void));
很多时候我们需要在程序退出的时候做一些诸如释放资源的操作,这个时候就需要注册main函数执行完之后的函数了。
atexit()函数的参数是一个函数指针,函数指针指向一个没有参数也没有返回值的函数。
在一个程序中最多可以用atexit()注册32个处理函数,这些处理函数的调用顺序与其注册的顺序相反,也即最先注册的最后调用,最后注册的最先调用。同一个函数如若登记多次,则也会被调用多次。
当atexit注册的函数中有一个没有正常返回或被kill,则后续的注册函数都不会被执行。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28#include
#include
#include
void fun1()
{
printf("fun1 is calledn");
}
void fun2()
{
printf("fun2 is calledn");
kill(getpid(),SIGINT);
}
int main(void)
{
printf("main functionn");
if(signal(SIGINT,SIG_DFL) == SIG_ERR){
perror("signal error");
exit(EXIT_FAILURE);
}
atexit(fun1);
atexit(fun2);
atexit(fun1);
return 0;
}
最先被注册的fun1不会执行
exit、_exit、atexit
进程终止有5种方式:
正常退出:从main函数返回、调用exit函数、调用_exit函数
异常退出:调用abort函数、由信号终止
exit和_exit区别
其中exit函数和_exit函数有些许区别,总结来说就是 exit = 清除I/O缓冲 + _exit
两个函数在不同的文件中定义1
2
3
4
5#include
void _exit(int status);
#include
void exit(int status);
_exit()函数直接使进程停止运行,清除其使用的内存空间,并销毁其在内核中的各种数据结构。
exit()函数在调用exit系统调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件,就是”清理I/O缓冲”。
exit过程和atexit函数
exit()在结束调用它的进程之前,要进行如下步骤:调用atexit()注册的函数(出口函数);按atexit注册时相反的顺序调用所有由它注册的函数,这使得我们可以指定在程序终止时执行自己的清理动作。例如,保存程序状态信息于某个文件,解开对共享数据库上的锁。
cleanup();关闭所有打开的流,这将导致写所有被缓冲的输出,删除用TMPFILE函数建立的所有临时文件。
最后调用_exit()函数终止进程。