2011-04-05 108 views
43

只是一個簡單的程序,但我不斷收到此編譯器錯誤。我使用MinGW編譯器。C錯誤:未定義的函數引用,但它被定義爲

這裏的頭文件,point.h

//type for a Cartesian point 
typedef struct { 
    double x; 
    double y; 
} Point; 

Point create(double x, double y); 
Point midpoint(Point p, Point q); 

而這裏的point.c

//This is the implementation of the point type 
#include "point.h" 

int main() { 
    return 0; 
} 
Point create(double x, double y) { 
    Point p; 
    p.x = x; 
    p.y = y; 
    return p; 
} 

Point midpoint(Point p, Point q) { 
    Point mid; 
    mid.x = (p.x + q.x)/2; 
    mid.y = (p.y + q.y)/2; 
    return mid; 
} 

這裏的地方編譯器問題進來我不斷收到:

testpoint.c: undefined reference to 'create(double x, double y)'

雖然它是在point.c中定義的。

這就是所謂的testpoint.c一個單獨的文件:

#include "point.h" 
#include <assert.h> 
#include <stdio.h> 
int main() { 
    double x = 1; 
    double y = 1; 
    Point p = create(x, y); 

    assert(p.x == 1); 
    return 0; 
} 

我在一個無所適從的問題可能是。

+0

你得到任何其他消息? – JaredPar 2011-04-05 22:20:22

+4

你可以發佈你的makefile嗎?另外,您定義了2個主要功能,這不可能是好的。 – George 2011-04-05 22:20:34

+0

可能是對main()進行重新定義,它是程序的入口點。擺脫'point.c'中的那一個 – RageD 2011-04-05 22:25:49

回答

61

你是如何編譯和鏈接的?您需要指定兩個文件,如下所示:

gcc testpoint.c point.c 

...因此它知道將兩個函數鏈接在一起。然而,隨着現在編寫的代碼,您將遇到相反的問題:main的多個定義。你需要/想要消除一個(無疑是point.c中的一個)。

編輯:在較大的程序中,您通常會分別進行編譯和鏈接,以避免重新編譯任何未更改的內容。您通常通過makefile指定需要完成的工作,並使用make來完成這項工作。在這種情況下,你會有這樣的事情:

OBJS=testpoint.o point.o 

testpoint.exe: $(OBJS) 
    gcc $(OJBS) 

第一個只是對象文件的名稱宏。你得到擴大與$(OBJS)。第二個是告訴make 1)可執行文件依賴於目標文件的規則,2)告訴它如何在/如果它與目標文件相比已過期時如何創建可執行文件。

大多數版本的make(包括MinGW中的一個我很確定)都有一個內置的「隱式規則」來告訴他們如何從C源文件創建一個目標文件。它通常看起來大致是這樣的:

.c.o: 
    $(CC) -c $(CFLAGS) $< 

這是假設C編譯器的名稱是在宏命名爲CC(如CC=gcc隱含定義),並允許您在一個名爲CFLAGS宏指定你關心的任何標誌(例如,CFLAGS=-O3打開優化)和$<是擴展到源文件名稱的特殊宏。

您通常會將其存儲在一個名爲Makefile的文件中,並且您只需在命令行鍵入make即可構建程序。它隱含地查找名爲Makefile的文件,並運行它包含的任何規則。

這樣做的好處是make會自動查看文件上的時間戳,因此它只會重新編譯自上次編譯以來發生更改的文件(即「.c」文件具有比匹配的「.o」文件更近的時間戳)。

另請注意,1)在涉及大型項目時,如何使用make有很多不同,以及2)還有很多替代方案。我只在這裏獲得最少的高分。

+1

這是行得通的,但是這通常是多大的C程序鏈接/編譯在一起的?頭文件中的extern關鍵字似乎不能解決任何問題。 – upswimsdn 2011-04-05 22:40:26

+0

@upswimsdn:查看編輯的答案。 – 2011-04-05 22:41:26

+0

看來你只能回答解決問題的問題。這與回答這個問題不一樣。例如,我有與標題完全相同的問題,但一切都在一個文件中。 stackoverflow的目的是提供可搜索的答案,而不僅僅是幫助某人然後誤導任何人搜索相同的問題。 – 2018-02-20 19:53:56

6

我認爲問題在於,當你試圖編譯testpoint.c時,它包含了point.h,但它並不知道point.c。由於point.c的定義爲create,沒有point.c會導致編譯失敗。

我不熟悉MinGW,但你需要告訴編譯器尋找point.c。例如用gcc,你可以這樣做:

gcc point.c testpoint.c 

當然爲others所指出的那樣,你還需要刪除您main功能之一,因爲你只能有一個。

3

將「extern」關鍵字添加到point.h中的函數定義中

+1

函數上的'extern'對於任何(至少現在)都沒有任何影響,因爲頭中聲明的每個函數都是public/external。 – 2014-11-05 15:52:24

2

我最近有過這個問題。在我的情況下,我讓IDE根據其擴展名選擇在每個文件上使用哪種編譯器(C或C++),並且我試圖從C++代碼調用C函數(即從.c文件)。

.h文件的C函數不是包裹在這種後衛:

#ifdef __cplusplus 
extern "C" { 
#endif 

// all of your legacy C code here 

#ifdef __cplusplus 
} 
#endif 

我可能已經添加了,但我不想修改它,所以我只是包括它在我的C++文件,像這樣:

extern "C" { 
#include "legacy_C_header.h" 
} 

(帽尖到UncaAlby他的effect of extern "C"的明確解釋。)