程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 把C程序的int main(void)改成static int main(void)會怎樣呢?

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

編輯:關於C語言

如題,把C程序中的主函數int main(void)改成static int main(void)會怎麼樣呢?   比如把   復制代碼 #include <stdio.h>   int main(void) {     printf("Hi\n");     return 0; } 復制代碼 修改為:   復制代碼 #include <stdio.h>   static int main(void) {     printf("Hi\n");     return 0; } 復制代碼     請讀者先自己想一想!           ————————————————————分割線———————————————————   這個問題是我在看static關鍵字的時候提出來的。   只要你了解static關鍵字會使標示符具有內部鏈接(Internel Linkage)屬性,並且了解過C程序的編譯鏈接流程,應該可以得出答案——   把C程序中的主函數int main(void)改成static int main(void)會導致鏈接失敗。   可以驗證一下:   [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只有預處理、編譯和匯編階段,沒有鏈接階段。       首先,我們要了解一下Linux下GCC環境中C程序的編譯鏈接流程——   編譯C程序,一般包括了C預處理階段、C到匯編的編譯階段、匯編到目標文件的編譯階段、目標文件的鏈接階段。   GCC支持下面幾個命令,使我們可以觀察到這些階段:   1)gcc -v GCC.c   編譯時打印出總的編譯流程,可以看到使用了哪些編譯工具。v是verbose(冗長)的意思,即盡可能多的打印信息。   2) gcc -E GCC.c   把源文件用預處理器處理,可重定向輸出到GCC.i文件再查看   3)gcc -S GCC.c   把源文件用預處理器和編譯器處理,自動輸出同名的GCC.s文件   4)gcc -c GCC.c   把源文件用預處理器、編譯器和匯編器處理,自動輸出同名.o文件   5)gcc GCC.c   把源文件用預處理器、編譯器、匯編器處理後,最後使用鏈接器生成缺省名為a.out的可執行文件   為什麼默認叫a.out?因為早期編譯並沒有鏈接器的概念,a.out是匯編器直接生成的,a意為assembly。但需要澄清的是在現代編譯器中a.out都是由鏈接器生成。   另外,使用選項-save-temps可以保留中間生成的文件,示范如下:    [zhanghaiba@Fedora code]$ ls | grep hi hi.c hi.i hi.o hi.s     我們再用gcc -v來觀察總的編譯流程   復制代碼 [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   復制代碼     注意紅色加粗部分(由綠色文件生成紅色文件)——   (1)cc1是GCC編譯環境中的C編譯器,把C代碼編譯為匯編代碼,輸出為.s文件   (2)as是匯編器,把匯編代碼編譯為目標文件,輸出為.o文件   (3)collect2是GCC後期版本使用的鏈接器(環境),其實是先調用GNU的鏈接器ld對目標文件進行鏈接,最後收集與程序初始化相關的信息,構造程序的初始化結構。     ld是真正的鏈接器,對上一步的.o目標文件和其它需要.o文件或靜態鏈接庫.a文件、動態鏈接庫.so文件(如解壓C標准庫libc.a中取出需要的printf.o文件),一起鏈接輸出為a.out文件。     GCC後期版本使用了collect2來作為鏈接器,其實是間接調用ld鏈接器。   上面用到的工具中,as是GNU自帶的匯編器,ld是GNU自帶的鏈接器,它倆是GNU Binutils中最主要的二進制工具。   其中,ld-linux.so.2是動態鏈接器。最後注意-lc參數,l表示鏈接,c表示標准C庫,即libc.a或libc.so。       讓我們回到問題本身——   main不是C語言的關鍵字,但卻是約定俗成的主函數名字,不過它並不是程序執行的入口,   C程序真正入口是_start全局符號(由匯編實現的函數),_start函數會調用庫函數__libc_start_main,然後__libc_start_main再調用main函數   我們知道main函數的聲明無非兩種形式,main函數的聲明(main符號)其實是在crt1.o目標文件中   通過nm工具可以查看crt1.o包括了哪些符號   復制代碼 [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 復制代碼 crt1.o中已經有了main符號,但卻是未定義(U)的,所以需要我們來實現main函數(即定義main符號),最後通過鏈接器來鏈接(這裡稱作符號解析)   如果把main函數定義為static,也就是具有內部鏈接(Internel Linkage)屬性,則編譯後的目標文件是局部符號(當前文件可見)   然而鏈接是不會對局部符號做符號解析的,只會根據目標文件的.rel.text段來指示鏈接全局的且未定義的符號(即修改可重定位目標文件REL的符號地址)   因此,鏈接時main符號找不到定義,這導致main符號找不到具體實現(定義),造成鏈接失敗       我們再看編譯失敗的反饋信息   /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   就不難理解了——   在函數_start中,引用了未定義的符號main   collect2外殼:鏈接器ld返回1標記退出狀態(出錯狀態)    

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved