主函数的原型为:
int main(int argc,char *argv[],char *envp);
argc:传递的参数列表中参数的个数
argv:传递的参数列表
envp:传递的环境变量
我们在Linux中编写一个主函数,看一下运行结果:
# include<stdio.h>
# include<string.h>
# include<stdlib.h>
# include<assert.h>
# include<unistd.h>
int main(int argc,char *argv[],char *envp[])
{
int i=0;
for(;i<argc;i++)
{
printf("%s\n",argv[i]);
}
exit(0);
}
将这个写的主函数编译以后结果如下图:
我们再来看看其他的情况,假设现在传入参数为“hello word”或者是“1,2,3,4,5”,运行情况如下图:
为什么我们在不传参或者是传参之后,都会打印打一个./zhuhanshu呢,那是因为系统默认会给主函数传递一个参数,参数就是执行的命令。
将传入的参数用双引号括起来就指示,它是一个字符串,例如:
还有一种方式,添加转义字符“\”,例如:
接下来是打印环境变量,代码如下:
# include<stdio.h>
# include<string.h>
# include<stdlib.h>
# include<assert.h>
# include<unistd.h>
int main(int argc,char *argv[],char *envp[])
{
int i=0;
for(;i<argc;i++)
{
printf("%s\n",argv[i]);
}
i=0;
while(envp[i] != NULL)
{
printf("%s\n",envp[i]);
i++;
}
exit(0);
}
下面是打印出的环境变量,参数还是默认的参数,没有额外传递参数。
附加讲一下printf这个函数:
首先我们在Linux中先写这样一个代码:
# include<stdio.h>
# include<string.h>
# include<stdlib.h>
# include<assert.h>
int main()
{
printf("hello");
sleep(5);
printf("world");
exit(0);
}
这个程序在没有执行前,我们认为它运行出来的结果应该是,先打印一个hello,然后睡眠5秒再打印world,但是实际运行结果却是如下图所示:
实际运行结果是,先睡眠了5秒,然后再打印出来“helloworld”,对此很多人都会有疑问,为什么不是按照顺序来执行程序的,其实是这样的,printf这个函数是首先将“hello"写到输出缓冲区,然后再将”world\n"写到输出缓冲区,最后再一起打印出来的。输出缓冲区在内存上。
输出缓冲区刷新的条件:
1、程序结束,但是以_exit(0)结束的程序不会刷新缓冲区。
2、遇到“\n”
3、缓冲区满,现在系统默认最多放1024个字节
4、主动刷新,用fflush(stdout)函数
现在我们将刚刚的代码,“hello"后面加上一个"\n",来看看它的执行结果:
这一次运行结果就是先打印hello,然后睡眠5秒再打印的world。
atexit函数:
函数原型:int atexit (void (*)(void));
它是在正常程序退出时调用的函数,我们把他叫为登记函数。 ⼀个进程可以登记若⼲个函数,这些函数由exit⾃动调⽤,这些函数被称为终⽌处理函数, atexit函数可以登记这些函数。 exit调⽤终⽌处理函数的顺序和atexit登记的顺序相反,如果⼀个函数被多次登记,也会被多次调⽤。
实例:
#include<stdio.h>
#include<stdlib.h>
void func1()
{
printf("fun1\n");
}
void func2()
{
printf("fun2\n");
}
void func3()
{
printf("fun3\n");
}
int main()
{
atexit(func1);
atexit(func2);
atexit(func3);
exit(0);
}
执行结果:
我们可以看到atexit函数的调用顺序是和登记顺序相反的。
atexit函数的用途也是比较广泛的:可以按照你预设的顺序摧毁全局变量(类),例如有个log类,你在其它的全局类里也有可能调用到Log类写日志。所以log 类必须最后被析构 。假如没有规定析构顺序,那么程序在退出时将有可能首先析构log类,那么其它的全局类在此时将无法正确写日志。 把数据写回文件, 删除临时文件, 这才是真正有用的.