为什么 main
函数中,总是 return 0
,如果我们 return
其它值可以吗 ❓
#include <stdio.h>
int main()
{
return 0;
}
答案是肯定可以!对于 main
函数的返回值,我们称之为 进程退出码,它代表进程退出后,结果是否正确!
通常进程退出码为 0
代表成功,非0
则代表其它含义,如果你愿意你也可以 return
其它值。大部分情况下,main
函数跑完后,默认结果是正确的,所以我们以前返回的都是 0
。
所以这里给的建议就是如果未来我们的程序中比较关心进程的退出码的时候,那么要返回特定的数据表明特定的错误信息;而如果不必关心进程退出码的话,我们直接 return 0
即可!
🔴 值得注意的是,这个退出码只记录最近一个进程在命令行中执行完毕时对应的退出码!
main
函数 return
的值是给谁看的呢?那肯定不用说,因为是系统调用的 main
函数,那肯定是返回给系统(系统如何调用的我们后面会讲),以此来判断进程执行后的结果!
这里又要引入一个新的环境变量: ? ,一般查看时候我们要配合 echo 进行打印使用,也就是 echo ?
假设我们写一个程序最后 return 250
,看看它的结果怎么样:
结果就是 250
啦,但是有没有发现很奇怪,就是第二次和第三次调用 echo $?
的时候这个退出码已经变成 0
了,这是为啥 ❓❓❓
其实很简单,因为 echo
也是一个指令,既然是指令那么肯定就是会创建一个子进程,这个子进程也会有退出码返回的,所以 我们每次打印的其实是最近一次的退出码!(当然 echo
这种指令属于内建指令,比较特殊,后面会讲)
还记得我们在 C
语言中学到的一个函数 strerror()
吗,它就是用来**打印错误信息**的,它大概有 134
种标识!
它的函数声明如下:
#include <string.h>
char* strerror(int errnum);
下面我们写个程序调用 strerror()
来查看一下全部的错误信息:
#include <stdio.h>
#include <string.h>
int main()
{
for(int i = 0; i < 134; ++i)
{
printf("num[%d]:%s\n", i, strerror(i));
}
return 0;
}
运行结果:
num[0]:Success
num[1]:Operation not permitted
num[2]:No such file or directory
num[3]:No such process
num[4]:Interrupted system call
......
num[129]:Key was rejected by service
num[130]:Owner died
num[131]:State not recoverable
num[132]:Operation not possible due to RF-kill
num[133]:Memory page has hardware error
可以看到不同的错误码对应的出错误信息!
接下来我们在命令行故意写错指令,看看报的是什么错误:
我们的进程只可能出现以下三种情况,不可能再出现其它的可能:
0
非0
ctrl+c
或除 0
错误终止)此时退出码没有意义。 一般来说退出码在第二种情况下才起作用!
return
(注意非主函数中 return
只是返回值而不是退出程序!)exit()
_exit()
对于后两个方法,我们在下面会详细的讲!
比如 ctrl + c
,进程通过接收到信号而终止!
我们在 C/C++
中也经常看到这个函数,一般用于我们进程的不正确结果的退出,在非主函数中也是直接退出进程的!
下面来看看它的函数声明:
#include <stdlib.h>
void exit(int status);
// 作用:任何函数 exit,都表示直接终止进程。
// 参数:status定义了进程的终止状态,父进程通过wait来获取该值
// 说明:虽然status是int,但是仅有低8位可以被父进程所用。所以_exit(-1)时,在终端执行$?发现返回值是255。
至于上面的参数 status
,我们目前只需要知道它是退出码即可,它主要是和我们后面学的进程等待部分有关系,后面会讲!
当 exit(-1)
时,结果如下:
[liren@VM-8-2-centos process]$ make
gcc -o mypro proc.c -std=c99
[liren@VM-8-2-centos process]$ ./mypro
[liren@VM-8-2-centos process]$ echo $?
255
_exit()
是系统提供的接口,它的原型同库里的 exit
函数,函数的声明如下:
#include <unistd.h>
void _exit(int status);
其实本质上因为 _exit()
是系统级别的接口,所以 exit()
其实就是去调用的 _exit()
,只不过要多做了一些工作,所以会有一些细节上面的差别,比如说下面这些工作:
atexit()
或 on_exit()
定义的清理函数_exit()
总结下来它们两者的差别如下:
return
一样,会进行后续资源处理,包括刷新缓冲区 写个代码展示一下它们的区别:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf("running ...");
sleep(3);
// 比较两者的区别
exit(1);
// _exit(1);
printf("done ...");
return 0;
}
理论上这里的退出码是未定义的、随机的,但实际上得到的退出码是 0
,因为你的 main
函数里总会调用其它函数,成功后,遗留的历史数据是会充当返回值去返回的。
#include <stdio.h>
int main()
{
printf("hello world");
}
运行结果:
[liren@VM-8-2-centos process]$ ./mypro
hello world[liren@VM-8-2-centos process]$ echo $?
0
main
函数里啥也不做,可以看到退出码依旧是 0
,不必太纠结,这个本就是标准未定义的。