没有main函数的helloworld
/* 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的更多相关文章
- main函数中System.exit()的作用
main()主函数再熟悉不过,了解java的人也都知道System.exit()方法是停止虚拟机运行.那这里为什么还要单独写一篇博客,都是源于朋友发的一张最近刚买的T恤照片,就是上面这张图.这是一个经 ...
- linux编程之main()函数启动过程【转】
转自:http://blog.csdn.net/gary_ygl/article/details/8506007 1 最简单的程序 1)编辑helloworld程序,$vim helloworld. ...
- 大数据学习之Scala中main函数的分析以及基本规则(2)
一.main函数的分析 首先来看我们在上一节最后看到的这个程序,我们先来简单的分析一下.有助于后面的学习 object HelloScala { def main(args: Array[String ...
- Knowledge Point 20180303 详解main函数
学习Java的朋友想来都是从HelloWorld学起的,那么想来都对main函数不陌生了,但是main函数究竟是怎么回事呢?main函数中的参数是做什么的呢?main函数为什么能作为程序的入口呢?可不 ...
- VS2010-如何建立并运行多个含有main函数的文件
一.先说两个概念,解决方案与工程 在VS2010中,工程都是在解决方案管理之下的.一个解决方案可以管理多个工程,可以把解决方案理解为多个有关系或者没有关系的工程的集合. 每个应用程序都作为一个工程来处 ...
- [C#] 了解过入口函数 Main() 吗?带你用批处理玩转 Main 函数
了解过入口函数 Main() 吗?带你用批处理玩转 Main 函数 目录 简介 特点 方法的参数 方法的返回值 与批处理交互的一个示例 简介 我们知道,新建一个控制台应用程序的时候,IDE 会同时创建 ...
- 选择目录,选择文件夹的COM组件问题。在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式。请确保您的 Main 函数带有 STAThreadAttribute 标记。 只有将调试器附加到该进程才会引发此异常。
异常: 在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式.请确保您的 Main 函数带有 STAThreadAttribute 标记. 只有将调试器附加到该进程才会引发此异常. ...
- eclipse的maven项目,如何使用java run main函数
项目使用maven管理,一般说来就使用jetty:run了.但是对于做功能测试和集成测试的用例,需要使用自定义的quickrun来运行进行测试环境的参数设定和功能隔离,google一番发现maven有 ...
- [汇编与C语言关系]2. main函数与启动例程
为什么汇编程序的入口是_start,而C程序的入口是main函数呢?以下就来解释这个问题 在<x86汇编程序基础(AT&T语法)>一文中我们汇编和链接的步骤是: $ as hell ...
随机推荐
- css选择器(第n个类选择器)的坑
css选择器选择第n个子元素,共有两种写法: .parent span:nth-child(n) 选择parent下的第n个子元素(不管前边是不是span,都算在内) .parent span:nth ...
- java的多线程之四(线程的操作)
本文来自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/17560467,转载请注明. 线程中断 线程中断涉及到三个方法,如下 ...
- 保存全局Crash报告&发送邮件
上篇写到,将程序中没有处理到的crash信息保存到本地文件夹下.但是实际的情况是,你不可能总是将用户的设备拿过来.所以一般性的处理是,将crash reports发送到服务器或者邮箱.所以针对上篇的代 ...
- bestcoder#23 1002 Sequence II 树状数组+DP
Sequence II Time Limit: 5000/2500 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- windows下tomcat的安装配置
一.下载相应的JDK以及tomcat的版本 JDK:jdk-8u131-windows-x64 tomcat:apache-tomcat-8.5.23-windows-x64.zip 二.JDK的安装 ...
- Oracle连接步骤
JDBC实现数据所有的操作: 数据库连接需要的步骤 1.数据库的驱动程序:oracle.jdbc.driver.OracleDriver; 2.连接地址:jdbc:oracle:thin:@主机地址: ...
- spring---transaction(2)---源代码分析(事务的定义TransactionDefinition)
写在前面 事务属性通过TransactionDefinition接口实现定义,主要有事务隔离级别.事务传播行为.事务超时时间.事务是否只读. public interface TransactionD ...
- Git_分支管理策略
通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息. 如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的comm ...
- ibatis实战之中的一个对多关联
在实际开发中,我们经常遇到关联数据的情况,如User对象拥有若干Book对象 每一个Book对象描写叙述了归属于一个User信息,这样的情况下,我们应该怎样处理? 通过单独的Statement操作固然 ...
- SQLite 管理工具 SQLite Expert
SQLite Expert – Personal Edition SQLite Expert 提供两个版本,分别是个人版和专业版.其中个人版是免费的,提供了大多数基本的管理功能. SQLite Exp ...