九九热这里直有精品,1区二区三区在线播放,玖玖爱在线观看资源,国产aⅴ综合网,午夜福利男女,日本亚洲欧美三级,日韩无码黄色导航,内射少妇13区,中文字幕高清网

您身邊的軟件定制專家--9年開發(fā)經(jīng)驗為您護航

18678812288
0531-88887250

把C程序的int main(void)改成static int main(void)會怎樣呢?

文章作者:濟南軟件開發(fā) 時間:2016年12月20日

如題,把C程序中的主函數(shù)int main(void)改成static int main(void)會怎么樣呢?

 

比如把

 

復(fù)制代碼

#include <stdio.h>

 

int main(void)

{

    printf("Hi\n");

    return 0;

}

復(fù)制代碼

修改為:

 

復(fù)制代碼

#include <stdio.h>

 

static int main(void)

{

    printf("Hi\n");

    return 0;

}

復(fù)制代碼

 

 

請讀者先自己想一想!

 

 

 

 

 

————————————————————分割線———————————————————

 

這個問題是我在看static關(guān)鍵字的時候提出來的。

 

只要你了解static關(guān)鍵字會使標(biāo)示符具有內(nèi)部鏈接(Internel Linkage)屬性,并且了解過C程序的編譯鏈接流程,應(yīng)該可以得出答案——

 

把C程序中的主函數(shù)int main(void)改成static int main(void)會導(dǎo)致鏈接失敗。

 

可以驗證一下:

 

[zhanghaiba@Fedora code]$ gcc static_int_main.c

/usr/lib/gcc/i686-redhat-linux/4.4.5/../../../crt1.o: In function `_start':

(.text+0x18): undefined reference to `main'

collect2: ld returned 1 exit status

 

如果換成gcc -c呢?

 

[zhanghaiba@Fedora code]$ gcc -c static_int_main.c

[zhanghaiba@Fedora code]$

 

可見換成gcc -c可以編譯成功,因為gcc -c只有預(yù)處理、編譯和匯編階段,沒有鏈接階段。

 

 

 

首先,我們要了解一下Linux下GCC環(huán)境中C程序的編譯鏈接流程——

 

編譯C程序,一般包括了C預(yù)處理階段、C到匯編的編譯階段、匯編到目標(biāo)文件的編譯階段、目標(biāo)文件的鏈接階段。

 

GCC支持下面幾個命令,使我們可以觀察到這些階段:

 

1)gcc -v GCC.c

 

編譯時打印出總的編譯流程,可以看到使用了哪些編譯工具。v是verbose(冗長)的意思,即盡可能多的打印信息。

 

2) gcc -E GCC.c

 

把源文件用預(yù)處理器處理,可重定向輸出到GCC.i文件再查看

 

3)gcc -S GCC.c

 

把源文件用預(yù)處理器和編譯器處理,自動輸出同名的GCC.s文件

 

4)gcc -c GCC.c

 

把源文件用預(yù)處理器、編譯器和匯編器處理,自動輸出同名.o文件

 

5)gcc GCC.c

 

把源文件用預(yù)處理器、編譯器、匯編器處理后,最后使用鏈接器生成缺省名為a.out的可執(zhí)行文件

 

為什么默認(rèn)叫a.out?因為早期編譯并沒有鏈接器的概念,a.out是匯編器直接生成的,a意為assembly。但需要澄清的是在現(xiàn)代編譯器中a.out都是由鏈接器生成。

 

另外,使用選項-save-temps可以保留中間生成的文件,示范如下: 

 

[zhanghaiba@Fedora code]$ ls | grep hi

hi.c

hi.i

hi.o

hi.s

 

 

我們再用gcc -v來觀察總的編譯流程

 

復(fù)制代碼

[zhanghaiba@Fedora code]$ gcc -v hi.c

Using built-in specs.

Target: i686-redhat-linux

Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic --with-arch=i686 --build=i686-redhat-linux

Thread model: posix

gcc version 4.4.5 20101112 (Red Hat 4.4.5-2) (GCC) 

COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=i686'

 /usr/libexec/gcc/i686-redhat-linux/4.4.5/cc1 -quiet -v hi.c -quiet -dumpbase hi.c -mtune=generic -march=i686 -auxbase hi -version -o /tmp/ccrwAICf.s

ignoring nonexistent directory "/usr/lib/gcc/i686-redhat-linux/4.4.5/include-fixed"

ignoring nonexistent directory "/usr/lib/gcc/i686-redhat-linux/4.4.5/../../../../i686-redhat-linux/include"

#include "..." search starts here:

#include <...> search starts here:

 /usr/local/include

 /usr/lib/gcc/i686-redhat-linux/4.4.5/include

 /usr/include

End of search list.

GNU C (GCC) version 4.4.5 20101112 (Red Hat 4.4.5-2) (i686-redhat-linux)

    compiled by GNU C version 4.4.5 20101112 (Red Hat 4.4.5-2), GMP version 4.3.1, MPFR version 2.4.2.

GGC heuristics: --param ggc-min-expand=81 --param ggc-min-heapsize=95788

Compiler executable checksum: e892644090a9a7e8c330a388c51818dd

COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=i686'

 as -V -Qy -o /tmp/cc1w7Hxi.o /tmp/ccrwAICf.s

GNU assembler version 2.20.51.0.2 (i686-redhat-linux) using BFD version version 2.20.51.0.2-15.fc13 20091009

COMPILER_PATH=/usr/libexec/gcc/i686-redhat-linux/4.4.5/:/usr/libexec/gcc/i686-redhat-linux/4.4.5/:/usr/libexec/gcc/i686-redhat-linux/:/usr/lib/gcc/i686-redhat-linux/4.4.5/:/usr/lib/gcc/i686-redhat-linux/:/usr/libexec/gcc/i686-redhat-linux/4.4.5/:/usr/libexec/gcc/i686-redhat-linux/:/usr/lib/gcc/i686-redhat-linux/4.4.5/:/usr/lib/gcc/i686-redhat-linux/

LIBRARY_PATH=/usr/lib/gcc/i686-redhat-linux/4.4.5/:/usr/lib/gcc/i686-redhat-linux/4.4.5/:/usr/lib/gcc/i686-redhat-linux/4.4.5/../../../:/lib/:/usr/lib/

COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=i686'

 /usr/libexec/gcc/i686-redhat-linux/4.4.5/collect2 --no-add-needed --eh-frame-hdr --build-id -m elf_i386 --hash-style=gnu -dynamic-linker /lib/ld-linux.so.2 /usr/lib/gcc/i686-redhat-linux/4.4.5/../../../crt1.o /usr/lib/gcc/i686-redhat-linux/4.4.5/../../../crti.o /usr/lib/gcc/i686-redhat-linux/4.4.5/crtbegin.o -L/usr/lib/gcc/i686-redhat-linux/4.4.5 -L/usr/lib/gcc/i686-redhat-linux/4.4.5 -L/usr/lib/gcc/i686-redhat-linux/4.4.5/../../.. /tmp/cc1w7Hxi.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i686-redhat-linux/4.4.5/crtend.o /usr/lib/gcc/i686-redhat-linux/4.4.5/../../../crtn.o

 

復(fù)制代碼

 

 

注意紅色加粗部分(由綠色文件生成紅色文件)——

 

(1)cc1是GCC編譯環(huán)境中的C編譯器,把C代碼編譯為匯編代碼,輸出為.s文件

 

(2)as是匯編器,把匯編代碼編譯為目標(biāo)文件,輸出為.o文件

 

(3)collect2是GCC后期版本使用的鏈接器(環(huán)境),其實是先調(diào)用GNU的鏈接器ld對目標(biāo)文件進行鏈接,最后收集與程序初始化相關(guān)的信息,構(gòu)造程序的初始化結(jié)構(gòu)。

 

  ld是真正的鏈接器,對上一步的.o目標(biāo)文件和其它需要.o文件或靜態(tài)鏈接庫.a文件、動態(tài)鏈接庫.so文件(如解壓C標(biāo)準(zhǔn)庫libc.a中取出需要的printf.o文件),一起鏈接輸出為a.out文件。

 

  GCC后期版本使用了collect2來作為鏈接器,其實是間接調(diào)用ld鏈接器。

 

上面用到的工具中,as是GNU自帶的匯編器,ld是GNU自帶的鏈接器,它倆是GNU Binutils中最主要的二進制工具。

 

其中,ld-linux.so.2是動態(tài)鏈接器。最后注意-lc參數(shù),l表示鏈接,c表示標(biāo)準(zhǔn)C庫,即libc.a或libc.so。

 

 

 

讓我們回到問題本身——

 

main不是C語言的關(guān)鍵字,但卻是約定俗成的主函數(shù)名字,不過它并不是程序執(zhí)行的入口,

 

C程序真正入口是_start全局符號(由匯編實現(xiàn)的函數(shù)),_start函數(shù)會調(diào)用庫函數(shù)__libc_start_main,然后__libc_start_main再調(diào)用main函數(shù)

 

我們知道m(xù)ain函數(shù)的聲明無非兩種形式,main函數(shù)的聲明(main符號)其實是在crt1.o目標(biāo)文件中

 

通過nm工具可以查看crt1.o包括了哪些符號

 

復(fù)制代碼

[zhanghaiba@Fedora code]$ nm /usr/lib/crt1.o

00000000 R _IO_stdin_used

00000004 D __data_start

         U __libc_csu_fini

         U __libc_csu_init

         U __libc_start_main

00000044 R _fp_hw

00000020 T _start

00000004 W data_start

         U main

復(fù)制代碼

crt1.o中已經(jīng)有了main符號,但卻是未定義(U)的,所以需要我們來實現(xiàn)main函數(shù)(即定義main符號),最后通過鏈接器來鏈接(這里稱作符號解析)

 

如果把main函數(shù)定義為static,也就是具有內(nèi)部鏈接(Internel Linkage)屬性,則編譯后的目標(biāo)文件是局部符號(當(dāng)前文件可見)

 

然而鏈接是不會對局部符號做符號解析的,只會根據(jù)目標(biāo)文件的.rel.text段來指示鏈接全局的且未定義的符號(即修改可重定位目標(biāo)文件REL的符號地址)

 

因此,鏈接時main符號找不到定義,這導(dǎo)致main符號找不到具體實現(xiàn)(定義),造成鏈接失敗

 

 

 

我們再看編譯失敗的反饋信息

 

/usr/lib/gcc/i686-redhat-linux/4.4.5/../../../crt1.o: In function `_start':

(.text+0x18): undefined reference to `main'

collect2: ld returned 1 exit status

 

就不難理解了——

 

在函數(shù)_start中,引用了未定義的符號main

 

collect2外殼:鏈接器ld返回1標(biāo)記退出狀態(tài)(出錯狀態(tài))


想要了解更多詳情歡迎來電咨詢18678812288
登陸網(wǎng)址:m.h6244.cn。
聯(lián)系人:王經(jīng)理。

永兴县| 汶川县| 渭南市| 锦州市| 庆安县| 本溪市| 云和县| 阿勒泰市| 高碑店市| 西平县| 博兴县| 福海县| 仪征市| 临沭县| 旺苍县| 沂源县| 莒南县| 唐山市| 临汾市| 会宁县| 遂溪县| 新兴县| 蓬溪县| 扬州市| 济阳县| 盱眙县| 拜泉县| 光山县| 三河市| 揭阳市| 临澧县| 北宁市| 渭源县| 永新县| 民和| 巍山| 南汇区| 饶河县| 辽宁省| 延吉市| 东乌珠穆沁旗|