**从刚开始写C程序,相比大家便开始写main()了。虽然无数的教科书和老师告诉我们main是程序的入口。那么main函数是怎么被调用的,怎么传入参数,返回的内容到哪里了,返回的内容是什么?接下来我们来探讨一下这个问题。**
  1. main()函数的形式
    早期教材这么写:void main(){}
    其实翻翻C/C++标准,从来没有定义过void main(),使用标准的编译器都会产生一个警告信息,而老师又教会我们,警告不用理会,我们只要关注错误即可,而C标准中main的定义只有两种:
    int main(void)
    int main(int argc, char *argv[])
    在C++标准中main的定义也只有两种:
    int main( )
    int main(int argc, char *argv[])

    前者表明不需要函数的参数时,使用(void)形式,如果需要参数,还是使用(int argc, char *argv[]),但是实际使用时,我也会使用(int argc, char *argv)这种形式,与不同平台和编译器有关而以,在Unix-like平台上,也可以int main(int argc, char *argv[], char *envp[]),后面需要再讨论。

  2. main()函数的返回
    int表明main()需要return一个int值,如果不写,有的编译器会自动帮你添加一个return 0;,而有的则会返回一个随机值。为了避免不必要的问题,建议写的时候还是加上一个return 0。
    一个简单而又完整的test.c文件如下:
    int main(int argc, char *argv[])
    {
    return 0;
    }
    当然我们也可以尝试着让main返回一个long, double甚至是struct,更改main函数中的形参定义。这在有些编译器上是能编译通过的,不过可能会有一些警告(如GCC)。但是运行的时候如果编译器能做转换的还好,如返回long,float. 如果不能的话(如返回struct,或者main(int argc, char *argv0,char *argv1,char *argv2))会造成segmentation fault。

  3. main()的被调用
    接下来看main函数是怎么被调用的,它又”return”给了谁。在”gcc的编译过程”一中,我们回顾了程序从源码到可执行程序的过程,在”应用程序在linux上是如何被执行的”一文中,我们回顾了可执行文件怎么被操作系统加载的,今天我们继续这个过程。
    上文提到不管是在load_elf_binary()中或者使用了动态链接库,最后都执行到了应用程序的入口。不过这个入口不是main.而是_start()。
    执行
    gcc -o test test.c
    readelf -a test
    可以看到test文件的Entry point address是0x80482e0,在往后看,这个地址是.text的地址(代码段的开始),也是_start()的地址。在_start()中又会调用__libc_start_main(),主要做一些程序的初始化工作,感兴趣的同学可以读读glibc中的源码,注释很清楚。然后主角登场了,在__libc_start_main()中最后会调用
    int result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);//这是Unix-like下main函数的调用方式,这下大家明白main函数中形参的由来了吧。
    result中放着main函数的返回值,然后带着这个值退出。
    exit (result);

注意:虽然main函数是一个特殊的函数,是程序运行的入口,但它毕竟也是一个函数,是可以被调用的。如:
int main()
{
if(…)
return 0;
main();
return 0;
}
不过要小心调用方式,和退出条件,避免无穷递归。

  1. shell中执行程序
    通过前几次和上面的分析,我们终于基本弄清了应用程序的执行过程,再回顾一遍: 在某个交互式shell中敲入./test, 此shell fork()/clone()出一个子进程,这个子进程执行

    execve(“./test”,char * const argv[], char * const envp[])

    execve加载./test,并把参数argv[],envp[]一步一步传递下去。加载了./test之后,从./test的入口开始执行,即ELF文件中的_start(),_start()调用__libc_start_main(),最后到了main。

    int main(int argc, char *argv[], char *envp[])

    看着这个main的定义和execve相似吧,没错main中的参数都是execve一步步传递下来的。argc是命令行参数个数,argv[]存储着各个参数的指针(注意argv[0]通常是程序名,argv[1]开始才是命令行参数。这是由shell设置的),envp[]存储着环境变量表。然而在标准C中只定义了int main(int argc, char *argv[]),所以unix-like平台也提供了全局变量environ指向环境变量表。
    extern char **environ;
    当然也可以用getenv和putenv来访问特定的环境变量。

    对了,父shell还在wait()./test的结束呢,不错,test中main函数return的值,在被__libc_start_main() exit之后,终于被父shell抓住了,可以用?访问。如> ./test
    >echo?
    可以得到test返回的值。这样,你就知道main()函数中return的意义,以及如何在shell中使用了吧。尽管可以return任何值,也建议用return 0来表示程序正常结束。这样别人用shell脚本调用你写的程序的时候,就可以$?等于0来判断你的程序是否正常执行了。

最后小结一下:
1. 避免使用void main(),尽量使用int main() 或者 int main(int argc, char *argv[])。
2. 在main的结尾记得 return int;, 最好用return 0;表示程序的正常结束。
3. main函数和普通函数一样也是能被调用的。
4. main return的值最终会返回给其调用者,如shell中执行的程序,可以在shell中用$?得到其返回值。
5. 在unix-like环境中,可以使用int main(int argc, char *argv[], char *envp[]), extern char **environ; , getenv()等方式来得到环境变量。

c语言中的main函数讨论的更多相关文章

  1. C语言中的main函数以及main函数是如何被调用的

    main函数是C语言中比较特殊的函数,C程序总是从main函数开始执行,main函数的原型是: int main(int argc, char *argv[]); 其中argc是命令行参数的个数,ar ...

  2. C语言中的main函数的参数解析

    main()函数既可以是无参函数,也可以是有参的函数.对于有参的形式来说,就需要向其传递参数.但是其它任何函数均不能调用main()函数.当然也同样无法向main()函数传递,只能由程序之外传递而来. ...

  3. c语言中的rewind函数,Win CE 不支持,可用fseek函数替换

    FILE *read = fopen(cXmlFile,"rb"); if (read) { fseek(read, 0L, SEEK_END); int len = ftell( ...

  4. c语言中的malloc函数

    少壮不努力,大一的时候c语言学得不扎实,最近学数据结构的时候看到c语言中malloc函数都不知道了,这里记录一下,避免以后再忘. malloc的全称是memory allocation,中文叫动态内存 ...

  5. C语言中的system函数参数及其作用

    函数名: system 功   能: 发出一个DOS命令  用   法: int system(char *command);  system函数已经被收录在标准c库中,可以直接调用 system() ...

  6. linux下C语言中的flock函数用法 【转】

    表头文件  #include<sys/file.h> 定义函数  int flock(int fd,int operation); 函数说明  flock()会依参数operation所指 ...

  7. C语言中的memset函数和数组指针

    代码: #include <iostream> #include <cstring> using namespace std; int main(){ ] = {}; mems ...

  8. C语言中的system函数參数具体解释

    http://blog.csdn.net/pipisorry/article/details/33024727 函数名: system 功   能: 发出一个DOS命令   用   法: int sy ...

  9. linux下C语言中的flock函数使用方法 .

    表头文件  #include<sys/file.h> 定义函数  int flock(int fd,int operation); 函数说明  flock()会依參数operation所指 ...

随机推荐

  1. DeepFaceLab报错,OOM如何解决?

    DeepFaceLab出错,虽然错误提示好几个屏幕,但是无非两种情况,一种是驱动没装好,一种是显存配置不够.上一篇文章说了驱动的问题,这一篇就说说配置不够的问题. 这个问题的表现形式,往往是各种OOM ...

  2. JZOJ 5455. 【NOIP2017提高A组冲刺11.6】拆网线

    455. [NOIP2017提高A组冲刺11.6]拆网线 (File IO): input:tree.in output:tree.out Time Limits: 1000 ms  Memory L ...

  3. Redis之List类型操作

    接口: package com.net.test.redis.base.dao; import java.util.List; /** * @author *** * @Time:2017年8月10日 ...

  4. 牛客练习赛42 A 字符串

    题目描述 给定两个等长的由小写字母构成的串 A,BA,B,其中 |A|=|B|=n|A|=|B|=n. 现在你需要求出一个子区间 [l,r][l,r] 使得 LCP(A[l,r],B[l,r])×LC ...

  5. Python 有序字典简介

    Table of Contents 1. 有序字典-OrderedDict简介 1.1. 示例 1.2. 相等性 1.3. 注意 2. 参考资料 有序字典-OrderedDict简介 示例 有序字典和 ...

  6. 第八届蓝桥杯C/C++ B组省赛----分巧克力

    分巧克力 问题描述 儿童节那天有K位小朋友到小明家做客.小明拿出了珍藏的巧克力招待小朋友们. 小明一共有N块巧克力,其中第i块是Hi x Wi的方格组成的长方形. 为了公平起见,小明需要从这 N 块巧 ...

  7. 微信SSL证书更换的检查与安装方法

    Ubuntu, Debian 查看根证书 确认操作系统上,是否存在以下文件: /etc/ssl/certs/DigiCert_Global_Root_CA.pem /etc/ssl/certs/Bal ...

  8. IOS笔记049-UITabBarController

    1.简单实现 效果:在视图底部显示一个工具栏 代码实现 // 创建窗口 self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScre ...

  9. psql 工具详细使用介绍

    psql 介绍 psql 是 PostgreSQL 中的一个命令行交互式客户端工具, 它允许你交互地键入 SQL 命令,然后把它们发送给 PostgreSQL 服务器,再显示 SQL 或命令的结果. ...

  10. P3369 【模板】普通平衡树(Splay)

    题目链接:https://www.luogu.org/problemnew/show/P3369 修改了一下之前的模板,支持重复数值的存储 #include<bits/stdc++.h> ...