2016-02-19 54 views
1

所以我有幾個文件爲我正在進行的大學項目;每個文件都有幾個功能和一個用於測試目的的功能。但是有些文件需要使用其他文件的功能,這些功能會導致連接器對多個main聲明的投訴。我想知道是否有辦法將這些文件編譯爲目標文件,而無需沿途編譯main函數,基本上只剝離main以僅留下可用的函數。編譯C代碼沒有它的主要功能

一個例子(我這是相當大的實際文件不是一個),每個我假設其中分別包含的foobar聲明的頭文件是否存在:

//File foo.c 
#include <stdio.h> 
#include <stdlib.h> 
#include "foo.h" 

int foo(int x, int y) 
{ 
    return x + y; 
} 

//Assumes the user gives 2 integer inputs 
int main(int argc, char **argv) 
{ 
    int x, y; 
    sscanf(argv[1], "%d", &x); 
    sscanf(argv[2], "%d", &y); 
    printf("foo(%d, %d) = %d\n", x, y, foo(x,y)); 
} 

//File bar.c 
#include <stdio.h> 
#include <stdlib.h> 
#include "foo.h" 
#include "bar.h" 

int bar(int x, int y, int z); 
{ 
    return foo(x, y) * z; 
} 

//Assums the user gives 3 integer inputs 
int main(int argc, char **argv) 
{ 
    int x, y, z; 
    sscanf(argv[1], "%d", &x); 
    sscanf(argv[2], "%d", &y); 
    sscanf(argv[3], "%d", &z); 
    printf("bar(%d, %d, %d) = %d\n", x, y, z, bar(x, y, z)); 
} 

是它可能將foo.c編譯爲目標文件,以便我可以撥打gcc bar.c foo.o並獲得bar.c的可執行文件?

+0

只能編譯,不要鏈接... –

+2

你可以把'main'放在'#ifdef TEST'之類的東西里面。 – interjay

回答

2

對於只在編譯生成「測試可執行文件」的文件時才定義的符號,而不是在與其他文件一起使用時定義的符號執行#ifdef;我已經看到這被用於小型圖書館的快速功能測試。

#include "foo.h" 

int foo(int x, int y) 
{ 
    return x + y; 
} 

#ifdef COMPILE_MAIN 
//Assumes the user gives 2 integer inputs 
int main(int argc, char **argv) 
{ 
    int x, y; 
    sscanf(argv[1], "%d", &x); 
    sscanf(argv[2], "%d", &y); 
    printf("foo(%d, %d) = %d\n", x, y, foo(x,y)); 
} 
#endif 

現在你可以做

gcc -DCOMPILE_MAIN foo.c -o foo.x 

使一個可執行foo.x,你可以直接打電話到測試foo,你可以做

gcc foo.c bar.c main.c -o yourapp.x 

使用其他做一個可執行文件(包括具有「真實」應用程序main的文件),而不會抱怨多重定義的符號。

+0

完美,我會嘗試這個,但這看起來正是我想要的。我正在編寫一個makefile來完成所有這些工作,所以至少我不必再繼續輸入。 編輯:我將不得不在明天嘗試一下,所以一旦我能夠測試這個,我會盡快接受你的答案。 – user163911

+0

測試出來,這工作很好 – user163911

0

,而把周圍的main功能#ifdef工作,國際海事組織它不是正確的做法,尤其是因爲...

[..]我的實際文件,這是相當大[..]

合理的做法是將代碼分成邏輯的,可重用的部分,並將每個(小)部分放入單獨的文件中。

一個小例子:假設你有一個項目foo,你正在爲你的大學做,其中一部分是實現一個鏈表。合理的做法是創建(至少)兩個源文件,foo.clist.c,並將項目特定部分(即使只是一些測試)放入foo.c,並將所有關於鏈接列表實現的代碼放入list.c。然後,將鏈接列表的界面(即可能是某些struct ...聲明以及創建和修改列表的函數)放入頭文件list.h中,該文件包含在需要鏈接列表的任何文件中,例如未來的項目bar

list.c

struct list * create_list(void) { 
    // awesome code 
} 
// and more awesome code 

名單。^ h

struct list { 
    // an awesome list 
}; 
struct list * create_list(void); // <- only a declaration 
// more awesome declarations 

foo.c的

#include "foo.h" 
int main() { 
    // awesome use of the list 
    return 0; 
} 

bar.c

#include "foo.h" 
int main() { 
    // also awesome 
    return 0; 
} 

此後,您可以:

一)單獨編譯每一個源文件,並將生成的目標文件鏈接在一起:至少在GNU

gcc -c list.c 
gcc -c foo.c 
gcc -o foo foo.o list.o 

這是也是用make完美表達的一種方式,其實做甚至還有編譯C文件的隱含規則目標文件(%.o: %.c)。

沒有鏈接時間優化,您可能會失去一些優化與這種方法。如果你的項目需要不同的編譯器標誌,那麼這不是一個好方法。

B)與組裝所需的源文件的命令行,做一個計算:

gcc -o foo foo.c list.c 

您可以用make做到這一點,太,例如使用可變FOO_DEPENDENCIES := list.c

請注意,使用此方法的編譯時間會更長。

1

如果您正在編譯使用gcc或鐺你的代碼,你可以宣佈測試main S作爲一個弱符號:

extern void main(int argc, char ** argv) __attribute__((weak)); 
void main(int argc, char ** argv) { ... } 

main只會在沒有強大的main使用。

對於Visual Studio(來自this answer):

// in foo.cpp 
extern "C" void main(int argc, char ** argv; 
extern "C" void defaultMainFoo(int argc, char ** argv) 
// different name for each file 
{ ... } 
#pragma comment(linker, "/alternatename:_main=_defaultMainFoo"); 

/alternatename意味着鏈接器將使用defaultMainmain如果main不別處提供。