Description
记写 c 程序库时遇到的“无法解析的外部符号”问题
问题描述
使用 c + CMake 编写一个程序库,使用 googletest 作为单元测试框架,目录结构如下:
其中:
- include 存放基础头文件及 googletest 的头文件
- libs 存放 googletest 编好的 lib 文件
- src 存放库源代码,CMake 配置生成为 lib
- test 存放单元测试代码,CMake 配置生成 exe
初始化文件也不多,test 里只有一个 main.cpp,内容如下
#include <stdio.h>
#include "rarray.h"
int main(int argc, char* argv[])
{
Array a;
ARR_INIT(&a, 10, int);
return 0;
}
可能到这里,有经验的老手就已经发现问题所在了
当我构建的时候,就报出了“无法解析的外部符号 "void __cdecl array_init(struct sArray *,int,int)" (?array_init@@YAXPEAUsArray@@hh@Z),函数 main 中引用了该符号”问题
解决方案
我反复检查了 CMake 配置,各种调整文件位置,将程序库改成 dll,依旧无法得到解决
也上网查了一下相关问题,不过他们的问题都跟我这个没有太大关系
百思不得其解之时,突然发现原来 test 目录里的 main 文件后缀是.cpp
,而我程序库是用纯 c 写的,问题就出在这里,将 main.cpp 改成 main.c 后,再构建便成功了
分析原因
由于我对c/c++也不是非常熟悉,简单总结下原因,有兴趣的同学可以自行查阅相关资料
由于 c++ 支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名,然而 c语言并不支持函数重载,因此编译 c 语言代码的函数时不会带上函数的参数类型,一般只包括函数名
也就是说同一份代码,如果后缀是 .c (即以 c 语言的方式去编译)和后缀是 .cpp (即以 cpp 语言的方式去编译)得到的函数签名是不一样的,因此,本案例中,使用 c++ 代码去访问该签名的 c 函数就返回了“无法解析的外部符号”这种错误
那么如何解决这个问题呢,可以使用extern "C"
,形式如下:
#ifdef __cplusplus //__cplusplus是 cpp 中自定义的一个宏
extern "C" // 告诉编译器,这部分代码按 c 语言的格式进行编译,而不是 c++
{
#endif
// 其他定义
#ifdef __cplusplus
}
#endif
这样,就可以将这些变量和函数声明为以 c 的方式去编译和链接
将本案例代码加上此声明后,也可以正常运行