zoukankan      html  css  js  c++  java
  • 编译过程中出现 'function' undefined

    最近在做一个项目,包含有C++和C的代码,

    在Makefile 里指定所有的cpp文件使用C++编译器(g++)编译,所有的c文件使用C编译器(gcc),

    然而在编译过程中(链接阶段)出现了 xxx undefined,函数未定义的错误提示。

    function undefined

    后来经过一番搜索,发现是linkage的问题,我也不知道这个词怎么翻译,简单的说就是要加 extern “C”

    为啥要extern “C”

    举个栗子好了,假如有3个文件,分别叫做
    zero.h
    zero.c
    one.cpp

    one.cpp:

    #include "zero.h"
    int add(int a,int b)
    {
    return a+b;
    }
    int main()
    {
    add(1,2);
    add(3.0,4.0);
    return 0;
    }

    zero.h:

    double add(double a,double b);

     

    zero.c:

    double add(double a,double b)
    {
    return a+b;
    }

    我们先用gcc编译zero.c文件

    gcc -o zero.o -c zero.c

    得到zero.o文件,然后再使用g++来编译one.cpp文件

    g++ -o one.o -c one.cpp

    得到one.o文件,最后组装

    g++ -o one zero.o one.o

    发现报错了,看看

    ryan@UNIX-10:~$ g++ -o one zero.o one.o
    one.o: In function `main':
    one.cpp:(.text+0x52): undefined reference to `add(double, double)'
    collect2: error: ld returned 1 exit status

    为啥?我们接着来看,分别看看两个.o文件的符号表

    ryan@UNIX-10:~$ readelf -s one.o
    Symbol table '.symtab' contains 11 entries:
    Num: Value Size Type Bind Vis Ndx Name
    0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
    1: 0000000000000000 0 FILE LOCAL DEFAULT ABS one.cpp
    2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
    3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
    4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
    5: 0000000000000000 0 SECTION LOCAL DEFAULT 6
    6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
    7: 0000000000000000 0 SECTION LOCAL DEFAULT 5
    8: 0000000000000000 20 FUNC GLOBAL DEFAULT 1 _Z3addii
    9: 0000000000000014 73 FUNC GLOBAL DEFAULT 1 main
    10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _Z3adddd
    ryan@UNIX-10:~$ readelf -s zero.o
    Symbol table '.symtab' contains 9 entries:
    Num: Value Size Type Bind Vis Ndx Name
    0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
    1: 0000000000000000 0 FILE LOCAL DEFAULT ABS zero.c
    2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
    3: 0000000000000000 0 SECTION LOCAL DEFAULT 2
    4: 0000000000000000 0 SECTION LOCAL DEFAULT 3
    5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
    6: 0000000000000000 0 SECTION LOCAL DEFAULT 6
    7: 0000000000000000 0 SECTION LOCAL DEFAULT 4
    8: 0000000000000000 44 FUNC GLOBAL DEFAULT 1 add
     

    在one.o里面,我们看序号为10的那一条,表示

    文件有对名称为_Z3adddd的项目持有引用,

    但此项目并没有在此文件中(好绕口,(╯‵□′)╯︵┻━┻)

    意思就是我要找_Z3adddd,但是我这个文件里没有,也许稍后我可以从其他.o文件里找。

    再来看看zero.o里面,序号8,有一个名字为add的项目,但是好像真的没看到_Z3adddd。

    好了,问题来了,总共就尼玛2个.o文件,无论哪里都找不到_Z3adddd的函数定义。

    然后编译器就只好提示你 ‘add(double, double)’ 是个未定义的引用了。。

    说好的add函数呢?

    明明定义了add函数,我们在zero.h里声明,在zero.c里实现,再标准不过了,

    可是为毛编译器就找不到了,还有他娘的 _Z3adddd 又是什么鬼  (生气脸

    这事吧,说(lan)来(de)话(da)长(zi)。。

    我们知道C编译器啊,只需要通过函数名就可以定位一个函数,

    因为她不支持重载(function overloading) <–英文是不是怎么写,但愿装逼成功了。= ̄ω ̄=

    然而西加加编译器啊不是,C++编译器啊,是支持函数重载的,所以她不能仅仅通过函数名来定位一个函数。

    然而有一天,C++编译器看到这个

    double add(double a,double b);
    int add(int a,int b);

    的时候,只能想想:
    · 到底哪个悟空(划掉) add 函数才是真的?
    · 我他妈到底要调用哪个?

    · 黑人问号.jpg

    所以啊,为了支持函数重载,C++还需要通过参数信息来定位一个函数,

    我猜她在编译过程中,会修改函数名称来达到不可告人的(划掉)目的。 <—-这条是我瞎胡诌的,我也不知道是不是真的

    这样,同样是add函数,你用C编译器编译出来的就是add,用C++编译器编译出来的就可能不是add了,而是其他的什么鬼。

    道理我都懂,那怎么解决

    简单说就是加extern “C” , 咱把zero.c 还有 zero.h 改改,就可以了,改成这样

    zero.h:

    #if __cplusplus
    extern "C" {
    #endif
    double add(double a,double b);
    #if __cplusplus
    }
    #endif

    zero.c:

    #include "zero.h"
    #if __cplusplus
    extern "C" {
    #endif
    double add(double a,double b)
    {
    return a+b;
    }
    #if __cplusplus
    }
    #endif

    然后再编译一下one.o ,再来看看readelf返回的结果

    ryan@UNIX-10:~$ g++ -o one.o -c one.cpp
    ryan@UNIX-10:~$ readelf -s one.o
    Symbol table '.symtab' contains 11 entries:
    Num: Value Size Type Bind Vis Ndx Name
    0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
    1: 0000000000000000 0 FILE LOCAL DEFAULT ABS one.cpp
    2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
    3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
    4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
    5: 0000000000000000 0 SECTION LOCAL DEFAULT 6
    6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
    7: 0000000000000000 0 SECTION LOCAL DEFAULT 5
    8: 0000000000000000 20 FUNC GLOBAL DEFAULT 1 _Z3addii
    9: 0000000000000014 73 FUNC GLOBAL DEFAULT 1 main
    10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND add

    看到了么,序号为10的条目那,名字变成了add。。

    就是这样,有什么说的不对的地方,你倒是来打我啊不是欢迎大家批评指正~

  • 相关阅读:
    centos 搭建 sftp 服务器
    apt-get 安装时,提示lock被占用
    Kafka
    设计模式-分类
    SparkSQL – 从0到1认识Catalyst
    Spark分布式计算引擎
    Spark存储管理
    Spart RDD
    硬件工程师的你也不想一辈子画图、调板子吧!!!
    如何理解IPD+CMMI+Scrum一体化研发管理解决方案之Scrum篇
  • 原文地址:https://www.cnblogs.com/gucnbar/p/6185743.html
Copyright © 2011-2022 走看看