指定extern“ C”时,为什么Visual Studio无法给出未定义的引用错误?

永远学习

给出以下代码:

A2

_declspec(dllimport) void SomeFunc();

struct Foo
{
  Foo();
  ~Foo();
};

inline Foo::Foo() { }

inline Foo::~Foo()
{
  SomeFunc();
}

1小时

#include "A2.h"

extern "C" void TriggerIssue(); // <-- This!

extern "C" inline void TriggerIssue()
{
  Foo f; 
}

MyTest.cpp

#include "A1.h"

int main()
{
  return 0;
}

在此处查看问题背景。

当MyTest.cpp编译为可执行文件时,链接器会抱怨它SomeFunc()是未解决的外部文件。

这似乎是由于A1.h中的TriggerIssue的多余(错误?)声明引起的。注释掉将导致链接器错误消失。

有人可以告诉我这是怎么回事吗?我只想了解到底是什么导致编译器在有或没有该声明的情况下表现不同。上面的代码段是我试图写出我所遇到的情况的最小可验证示例。请不要问我为什么它是这样写的。

下注者注意:这不是有关如何解决未解决的外部符号错误的问题。因此,请停止投票以将其作为重复项关闭。我没有足够的信誉来删除该链接,该链接一直显示在此帖子的顶部,并声称此问题“可能有一个可能的答案”。

贾斯汀时间-恢复莫妮卡

无论第一个声明如何,都存在该问题,如果注释掉第一个声明并TriggerIssue()在程序中调用该问题仍然存在

这是由于cl生成的代码退出SomeFunc()时调用Foo的析构函数时要调用而引起的TriggerIssue(),而不是由两个声明之间的任何怪癖或相互作用引起的。如果您不注释掉非inline声明,它之所以出现,是因为另一个声明告诉编译器您希望它为该函数生成一个符号,以便可以将其导出到其他模块,从而阻止它实际内联。代码,而是强制其生成常规函数。生成函数主体时,它以对的隐式调用结尾~Foo(),这是问题的根源。

inline但是,如果注释掉了声明,则编译器会很乐意将代码视为内联代码,并且只有在您实际调用它时才会生成它;因为您的测试程序实际上并未调用TriggerIssue(),所以代码永远不会生成,~Foo()也不会调用;由于析构函数也是inline,因此允许编译器完全忽略它,而不为其生成代码。但是,如果您确实TriggerIssue()在测试程序中插入了对的调用,则会看到完全相同的错误消息。


测试1:两个声明都存在。

我直接编译了代码,将输出传递到日志文件。

cl MyTest.cpp > MyTest.log

生成的日志文件为:

MyTest.cpp
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:MyTest.exe 
MyTest.obj 
MyTest.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) void __cdecl SomeFunc(void)" (__imp_?SomeFunc@@YAXXZ) referenced in function "public: __thiscall Foo::~Foo(void)" (??1Foo@@QAE@XZ)
MyTest.exe : fatal error LNK1120: 1 unresolved externals

测试2:未inline声明的注释掉了,TriggerIssue()称为main()

我对您的代码进行了几处更改:

// A2.h was unchanged.

// -----

// A1.h:
#include "A2.h"

//extern "C" void TriggerIssue(); // <-- This!

extern "C" inline void TriggerIssue()
{
  Foo f; 
}

// -----

// MyTest.cpp
#include "A1.h"

int main()
{
  TriggerIssue();
  return 0;
}

我再次编译代码,并使用与之前相同的命令行将结果通过管道传输到日志文件:

MyTest.cpp
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:MyTest.exe 
MyTest.obj 
MyTest.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) void __cdecl SomeFunc(void)" (__imp_?SomeFunc@@YAXXZ) referenced in function "public: __thiscall Foo::~Foo(void)" (??1Foo@@QAE@XZ)
MyTest.exe : fatal error LNK1120: 1 unresolved externals

请注意,如果您愿意的话,两次尝试编译代码都会在相同的函数中针对相同的符号导致相同的链接器错误。这是因为问题实际上是由引起的~Foo(),而不是TriggerIssue()TriggerIssue()通过强制编译器为生成代码仅公开它的第一个声明~Foo()

[请注意,以我的经验,Visual C ++会尝试尽可能安全地优化类inline,如果实际未使用该类,则拒绝为其成员函数生成代码这就是为什么阻止调用函数的原因:由于未调用TriggerIssue()inline函数,因此编译器可以自由地对其进行完全优化,从而可以对其进行完全优化,包括对的调用SomeFunc()TriggerIssue()~Foo()SomeFunc()


测试3:提供了外部符号。

使用相同的A2.hA1.h以及MyTest.cpp测试2,我做了一个简单的DLL,出口标志,然后告诉编译器与它链接:

// SomeLib.cpp
void __declspec(dllexport) SomeFunc() {}

编译:

cl SomeLib.cpp /LD

这将创建SomeLib.dllSomeLib.lib,以及编译器和链接器使用的一些其他文件。然后,您可以使用以下代码编译示例代码:

cl MyTest.cpp SomeLib.lib > MyTest.log

这将生成一个可执行文件和以下日志:

MyTest.cpp
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:MyTest.exe 
MyTest.obj 
SomeLib.lib 

解决方案:

要解决此问题,您需要为编译器或链接器提供与SomeFunc()从其中导入DLL对应的库如果提供给编译器,它将直接传递给链接器。例如,如果SomeFunc()包含在中SomeFuncLib.dll,则可以使用以下代码进行编译:

cl MyTest.cpp SomeFuncLib.lib

为了说明两者之间的区别,我成功地两次编译了测试代码(每次都稍作修改),然后将其用于dumpbin /symbols生成的目标文件。

dumpbin/symbols MyTest.obj > MyTest.txt

示例1:未inline声明注释掉,TriggerIssue()未调用。

该对象文件是通过注释掉TriggerIssue()示例代码中的第一个声明而生成的,但未修改任何内容A2.hMyTest.cpp以任何方式进行修改TriggerIssue()inline,未调用。

如果未调用该函数,并且允许使用编译器inline,则仅生成以下内容:

COFF SYMBOL TABLE
000 00AB9D1B ABS    notype       Static       | @comp.id
001 00000001 ABS    notype       Static       | @feat.00
002 00000000 SECT1  notype       Static       | .drectve
    Section length   2F, #relocs    0, #linenums    0, checksum        0
004 00000000 SECT2  notype       Static       | .debug$S
    Section length   68, #relocs    0, #linenums    0, checksum        0
006 00000000 SECT3  notype       Static       | .text
    Section length    7, #relocs    0, #linenums    0, checksum 96F779C9
008 00000000 SECT3  notype ()    External     | _main

请注意,如果您愿意的话,生成的唯一功能符号是for main()(隐式的,extern "C"因此可以链接到CRT)。

示例2:上述测试3的结果。

成功编译上述测试3的结果是生成了此目标文件TriggerIssue()inline,并且被叫入main()

COFF SYMBOL TABLE
000 00AB9D1B ABS    notype       Static       | @comp.id
001 00000001 ABS    notype       Static       | @feat.00
002 00000000 SECT1  notype       Static       | .drectve
    Section length   2F, #relocs    0, #linenums    0, checksum        0
004 00000000 SECT2  notype       Static       | .debug$S
    Section length   68, #relocs    0, #linenums    0, checksum        0
006 00000000 SECT3  notype       Static       | .text
    Section length    C, #relocs    1, #linenums    0, checksum 226120D7
008 00000000 SECT3  notype ()    External     | _main
009 00000000 SECT4  notype       Static       | .text
    Section length   18, #relocs    2, #linenums    0, checksum  6CFCDEF, selection    2 (pick any)
00B 00000000 SECT4  notype ()    External     | _TriggerIssue
00C 00000000 SECT5  notype       Static       | .text
    Section length    E, #relocs    0, #linenums    0, checksum 4DE4BFBE, selection    2 (pick any)
00E 00000000 SECT5  notype ()    External     | ??0Foo@@QAE@XZ (public: __thiscall Foo::Foo(void))
00F 00000000 SECT6  notype       Static       | .text
    Section length   11, #relocs    1, #linenums    0, checksum DE24CF19, selection    2 (pick any)
011 00000000 SECT6  notype ()    External     | ??1Foo@@QAE@XZ (public: __thiscall Foo::~Foo(void))
012 00000000 UNDEF  notype       External     | __imp_?SomeFunc@@YAXXZ (__declspec(dllimport) void __cdecl SomeFunc(void))

通过比较这两个符号表中,我们可以看到,当TriggerIssue()inline剐,将产生以下四个符号,如果它被调用,或省略如果不是:

  • _TriggerIssueextern "C" void TriggerIssue()
  • ??0Foo@@QAE@XZpublic: __thiscall Foo::Foo(void)
  • ??1Foo@@QAE@XZpublic: __thiscall Foo::~Foo(void)
  • __imp_?SomeFunc@@YAXXZ__declspec(dllimport) void __cdecl SomeFunc(void)

如果SomeFunc()未生成for的符号,则无论链接器是否已声明,都无需链接它。



因此,总结一下:

  • 问题是导致通过~Foo()调用SomeFunc(),当链接器没有任何SomeFunc()的号召链接。
  • 问题是通过创建的实例而暴露出来TriggerIssue()Foo,如果TriggerIssue()不是inline(通过第一个声明)或在时调用,则会显示此问题inline
  • 如果您注释掉第一个声明而不实际调用它,该问题将隐藏TriggerIssue()由于您希望内联函数并且实际上并未调用它,因此cl可以自由地对其进行完全优化。优化TriggerIssue()输出还使其能够优化Fooinline成员函数,从而防止~Foo()生成成员函数反过来,这可以防止链接程序抱怨SomeFunc()析构函数中调用,因为SomeFunc()从未生成过要调用的代码

甚至更短:

  • 的第一个声明TriggerIssue()间接阻止编译器优化对的调用SomeFunc()如果您注释掉该声明,则编译器可以自由地进行优化TriggerIssue()~Foo()完全退出,从而使编译器无法生成对的调用SomeFunc(),从而使链接程序可以完全忽略它。

要修复此问题,您需要提供一个库,该库link可用于生成SomeFunc()从适当的DLL导入的适当代码



编辑:正如user657267在注释中指出的TriggerIssue(),暴露该问题的第一个声明的特定部分extern "C"从问题的示例程序开始:

  • 如果extern "C"从两个声明中完全删除了,并且没有其他任何更改,则编译器将在编译代码时进行优化TriggerIssue()(并扩展为~Foo()),从而生成与上述示例1中的符号表相同的符号表
  • 如果"C"从两个声明中都删除了,而将函数保留为extern,则其他任何事情都没有改变,则链接阶段将失败,并生成与测试1和2中相同的日志文件

这表明extern声明cl通过强制编译器生成可以在其他模块中进行外部链接的符号,专门负责防止优化问题代码。如果编译器不需要担心外部链接,它将完全优化TriggerIssue()并扩展~Foo()整个已完成的程序,从而消除了链接到另一个模块的需求SomeFunc()

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

指定extern“ C”时,为什么Visual Studio无法给出未定义的引用错误?

来自分类Dev

声明变量时extern未定义引用错误

来自分类Dev

无法找到对extern变量的未定义引用(dev c ++)

来自分类Dev

C:使用extern时未定义对变量的引用

来自分类Dev

为什么实现单例类时出现“未定义的引用错误”?

来自分类Dev

使用 extern 关键字的链接中出现未定义的引用错误

来自分类Dev

为什么未在错误消息中指定此未定义的引用?

来自分类Dev

C ++标头中extern变量的未定义引用

来自分类Dev

为什么会收到有关此脚本{未定义}的{引用错误}?

来自分类Dev

运行Google Spreadsheet API脚本时,为什么会看到“引用错误:“ Calendar”未定义“?

来自分类Dev

使用类进行链接时发生简单的C ++未定义引用错误

来自分类Dev

在函数中使用简单类时出现未定义的引用错误

来自分类Dev

尝试实现抽象类时出现未定义的引用错误

来自分类Dev

使用独立工具链为 android 构建鱿鱼时出现未定义的引用错误

来自分类Dev

接收未定义的引用错误 C++ 继承/多态性

来自分类Dev

使用 extern inline 时,G++ 会发出“存储类指定”错误

来自分类Dev

C ++如何在cpp之间与extern共享常量-错误:指定了存储类

来自分类Dev

C ++对共享库中类的构造函数和析构函数的未定义引用错误

来自分类Dev

C ++ Singleton:“未定义引用”错误

来自分类Dev

C ++“未定义引用”错误

来自分类Dev

使用extern在文件之间共享const时,为什么在定义上需要extern?

来自分类Dev

为什么我不断收到“未定义的引用”错误?

来自分类Dev

为什么在Visual Studio中使用LLVM + Clang时未定义__clang__?

来自分类Dev

为什么在Racket Dr中引用“内置”函数时出现未定义的错误?

来自分类Dev

为什么要定义“ extern_”而不是使用“ extern”?

来自分类Dev

是否可以在C#中动态指定extern别名?

来自分类Dev

错误:编译C ++时出现“未定义引用”

来自分类Dev

添加-lrt时无法跟踪编译器错误“未定义的引用”

来自分类Dev

为什么cmake在Windows上使用cygwin给出netcdf的未定义引用?

Related 相关文章

  1. 1

    指定extern“ C”时,为什么Visual Studio无法给出未定义的引用错误?

  2. 2

    声明变量时extern未定义引用错误

  3. 3

    无法找到对extern变量的未定义引用(dev c ++)

  4. 4

    C:使用extern时未定义对变量的引用

  5. 5

    为什么实现单例类时出现“未定义的引用错误”?

  6. 6

    使用 extern 关键字的链接中出现未定义的引用错误

  7. 7

    为什么未在错误消息中指定此未定义的引用?

  8. 8

    C ++标头中extern变量的未定义引用

  9. 9

    为什么会收到有关此脚本{未定义}的{引用错误}?

  10. 10

    运行Google Spreadsheet API脚本时,为什么会看到“引用错误:“ Calendar”未定义“?

  11. 11

    使用类进行链接时发生简单的C ++未定义引用错误

  12. 12

    在函数中使用简单类时出现未定义的引用错误

  13. 13

    尝试实现抽象类时出现未定义的引用错误

  14. 14

    使用独立工具链为 android 构建鱿鱼时出现未定义的引用错误

  15. 15

    接收未定义的引用错误 C++ 继承/多态性

  16. 16

    使用 extern inline 时,G++ 会发出“存储类指定”错误

  17. 17

    C ++如何在cpp之间与extern共享常量-错误:指定了存储类

  18. 18

    C ++对共享库中类的构造函数和析构函数的未定义引用错误

  19. 19

    C ++ Singleton:“未定义引用”错误

  20. 20

    C ++“未定义引用”错误

  21. 21

    使用extern在文件之间共享const时,为什么在定义上需要extern?

  22. 22

    为什么我不断收到“未定义的引用”错误?

  23. 23

    为什么在Visual Studio中使用LLVM + Clang时未定义__clang__?

  24. 24

    为什么在Racket Dr中引用“内置”函数时出现未定义的错误?

  25. 25

    为什么要定义“ extern_”而不是使用“ extern”?

  26. 26

    是否可以在C#中动态指定extern别名?

  27. 27

    错误:编译C ++时出现“未定义引用”

  28. 28

    添加-lrt时无法跟踪编译器错误“未定义的引用”

  29. 29

    为什么cmake在Windows上使用cygwin给出netcdf的未定义引用?

热门标签

归档