【问题标题】:Linker error when calling a C function from C++ code in different VS2010 project从不同 VS2010 项目中的 C++ 代码调用 C 函数时出现链接器错误
【发布时间】:2023-10-09 22:15:01
【问题描述】:

我正在尝试包含一些我在 C++ 项目中找到的 C 代码。该函数在 C 文件中是这样定义的。

#ifdef __cplusplus
extern "C" {
#endif
 extern char *dtoa(double, int, int, int *, int *, char **);
 extern char *g_fmt(char *, double);
 extern void freedtoa(char*);
#ifdef __cplusplus
    }
#endif

 char *
g_fmt(register char *b, double x)
{

我包含在其中的 VS 项目正在创建一个 dll。该文件正在编译为 C,项目中的其他文件正在编译为 C++。

我添加了一个标头以包含在我的 C++ 文件中

#ifndef G_FMT_H
#define G_FMT_H
#ifdef __cplusplus
extern "C" {
#endif
 extern char *dtoa(double, int, int, int *, int *, char **);
 extern char *g_fmt(char *, double);
 extern void freedtoa(char*);

#ifdef __cplusplus
}
#endif
#endif //G_FMT_H

在解决方案的另一个项目中,我包含我的标题并尝试调用 g_fmt 函数。

#include "header.h"
...
g_fmt(my_array, 2.0);

这个项目正在链接到另一个项目,我可以毫无问题地调用第一个库中的 C++ 函数。但是,添加上面的行会给我一个 lnk2001 错误。

error LNK2001: unresolved external symbol g_fmt

我发现了许多其他关于混合 C 和 C++ 的问题,我似乎已经在正确的地方使用 extern 关键字完成了所有必要的事情,但是我仍然无法链接。在 VS2010 中我需要做什么具体的事情吗?

【问题讨论】:

  • 所以你可以使用 dtoa 但不能使用 g_fmt?
  • 不,我的意思是我可以使用在同一个库中其他地方声明的 C++ 函数。我在其他帖子中读过,您可以在同一个项目中混合使用 C 和 C++,也许这不太正确?
  • 确实如此,重点是如何导出函数?通常你需要一个 __declspec(dllexport) 或一个 def 文件。你这里用什么方法?
  • 链接器正在寻找实际函数的目标代码(或导入库存根)。它没有查看头文件或 C++ 源代码——编译阶段已经过去。那么g_fmt函数实际实现在哪里呢?是在图书馆吗?

标签: c++ c visual-studio-2010 extern lnk2001


【解决方案1】:

问题是针对 VStudio 2010,但适用于任何版本

CC++中,考虑到一个模块可能由多个对象组成,每个对象都应该有明确的界限。此外,每个对象(.c.cpp 文件)都应该有自己的头文件。因此,您应该有一个头文件和一个 .c(xx) 文件用于 C 函数(在您的示例中为 3)。
此外,作为一般准则,在命名文件和宏时尽量保持一致:您的 header.h 文件使用 G_FMT_H 宏作为包含保护。
尝试将您的 .h.c 文件重新组织为:

functions.h

#pragma once

#if defined(_WIN32)
#  if defined(FUNCTIONS_STATIC)
#    define FUNCTIONS_API
#  else
#    if defined(FUNCTIONS_EXPORTS)
#      define FUNCTIONS_API __declspec(dllexport)
#    else
#      define FUNCTIONS_API __declspec(dllimport)
#    endif
#  endif
#else
#  define FUNCTIONS_API
#endif

#if defined(__cplusplus)
extern "C" {
#endif

    FUNCTIONS_API char *dtoa(double, int, int, int*, int*, char**);
    FUNCTIONS_API char *g_fmt(char*, double);
    FUNCTIONS_API void freedtoa(char*);

#if defined(__cplusplus)
}
#endif

以及对应的实现(functions.c):

#define FUNCTIONS_EXPORTS
#include "functions.h"


char *dtoa(double, int, int, int*, int*, char**) {
    //function statements
}


char *g_fmt(char*, double) {
    //function statements
}


void freedtoa(char*) {
    //function statements
}

头文件中需要注意的两件事(除了重组和重命名):

  • 每个函数的 extern 存储说明符已消失
  • 导出逻辑:您的项目现在将定义 FUNCTIONS_EXPORT(来自 functions.c,或 desirableVStudio em> 项目设置 - 无论如何,某处之前 #include "functions.h")
    • 编译此项目 (functions.c) 时,将导出函数(由于宏定义)
    • 当头文件将包含在另一个项目中(未定义FUNCTIONS_EXPORTS)时,函数将被标记为导入,链接器将在导入的中搜索它们.lib(s) - 其中一个应该是这个项目生成的
    • 为了更严格,您可以用自动定义的宏替换 FUNCTIONS_EXPORTS(并从 functions.c 中删除其定义) VStudio IDE${YOUR_PROJECT_NAME}_EXPORTS(例如,如果您的 Dll 项目名为 ExportFunctionsProject.vc(x)proj em>,那么宏名称将是 EXPORTFUNCTIONSPROJECT_EXPORTS)
    • 您可以在 VStudio (2010) IDE 中查看宏名称项目属性 -> C/C++ -> 预处理器 -> 预处理器定义。更多详情,请查看[MS.Docs]: /D (Preprocessor Definitions)

类似(或相关)问题:

【讨论】: