C语言程序可以没有main函数
原文鏈接:http://www.wufangbo.com/175871/
學(xué)習(xí)C語言的同學(xué)都知道,每個C程序要有一個main函數(shù),程序從main函數(shù)開始執(zhí)行,在main函數(shù)中結(jié)束。但事實(shí)上,C程序也可以沒有main函數(shù),或者說自己可以指定入口函數(shù)。下面這篇文章介紹了如何實(shí)現(xiàn)這一過程。這篇文章轉(zhuǎn)自:http://www.codeweblog.com,作者不詳。學(xué)習(xí)這個內(nèi)容對程序設(shè)計沒啥影響,但能更深入地了解程序編譯和鏈接的原理。
? ? 這篇文章主要介紹了c語言之沒有main函數(shù)的helloworld示例,本文分解了帶main函數(shù)的helloworld示例,從而分析出不需要main函數(shù)的helloworld示例,需要的朋友可以參考下。
? ? 幾乎所有程序員的第一堂課都是學(xué)習(xí)helloworld程序,下面我們先來重溫一下經(jīng)典的C語言helloworld。
/* hello.c */?
#include <stdio.h>?
int main()?
{?
??? printf("helloworld!/n");?
??? return 0;?
}?
? ? 這是一個簡單的不能再簡單的程序,但它包含有一個程序最重要的部分,那就是我們在幾乎所有代碼中都能看到的main函數(shù),我們編譯成可執(zhí)行文件并查看符號表,過濾出里面的函數(shù)如下(為了方便查看我手動調(diào)整了grep的輸出的格式,所以和你的輸出格式是不一樣的)
$ gcc hello.c -o hello?
$ readelf -s hello | grep FUNC
Num: ? ? Value ? ? ? ? ? Size ? ?Type??? Bind ? ? Vis ? ? ? ? NdxName?
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 aliyunzixun@xxx.com@GLIBC_2.2.5?
52: 00000000004005b8 ? ? ?0 ? ? ?FUNC??? GLOBAL ?DEFAULT ? ? ?14 _fini?
53: 0000000000000000 ? ? ?0 ? ? ?FUNC??? GLOBAL ?DEFAULT ? ? ?UND aliyunzixun@xxx.com@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函數(shù)開始執(zhí)行的,雖然我們只寫了一個main函數(shù),但從上面的函數(shù)表可以看到還有其它很多函數(shù),比如_start函數(shù)。實(shí)際上程序真正的入口并不是main函數(shù),我們以下面命令對hello.c代碼進(jìn)行編譯:
$ gcc hello.c -nostdlib?
/usr/bin/ld: warning: cannot find entrysymbol _start; defaulting to 0000000000400144
? ? -nostdlib命令是指不鏈接標(biāo)準(zhǔn)庫,報錯說找不到entry symbol _start,這里是說找不到入口符號_start,也就是說程序的真正入口是_start函數(shù)。
? ? 實(shí)際上main函數(shù)只是用戶代碼的入口,它會由系統(tǒng)庫去調(diào)用,在main函數(shù)之前,系統(tǒng)庫會做一些初始化工作,比如分配全局變量的內(nèi)存,初始化堆、線程等,當(dāng)main函數(shù)執(zhí)行完后,會通過exit()函數(shù)做一些清理工作,用戶可以自己實(shí)現(xiàn)_start函數(shù):
/* hello_start.c */?
#include <stdio.h>?
#include <stdlib.h>?
_start(void)?
{?
??? printf("hello world!/n");?
??? exit(0);?
}?
? ? 執(zhí)行如下編譯命令并運(yùn)行:
$ gcc hello_start.c -nostartfiles -ohello_start?
$ ./hello_start?
hello world!
? ? 這里的-nostartfiles的功能是Do notuse the standard system startup files when linking,也就是不使用標(biāo)準(zhǔn)的startupfiles,但是還是會鏈接系統(tǒng)庫,所以程序還是可以執(zhí)行的。同樣我們查看符號表:
$ readelf -s hello_start | grep FUNC?
Num: ? ? ?Value ? ? ? ? ? Size ? ?Type ? ? Bind ? ? Vis ? ? ? ? ?NdxName?
20: ?0000000000400350 ? ? ?24 ? ? FUNC??? GLOBAL ? DEFAULT ? ? ? 10 _start?
21: ?0000000000000000 ? ? ?0 ? ? ?FUNC??? GLOBAL ? DEFAULT ? ? ? UND aliyunzixun@xxx.com@GLIBC_2.2.5?
22: ?0000000000000000 ? ? ?0 ? ? ?FUNC??? GLOBAL ? DEFAULT ? ? ? UND aliyunzixun@xxx.com@GLIBC_2.2.5
? ? 現(xiàn)在就只剩下三個函數(shù)了,并且都是我們自己實(shí)現(xiàn)的,其中printf由于只有一個參數(shù)會被編譯器優(yōu)化為puts函數(shù),在編譯時加-fno-builtin選項可以關(guān)掉優(yōu)化。
? ? 如果我們在_start函數(shù)中去掉exit(0)語句,程序執(zhí)行會出core,這是因為_start函數(shù)執(zhí)行完程序就結(jié)束了,而我們自己實(shí)現(xiàn)的_start里面沒有調(diào)用exit()去清理內(nèi)存。
? ? 好不容易去掉了main函數(shù),這時又發(fā)現(xiàn)必須得有一個_start函數(shù),是不是讓人很煩,其實(shí)_start函數(shù)只是一個默認(rèn)入口,我們是可以指定入口的
/* hello_nomain.c */?
#include <stdio.h>?
#include <stdlib.h>?
int nomain()?
{?
??? printf("helloworld!/n");?
??? exit(0);?
}
采用如下命令編譯?
$ gcc hello_nomain.c -nostartfiles -enomain -o hello_nomain?
? ? 其中-e選項可以指定程序入口符號,查看符號表如下:
$ readelf -s hello_nomain | grep FUNC?
Num: ? ? Value ? ? ? ? ? ?Size ? ?Type ? ? ?Bind ? ? ? Vis ? ? ? NdxName?
20: ? 0000000000000000???? 0 ? ? ?FUNC??? ?GLOBAL ? ?DEFAULT ? ? aliyunzixun@xxx.com@GLIBC_2.2.5?
21: ? 0000000000000000???? 0 ? ? ?FUNC??? ?GLOBAL ? ?DEFAULT ? ? aliyunzixun@xxx.com@GLIBC_2.2.5?
22: ? 0000000000400350 ? ? 24 ? ? FUNC ? ? GLOBAL ? ?DEFAULT ? ? 10 nomain
?
? ? 對比hello_start的符號表發(fā)現(xiàn)只是將_start換成了nomain。
? ? 到這里我們就很清楚了,程序默認(rèn)的入口是標(biāo)準(zhǔn)庫里的_start函數(shù),它會做一些初始化工作,調(diào)用用戶的main函數(shù),最后再做一些清理工作,我們可以自己寫_start函數(shù)來覆蓋標(biāo)準(zhǔn)庫里的_start,甚至可以自己指定程序的入口。
總結(jié)
以上是生活随笔為你收集整理的C语言程序可以没有main函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vim 打造属于自己的 IDE
- 下一篇: lds文件分析