從零開始,學習windows編程(6)--改換entry,link錯誤的簡單分析
還是那個hello.c程序,我們將其小修改一下,來開始今天的話題。
1 #include <stdio.h>
2
3 int myentry()
4 {
5 printf("hello world");
6 return 0;
7 }可以看到,我將原來main的位置換成了myentry,這會有什麼結果發生呢?
D: est>cl /c hello.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
hello.c
OK,沒有問題,生成了hello.obj。
D: est>link hello.obj
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
LIBC.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
hello.exe : fatal error LNK1120: 1 unresolved externals
link過程出現了問題,報了一個LINK2001錯誤。這個錯誤在MSDN上是這麼說的:
unresolved external symbol "symbol"
Code references something (such as a function, variable, or label) that the linker cant find in the libraries and object files.
Possible causes
What the code asks for doesnt exist (the symbol is spelled incorrectly or uses the wrong case, for example).
The code asks for the wrong thing (you are using mixed versions of the libraries, some from one version of the product, others from another version).
This error message is followed by fatal error LNK1120.
具體原因,其實結合前面幾篇的知識,就可以很容易的推理出來。如果大家有興趣,可以試著先推理一下,再看下面的分析過程,這樣理解更深刻一些。
對於LINK錯誤,應該算是C/C++特有的,而且比較麻煩,難以理解和解決的一類錯誤。C語言還好一些,到了C++中,LINK錯誤常常會帶一些“亂碼”符號,就更讓初次接觸到的童鞋們摸不到頭腦了。正好借這一個小例子,來看一下思路。
首先,我們使用cl從hello.c生成了hello.obj文件,由於沒有加上/Zl選項,所以生成的hello.obj文件還是帶有兩個defaultlib的,一個為libc.lib,還有一個oldnames.lib。這樣,在LINK的時候,會將hello.obj與libc.lib進行鏈接,而libc.lib是很多obj合在一起形成的,裡面有crt0.c生成的名為crt0.obj文件。回想我們上次看到的crt0.c的源碼文件,裡面函數為mainCRTStartup,在mainCRTStartup函數中調用到main函數了,main函數這個external symbol沒有找到(其他函數如_heap_init。。。看來都是找到了),所以報出來一個LNK2001錯誤,告訴用戶,main這個symbol沒有找到,您把它忘哪兒啦。
相關擴展
link時使用/entry選項選定入口
D: est>cl /c hello.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
hello.c
D: est>link /entry:myentry hello.obj
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
LIBC.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
hello.exe : fatal error LNK1120: 1 unresolved externals
其他不變,在link的時候加上/entry選項,結果還是和原來一樣。因此,可以確定,link過程需要將所有的符號找到其所,而不像編譯過程可以“打馬虎眼”,即使是有一些external的也可以不理。另外,不管這個函數有沒有“使用”到,都必須被link程序找到。這裡的“使用”意思為執行過程中,執行到它。可以再看一個例子:
1 #include <stdio.h> 2 3 int myentry() 4 { 5 printf("hello world"); 6 return 0; 7 } 8 9 int main()10 {11 myentry();12 }13 14 int test()15 {16 nofunc();17 return 0;18 }從上面的代碼可以看到,在默認編譯鏈接的情況下,test函數是沒有被調用到的,而其中的nofunc函數是沒有定義的。我們來對其做一下編譯鏈接。
D: est>cl /c hello.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
hello.c
D: est>link hello.obj
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
hello.obj : error LNK2001: unresolved external symbol _nofunc
hello.exe : fatal error LNK1120: 1 unresolved externals
可以看到,link的時候,找不到nofunc這個symbol,從而報錯。
這也是C/C++的一個比其他語言麻煩的地方,有編譯時、鏈接時和運行時的劃分,這樣子清晰了,但是概念也多了,如果再加上隱藏了一些東西的實現和原理,理解起來就更不容易了。
使用/Zl選項
如果對myentry代碼使用/Zl選項。
D: est>cl /c /Zl hello.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
hello.c
D: est>link hello.obj
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation. All rights reserved.
LINK : fatal error LNK1561: entry point must be defined
此時,則出現LNK1561錯誤。MSDN上對應的解釋為:
entry point must be defined
The linker did not find an entry point. You may have intended to link as a DLL, in which case you should link with the /DLL option. You may have also forgotten to specify the name of the entry point; link with the /ENTRY option.
Otherwise, you should include a main, wmain, WinMain, or wMain function in your code.
If you using LIB and intend to build a .dll, one reason for this error is that you supplied a .def file. If so, remove the .def file from the build.
因為/Zl去掉了defaultlib,所以link直接就是hello.obj,而不會去鏈接libc.lib,因為默認定義的entry symbol為mainCRTStartup,此時沒有libc.lib,裡面的mainCRTStartup也就沒有,所以會提示出沒有定義entry point錯誤。
Way1:
照MSDN上面的解釋,我們再來將main函數使用上,然後做編譯/Zl,以及鏈接工作會怎樣呢?
首先將myentry改為main。之後:
D: est>cl /c /Zl hello.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
hello.c
D: est>link hello.obj
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
hello.obj : error LNK2001: unresolved external symbol _printf
LINK : error LNK2001: unresolved external symbol _mainCRTStartup
hello.exe : fatal error LNK1120: 2 unresolved externals
此時的錯誤又不相同,這裡出現尋找不到printf和mainCRTStartup的鏈接錯誤。
Way2:
將/entry選項指定為myentry。
D: est>cl /c /Zl hello.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
hello.c
D: est>link /entry:myentry hello.obj
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
LINK : fatal error LNK1221: a subsystem cant be inferred and must be defined
又出現一個新錯誤,LNK1221。來看看解釋:
a subsystem can’t be inferred and must be defined
The linker does not have enough information to infer which subsystem you will target your application.
To fix this error, use the /SUBSYSTEM option.
看來link的時候,還可以根據搜到的symbol以及定義的entry來自動判斷編譯出來的目標應用的subsystem,關於subsystem,似乎只有windows下面有這個編譯選項,加上來再看一下。
D: est>link /entry:myentry /subsystem:console hello.obj
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
hello.obj : error LNK2001: unresolved external symbol _printf
hello.exe : fatal error LNK1120: 1 unresolved externals
OK,當加上之後,也出現了LNK2001錯誤,比上面要少一個_mainCRTStartup,還需要一個_printf。
Way3:
而綜合上面兩種方法,將way1和way2合起來使用,使用main函數代替掉myentry的源碼,然後將main設置entry point。<