提问人:David Brown 提问时间:8/28/2009 最后编辑:David Brown 更新时间:2/15/2013 访问量:1729
对未解析的外部符号错误感到困惑
Confused by Unresolved external symbol error
问:
我正在尝试使用 VC++ 的编译器构建一个包装库。
ErlDriver.c网站
#define __WIN32__
#define DLL_EXPORT __declspec(dllexport)
#include "erl_driver.h"
DLL_EXPORT int _driver_output(ErlDrvPort port, char *buf, int len) {
return driver_output(port, buf, len);
}
构建.bat
cl /I%ERL_DRIVER_H% /LD /MD ErlDriver.c
当我尝试构建它时,我收到以下链接器错误:
ErlDriver.obj:错误 LNK2019:函数 __driver_output 中引用了未解析的外部符号_WinDynDriverCallbacks
erl_win_dyn_driver.h(包含在 erl_driver.h 中))
typedef struct {
WDD_FTYPE(driver_output) *driver_output;
// a ton more of those
} TWinDynDriverCallbacks;
extern TWinDynDriverCallbacks WinDynDriverCallbacks;
#define driver_output (WinDynDriverCallbacks.driver_output)
因此,如您所见,WinDynDriverCallbacks 是声明的。
那么,是什么原因导致了链接器错误呢?
答:
不,它没有定义(至少在你引用的内容中)。它被宣布。“extern”关键字表示“此符号的定义出现在另一个编译单元(源文件)中”。您需要与通过编译定义该符号的源文件生成的对象文件(或库)进行链接。
评论
在 C 或 C++ 中“声明”某物和“定义”它之间存在微妙的区别。当你声明它时,它会告诉编译器某个符号将在其他地方定义 - 这可以允许代码使用该符号,而无需查看实际定义。您仍然必须在链接的代码中的某个位置定义符号,否则您将收到您看到的错误消息。
例如,这是符号的声明:WinDynDriverCallbacks
extern TWinDynDriverCallbacks WinDynDriverCallbacks;
您的代码具有此声明 - 它允许使用该符号的代码成功编译(但不能链接)。
您需要在某处添加定义:
TWinDynDriverCallbacks WinDynDriverCallbacks;
定义必须进入某处的源代码文件(通常不在头文件中)。这告诉编译器在目标代码中为该对象分配空间,并允许程序成功链接。
评论
我在 Windows 上构建 NIF 时遇到了一个非常相似的问题。未解析的外部符号_WinDynNifCallbacks。事实证明,这是由 ERL_NIF_INIT 宏定义的,在我的情况下,整个宏需要包含在外部 C 块中。
即这失败了
extern "C" ERL_NIF_INIT(...)
虽然这成功了
extern "C"
{
ERL_NIF_INIT(...)
}
我强烈怀疑这个问题是由于相同的问题造成的,但与 erlang 端口驱动程序的 DRIVER_INIT 宏有关。
Driver_Init 是声明“TWinDynDriverCallbacks WinDynDriverCallbacks”的主循环,但它在driver_init的多行定义中正确声明。你不需要把它包装在extern “c”中。
由于这个线程在尝试设置我的准系统 Erlang 端口驱动程序时出现了大约一百万次,我将在这里说。我正在研究 Joe Armstrong 的编程 erlang 书,第 12 章接口技术。使用 erl5.9 和 vs2010。
书中的代码在example1_lib.c中有一个遗漏和错误。尽管该错误很可能是由于本书的年龄与erlang版本的变化。
需要在 example1_lib.c 的最顶部设置 (#define WIN32),否则 erlang 默认为所有 Linux 选项。
第二个需要在 example_drv_output 中将 (int bufflen) 更改为 (ErlDrvSizeT bufflen)。
在那之后,它变得干净了。
评论