本文已参与「新人创作礼」活动,一起开启掘金创作之路
文章目录
一、main 函数的概念
- C语言中 main 函数称之为主函数
- 一个C程序是从 main 函数开始执行的
二、main 函数的本质
- main 函数是操作系统调用的函数
- 操作系统总是将 main 函数作为应用程序的开始
- 操作系统将 main 函数的返回值作为程序的退出状态
下面看一下 main 函数的返回值:
A.c:
#include <stdio.h>
int main()
{
printf("I'm A!\n");
return 0;
}
B.c:
#include <stdio.h>
int main()
{
printf("I'm B!\n");
return 99;
}
在 windows 下通过 VS012 命令行编译代码,如果对命令行编译不熟悉,可以看我这篇文章:入口
以 B.c 为例,编译后通过 echo %ERRORLEVEL% 命令后,看到返回值为 99
编辑
下面再来进行一个实验,先将 A 进行编译,然后输入 B.exe && A.exe,发现只打印出 I'm B!,因为 B.exe运行结束后操作系统返回了 99,99 在操作系统看来 B.exe 没有正常运行退出,所以操作系统就不会运行 A.exe。
编辑
如果输入 A.exe && B.exe,结果如下,这是因为 A.exe 运行结束后正常返回 0 给操作系统,所以就会往下执行 B.exe
编辑
三、main 函数的参数
- 程序执行时可以向 main 函数传递参数
编辑
- gcc 编译器的常见用法:
编辑
下面看一个 main 函数的参数示例:
#include <stdio.h>
int main(int argc, char* argv[], char* env[])
{
int i = 0;
printf("============== Begin argv ==============\n");
for(i=0; i<argc; i++)
{
printf("%s\n", argv[i]);
}
printf("============== End argv ==============\n");
printf("\n");
printf("\n");
printf("\n");
printf("============== Begin env ==============\n");
for(i=0; env[i]!=NULL; i++)
{
printf("%s\n", env[i]);
}
printf("============== End env ==============\n");
return 0;
}
输出结果如下:
delphi@delphi-vm:~ ./a.out a.c b.c c.c
============== Begin argv ==============
./a.out
a.c
b.c
c.c
============== End argv ============================ Begin env ==============
ORBIT_SOCKETDIR=/tmp/orbit-delphi
SSH_AGENT_PID=1781
TERM=xterm
SHELL=/bin/bash
XDG_SESSION_COOKIE=6c560f89cd4609726ff940b800000007-1648713669.66817-1997070498
WINDOWID=71303204
QTDIR=/usr/local/Trolltech
GNOME_KEYRING_CONTROL=/tmp/keyring-ZV2hRj
GTK_MODULES=canberra-gtk-module
USER=delphi
LD_LIBRARY_PATH=/usr/local/Trolltech/Qt-4.7.4/lib:/usr/local/Trolltech/qtcreator-2.4.1/lib:
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:.tar=01;31:.tgz=01;31:.arj=01;31:.taz=01;31:.lzh=01;31:.lzma=01;31:.tlz=01;31:.txz=01;31:.zip=01;31:.z=01;31:.Z=01;31:.dz=01;31:.gz=01;31:.lz=01;31:.xz=01;31:.bz2=01;31:.bz=01;31:.tbz=01;31:.tbz2=01;31:.tz=01;31:.deb=01;31:.rpm=01;31:.jar=01;31:.rar=01;31:.ace=01;31:.zoo=01;31:.cpio=01;31:.7z=01;31:.rz=01;31:.jpg=01;35:.jpeg=01;35:.gif=01;35:.bmp=01;35:.pbm=01;35:.pgm=01;35:.ppm=01;35:.tga=01;35:.xbm=01;35:.xpm=01;35:.tif=01;35:.tiff=01;35:.png=01;35:.svg=01;35:.svgz=01;35:.mng=01;35:.pcx=01;35:.mov=01;35:.mpg=01;35:.mpeg=01;35:.m2v=01;35:.mkv=01;35:.ogm=01;35:.mp4=01;35:.m4v=01;35:.mp4v=01;35:.vob=01;35:.qt=01;35:.nuv=01;35:.wmv=01;35:.asf=01;35:.rm=01;35:.rmvb=01;35:.flc=01;35:.avi=01;35:.fli=01;35:.flv=01;35:.gl=01;35:.dl=01;35:.xcf=01;35:.xwd=01;35:.yuv=01;35:.cgm=01;35:.emf=01;35:.axv=01;35:.anx=01;35:.ogv=01;35:.ogx=01;35:.aac=00;36:.au=00;36:.flac=00;36:.mid=00;36:.midi=00;36:.mka=00;36:.mp3=00;36:.mpc=00;36:.ogg=00;36:.ra=00;36:.wav=00;36:.axa=00;36:.oga=00;36:.spx=00;36:.xspf=00;36:
SSH_AUTH_SOCK=/tmp/keyring-ZV2hRj/ssh
DEFAULTS_PATH=/usr/share/gconf/gnome.default.path
SESSION_MANAGER=local/delphi-vm:@/tmp/.ICE-unix/1750,unix/delphi-vm:/tmp/.ICE-unix/1750
USERNAME=delphi
XDG_CONFIG_DIRS=/etc/xdg/xdg-gnome:/etc/xdg
DESKTOP_SESSION=gnome
PATH=/usr/local/Trolltech/Qt-4.7.4/bin:/usr/local/Trolltech/qtcreator-2.4.1/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
QT_IM_MODULE=xim
PWD=/home/delphi
XMODIFIERS=@im=ibus
GDM_KEYBOARD_LAYOUT=cn
LANG=zh_CN.utf8
GNOME_KEYRING_PID=1731
MANDATORY_PATH=/usr/share/gconf/gnome.mandatory.path
GDM_LANG=zh_CN.utf8
GDMSESSION=gnome
SHLVL=1
HOME=/home/delphi
LANGUAGE=zh_CN:zh
GNOME_DESKTOP_SESSION_ID=this-is-deprecated
LOGNAME=delphi
XDG_DATA_DIRS=/usr/share/gnome:/usr/local/share/:/usr/share/
DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-k3N1W1OvOx,guid=5f504019e47302c3a7df0e2000000017
LESSOPEN=| /usr/bin/lesspipe %s
WINDOWPATH=7
DISPLAY=:0.0
GTK_IM_MODULE=ibus
LESSCLOSE=/usr/bin/lesspipe %s %s
XAUTHORITY=/var/run/gdm/auth-for-delphi-omqXhW/database
COLORTERM=gnome-terminal
_=./a.out
============== End env ==============
四、main 函数一定是程序执行的第一个函数吗?
下面看一个示例:
#include <stdio.h>
#ifndef __GNUC__
#define __attribute__(x)
#endif
__attribute__((constructor))
void before_main()
{
printf("%s\n",__FUNCTION__);
}
__attribute__((destructor))
void after_main()
{
printf("%s\n",__FUNCTION__);
}
int main()
{
printf("%s\n",__FUNCTION__);
return 0;
}
输出结果如下:
编辑
constructor 告诉编译器在 main 函数之前先调用,destructor 告诉编译器在 main 函数之后调用。
如果在 windows 下的 VS2012 命令行窗口运行
#include <stdio.h>
#ifndef __GNUC__
#define __attribute__(x)
#endif
__attribute__((constructor))
void before_main()
{
printf("%s\n", "before_main");
}
__attribute__((destructor))
void after_main()
{
printf("%s\n", "after_main");
}
int main()
{
printf("%s\n", "main");
return 0;
}
输出结果如下:
编辑
所以再来看这个问题:main 函数一定是程序执行的第一个函数吗?
回答如下: 不一定。假设使用 gcc 编译器中的属性关键字,就可以指定 main 函数之前可以执行一个函数,在 main 函数之后执行一个函数;对于没有扩展属性关键字的编译器而言,main 函数一定是第一个函数。
五、小结
- 一个 C 程序是从 main 函数开始执行的
- main 函数是操作系统调用的函数
- main 函数有参数和返回值
- 现代编译器支持在 main 函数前调用其它函数