java - 对 GCC 编译的 C 库的 Java 调用有效,但在使用 G++ 编译时失败
问题描述
我尝试调用这个最小的 C 代码(文件TEST.c
):
void Java_TEST_run() {}
来自这个 Java 代码(文件Example.java
):
public class Example {
public static void main(String args[]) {
System.out.println("START");
TEST test = new TEST();
test.dll_call();
System.out.println("ALL DONE!");
}
}
class TEST {
public void dll_call() {
run();
}
static {
try {
System.out.println("Load DLL = start ");
System.load("/home/user/Desktop/TEST.dll");
System.out.println("Load DLL = finish ");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load.\n");
System.exit(1);
}
}
public native void run();
}
我通过以下方式创建一个库:
gcc -c TEST.c
g++ -shared -o TEST.dll TEST.o
如果我使用 GCC 编译(甚至使用 G++ 创建 lib!)一切正常,但如果我使用 G++ ( g++ -c TEST.c
) 编译,我会在运行 Java 示例时收到以下错误输出:
START
Load DLL = start
Load DLL = finish
Exception in thread "main" java.lang.UnsatisfiedLinkError: 'void TEST.run()'
at TEST.run(Native Method)
at TEST.dll_call(Example.java:21)
at Example.main(Example.java:9)
解决方案
C 和 C++ 是不同的语言,遵循不同的编译规则。如果您使用 编译g++
,则代码将被视为 C++ 而不是 C。常见的误解是 C++ 是 C 的超集。
C++ 做了一些称为名称修饰的事情:将函数和变量名称编码为唯一名称,以便链接器可以分隔语言中的通用名称。多亏了它,函数重载等特性才成为可能。
让我们考虑一个例子(来源):
int f (void) { return 1; }
int f (int) { return 0; }
void g (void) { int i = f(), j = f(0); }
编译器可以将上述内容更改为:
int __f_v () { return 1; }
int __f_i (int) { return 0; }
void __g_v () { int i = __f_v(), j = __f_i(0); }
尽管名称g()
是唯一的,但仍然是错误的。名称修改适用于所有 C++ 符号。
如果您想防止名称修改,您需要使用extern "C"{}
块(但上面的示例将导致错误,因为 C 没有函数重载)。
推荐阅读
- google-people-api - 我们可以更改谷歌服务帐户的电子邮件地址吗?
- soapui - 使用 API 在 PC 上安装应用程序 - 我们如何识别端点以从 Internet 访问 API
- mysql - MySQL 是否支持“error_on_nondeterministic_update”之类的参数?
- reactjs - 在反应中实现自定义无限滚动
- javascript - 在 Typescript 源代码与生成的 JavaScript 上运行测试
- javascript - 如何在 JavaScript 中实现工厂设计模式?需要帮助在 JavaScript 中清理代码
- unity3d - Unity 画布动画
- kotlin - Ktor 服务器的一个实例上的多个站点
- firebase - 不同的 Firebase 参考
- laravel - 如何让asset() 在 Laravel 8.x 中正常工作?