1、define预处理指令
这种方式很简单,只是简单地将main字符串用宏来代替,或者使用##拼接字符串。示例程序如下:

#include <stdio.h>

#define begin main

int begin(void)

{

printf(
"Hello, World!\n");

return
;

}

#include <stdio.h>

#define begin m##a##i##n

int begin(void)

{

printf(
"Hello, World!\n");

return
;

}

严格来说,这种方式只算是一种技巧......

2、_start函数
_start函数是C程序的入口函数,会调用main函数。在调用main函数之前,会先执行_start函数分配必要的资源,然后再调用main函数。但是在用gcc编译程序时可以使用-nostartfiles选项来重写_start函数。示例程序如下:
#include <stdio.h>


#include <stdlib.h>

_start(void) {

printf(
"Hello, World!\n");


exit(
);

}

编译上面的程序的命令为:
gcc
-nostartfiles _start.c
-o a.out

反汇编生成的可执行程序,如下所示:

a.out
: file format elf64
-x86
-

Disassembly of section .plt
:

<puts@plt
-0x10
>
:

: ff
ea

00 pushq 0x2001ea(
%rip)
# 600510 <_GLOBAL_OFFSET_TABLE_+0x8>

: ff
ec

00 jmpq
*0x2001ec(
%rip)
# 600518 <_GLOBAL_OFFSET_TABLE_+0x10>


40032c
: 0f
1f
00 nopl 0x0(
%rax)

<puts@plt
>
:

: ff
ea

00 jmpq
*0x2001ea(
%rip)
# 600520 <_GLOBAL_OFFSET_TABLE_+0x18>

:
00 00 00 00 pushq $0x0


40033b
: e9 e0 ff ff ff jmpq

<puts@plt
-0x10
>

<
exit@plt
>
:

: ff
e2

00 jmpq
*0x2001e2(
%rip)
# 600528 <_GLOBAL_OFFSET_TABLE_+0x20>

:

00 00 00 pushq $0x1


40034b
: e9 d0 ff ff ff jmpq

<puts@plt
-0x10
>

Disassembly of section .text
:

<_start
>
:

:
push
%rbp

:

e5 mov
%rsp,
%rbp

: bf

00 mov $0x400368,
%edi

: e8 d2 ff ff ff callq

<puts@plt
>


40035e
: bf 00 00 00 00 mov $0x0,
%edi

: e8 d8 ff ff ff callq

exit@plt

上面的结果是完整的反汇编结果,我们可以看到_start函数中只有我们调用printf和exit函数相关的一些指令,并且.txt段中只有_start函数,没有看到main函数。如果将源代码中的_start替换为main,重新编译程序,反汇编的结果中会看到_start函数会调用到main。
另外还有一点需要注意,因为这里重写了_start函数,所以gcc为默认的main函数准备的清理动作就没用上,所以如果退出的时候直接使用return,会导致程序崩溃。所以这里要使用exit()来退出程序。具体的原因可以参见这篇文章
3、gcc的-e选项
示例程序如下:

#include <stdio.h>


#include <stdlib.h>

int nomain(int i, int j, int k) {

printf(
"Hello, World!\n");


exit(
);

}

将上面的程序保存为m.c,编译命令如下所示:
gcc
-nostartfiles
-e nomain m.c
-o a.out

继续使用objdump反汇编生成的可执行程序,结果如下:

a.out
: file format elf64
-x86
-

Disassembly of section .plt
:

<puts@plt
-0x10
>
:

: ff
f2

00 pushq 0x2001f2(
%rip)
# 600518 <_GLOBAL_OFFSET_TABLE_+0x8>

: ff
f4

00 jmpq
*0x2001f4(
%rip)
# 600520 <_GLOBAL_OFFSET_TABLE_+0x10>


40032c
: 0f
1f
00 nopl 0x0(
%rax)

<puts@plt
>
:

: ff
f2

00 jmpq
*0x2001f2(
%rip)
# 600528 <_GLOBAL_OFFSET_TABLE_+0x18>

:
00 00 00 00 pushq $0x0


40033b
: e9 e0 ff ff ff jmpq

<puts@plt
-0x10
>

<
exit@plt
>
:

: ff
ea

00 jmpq
*0x2001ea(
%rip)
# 600530 <_GLOBAL_OFFSET_TABLE_+0x20>

:

00 00 00 pushq $0x1


40034b
: e9 d0 ff ff ff jmpq

<puts@plt
-0x10
>

Disassembly of section .text
:

<nomain
>
:

:
push
%rbp

:

e5 mov
%rsp,
%rbp

:

ec
sub $0x10,
%rsp

:

7d fc mov
%edi,
-0x4(
%rbp)


40035b
:

f8 mov
%esi,
-0x8(
%rbp)


40035e
:

f4 mov
%edx,
-0xc(
%rbp)

: bf

00 mov $0x400375,
%edi

: e8 c5 ff ff ff callq

<puts@plt
>


40036b
: bf 00 00 00 00 mov $0x0,
%edi

: e8 cb ff ff ff callq

<
exit@plt
>

从上面我们可以看到指定的nomain函数位于.text段的开始位置,同样在函数结束的时候没有gcc为main函数准备的清理动作,所以在这里也只能使用exit()来退出程序,而不能使用return。
4、nostartfiles选项
前面已经多次使用了该选项,不过都是配合其他选项使用的,这个选项也可以单独使用,其含义为"Do not use the standard system startup files when linking"。
示例程序如下:

#include <stdio.h>


#include <stdlib.h>

void func() {

printf(
"I am func....\n");

}

int nomain1(int i, int j, int k) {

func();

printf(
"%s: Hello, World!\n", __func__);


exit(
);

}

上面的程序保存为k.c,然后使用下面的命令编译:

[root@CentOS_190
~]
# gcc -nostartfiles p.c


/usr
/bin
/ld
: warning
: cannot find entry symbol _start; defaulting to

在单独使用nostartfiles选项时会报警告,生成的可执行程序可以执行,但是会产生段错误,去掉对func()函数的调用就不会产生段错误了。将生成的可执行程序反汇编,和使用前面的方法生成可执行程序的反汇编结果比较,发现除了函数名不一样外,没有其他区别,不知道为什么会产生段错误。知道的麻烦告知一声,拜谢!

C语言中没有main函数生成可执行程序的几种方法的更多相关文章

  1. C语言中存储多个字符串的两种方式

    C语言中存储多个字符串的两种方式 方式一    二维字符串数组 声明: char name[][] = { "Justinian", "Momo", " ...

  2. 20155212 C语言实现linux下pwd命令的两种方法

    20155212 C语言实现linux下pwd命令的两种方法 学习pwd命令 通过man pwd命令查看 pwd [OPTION],一般不加参数 -P显示当前目录的物理路径 -L显示当前目录的连接路径 ...

  3. Java中取小数点后两位(四种方法)

    摘自http://irobot.iteye.com/blog/285537 Java中取小数点后两位(四种方法)   一 Long是长整型,怎么有小数,是double吧     java.text.D ...

  4. linux中删除文件内空白行的几种方法。

    linux中删除文件内空白行的几种方法 有时你可能需要在 Linux 中删除某个文件中的空行.如果是的,你可以使用下面方法中的其中一个.有很多方法可以做到,但我在这里只是列举一些简单的方法. 你可能已 ...

  5. (转载)Java中如何遍历Map对象的4种方法

    在Java中如何遍历Map对象 How to Iterate Over a Map in Java 在java中遍历Map有不少的方法.我们看一下最常用的方法及其优缺点. 既然java中的所有map都 ...

  6. 服务器文档下载zip格式 SQL Server SQL分页查询 C#过滤html标签 EF 延时加载与死锁 在JS方法中返回多个值的三种方法(转载) IEnumerable,ICollection,IList接口问题 不吹不擂,你想要的Python面试都在这里了【315+道题】 基于mvc三层架构和ajax技术实现最简单的文件上传 事件管理

    服务器文档下载zip格式   刚好这次项目中遇到了这个东西,就来弄一下,挺简单的,但是前台调用的时候弄错了,浪费了大半天的时间,本人也是菜鸟一枚.开始吧.(MVC的) @using Rattan.Co ...

  7. linux中快速清空文件内容的几种方法

    这篇文章主要介绍了linux中快速清空文件内容的几种方法,需要的朋友可以参考下 $ : > filename $ > filename $ echo "" > f ...

  8. (转)在网页中JS函数自动执行常用三种方法

    原文:http://blog.sina.com.cn/s/blog_6f6b4c3c0100nxx8.html 在网页中JS函数自动执行常用三种方法 在网页中JS函数自动执行常用三种方法 在HTML中 ...

  9. Java 获取*.properties配置文件中的内容 ,常见的两种方法

    import java.io.InputStream; import java.util.Enumeration; import java.util.List; import java.util.Pr ...

随机推荐

  1. 高效操作DOM

    DOM树与Render树 这个应该都是知道的.就是用户请求HTML下来后,浏览器渲染引擎的基本工作中两个概念. copy一张图,流程大概就是:解析html构建DOM树,渲染树构建,渲染树布局,绘制渲染 ...

  2. 分布式配置管理平台 Disconf

    Distributed Configuration Management Platform(分布式配置管理平台) 专注于各种 分布式系统配置管理 的通用组件/通用平台, 提供统一的配置管理服务. 包括 ...

  3. 过拟合/欠拟合&logistic回归等总结(Ng第二课)

    昨天学习完了Ng的第二课,总结如下: 过拟合:欠拟合: 参数学习算法:非参数学习算法 局部加权回归 KD tree 最小二乘 中心极限定律 感知器算法 sigmod函数 梯度下降/梯度上升 二元分类 ...

  4. qt example

    #include"mainwindow.h" #include"ui_mainwindow.h" #include<QWebFrame> #incl ...

  5. swift3.0 hello swift(1)

    一直对swift感兴趣,在前段时间的新闻中,大多是swift3.0发布和xcode8.0的改进,因为改动比较大,以前使用swift2.x做项目的人,都在担心其项目从2.x迁移到3.0+的问题.以前简单 ...

  6. 2014 android毕设代做 代做Android毕设 安卓毕设

    近期略有闲暇. 看到部分老友帮忙做毕业设计. 一来能够锻炼下小技能,二来能够得点零花小钱. 心遂小动.跃跃欲试. 毕设这件事情,个人觉得还是自己做的好. 只是每一个人都有各种各样的原因不能自己完毕. ...

  7. Linux下启用Chrome/Firefox的Java插件

    JDK 已经安装好,可是浏览器执行 Java Applet 时提示需安装 Java 插件. 这时,在浏览器安装文件夹中 plugins 文件夹下创建2个重要的符号链接就可以. libnpjp2.so ...

  8. javadoc入门

    斌斌 (给我写信) 原创博文(http://blog.csdn.net/binbinxyz),转载请注明出处! java凝视 java里面有两种类型的凝视.一种是以"/*"起头,以 ...

  9. 【转】Ubuntu常用软件合集

    [转]Ubuntu常用软件合集 Ubuntu常用软件合集 我用的使Ubuntu-Kylin14.04,原因呢主要是觉得使本土化的,自带了日历.输入法.优客助手等易于上手的应用.也省的每次安装完原生的系 ...

  10. Enze fourth day(循环语句 一)

    哈喽,大家好.又到了总结知识的时间了.今天在云和学院自学了一下循环语句,下面是自己总的一些知识点. 先补充一下选择结构中的switch语句. 理论:switch语句是一种多分支选择语句,当需要测试大量 ...