没有了 main 函数,程序还能跑吗?

Posted Li-Yongjun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了没有了 main 函数,程序还能跑吗?相关的知识,希望对你有一定的参考价值。

刑天

在这里插入图片描述

刑天,是中国远古神话传说人物,手使一柄巨斧和盾牌,身强力壮,体型巨大的上古巨人,炎帝手下大将,和黄帝争位,被斩去头颅,失了首级后,以双乳为眼,肚脐为口,再战黄帝。

刑天没有了头仍然可以战斗,程序没有了 main 函数,还能跑吗?


答案是:可以的。

代码

nomain.c

#include <stdio.h>
#include <stdlib.h>

void nomain()
{
    printf("hello world\\n");
    exit(0);
}

编译

$ gcc -nostartfiles nomain.c -o nomain.out
/usr/bin/ld: 警告: 无法找到项目符号 _start; 缺省为 0000000000001050

忽略警告
-nostartfiles 选项是让链接器在链接时不使用标准启动文件


运行

$ ./nomain.out 
hello world

探索

我们使用 -S 参数将 c 程序编译成汇编,一探究竟

$ gcc -S -nostartfiles nomain.c
liyongjun@Box:~/project/c/C_study/others/ld$ cat nomain.s 
        .file   "nomain.c"
        .text
        .section        .rodata
.LC0:
        .string "hello world"
        .text
        .globl  nomain
        .type   nomain, @function
nomain:
.LFB6:
        .cfi_startproc
        endbr64
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        leaq    .LC0(%rip), %rdi
        call    puts@PLT
        movl    $0, %edi
        call    exit@PLT
        .cfi_endproc
...

可以看到程序的入口点确实是 nomain,所以我们的程序可以正常运行。


疑问

如果代码里有两个函数,程序该选择哪个作为入口呢?

#include <stdio.h>
#include <stdlib.h>

void nomain()
{
    printf("hello world\\n");
    exit(0);
}

void nomain_2()
{
    printf("hello world 2\\n");
    exit(0);
}
$ gcc -nostartfiles nomain.c -o nomain.out
/usr/bin/ld: 警告: 无法找到项目符号 _start; 缺省为 0000000000001050
$ ./nomain.out 
hello world

从执行的结果可以看到程序选择了 nomain() 函数作为了程序入口点,看下汇编的内容:

$ gcc -S -nostartfiles nomain.c
liyongjun@Box:~/project/c/C_study/others/ld$ cat nomain.s 
        .file   "nomain.c"
        .text
        .section        .rodata
.LC0:
        .string "hello world"
        .text
        .globl  nomain
        .type   nomain, @function
nomain:
.LFB6:
        .cfi_startproc
        endbr64
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        leaq    .LC0(%rip), %rdi
        call    puts@PLT
        movl    $0, %edi
        call    exit@PLT
        .cfi_endproc
.LFE6:
        .size   nomain, .-nomain
        .section        .rodata
.LC1:
        .string "hello world 2"
        .text
        .globl  nomain_2
        .type   nomain_2, @function
nomain_2:
.LFB7:
        .cfi_startproc
        endbr64
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        leaq    .LC1(%rip), %rdi
        call    puts@PLT
        movl    $0, %edi
        call    exit@PLT
        .cfi_endproc
...

显然,在 c 程序中,nomain 函数在 nomain_2 函数之前,编译成汇编后,nomain 依然在前面,就被选择作为了程序的入口点。如果我们把 nomain_2 写在前面,那么 nomain_2 就会被选为函数的入口点,验证如下:

#include <stdio.h>
#include <stdlib.h>

void nomain_2()
{
    printf("hello world 2\\n");
    exit(0);
}

void nomain()
{
    printf("hello world\\n");
    exit(0);
}
$ gcc -nostartfiles nomain.c -o nomain.out
/usr/bin/ld: 警告: 无法找到项目符号 _start; 缺省为 0000000000001050
$ ./nomain.out 
hello world 2

指定

在不改变代码的情况下,我们可以使用 gcc 的 -e 选项来指定程序的入口函数

#include <stdio.h>
#include <stdlib.h>

void nomain_2()
{
    printf("hello world 2\\n");
    exit(0);
}

void nomain_3()
{
    printf("hello world 3\\n");
    exit(0);
}

void nomain()
{
    printf("hello world\\n");
    exit(0);
}
$ gcc -nostartfiles -e nomain_3 nomain.c -o nomain.out
$ ./nomain.out 
hello world 3

就像换挡,指定一个挡位作为动力运行点。
在这里插入图片描述

以上是关于没有了 main 函数,程序还能跑吗?的主要内容,如果未能解决你的问题,请参考以下文章

用服务写USB的拔插程序。求详细源代码,能用能跑的啊

Dart 官方开源的在线编辑器,还能跑 Flutter

arp 的基础概念

能天天跑步吗?跑多久对身体是最好的?

jsp页面被tomcat引擎运行的时候组装成java片段,但是这些java片段怎么没有main方法作为程序的入口啊?

Android源码怎么没有main方法还能运行?