【问题标题】:How can I use Java Native Interface to pass a byte array into a C function which takes a char* as an argument?如何使用 Java Native Interface 将字节数组传递给以 char* 作为参数的 C 函数?
【发布时间】:2016-09-22 06:35:30
【问题描述】:

所以我需要使用 JNI 从 java 调用 C 函数。在传入不同的数据类型(创建本机变量、头文件、共享库等)时,我已经能够成功地做到这一点,但无法让它与字节数组一起使用。这是我的 C 函数:

#include <stdio.h>
void encrypt(int size, unsigned char *buffer);
void decrypt(int size, unsigned char *buffer);

void encrypt(int size, unsigned char *buffer){
    for(int i=0; i<size; i++){
        unsigned char c = buffer[i];
        printf("%c",c);
    }
}
void decrypt(int size, unsigned char *buffer){
    for(int i=0; i<size; i++){
        unsigned char c = buffer[i];
        printf("%c",c);
    }
}

这是我的java代码(我知道在制作头文件后,我必须用头文件中的JNI代码替换C函数声明)

class Tester{
    public native void encrypt(int size, char *buffer);
    public native void decrypt(int size, char *buffer);
    static{
    System.loadLibrary("buffer");
    {
    public static void main(String[] args){
        Tester test = new Tester();
        String hello = "hello";
        byte[] byteHello = hello.getBytes();
        test.encrypt(5,byteHello);
        test.decrypt(5,byteHello);
    }
}

我知道 Java 不支持 char* 类型,这就是我在尝试编译时遇到错误的原因。也许我应该在 Java 中将类型更改为 char[]?无论如何,我的目标是能够将 Java 中的字节数组传递给我的 C 函数,遍历字节数组并打印出每个元素。

【问题讨论】:

  • java char 和 C char 类型不兼容,最好将 byte[] 传递给 C,然后按需转换每个元素
  • "如何使用 Java Native Interface 将字节数组传递给以 char* 作为参数的 C 函数?" - 你不能,但你可以将一个字节数组传递给一个 C 函数,该函数将一个 Java 字节数组作为参数。

标签: java c char java-native-interface buffer


【解决方案1】:

Java char 和 C char 类型不兼容,最好将 byte[] 传递给 C,然后按需转换每个元素。


类似的东西:

Main.java:

//...Code to load library...

public static void main(String[] args) {
    passBytes("hello".getBytes());
}

public static native void passBytes(byte[] bytes);

Main.c:

#include "Main.h" // Main.h is generated

JNIEXPORT void JNICALL Java_Main_passBytes
  (JNIEnv *env, jclass clazz, jbyteArray array) {
    unsigned char* buffer = (*env)->GetByteArrayElements(env, array, NULL);
    jsize size = (*env)->GetArrayLength(env, array);

    for(int i = 0; i < size; i++) {
        printf("%c", buffer[i]);
    }

    (*env)->ReleaseByteArrayElements(env, array, buffer, JNI_ABORT);
 }

jbyteArray 只不过是一个存根类型,在jni.h 中定义:

struct _jobject;
typedef struct _jobject *jobject;
typedef jobject jarray;
typedef jarray jbyteArray;

它实际上不包含任何数据。它或多或少只是一个内存地址。

要从中获取元素,我们将其传递给GetByteArrayElements(因为类型是byte[]),然后它可以要求VM 检索C 样式数组中的元素。 (它可能会或可能不会复制。请参阅文档)

数组长度也是如此。

告诉虚拟机我们已经完成了阵列。我们打电话给ReleaseArrayElements

另外,jbyte 被定义为signed char,因此在这种情况下,仅使用GetByteArrayElements 的结果作为unsigend char* 而不是jbyte* 是安全的。

【讨论】:

  • 多一点解释会使这成为一个更好的答案。您的评论副本将是一个好的开始,但还值得一提的是,本机方法接收的 Java 数组不是 C 数组。您必须使用适当的 JNI 函数从中获取一个实际的 C 数组(如您所演示的),这对整体本机方法实现提出了一些额外的要求。
  • @JohnBollinger 感谢您的建议。我有点假设 OP 已经知道其中的大部分内容。但无论如何,我有时会忘记其他人也会读到这篇文章;)
  • Release* 函数的重要性在于取消固定 JVM 堆中的对象。数组元素不一定被 Get* 复制,因此 JNI_ABORT 不一定避免修改 JVM 数组;它只会节省时间如果有一个副本..
  • @JornVernee 嘿,非常感谢!这行得通,我可以有效地打印和更改内存缓冲区中的值。你有没有机会回答我关于从 python 调用 C 库的非常类似的问题?你似乎知道你在说什么。再次感谢。
  • @Mr.Pickles 不抱歉,我对 python 了解不多。
猜你喜欢
  • 2014-07-27
  • 2016-07-21
  • 2016-09-22
  • 1970-01-01
  • 1970-01-01
  • 2015-12-23
  • 1970-01-01
  • 1970-01-01
  • 2015-03-17
相关资源
最近更新 更多