原文 http://blog.csdn.net/shendl/archive/2008/12/26/3599849.aspx
2
,
C
语言中,结构体内部必须进行数组定义。
Java
中最好也这样做。
C
语言的结构体是一段连续的内存,内存的大小是编译时确定的。
因此,数组必须定义。否则编译不会成功。
对应的
Java
类中,我们也应该在类定义时为数组定义。尽管实际上在使用时再赋值也可以。
但是,强烈不建议你这样做。
如,上面
public
UserStruct.ByValue[]
users
=
new
UserStruct.ByValue[100];
定义
100
个元素的数组,如果你不再类内部定义。
而在使用时定义,如果你没有正确赋值,没有定义为
100
个元素,就会出错。
从表面上看,
CompanyStruct
类
占用的内存是:
NativeLong
id
WString
name
;
public
UserStruct.ByValue[]
users
=
new
UserStruct.ByValue[100];
public
int
count
;
这
4
个元素占用的内存的总和。
由于
Java
的数组是一个对象,
users
中实际保存的也应该是一个引用,也就是指针,
32bit
。
那么
CompanyStruct
类占用的内存就比对应的
C
结构体:
struct
CompanyStruct{
long
id;
wchar_t
*
name;
UserStruct
users[100];
int
count;
};
小很多。内存少用很多。
我在例
3
的说明中曾经说过:
“
Java
调用
dll
中的
C
函数,实际上就是一段内存作为函数的参数传递给
dll
。
Dll
以为这个参数就是
C
语言传过来的参数。
”
那么,
JNA
怎么还能正确调用
C
函数呢。
事实上,在调用
C
函数时,
JNA
会把
UserStruct
类的实例的所有数据全部
*100
倍。(我的例子里数组是
100
个,你的例子当然可以有不一样的数值)
这样,
Java
中的结构体的内存量就和
C
语言的
CompanyStruct
结构体占据相同大小和结构的内存,从而正确调用
C
函数。
例
5
使用
JNA
调用使用嵌套
Struct
的指针的数组的
C
函数
现在给大家看看最复杂的
Struct
的例子。
Struct
中嵌套的是一个结构体的指针的数组。
C
语言代码
struct
CompanyStruct2{
long
id;
wchar_t
*
name;
UserStruct*
users[100];
// UserStruct
users[100];
int
count;
};
MYLIBAPI
void
sayCompany2(CompanyStruct2* pCompanyStruct);
这里,把刚才使用的
UserStruct
数组换成
UserStruct
指针的数组。
JNA
代码
public
static
class
CompanyStruct2
extends
Structure{
public
static
class
ByReference
extends
CompanyStruct2
implements
Structure.ByReference { }
public
NativeLong
id
;
public
WString
name
;
public
UserStruct.ByReference[]
users
=
new
UserStruct.ByReference[100];
public
int
count
;
}
public
void
sayCompany2(CompanyStruct2.ByReference
pCompanyStruct);
测试代码:
CompanyStruct2.ByReference companyStruct2=
new
CompanyStruct2.ByReference();
companyStruct2.
id
=
new
NativeLong(2);
companyStruct2.
name
=
new
WString(
"Yahoo"
);
companyStruct2.
count
=10;
UserStruct.ByReference pUserStruct=
new
UserStruct.ByReference();
pUserStruct.
id
=
new
NativeLong(90);
pUserStruct.
age
=99;
pUserStruct.
name
=
new
WString(
"
良少
"
);
pUserStruct.write();
//
TestDll1.INSTANCE.sayUser(pUserStruct);
for
(
int
i=0;i<companyStruct2.
count
;i++){
companyStruct2.
users
[i]=pUserStruct;
}
TestDll1.
INSTANCE
.sayCompany2(companyStruct2);
程序说明
----Pin
锁住
Java
对象:
因为是结构体的指针的数组,所以,我们使用了
public UserStruct.ByReference[] users=new UserStruct.ByReference[100];
来对应
C
语言中的
UserStruct*
users[100];
但是,有问题,如果去除
pUserStruct.write();
这一行代码,就会报错。
如果去除
pUserStruct.write();
但是使用
TestDll1.INSTANCE.sayUser(pUserStruct);
也不会有问题。
这是怎么回事?
原来,错误的原因就是内存锁定!
java
内存锁定机制和
JNI
,
JNA
调用
我们知道,
java
的内存是
GC
管理的。它会自动管理
JVM
使用的堆内存。删除不再使用的内存,并把
Java
对象使用的内存自动移动,以防止内存碎片增多。
因此,虽然
Java
的引用实际上就是指针,但还是和指针不同。
Java
中不能直接使用指针。因为对象在内存中的地址都不是固定的。说不准什么时候
GC
就把它给移位了。
如果使用
JNI
,
JNA
等技术调用
C
函数。那么就会有问题。因此,
C
语言使用的是指针。如果想要获取
C
函数的返回值。那么我们必须提供一块内存给
C
语言访问。而
C
语言是不知道你
Java
的引用的。它只能访问固定的内存地址。
如果
GC
把
Java
对象移来移去,那么
C
函数就没办法和
Java
交互了。
因此,在使用
JNI
和
JNA
时,都会把
Java
对象锁住。
GC
不再管理。不删除,也不移动位置。由此出现的内存碎片,也不管了!
这种技术的术语是
PIN
。
.NET
也有同样的概念。
上面
TestDll1.INSTANCE.sayUser(pUserStruct);
这个调用是
JNA
调用。这个操作就把
pUserStruct
这个
Java
对象锁住了。
例
4
中,嵌套的是结构体,因此也会直接把
CompanyStruct
类的实例锁住。
但是例
5
中,嵌套的是结构体的指针的数组。
CompanyStruct
2
类的实例
companyStruct2
在执行
TestDll1.INSTANCE.sayCompany2(companyStruct2);
时是被锁住了,可以
companyStruct2
的
users
指针数组
的成员:
pUserStruct
都没有被锁住。
怎么办呢?
难道每一个
UserStruct.ByReference
的实例都先调用一遍不需要的
TestDll1.INSTANCE.sayUser(pUserStruct);
方法?
没事!
JNA
开发人员早已想到了:
Structure
类中有方法:
write
public void write
()
Writes the fields of the struct to native memory
writeField
public void writeField
(
String
name)
Write the given field to native memory. The current value in the Java field will be translated into native memory.
Throws:
IllegalArgumentException
- if no field exists with the given name
这些
write
方法,会把
Java
的内存
Pin
住。
就是
C
语言可以使用。
GC
不再管理它们。
现在只要调用
pUserStruct.write();
把
java
模拟结构体实例给
Pin
住就可以了。
题外话,
C#
定义了语法,可以使用关键字
pin
锁住
.NET
对象。
结论:
结构体是
C
语言模拟
OOP
开发中经常使用的一种数据组织形式。搞定了结构体
Struct
,我们就可以放心大胆、轻轻松松地把
C
程序随便拿来用了!
分享到:
相关推荐
### 深入解析JNA—模拟C语言结构体 #### 前言 在《JNA—JNI终结者》一文中介绍了Java Native Access (JNA) 的基本使用方法及其优势,但当时并未深入探讨如何在Java中模拟C语言中的结构体(structure)。结构体在C语言...
Java通过JNA(Java ...通过理解JNA的工作原理,创建对应的Java结构体类,以及正确地传递和解析结构体指针,可以有效地在Java和C之间进行数据交换。对于需要跨平台或利用C库的Java开发者来说,这是一个重要的技能。
在"JNA 转java接口以及指针结构体解析"这个主题中,我们将深入探讨如何使用JNA来处理C语言中的结构体和指针。 首先,理解JNA的基本工作原理至关重要。JNA通过定义一个`Interface`,该接口中的方法对应于要调用的...
本文将深入探讨JNA在处理复杂结构体传递,包括结构体数组和结构体指针方面的知识。 JNA是一种Java库,允许Java代码直接调用本机API,而无需编写JNI代码。相比于JNI,JNA的使用更为简单和直观,但可能在性能上稍逊...
可实现将C语言中的结构体转换为JAVA类型的实体类。 目前支持基础数据类型int、long、foloat、double、const char *、bool的转换,也支持将数组装换为JAVA中的ArrayList。目前有个难点是将枚举类型直接转换为JAVA中的...
在标题中提到的“jna调用C语言函数库dll、so例子”,这里的"dll"是Windows系统中的动态链接库(Dynamic Link Library),"so"则是Linux和其他类Unix系统中的共享对象(Shared Object)。这两种都是C语言或其他编译...
Java调用C语言动态库(JNA方式):回调函数、结构体数组传参、结构体数组返回-附件资源
2:基本数据类型的指针和引用 3:结构体 4:结构体的指针和引用 5:函数指针和回调函数 6:字符串指针 7:输入一个数组 8:输出一个数组并释放空间 本资源包括三个工程: C++动态链接库; VC调用本地动态链接库; ...
这些函数应该遵循C语言的调用约定,因为JNA主要支持C风格的接口。 2. **创建SWIG接口文件**:创建一个SWIG接口文件(.i文件),在这个文件中,你需要引用C++的头文件,并且声明哪些函数和类型需要被暴露给Java。 3...
《深入解析JNA-4.4.0.jar:Java Native Access技术详解》 在Java编程领域,有时我们需要调用操作系统底层的功能,例如访问硬件设备、操作系统API或使用C/C++库。这时,Java Native Interface (JNI) 和 Java Native ...
2. **定义接口**:JNA允许我们定义一个Java接口,该接口对应于要调用的本机库函数。例如,我们可以定义一个接口来表示Windows API的`SetWindowsHookEx`函数。 ```java public interface User32 extends Library { ...
### 深入浅出JNA — 快速调用原生函数 #### 为什么需要JNA? 在软件开发过程中,特别是在需要与操作系统底层交互或利用高性能计算资源时,经常需要用到原生函数(通常指C/C++语言编写的库)。Java作为一种高级语言...
通过这个JNA实例,我们可以看到Java程序是如何利用JNA库来调用C语言编写的函数和处理结构体数据的。这种技术不仅简化了跨语言编程的过程,还提高了代码的可维护性和扩展性。对于需要与现有C/C++代码集成的项目来说,...
JNA通过映射机制支持常见的数据类型,包括整型、浮点型、字符串等基本数据类型的转换,并提供了一套丰富的API来模拟原生语言中的指针、结构体等复杂数据结构。通过JNA,开发者可以更轻松地调用原生函数,也更容易...
这段代码展示了如何使用JNA加载`user32.dll`库并定义`SendInput()`函数,接着可以创建`INPUT`结构体实例,填充模拟的鼠标或键盘事件,调用`SendInput()`来发送这些事件。 通过这种方式,Java开发者可以利用JNA库在...
本示例将详细解析如何使用JNA在Java中调用C语言库文件。 首先,我们需要理解JNA的工作原理。JNA通过映射Java方法到本地函数来实现调用,这得益于其内建的类型映射系统。Java中的数据类型会自动转换为对应的C语言...
2. **模拟C语言数组**: C语言中,函数可能接受字符数组,如`char *data`。在JNA中,你可以使用Java的数组类型来模拟这些参数,如`void function1(char[] data)`。由于Java没有原始字符类型,`char[]`可以被替换为`...