差点儿全部程序猿的第一堂课都是学习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. [BZOJ3595][SCOI2014]方伯伯的OJ(裂点Splay)

    用一棵Splay按名次维护每个点,其中一个节点对应初始编号连续的一段区间,这样总节点数是$O(m)$的. 对每个编号记录这个点被Splay的那个节点维护,用std::map存储,只记录被修改的点. 每 ...

  2. 【取对数】【哈希】Petrozavodsk Winter Training Camp 2018 Day 1: Jagiellonian U Contest, Tuesday, January 30, 2018 Problem J. Bobby Tables

    题意:给你一个大整数X的素因子分解形式,每个因子不超过m.问你能否找到两个数n,k,k<=n<=m,使得C(n,k)=X. 不妨取对数,把乘法转换成加法.枚举n,然后去找最大的k(< ...

  3. java多线程技术之八(锁机制)

    Lock是java.util.concurrent.locks包下的接口,Lock 实现提供了比使用synchronized 方法和语句可获得的更广泛的锁定操作,它能以更优雅的方式处理线程同步问题,我 ...

  4. [Visual Studio] 安装清单

    VS安装位置要求路径必须是英文,且位于Program Files (x86)文件夹下. 下载工具vs_Professional.exe:https://pan.baidu.com/s/1jHRjiia ...

  5. android手机安全:被攻陷的一个场景

     到处找WIFI,对于我们的手机控来说是相当普遍的了.假设你发现了有可用的wifi,并选择了浏览器连接,当浏览器出现一个web 页面的时候,你可能已经中招了. 相同,当你的手机使用一些免费应用的时候, ...

  6. SQL语句200条(转)

    //重建数据库 101, create database testdatabase;use database testdatabase; 102, create table tt1(id int, n ...

  7. 【docker】docker基础原理,核心技术简介

    关于docker的核心技术,就是以下的三大技术: 1.namespaces [命名空间] 使用linux的命名空间实现的进程间隔离.Docker 容器内部的任意进程都对宿主机器的进程一无所知. 除了进 ...

  8. 不知道如何处理的Windows Phone异常

    后台收到这么一段异常信息,从下文中看是SQL Server Compact database出错了,但怎么出错的,为什么会出错,如何避免? 没有一点办法,如果您fix过这种问题,恳请指点迷津,谢谢! ...

  9. java自动创建多级目录

    // 创建文件上传路径 public static void mkdir(String path) { File fd = null; try { fd = new File(path); if (! ...

  10. chromium对网页获取favicon

    每一个网页都有一个favicon,在历史记录的保存中须要用到.在content文件夹下,这个没有实现. 以下说一下我的实现过程: web_contents_impl.cc文件里有方法:WebConte ...