天天看點

"undefined reference to" 問題彙總及解決方法

         轉載位址: https://segmentfault.com/a/1190000006049907?utm_source=tuicool&utm_medium=referral

在實際編譯代碼的過程中,我們經常會遇到

"undefined reference to"

的問題,簡單的可以輕易地解決,但有些卻隐藏得很深,需要花費大量的時間去排查。工作中遇到了各色各樣類似的問題,按照以下幾種可能出現的狀況去排查,可有利于理清頭緒,進而迅速解決問題。

連結時缺失了相關目标檔案

首先編寫如下的測試代碼:

// test.h

#ifndef __TEST_H__
#define __TEST_H__

void test();

#endif

// test.c

#include <string.h>
#include <stdio.h>



void test()
{
    printf("just test it\n");
}

// main.c

#include "test.h"

int main(int argc, char **argv)
{
    test();

    return ;
}                

通過以下的指令,我們将會得到兩個

.o

檔案。

$ gcc -c test.c  
$ gcc –c main.c 
           

随後,我們将

main.o

這個檔案,編譯成可執行檔案。

$ gcc -o main main.o
Undefined symbols for architecture x86_64:
  "_test", referenced from:
      _main in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code  (use -v to see invocation)
           

編譯時報錯了,這是最典型的

undefined reference

錯誤,因為在連結時發現找不到某個函數的實作檔案。如果按下面這種方式連結就正确了。

當然,也可以按照如下的指令編譯,這樣就可以一步到位。

連結時缺少相關的庫檔案

我們把第一個示例中的

test.c

編譯成靜态庫。

$ gcc -c test.c  
$ ar -rc test.a test.o 
           

接着編譯可執行檔案,使用如下指令:

$ gcc -o main main.c 
Undefined symbols for architecture x86_64:
  "_test", referenced from:
      _main in main-ac26d.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code  (use -v to see invocation)
           

其根本原因也是找不到

test()

函數的實作檔案,由于

test()

函數的實作在

test.a

這個靜态庫中,故在連結的時候需要在其後加入

test.a

這個庫,連結指令修改為如下形式即可。

$ gcc -o main main.c test.a
           

連結的庫檔案中又使用了另一個庫檔案  (這個例子非常非常好, 我就是犯了這種錯誤!!!)

先更改一下第一個示例中使用到的代碼,在

test()

中調用其它的函數,更改的代碼如下所示。

// func.h

#ifndef __FUNC_H__
#define __FUNC_H__

void func();

#endif

// func.c

#include <stdio.h>

void func()
{
    printf("call it\n");
}

// test.h

#ifndef __TEST_H__
#define __TEST_H__

void test();

#endif

// test.c

#include <string.h>
#include <stdio.h>

#include "func.h"



void test()
{
    printf("just test it\n");

    func();
}

// main.c

#include "test.h"

int main(int argc, char **argv)
{
    test();

    return ;
}                

我們先對

fun.c

test.c

進行編譯,生成

.o

檔案。

$ gcc -c func.c  
$ gcc -c test.c
           

然後,将

test.c

func.c

各自打包成為靜态庫檔案。

$ ar –rc func.a func.o  
$ ar –rc test.a test.o 
           

這時将

main.c

編譯為可執行程式,由于

main.c

中包含了對

test()

的調用,是以,應該在連結時将

test.a

作為我們的庫檔案,連結指令如下。

$ gcc -o main main.c test.a
Undefined symbols for architecture x86_64:
  "_func", referenced from:
      _test in test.a(test.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code  (use -v to see invocation)
           

就是說,連結的時候發現

test.a

調用了

func()

函數,找不到對應的實作,我們還需要将

test.a

所引用到的庫檔案也加進來才能成功連結,是以指令如下。

同樣,如果我們的庫或者程式中引用了第三方庫(如

pthread.a

)則在連結的時候需要給出第三方庫的路徑和庫檔案,否則就會得到

undefined reference

的錯誤。

多個庫檔案連結順序問題

這種問題非常隐蔽,不仔細研究,可能會感到非常地莫名其妙。以第三個示例為測試代碼,把連結庫的順序換一下,如下所示:

$ gcc -o main main.c func.a test.a
test.a(test.o): In function `test':  
test.c:(.text+0x13): undefined reference to `func'  
collect2: ld returned 1 exit status
           

是以,在連結指令中給出所依賴的庫時,需要注意庫之間的依賴順序,依賴其他庫的庫一定要放到被依賴庫的前面,這樣才能真正避免

undefined reference

的錯誤,完成編譯連結。

備注:在

MAC

上可以正常編譯通過。

定義與實作不一緻

編寫測試代碼如下:

// test.h

#ifndef __TEST_H__
#define __TEST_H__

void test(unsigned int c);

#endif

// test.c

#include <string.h>
#include <stdio.h>



void test(int c)
{
    printf("just test it\n");
}

// main.c

#include "test.h"

int main(int argc, char **argv)
{
    test();

    return ;
}                

先将

test.c

編譯成庫檔案。

$ gcc -c test.c 
$ ar -rc test.a test.o
           

main.c

編譯成可執行檔案。

$ gcc -o main main.c test.a
ld: warning: ignoring file test.a, file was built for archive which is not the architecture being linked (x86_64): test.a
Undefined symbols for architecture x86_64:
  "_test", referenced from:
      _main in main-f27cf1.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code  (use -v to see invocation)
           

連結出錯了,原因很簡單,

test()

這個函數的聲明和定義不一緻導緻,将兩者更改成一樣即可通過編譯。

c++

代碼中連結

c

語言的庫

代碼同示例一的代碼一樣,隻是把

main.c

更改成了

main.cpp

。編譯

test.c

,并打包為靜态庫。

$ gcc -c test.c  
$ ar -rc test.a test.o
           

編譯可執行檔案,用如下指令:

$ g++ -o main main.cpp test.a 
Undefined symbols for architecture x86_64:
  "test()", referenced from:
      _main in main-d7fde.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code  (use -v to see invocation)
           

原因就是

main.cpp

c++

代碼,調用了

c

語言庫的函數,是以連結的時候找不到,解決方法是在相關檔案添加一個

extern "C"

的聲明即可,例如修改

test.h

檔案。

// test.h

#ifndef __TEST_H__
#define __TEST_H__

#ifdef __cplusplus
extern "C" {
#endif

void test();

#ifdef __cplusplus
}
#endif

#endif