JNI编程之如何传递参数和返回值
- 格式:doc
- 大小:36.50 KB
- 文档页数:4
jni传类指针-回复JNI(Java Native Interface)是Java语言与本地(C或C++)代码进行交互的一种机制。
它提供了在Java虚拟机(JVM)中调用C或C++代码的能力,同时也能让本地代码调用Java代码。
在JNI中,通过使用类指针(Class Pointers)来对Java类进行引用。
本文将介绍有关JNI中类指针的知识。
在JNI中,类指针是一种特殊类型的指针,它指向Java中的类对象。
在C 或C++代码中,我们可以通过JNI函数`GetObjectClass`来获取一个类的指针。
比如下面这段代码:cppJNIEnv *env; JNI环境指针jobject obj; Java对象jclass cls = env->GetObjectClass(obj);上述代码中,`GetObjectClass`函数返回一个`jclass`类型的指针,指向`obj`对象所属的类。
一旦我们拥有了一个类的指针,我们就可以使用其他JNI函数来操作这个类。
例如,我们可以使用`GetMethodID`函数来获取类的方法ID,然后使用`CallVoidMethod`函数来调用这个方法。
下面是一个简单的示例:cppjmethodID methodId = env->GetMethodID(cls, "methodName", "()V");env->CallVoidMethod(obj, methodId);上述代码中,`GetMethodID`函数用于获取`cls`类中名为`methodName`的无参数、无返回值的方法的方法ID。
然后,`CallVoidMethod`函数用于调用该方法。
除了获取方法ID和调用方法,类指针还可以用于获取和设置类的字段。
我们可以使用`GetFieldID`函数获取字段ID,然后使用`Get<Type>Field`和`Set<Type>Field`函数来读取和修改字段的值。
C语言教程十一、函数参数的传递和值返回前面我们说的都是无参数无返回值的函数,实际程序中,我们经常使用到带参数有返回值的函数。
一、函数参数传递1.形式参数和实际参数函数的调用值把一些表达式作为参数传递给函数。
函数定义中的参数是形式参数,函数的调用者提供给函数的参数叫实际参数。
在函数调用之前,实际参数的值将被拷贝到这些形式参数中。
2.参数传递先看一个例子:void a(int); /*注意函数声明的形式*/main(){int num;scanf(%d,&num);a(num); /*注意调用形式*/}void a(int num_back) /*注意定义形式*/{printf(%d\n,num_back);}在主函数中,先定义一个变量,然后输入一个值,在a()这个函数中输出。
当程序运行a(num);这一步时,把num的值赋值给num_back,在运行程序过程中,把实际参数的值传给形式参数,这就是函数参数的传递。
形参和实参可能不只一个,如果多于一个时,函数声明、调用、定义的形式都要一一对应,不仅个数要对应,参数的数据类型也要对应。
void a(int,float);main(){int num1;float num2;scanf(%d,&num1);scanf(%f,&num2);a(num1,num2);}void a(int num1_back,float num2_back){printf(%d,%f\n,num1_back,num2_back);}上面的例子中,函数有两个参数,一个是整型,一个是浮点型,那么在声明、调用、定义的时候,不仅个数要一样,类型也要对应。
如果不对应,有可能使的编译错误,即使没错误,也有可能让数据传递过程中出现错误。
再看一个例子:void a(int);main(){int num;scanf(%d,&num);a(num);}void a(int num){printf(%d\n,num);}看上面的例子,形式参数和实际参数的标识符都是num,程序把实际参数num 的值传递给形式参数num。
jni 结构体参数(实用版)目录1.JNI 简介2.JNI 结构体参数概述3.JNI 结构体参数详细说明4.JNI 结构体参数的应用示例5.总结正文1.JNI 简介JNI,即 Java Native Interface,是 Java 与本地代码(如 C、C++)交互的桥梁。
通过 JNI,Java 程序可以调用本地代码实现的功能,本地代码也可以调用 Java 程序实现的功能。
这使得 Java 程序可以充分利用本地代码的性能优势,同时也可以方便地实现跨平台开发。
2.JNI 结构体参数概述JNI 结构体参数主要包括两个方面:函数指针和数据类型。
函数指针用于表示本地函数,数据类型用于表示本地函数的参数和返回值类型。
通过这些结构体参数,JNI 可以确保 Java 程序与本地代码之间的数据传递和函数调用正确无误。
3.JNI 结构体参数详细说明(1)函数指针JNI 中的函数指针主要有以下几种:- void* (*func)():表示一个返回值为 void 类型的函数指针。
- void* (*func)(int, int):表示一个返回值为 void 类型的函数指针,该函数有两个 int 类型的参数。
- int (*func)():表示一个返回值为 int 类型的函数指针。
- int (*func)(int):表示一个返回值为 int 类型的函数指针,该函数有一个 int 类型的参数。
(2)数据类型JNI 中的数据类型主要有以下几种:- jobject:表示 Java 对象。
- jbyte:表示 1 个字节。
- jshort:表示 2 个字节。
- jint:表示 4 个字节(即 int 类型)。
- jlong:表示 8 个字节(即 long 类型)。
- jfloat:表示 4 个字节(即 float 类型)。
- jdouble:表示 8 个字节(即 double 类型)。
- jboolean:表示布尔值。
- jmethodID:表示 Java 方法的唯一标识符。
JNI详解javanativeinterface,(jni)是一个标准的javaapi,它支持将java代码与使用其他编程语言编写的代码相集成。
在这里主要就是java和c++的交互。
1:java调用c++:首先,对java文件中要调用的方法进行本地声明,关键字为native。
它只需要宣布,而不是实施。
像publicnativevoidloginsuccessnative(stringp_qqid);publicnativestaticvoidsetstat enative(inti);然后我们需要在C++文件中实现这些方法。
这些方法有特定的格式。
我们可以使用javah命令来帮助生成这些方法的声明,调用javac来编译我们的Java类,得到类文件,然后javahyourclassname可以得到一个H文档:#include#ifdef_cplusplus}#endif#endif事实上,只要我们知道这种格式,我们也可以手动编写。
因为Java直接在CPP中查找方法,所以H文件,我们也不需要声明。
最后我们需要将c++文件,编译成so然后加入java工程,并在java中导入。
静态{system.loadlibrary(“test”);}//系统将自行判断后缀。
2:在c++中调用java方法。
在C++中调用java是比较麻烦的,因为我们需要在C++中获得java的运行环境,并找到我们想要使用的类和方法。
首先,我们需要理解几个概念:javavm:这个代表java的虚拟机。
所有的工作都是从获取虚拟机的接口开始的,如何获取这个接口呢?我们之前导入c的组件时调用了:system.loadlibrary(“test”);调用此方法时,Java将调用组件first_Onload()函数的JNI,该函数用于两个目的:一是:告诉javavm此c组件使用那一个jni版本。
如果你没有提供jni_onload()函数,vm会默认使用最老的jni1.1版本。
c语言函数调用时参数传递方式的有哪几种,分别简述他们的传
递方式
C语言函数调用时参数的传递方式主要有以下几种:
1. 值传递:函数调用时,将实际参数的值复制给形式参数,函数内部对形式参数进行修改不会影响实际参数的值。
这是最常见的参数传递方式。
2. 引用传递:通过传递变量的指针作为参数,函数内部可以直接通过指针访问和修改实际参数的值。
这种方式可以实现在函数内部改变实参的值。
3. 地址传递:传递变量的地址作为参数,在函数内部通过指针来访问和修改实际参数的值。
和引用传递类似,通过地址传递也可以改变实参的值。
4. 数组传递:将数组的首地址作为参数传递给函数,函数内部可以通过指针来访问和修改数组的元素。
5. 结构体传递:将整个结构体作为参数传递给函数,在函数内部可以直接访问和修改结构体中的成员。
需要注意的是,C语言中的参数传递都是按值传递的,包括引
用传递和地址传递。
所谓按值传递,是指在函数调用时将实参的值复制给形参,函数内部对形参的操作不会影响到实参的值。
但是通过引用传递和地址传递,可以通过指针来访问和修改实参的值,使得函数可以改变实参的值。
C多线程函数如何传参数和返回值在C多线程编程中,可以通过以下几种方式来传递参数和获取返回值:1.传递参数:-通过结构体:可以使用一个结构体来封装所有需要传递的参数,并将该结构体作为线程的参数传递。
在线程函数中,可以通过强制类型转换将参数还原为原始类型,并使用其中的成员变量。
-通过指针:可以将需要传递的参数作为指针进行传递,线程函数在收到指针后,可以通过解引用来获得参数值,或者使用指针指向的数据。
-通过全局变量:可以将参数设置为全局变量,在线程函数中直接使用该全局变量进行操作。
需要注意的是,多个线程同时修改全局变量时可能会发生竞争条件,需要使用互斥锁来保护。
2.获取返回值:-通过指针传递返回值:可以将需要返回的值设置为指针参数,线程函数在执行完毕后将结果写入该指针指向的内存位置。
主线程可以通过读取该内存位置来获取返回值。
-通过全局变量:可以将返回值设置为全局变量,在线程函数执行完毕后,在全局变量中存储结果。
主线程可以直接读取该全局变量来获取返回值。
但是同样需要注意并发操作时的竞争条件问题。
- 使用线程函数返回值:线程函数本身是可以返回一个值的,这个返回值可以通过pthread_join函数来获取。
主线程可以通过调用pthread_join函数来等待子线程执行完毕,并获取线程函数的返回值。
需要注意的是,在C多线程编程中,传递参数和获取返回值都需要考虑数据的一致性和并发性,尤其是多个线程同时对数据进行修改时可能会导致的问题。
可以使用互斥锁来保护对共享数据的访问,或者使用其他的线程同步机制来协调线程之间的执行顺序,并保证数据的一致性。
综上所述,C多线程函数可以通过结构体、指针、全局变量等方式来传递参数和获取返回值。
通过合理的设计和使用线程同步机制,可以确保线程之间的安全操作,并实现多线程程序的正确执行。
序这四种情况下你会用到本书:1、在Java程序中复用以前写过的C/C++代码。
2、自己实现一个java虚拟机3、学习不同语言如何进行协作,尤其是如何实现垃圾回收和多线程。
4、把一个虚拟机实现整合到用C/C++写的程序中。
本书是写给开发者的。
JNI在1997年第一次发布,本书总结了SUN工程师和大量开发者两年来积累的经验。
本书介绍了JNI的设计思想,对这种思想的理解是使用JNI的各种特性的基础。
本书有一部分是JA VA2平台上面的JNI特征的规范说明。
JNI程序员可以把这部分用作一个手册。
JVM开发者在实现虚拟机的时候必须遵守这些规范。
JNI的部分设计思想来源于Netscape的Java Runtime Interface(JRI)。
第一章简介JNI是JA V A平台的一个重要特征,使用它我们可以重用以前用C/C++写的大量代码。
本书既是一个编程指南也是一个JNI手册。
本书共包括三部分:1、第二章通过一个简单的例子介绍了JNI。
它的对象是对JNI不熟悉的初学者。
2、3~10章对JNI的特征进行了系统的介绍。
我们会举大量的例子来说明JNI的各个特征,这些特征都是JNI中重要且常用的。
3、11~13章是关于JNI的技术规范。
可以把这两章当作一个手册。
本书尽量去满足各类读者的需要。
指南面向初学者,手册面向有经验的人和自己实现JNI 规范的人。
大部分读者可能是用JNI来写程序的开发者。
本书会假设你有JA V A,C/C++基础。
本章的剩余部分介绍了JNI的背景,扮演的角色和JNI的演化。
1.1 JA V A平台和系统环境(Host Environment)系统环境代指本地操作系统环境,它有自己的本地库和CPU指令集。
本地程序(Native Applications)使用C/C++这样的本地语言来编写,被编译成只能在本地系统环境下运行的二进制代码,并和本地库链接在一起。
本地程序和本地库一般地会依赖于一个特定的本地系统环境。
Androidjnindk编程⼆:jni数据类型转换(primitive,String,a。
⼀.数据类型映射概述从我们开始jni编程起,就不可能避开函数的参数与返回值的问题。
java语⾔的数据类型和c/c++有很多不同的地⽅,所以我们必须考虑当在java层调⽤c/c++函数时,怎么正确的把java的参数传给c/c++函数,怎么正确的从c/c++函数获取正确的函数返回值;反之,当我们在c/c++中使⽤java的⽅法或属性时,如何确保数据类型能正确的在java和c/c++之间转换。
回顾我们上⼀篇⽂章中的那个c函数:#include <stdio.h>#include <jni.h>#include <stdlib.h>JNIEXPORT jstring JNICALL Java_com_jinwei_jnitesthello_MainActivity_sayHello(JNIEnv * env, jobject obj){return (*env)->NewStringUTF(env,"jni say hello to you");}这个函数⾮常简单,它没有接受参数,但是它返回了⼀个字符串给java层。
我们不能简单的使⽤return “jni say hello to you”;⽽是使⽤了NewStringUTF函数做了个转换,这就是数据类型的映射。
普通的jni函数⼀般都会有两个参数:JNIEnv * env, jobject obj,第三个参数起才是该函数要接受的参数,所以这⾥说它没有接受参数。
1.1JNIEnv * envJNIEnv是⼀个线程相关的结构体, 该结构体代表了 Java 在本线程的运⾏环境。
这意味不同的线程各⾃拥有各⼀个JNIEnv结构体,且彼此之间互相独⽴,互不⼲扰。
NIEnv结构包括了JNI函数表,这个函数表中存放了⼤量的函数指针,每⼀个函数指针⼜指向了具体的函数实现,⽐如,例⼦中的NewStringUTF函数就是这个函数表中的⼀员。
jni 参数返回值JNI (Java Native Interface) 是 Java 平台标准的一部分,它允许 Java 代码与其他语言编写的代码进行交互。
JNI 允许 Java 代码调用本地方法,这些本地方法通常是用 C、C++ 或其他语言编写的。
当在 JNI 中定义本地方法时,你需要考虑参数和返回值的类型。
下面是一些常见的 JNI 参数和返回值类型:1. 基本数据类型:如 `int`, `char`, `boolean`, `float`, `double` 等。
2. 引用类型:如 `String`, `Object` 等。
3. 数组:可以是基本数据类型数组或对象数组。
4. 结构体:如 `jobject`, `jclass`, `jarray` 等。
返回值类型与参数类型类似,也可以是上述任何类型。
下面是一个 JNI 方法定义的示例,该方法接受一个字符串参数并返回一个字符串:```cJNIEXPORT jstring JNICALL Java_MyClass_myMethod(JNIEnv env, jobject obj, jstring param) {const char nativeParam = (env)->GetStringUTFChars(env, param, NULL);// 在这里执行一些操作,例如将 nativeParam 转换为其他字符串jstring returnValue = (env)->NewStringUTF(env, "Some string"); // 释放本地字符串资源(env)->ReleaseStringUTFChars(env, param, nativeParam);return returnValue;}```在这个示例中,`Java_MyClass_myMethod` 是本地方法的名称,它遵循特定的命名规则:`Java_` + 包名 + 类名 + 方法名。
首先要强调的是,native方法不但可以传递Java的基本类型做参数,还可以传递更复杂的类型,比如String,数组,甚至自定义的类。
这一切都可以在jni.h中找到答案。
1. Java基本类型的传递用过Java的人都知道,Java中的基本类型包括boolean,byte,char,short,int,long,float,double 这样几种,如果你用这几种类型做native方法的参数,当你通过javah -jni 生成.h文件的时候,只要看一下生成的.h文件,就会一清二楚,这些类型分别对应的类型是 jboolean,jbyte,jchar,jshort,jint,jlong,jfloat,jdouble 。
这几种类型几乎都可以当成对应的C++类型来用,所以没什么好说的。
2. String参数的传递Java的String和C++的string是不能对等起来的,所以处理起来比较麻烦。
先看一个例子,class Prompt {// native method that prints a prompt and reads a lineprivate native String getLine(String prompt);public static void main(String args[]) {Prompt p = new Prompt();String input = p.getLine("Type a line: ");System.out.println("User typed: " + input);}static {System.loadLibrary("Prompt");}}在这个例子中,我们要实现一个native方法String getLine(String prompt);读入一个String参数,返回一个String值。
通过执行javah -jni得到的头文件是这样的#include <jni.h>#ifndef _Included_Prompt#define _Included_Prompt#ifdef __cplusplusextern "C" {#endifJNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv *env, jobject this, jstring prompt);#ifdef __cplusplus}#endif#endifjstring是JNI中对应于String的类型,但是和基本类型不同的是,jstring不能直接当作C++的string用。
如果你用cout << prompt << endl;编译器肯定会扔给你一个错误信息的。
其实要处理jstring有很多种方式,这里只讲一种我认为最简单的方式,看下面这个例子,#include "Prompt.h"#include <iostream>JNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt){const char* str;str = env->GetStringUTFChars(prompt, false);if(str == NULL) {return NULL; /* OutOfMemoryError already thrown */}std::cout << str << std::endl;env->ReleaseStringUTFChars(prompt, str);char* tmpstr = "return string succeeded";jstring rtstr = env->NewStringUTF(tmpstr);return rtstr;}在上面的例子中,作为参数的prompt不能直接被C++程序使用,先做了如下转换str = env->GetStringUTFChars(prompt, false);将jstring类型变成一个char*类型。
返回的时候,要生成一个jstring类型的对象,也必须通过如下命令,jstring rtstr = env->NewStringUTF(tmpstr);这里用到的GetStringUTFChars和NewStringUTF都是JNI提供的处理String类型的函数,还有其他的函数这里就不一一列举了。
3. 数组类型的传递和String一样,JNI为Java基本类型的数组提供了j*Array类型,比如int[]对应的就是jintArray。
来看一个传递int数组的例子,Java程序就不写了,JNIEXPORT jint JNICALL Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr){jint *carr;carr = env->GetIntArrayElements(arr, false);if(carr == NULL) {return 0; /* exception occurred */}jint sum = 0;for(int i=0; i<10; i++) {sum += carr[i];}env->ReleaseIntArrayElements(arr, carr, 0);return sum;}这个例子中的GetIntArrayElements和ReleaseIntArrayElements函数就是JNI提供用于处理int数组的函数。
如果试图用arr[i]的方式去访问jintArray类型,毫无疑问会出错。
JNI还提供了另一对函数GetIntArrayRegion和 ReleaseIntArrayRegion访问int数组,就不介绍了,对于其他基本类型的数组,方法类似。
4. 二维数组和String数组在JNI中,二维数组和String数组都被视为object数组,因为数组和String被视为object。
仍然用一个例子来说明,这次是一个二维int数组,作为返回值。
JNIEXPORT jobjectArray JNICALL Java_ObjectArrayTest_initInt2DArray(JNIEnv *env, jclass cls, int size){jobjectArray result;jclass intArrCls = env->FindClass("[I");result = env->NewObjectArray(size, intArrCls, NULL);for (int i = 0; i < size; i++) {jint tmp[256]; /* make sure it is large enough! */jintArray iarr = env->NewIntArray(size);for(int j = 0; j < size; j++) {tmp[j] = i + j;}env->SetIntArrayRegion(iarr, 0, size, tmp);env->SetObjectArrayElement(result, i, iarr);env->DeleteLocalRef(iarr);}return result;}上面代码中的第三行,jobjectArray result;因为要返回值,所以需要新建一个jobjectArray对象。
jclass intArrCls = env->FindClass("[I");是创建一个jclass的引用,因为result的元素是一维int数组的引用,所以intArrCls必须是一维int数组的引用,这一点是如何保证的呢?注意FindClass的参数"[I",JNI 就是通过它来确定引用的类型的,I表示是int类型,[标识是数组。
对于其他的类型,都有相应的表示方法,Z booleanB byteC charS shortI intJ longF floatD doubleString是通过“Ljava/lang/String;”表示的,那相应的,String数组就应该是“[Ljava/lang/String;”。
还是回到代码,result = env->NewObjectArray(size, intArrCls, NULL);的作用是为result分配空间。
jintArray iarr = env->NewIntArray(size);是为一维int数组iarr分配空间。
env->SetIntArrayRegion(iarr, 0, size, tmp);是为iarr赋值。
env->SetObjectArrayElement(result, i, iarr);是为result的第i个元素赋值。
通过上面这些步骤,我们就创建了一个二维int数组,并赋值完毕,这样就可以做为参数返回了。
如果了解了上面介绍的这些内容,基本上大部分的任务都可以对付了。
虽然在操作数组类型,尤其是二维数组和String数组的时候,比起在单独的语言中编程要麻烦,但既然我们享受了跨语言编程的好处,必然要付出一定的代价。
有一点要补充的是,本文所用到的函数调用方式都是针对C++的,如果要在C中使用,所有的env->都要被替换成(*env)->,而且后面的函数中需要增加一个参数env,具体请看一下jni.h的代码。
另外还有些省略的内容,可以参考JNI的文档:Java Native Interface 6.0 Specification,在JDK的文档里就可以找到。
如果要进行更深入的JNI编程,需要仔细阅读这个文档。