我尝试调用这个最小的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没有函数重载(。
正如另一个答案中所解释的,失败的原因是C++编译器的名称篡改。
为了解决这个问题(即使C++与Java JNI一起工作(,您需要用extern "C"
语言链接声明来装饰您的C++函数:
extern "C"
void Java_TEST_run() {}