认识main函数

一、main函数的写法

51单片机里面我们写main函数都是以

void main() {}

这种形式来写的,其实这样写在51中是可以的,但是严格来说是不规范的。
规范的写法一共有三种:

1.无参数

main 函数不带参数的写法

  1 #include <stdio.h>
  2 
  3 int main(void)
  4 {
  5   printf("Hello Wrold!\n");
  6   return 0;
  7 }

2.两个参数

带两个参数,习惯上

  • 第一个参数是整型argc,保存了外部调用命令的参数个数
  • 第二个参数是指针数组或二级指针argv,以字符串形式保存了与argc对应的参数
  1 #include <stdio.h>
  2 
  3 int main(int argc, char *argv[])
  4 {
  5   printf ("argc num  :  %d\n",argc);
  6 
  7   for (int i = 0; i < argc; i++) {
  8     printf("argv[%d]   :  %s\n", i, argv[i]);
  9   }
 10 
 11   return 0;
 12 }

执行如下:
在这里插入图片描述

3. 三个参数

带三个参数,习惯上

  • 第一个参数是整型argc,保存了外部调用命令的参数个数
  • 第二个参数是指针数组或二级指针argv,以字符串形式保存了与argc对应的参数
  • 第三个参数是一个环境变量参数,环境变量的形式是“ENV=value”,参数类型是指针数组或二级指针
  1 #include <stdio.h>
  2 
  3 int main(int argc, char *argv[], char *env[])
  4 {
  5   printf ("argc num  :  %d\n",argc);
  6 
  7   for (int i = 0; i < argc; i++) {
  8     printf ("argv[%d]   :  %s\n", i, argv[i]);
  9   }
 10 
 11   printf ("\nenvironment variable:\n\n");
 12   for (int j = 0; env[j] != NULL; j++) {
 13     printf ("%s\n",env[j]);
 14   }
 15 
 16   return 0;
 17 }              

执行结果如下 ,后面的就是执行的环境变量:

zl@zl-GREATWALL:~/test/3test$ ./a.out 
argc num  :  1
argv[0]   :  ./a.out

environment variable:

CLUTTER_IM_MODULE=xim
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:mi=00: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:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=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:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=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:*.webm=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:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=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:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:
LC_MEASUREMENT=zh_CN.UTF-8
LESSCLOSE=/usr/bin/lesspipe %s %s
LC_PAPER=zh_CN.UTF-8
LC_MONETARY=zh_CN.UTF-8
XDG_MENU_PREFIX=gnome-
SNAP_USER_DATA=/home/zl/snap/sublime-text/85
LANG=en_US.UTF-8
DISPLAY=:0
GNOME_SHELL_SESSION_MODE=ubuntu
SNAP_REVISION=85
SNAP_ARCH=amd64
SNAP_INSTANCE_KEY=
COLORTERM=truecolor
USERNAME=zl
XDG_VTNR=2
SSH_AUTH_SOCK=/run/user/1000/keyring/ssh
MANDATORY_PATH=/usr/share/gconf/ubuntu.mandatory.path
LC_NAME=zh_CN.UTF-8
XDG_SESSION_ID=2
SNAP_USER_COMMON=/home/zl/snap/sublime-text/common
USER=zl
DESKTOP_SESSION=ubuntu
QT4_IM_MODULE=fcitx
TEXTDOMAINDIR=/usr/share/locale/
GNOME_TERMINAL_SCREEN=/org/gnome/Terminal/screen/7c458ce3_e47c_4807_a823_15d23e167672
DEFAULTS_PATH=/usr/share/gconf/ubuntu.default.path
PWD=/home/zl/test/3test
HOME=/home/zl
TEXTDOMAIN=im-config
SNAP=/snap/sublime-text/85
SSH_AGENT_PID=1972
QT_ACCESSIBILITY=1
SNAP_COMMON=/var/snap/sublime-text/common
XDG_SESSION_TYPE=x11
SNAP_NAME=sublime-text
XDG_DATA_DIRS=/usr/share/ubuntu:/usr/local/share:/usr/share:/var/lib/snapd/desktop
SNAP_INSTANCE_NAME=sublime-text
SNAP_DATA=/var/snap/sublime-text/85
XDG_SESSION_DESKTOP=ubuntu
LC_ADDRESS=zh_CN.UTF-8
GJS_DEBUG_OUTPUT=stderr
LC_NUMERIC=zh_CN.UTF-8
GTK_MODULES=gail:atk-bridge
PAPERSIZE=a4
SNAP_COOKIE=RLsJjTtsDfwBXcvtgr3Sx2hrNW0nHgkJ76tm81FJmPcB
WINDOWPATH=2
TERM=xterm-256color
VTE_VERSION=5202
SHELL=/bin/bash
QT_IM_MODULE=fcitx
XMODIFIERS=@im=fcitx
IM_CONFIG_PHASE=2
XDG_CURRENT_DESKTOP=ubuntu:GNOME
GPG_AGENT_INFO=/run/user/1000/gnupg/S.gpg-agent:0:1
BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/sublime-text_subl.desktop
SNAP_REEXEC=
GNOME_TERMINAL_SERVICE=:1.141
SHLVL=1
XDG_SEAT=seat0
LANGUAGE=en_US:en
LC_TELEPHONE=zh_CN.UTF-8
GDMSESSION=ubuntu
GNOME_DESKTOP_SESSION_ID=this-is-deprecated
LOGNAME=zl
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
XDG_RUNTIME_DIR=/run/user/1000/snap.sublime-text
XAUTHORITY=/run/user/1000/gdm/Xauthority
SNAP_CONTEXT=RLsJjTtsDfwBXcvtgr3Sx2hrNW0nHgkJ76tm81FJmPcB
XDG_CONFIG_DIRS=/etc/xdg/xdg-ubuntu:/etc/xdg
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
LC_IDENTIFICATION=zh_CN.UTF-8
SNAP_VERSION=3211
GJS_DEBUG_TOPICS=JS ERROR;JS LOG
SNAP_LIBRARY_PATH=/var/lib/snapd/lib/gl:/var/lib/snapd/lib/gl32:/var/lib/snapd/void
SESSION_MANAGER=local/zl-GREATWALL:@/tmp/.ICE-unix/1879,unix/zl-GREATWALL:/tmp/.ICE-unix/1879
LESSOPEN=| /usr/bin/lesspipe %s
GTK_IM_MODULE=fcitx
LC_TIME=zh_CN.UTF-8
OLDPWD=/home/zl/test
_=./a.out

二、main理解

其实在C语言标准里面规定了C语言执行的环境,无操作系统(比如说51单片机)就不需要返回值,而在操作系统里,有三种形式,如上。
其实单从函数角度来看,main函数跟其他函数一样,有返回值和参数,只不过编译器默认规定用户程序从它开始。

1.main函数执行之前

我们从linux C (GLibC)角度来说,程序和GlibC链接生成可执行文件的时候,main函数就成为了程序执行的入口函数。
然而编译器最开始是去找_start这个函数,再从这里指定main函数。_start伪代码:

void _start()
{
  %ebp = 0;
  int argc = pop from stack
  char ** argv = top of stack;
  __libc_start_main(main, argc, argv, __libc_csu_init, __linc_csu_fini,
  edx, top of stack);
}

_start作用总结为5点:
1.设置栈指针
2.初始化static静态和global全局变量,即data段的内容
3.将未初始化部分的赋初值:数值型short,int,long等为0,bool为FALSE,指针为NULL,等等,即.bss段的内容
4.运行全局构造器,类似c++中全局构造函数
5.将main函数的参数,argc,argv等传递给main函数,然后才真正运行main函数

我们可以使用gcc改变我们的入口函数,如下:

 1 #include <stdio.h>
  2 #include <stdlib.h>
  3 int MyEntry(void)
  4 {
  5   printf ("Hello Wrold!\n");
  6   exit(0);
  7 }

直接用 gcc MyEntry.c执行会报错,如下
在这里插入图片描述意思差不多是,从_start应该直接链接到main,但是找不到main
我们用gcc改变入口函数,执行

gcc MyEntry.c -e MyEntry -nostartfiles

-e 参数指定我们的入口函数
-nostartfiles 连接时候不使用标准系统启动文件

结果如下:
在这里插入图片描述

2.main函数执行之后

  1. 全局对象的析构函数会在main函数之后执行;
  2. 用atexit注册的函数也会在main之后执行。

atexit函数:int atexit(void (*func)(void));
texit 函数可以“注册”一个函数,使这个函数将在main函数正常终止时被调用,当程序异常终止时,通过它注册的函数并不会被调用。编译器必须至少允许程序员注册32个函数。如果注册成功,atexit 返回0,否则返回非零值,没有办法取消一个函数的注册。在 exit 所执行的任何标准清理操作之前,被注册的函数按照与注册顺序相反的顺序被依次调用。每个被调用的函数不接受任何参数,并且返回类型是 void。被注册的函数不应该试图引用任何存储类别为 auto 或 register 的对象(例如通过指针),除非是它自己所定义的。多次注册同一个函数将导致这个函数被多次调用。
  函数调用的最后的操作就是出栈过程。main()同样也是一个函数,在结束时,按出栈的顺序调用使用atexit函数注册的,所以说,函数atexit是注册的函数和函数入栈出栈一样,是先进后出的,先注册的后执行。
  通过atexit可以注册回调清理函数。可以在这些函数中加入一些清理工作,比如内存释放、关闭打开的文件、关闭socket描述符、释放锁等等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值