【问题标题】:Using a C extern function inside a C++ file: Linking issues with Makefile在 C++ 文件中使用 C extern 函数:使用 Makefile 链接问题
【发布时间】:2010-09-16 05:05:25
【问题描述】:

我有一个问题。我在 C 文件 read-line.c 中定义了一个 print 函数,如下所示:

void history_print(void)
{
    /* some stuff */
}

在 C++ 文件 command.cc 中,我有以下内容:

extern "C" void history_print(void);

然后我只需调用 history_print()。

#Use GNU compiler
cc = gcc -g
CC = g++ -g   

all: shell

tty-raw-mode.o: tty-raw-mode.c
    gcc -c tty-raw-mode.c

read-line.o: read-line.c
    gcc -c read-line.c

lex.yy.o: shell.l
    lex shell.l
    $(cc) -c lex.yy.c

y.tab.o: shell.y
    yacc -d shell.y
    $(CC) -c y.tab.c

command.o: command.cc
    $(CC) -c command.cc

shell: y.tab.o lex.yy.o tty-raw-mode.o read-line.o command.o
    $(CC) -o shell lex.yy.o y.tab.o tty-raw-mode.o read-line.o command.o -ll -lgen

在我的 Makefile 中链接规则输出时遇到问题:

Undefined                       first referenced
 symbol                             in file
history_print                       command.o
ld: fatal: Symbol referencing errors. No output written to shell
collect2: ld returned 1 exit status
*** Error code 1
make: Fatal error: Command failed for target `shell'

make 的 -v 选项标志的输出:

yacc -d shell.y
g++ -g -v -c y.tab.c
Reading specs from /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/specs
Configured with: ../sources/gcc-3.4.6/configure --prefix=/opt/csw/gcc3 --with-local-    prefix=/opt/csw --without-gnu-as --with-as=/usr/ccs/bin/as --without-gnu-ld     --with-ld=/usr/ccs/bin/ld --enable-threads=posix --enable-shared --enable-multilib     --enable-nls --with-included-gettext --with-libiconv-prefix=/opt/csw --with-x --enable-    java-awt=xlib --enable-languages=all
Thread model: posix
gcc version 3.4.6
 /opt/csw/gcc3/libexec/gcc/sparc-sun-solaris2.8/3.4.6/cc1plus -quiet -v y.tab.c -quiet     -dumpbase y.tab.c -mcpu=v7 -auxbase y.tab -g -version -o /var/tmp//ccCi8vXj.s
ignoring nonexistent directory "/opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/../..    /../../sparc-sun-solaris2.8/include"
#include "..." search starts here:
#include <...> search starts here:
 /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/../../../../include/c++/3.4.6
 /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/../../../../include/c++/3.4.6/sparc-    sun-solaris2.8
 /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/../../../../include/c++/3.4.6/backward
 /opt/csw/include
 /opt/csw/gcc3/include
 /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/include
 /usr/include
End of search list.
GNU C++ version 3.4.6 (sparc-sun-solaris2.8)
        compiled by GNU C version 3.4.6.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
 /usr/ccs/bin/as -V -Qy -s -xarch=v8 -o y.tab.o /var/tmp//ccCi8vXj.s
/usr/ccs/bin/as: SunOS 5.10 118683-05 Patch 04/30/2010
lex shell.l
gcc -g -c lex.yy.c
gcc -c tty-raw-mode.c
gcc -c read-line.c
g++ -g -v -c command.cc
Reading specs from /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/specs
Configured with: ../sources/gcc-3.4.6/configure --prefix=/opt/csw/gcc3 --with-local-    prefix=/opt/csw --without-gnu-as --with-as=/usr/ccs/bin/as --without-gnu-ld --with-ld=/usr/ccs/bin/ld --enable-threads=posix --enable-shared --enable-multilib --enable-nls --with-included-gettext --with-libiconv-prefix=/opt/csw --with-x --enable-    java-awt=xlib --enable-languages=all
Thread model: posix
gcc version 3.4.6
 /opt/csw/gcc3/libexec/gcc/sparc-sun-solaris2.8/3.4.6/cc1plus -quiet -v command.cc -quiet -dumpbase command.cc -mcpu=v7 -auxbase command -g -version -o /var/tmp//cckVWlC7.s
ignoring nonexistent directory "/opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/../../../../sparc-sun-solaris2.8/include"
#include "..." search starts here:
#include <...> search starts here:
 /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/../../../../include/c++/3.4.6
 /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/../../../../include/c++/3.4.6/sparc-sun-solaris2.8
 /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/../../../../include/c++/3.4.6/backward
 /opt/csw/include
 /opt/csw/gcc3/include
 /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/include
 /usr/include
End of search list.
GNU C++ version 3.4.6 (sparc-sun-solaris2.8)
        compiled by GNU C version 3.4.6.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
 /usr/ccs/bin/as -V -Qy -s -xarch=v8 -o command.o /var/tmp//cckVWlC7.s
/usr/ccs/bin/as: SunOS 5.10 118683-05 Patch 04/30/2010
g++ -g -v -o shell lex.yy.o y.tab.o tty-raw-mode.o read-line.o command.o -ll -lgen
Reading specs from /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/specs
Configured with: ../sources/gcc-3.4.6/configure --prefix=/opt/csw/gcc3 --with-local-    prefix=/opt/csw --without-gnu-as --with-as=/usr/ccs/bin/as --without-gnu-ld --with-ld=/usr/ccs/bin/ld --enable-threads=posix --enable-shared --enable-multilib --enable-nls --with-included-gettext --with-libiconv-prefix=/opt/csw --with-x --enable-    java-awt=xlib --enable-languages=all
Thread model: posix
gcc version 3.4.6
 /opt/csw/gcc3/libexec/gcc/sparc-sun-solaris2.8/3.4.6/collect2 -V -R /opt/csw/lib -Y P,/opt/csw/lib:/usr/ccs/lib:/usr/lib -Qy -o shell /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/crt1.o /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/crti.o     /usr/ccs/lib/values-Xa.o /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/crtbegin.o -L/opt    /csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6 -L/usr/ccs/bin -L/usr/ccs/lib -L/opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/../../.. lex.yy.o y.tab.o tty-raw-mode.o read-line.o command.o -ll -lgen -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc -lc /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/crtend.o /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/crtn.o
ld: Software Generation Utilities - Solaris Link Editors: 5.10-1.497
Undefined                       first referenced
 symbol                             in file
history_print                       command.o
ld: fatal: Symbol referencing errors. No output written to shell
collect2: ld returned 1 exit status
*** Error code 1
make: Fatal error: Command failed for target `shell'

我猜这个问题是由于链接造成的,它与 Makefile 有关,但我不确定如何解决。谁能帮帮我?

非常感谢,非常感谢您的帮助。

杰瑞

【问题讨论】:

  • 我强烈反对CC = g++。 CC 应该是 c 编译器,您应该使用 CXX (GNU make) 或 CPP 作为 c++ 编译器。是的,网上有一些教程建议将 CC 用于 c++ 编译器,但它们通常在 c++ only 上下文中这样做。因为你有两种语言,你需要来区分。
  • 感谢您的评论。我会换成CXX,谢谢!
  • CPP 用于 C 预处理器。
  • 只是出于兴趣,如果您使用 GNU 链接器而不是 Solaris 链接器会发生什么?
  • 对不起,我仍然对 Makefile 很笨拙。请问我该如何精确?

标签: c++ c linker makefile


【解决方案1】:

这是因为您正在使用 g++ 重新编译 a.c 文件,并且函数定义周围没有 extern C 包装器。因此,名称将经过 C++ 修改,print 目标文件中将没有 print

但是,b.cc 会认为 print 是一个 C 名称(因为它 extern C)所以它会去寻找未修改的版本。

要么将 extern C 放在 a.c 中的定义周围(这可能使常规 C 编译器无法使用它,因此请记住这一点)或更改您的 g++ 命令以使用 a.o(它具有未损坏的名称,因为它是用 C 编译器完成的)而不是 a.c


事实上,这是行不通的,因为您没有在g++ 命令中调用链接器。我什至不确定为什么你试图将a.o 包含到b.o 中。这样做的最佳时间可能是在链接时创建 ab 可执行文件,而不是编译时,例如:

CC = g++ -g

a.o: a.c
    gcc -o a.o -c a.c

b.o: b.cc
    $(CC) -o b.o -c b.cc

ab: a.o b.o
    $(CC) -o ab a.o b.o

更新:将其分解为最简单的测试用例,以便您了解我的意思:

a.c:
    #include <stdio.h>
    void print(void) {
        printf ("7\n");
    }

b.cc:
    extern "C" void print(void);
    int main(void) {
        print();
        return 0;
    }

Makefile:
    output: a.o b.o Makefile
        g++ -o output a.o b.o

    a.o: a.c Makefile
        gcc -o a.o -c a.c

    b.o: b.cc Makefile
        g++ -o b.o -c b.cc

然后运行 ​​make 和程序:

pax> make
gcc -o a.o -c a.c
g++ -o b.o -c b.cc
g++ -o output a.o b.o

pax> ./output
7

如果您将a.c 的编译更改为使用g++,则会在找不到print 的位置出现原始错误:

pax> make
g++ -o a.o -c a.c
g++ -o b.o -c b.cc
g++ -o output a.o b.o
b.o:b.cc:(.text+0x2b): undefined reference to `_print'
collect2: ld returned 1 exit status
make: *** [output] Error 1

现在看来您的最终 Makefile 是正确的。它使用 C 编译器编译 a.c,因此不会发生名称修改。我的建议是清理所有内容(删除所有 *.o 文件)并再次运行 make,然后将输出发布到问题的底部。

可能有些文件是旧文件,这就是为什么你应该在运行make之前删除所有目标文件。

【讨论】:

  • 用 a.o 替换 a.c,我得到同样的错误,但之前有一个警告:g++: a.o: linker input file used because linking not done。请问我该如何解决这个问题?
  • 使用 a.o 没有帮助,因为那是编译到对象的命令,而不是链接命令。
  • 当使用 -o 而不是 -c 时,我得到以下信息:g++: no input files *** Error code 1 make: Fatal error: Command failed for target `a.o'
  • 我贴了一个比较完整的代码,希望大家帮帮我。抱歉,我在使用 Makefile 时遇到了此类问题。
  • @Jary,你不使用-o 代替 -c,你使用它除了。我把它放进去是因为我喜欢总是显式地设置输出文件,因为许多链接器总是创建一个a.out 文件而不是更智能的命名文件。而且我看不出你的最新更新有什么问题。你现在有什么问题?
【解决方案2】:

你的 makefile 需要一个链接规则而不是单独的编译规则:

CC  = gcc -g
CPP = g++ -g

program: a.o b.o
    ${CPP} -o $@ a.o b.o

Make 已经知道如何将源文件转换为目标文件 - 您无需重新训练它。

【讨论】:

  • 实际上,我有更多的链接规则,我只是展示了此时产生问题的规则。
  • OK - 然后不要用 C++ 编译器编译 a.c;毕竟,它是 C 源代码,而不是 C++ 源代码。而且,尽管关于 SO 的 C/C++ 问题过多,但它们是独立的(尽管相关的)语言。这个问题实际上正确标记为 C 和 C++,因为它涉及两种语言,尽管在单独的源文件中。
  • 我想我实际上已经掌握了乔纳森的建议。请检查我最初帖子的底部,我发布了整个makefile。在输出中,似乎你告诉我的就是我在做什么,不是吗?
  • @Jary:您在整个 makefile 中获得的内容与您最初发布的内容不同 - 它仍然出现在顶部。最初的b.o 规则不应该调用加载程序——我不完全相信您发布了正确的makefile 片段。您的 lex 和 yacc 规则也有点简单 - 进一步证明“我们所看到的并不完全是您执行的”。你的整个 makefile 看起来不错。
  • @Jary:请注意,D McKee 指出 CC 是 POSIX make 为 C 编译器保留的;它不应该用于 C++ 编译器。对于 C++ 编译器,建议使用 CXX 或 CPP。
猜你喜欢
  • 2017-08-13
  • 2021-07-15
  • 1970-01-01
  • 2011-04-30
  • 2014-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多