差点儿全部程序猿的第一堂课都是学习helloworld程序,以下我们先来重温一下经典的C语言helloworl
/* hello.c */
#include <stdio.h> int main()
{
printf("hello world!\n");
return 0;
}

这是一个简单得不能再单的程序。但它包括有一个程序最重要的部分。那就是我们在差点儿全部代码中都能看到的main函数。我们编译成可运行文件并查看符号表,过滤出里面的函数例如以下(为了方便查看我手动调整了grep的输出的格式,所以和你的输出格式是不一样的)

$ gcc hello.c -o hello
$ readelf -s hello | grep FUNC
Num: Value Size Type Bind Vis Ndx Name
27: 000000000040040c 0 FUNC LOCAL DEFAULT 13 call_gmon_start
32: 0000000000400430 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux
35: 00000000004004a0 0 FUNC LOCAL DEFAULT 13 frame_dummy
40: 0000000000400580 0 FUNC LOCAL DEFAULT 13 __do_global_ctors_aux
47: 00000000004004e0 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
48: 00000000004003e0 0 FUNC GLOBAL DEFAULT 13 _start
51: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.2.5
52: 00000000004005b8 0 FUNC GLOBAL DEFAULT 14 _fini
53: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
58: 00000000004004f0 137 FUNC GLOBAL DEFAULT 13 __libc_csu_init
62: 00000000004004c4 21 FUNC GLOBAL DEFAULT 13 main
63: 0000000000400390 0 FUNC GLOBAL DEFAULT 11 _init

大家都知道用户的代码是从main函数開始运行的。尽管我们仅仅写了一个main函数,但从上面的函数表能够看到还有其他非常多函数,比方_start函数。实际上程序真正的入口并非main函数,我们以以下命令对hello.c代码进行编译

$ gcc hello.c -nostdlib
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400144

-nostdlib命令是指不链接标准库,报错说找不到entry symbol _start,这里是说找不到入口符号_start,也就是说程序的真正入口是_start函数

实际上main函数仅仅是用户代码的入口,它会由系统库去调用,在main函数之前,系统库会做一些初始化工作,比方分配全局变量的内存。初始化堆、线程等,当main函数运行完后,会通过exit()函数做一些清理工作。用户能够自己实现_start函数

/* hello_start.c */
#include <stdio.h>
#include <stdlib.h> _start(void)
{
printf("hello world!\n");
exit(0);
}

执行例如以下编译命令并执行

$ gcc hello_start.c -nostartfiles -o hello_start
$ ./hello_start
hello world!

这里的-nostartfiles的功能是Do not use the standard system startup files when linking,也就是不使用标准的startup files,可是还是会链接系统库,所以程序还是能够运行的。相同我们查看符号表

$ readelf -s hello_start | grep FUNC
Num: Value Size Type Bind Vis Ndx Name
20: 0000000000400350 24 FUNC GLOBAL DEFAULT 10 _start
21: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.2.5
22: 0000000000000000 0 FUNC GLOBAL DEFAULT UND exit@@GLIBC_2.2.5

如今就仅仅剩下三个函数了。而且都是我们自己实现的。当中printf因为仅仅有一个參数会被编译器优化为puts函数。在编译时加-fno-builtin选项能够关掉优化

假设我们在_start函数中去掉exit(0)语句。程序运行会出core。这是由于_start函数运行完程序就结束了。而我们自己实现的_start里面没有调用exit()去清理内存

好不easy去掉了main函数。这时又发现必须得有一个_start函数,是不是让人非常烦,事实上_start函数仅仅是一个默认入口,我们是能够指定入口的

/* hello_nomain.c */
#include <stdio.h>
#include <stdlib.h> int nomain()
{
printf("hello world!\n");
exit(0);
}

採用例如以下命令编译

$ gcc hello_nomain.c -nostartfiles -e nomain -o hello_nomain

当中-e选项能够指定程序入口符号,查看符号表例如以下

$ readelf -s hello_nomain | grep FUNC
Num: Value Size Type Bind Vis Ndx Name
20: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.2.5
21: 0000000000000000 0 FUNC GLOBAL DEFAULT UND exit@@GLIBC_2.2.5
22: 0000000000400350 24 FUNC GLOBAL DEFAULT 10 nomain

对照hello_start的符号表发现仅仅是将_start换成了nomain

到这里我们就非常清楚了,程序默认的入口是标准库里的_start函数。它会做一些初始化工作,调用用户的main函数。最后再做一些清理工作,我们能够自己写_start函数来覆盖标准库里的_start。甚至能够自己指定程序的入口

没有main函数的helloworld的更多相关文章

  1. main函数中System.exit()的作用

    main()主函数再熟悉不过,了解java的人也都知道System.exit()方法是停止虚拟机运行.那这里为什么还要单独写一篇博客,都是源于朋友发的一张最近刚买的T恤照片,就是上面这张图.这是一个经 ...

  2. linux编程之main()函数启动过程【转】

    转自:http://blog.csdn.net/gary_ygl/article/details/8506007 1 最简单的程序  1)编辑helloworld程序,$vim helloworld. ...

  3. 大数据学习之Scala中main函数的分析以及基本规则(2)

    一.main函数的分析 首先来看我们在上一节最后看到的这个程序,我们先来简单的分析一下.有助于后面的学习 object HelloScala { def main(args: Array[String ...

  4. Knowledge Point 20180303 详解main函数

    学习Java的朋友想来都是从HelloWorld学起的,那么想来都对main函数不陌生了,但是main函数究竟是怎么回事呢?main函数中的参数是做什么的呢?main函数为什么能作为程序的入口呢?可不 ...

  5. VS2010-如何建立并运行多个含有main函数的文件

    一.先说两个概念,解决方案与工程 在VS2010中,工程都是在解决方案管理之下的.一个解决方案可以管理多个工程,可以把解决方案理解为多个有关系或者没有关系的工程的集合. 每个应用程序都作为一个工程来处 ...

  6. [C#] 了解过入口函数 Main() 吗?带你用批处理玩转 Main 函数

    了解过入口函数 Main() 吗?带你用批处理玩转 Main 函数 目录 简介 特点 方法的参数 方法的返回值 与批处理交互的一个示例 简介 我们知道,新建一个控制台应用程序的时候,IDE 会同时创建 ...

  7. 选择目录,选择文件夹的COM组件问题。在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式。请确保您的 Main 函数带有 STAThreadAttribute 标记。 只有将调试器附加到该进程才会引发此异常。

    异常: 在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式.请确保您的 Main 函数带有 STAThreadAttribute 标记. 只有将调试器附加到该进程才会引发此异常. ...

  8. eclipse的maven项目,如何使用java run main函数

    项目使用maven管理,一般说来就使用jetty:run了.但是对于做功能测试和集成测试的用例,需要使用自定义的quickrun来运行进行测试环境的参数设定和功能隔离,google一番发现maven有 ...

  9. [汇编与C语言关系]2. main函数与启动例程

    为什么汇编程序的入口是_start,而C程序的入口是main函数呢?以下就来解释这个问题 在<x86汇编程序基础(AT&T语法)>一文中我们汇编和链接的步骤是: $ as hell ...

随机推荐

  1. 通过Nuget添加Mvvmlight框架发生错误

    IDE:Visual Studio 2013 场景:通过Nuget添加Mvvmlight框架 具体错误: 解决办法:删除Nuget,然后添加新版本的Nuget Package Manager 具体操作 ...

  2. Codeforces Round #228 (Div. 1) B. Fox and Minimal path 构造

    B. Fox and Minimal path 题目连接: http://codeforces.com/contest/388/problem/B Description Fox Ciel wants ...

  3. Xcode 中的IOS工程模板

    1.IOS模板主要分为: Application .Framework.Other application 分为:Master-Detail Application 可以构建树形导航模式引用,生成的代 ...

  4. Jmeter学习之— 参数化、关联、断言、数据库的操作

    一. Jmeter参数化1. 文件参数化解释:创建测试数据,将数据写入TXT文件文件中,然后Jmeter从文件中读取数据.例如用户注册操作:1. 首先在Jmeter下创建一个线程组,如下图: 2. 然 ...

  5. 通过yum来安装vsftpd

    Linux系统:centos6.6.  安装步骤  1.通过yum来安装vsftpd [root@localhost ~]# yum -y install vsftpd 2.设置为开机启动 [root ...

  6. Lucene——索引的创建、删除、修改

    package cn.tz.lucene; import java.io.File; import java.util.ArrayList; import java.util.List; import ...

  7. Automate Screen or Button Taps via Tasker : Simulating keypress events

    When using Tasker, sometimes we want to do some automation on screen e.g. screen or button taps. At ...

  8. XBee Level Shifting

    http://www.faludi.com/bwsn/xbee-level-shifting/ The XBee communication (RX/TX) pins definitely opera ...

  9. IDC门外汉-单线、双线、智能多线、BGP的区别

    一.单线在此不多说,不是电信,就是网通机房,是,2005年前的机房情况: 二.双线:上般是从2004年到2008年用此方法较多,此今还有不少在用此法如下: 双线主机 有单IP和单网卡双IP地址,双网卡 ...

  10. Unity3D中的弹道和移动目标提前量计算

    弹道计算是游戏里常见的问题,其中关于击中移动目标的自动计算提前量的话题,看似简单,其实还是挺复杂的数学.网上这方面的资料还真不多,而且都是写的含含糊糊.抽空总结一下自己的方法. 讨论的前提是,假设目标 ...