Java对GCC编译的C库的调用可以工作,但在用G++编译时失败

s2j5cfk0  于 2023-11-19  发布在  Java
关注(0)|答案(2)|浏览(121)

我试着把这个最小的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)

jmo0nnb3

jmo0nnb31#

C和C是不同的语言,遵循不同的编译规则。如果你用g++编译,那么代码将被视为C而不是C。C是C的超集是常见的**误解 * 陷阱。
C
做了一件叫做 name mangling 的事情:将函数名和变量名编码成唯一的名称,这样链接器就可以将语言中的常用名称分开。多亏了它,像函数重载这样的特性才成为可能。
让我们看一个例子(来源):

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没有函数重载)。

qacovj5a

qacovj5a2#

正如在另一个答案中所解释的,失败的原因是C编译器的名称修改。
为了解决这个问题(即让C
与Java JNI一起工作),你需要用extern "C"language linkage声明来装饰你的C++函数:

extern "C"
void Java_TEST_run() {}

字符串

相关问题