c语言的typedef struct 对应java参数类型,JNA实战系列:02JNA与C语言中的数据类型映射以及复杂结构体传参示例...

概述

我们知道,C语言中的很多数据类型与Java中的数据类型存在很大的区别,那么我们在java中调用C、C++的函数时必然存在一个数据类型的转换,所以弄清楚这些数据类型之间的映射关系对于我们的程序开发有着至关重要的意义,否则很难正确的调用动态类库中的某些功能。

常见普通数据类型的映射转换

C/C++类型

Java 类型

原生表现

bool

boolean

32位(同平台有关,可定制)

char

byte

8位整数

wchart_t

char

平台依赖

short

short

16位整数

int

int

32位整数

long long,_int64

long

64位整数

float

float

32位浮点数

double

double

64位浮点数

pointer

Buffer/Pointer

平台依赖(32位或者64位)

pointer array

[] (基本类型的数组)

32位或64位的指针

JNA常见的数据类型的映射

Java类型

C类型

说明

String

char*

\0结尾的字符数组

WString

wchar_t*

\0结尾的数组(unicode字符串)

String[]

char**

字符串数组

WString[]

wchar_t**

字符串数组(unicode)

Structure

struct*/struct

结构体指针或者结构体

Union

union

联合,等同于结构体

Structure[]

struct[]

结构体数组,临接内存

Callback

(*fp)()

函数指针

NativeMapped

varies

NativeLong

long

长整型

PointerType

pointer

同Pointer

Java模拟C语言的常见数据类型以及实现的示例

一、JNA模拟结构体

C语言中的定义

#pragma pack(push,1)

typedef struct tagValueItem

{

double _dValue;

int _lDate;

int _lTime;

char _exData[6];

}VALUE_ITEM, * PVALUE_ITEM;

#pragma pack(pop)

在java中的模拟

@Structure.FieldOrder({"_dValue", "_lDate", "_lTime", "_exData"})

class VALUE_ITEM extends Structure {

public double _dValue;

public int _lDate;

public int _lTime;

public byte[] _exData = new byte[6];

//内存对齐

public VALUE_ITEM() {

super(ALIGN_NONE);

}

//指针实现

public static class ByReference extends VALUE_ITEM implements Structure.ByReference {

}

//数组实现[]

public static class ByValue extends VALUE_ITEM implements Structure.ByValue{

}

}

代码说明与使用总结

C、C++中的结构体成员默认是public的,无需使用public关键字修饰,而在Java的实现中必须使用public关键字修饰,否则会报成员不存在的错误。

在Java中模拟C、C++的结构体时数据类型的映射必须一一对应,成员属性的名字可以不同(但一般为了方便追踪问题,建议定义成和.h头文件中一致)。

在Java中定义的成员属性的顺序必须和C、C++中头文件结构体中定义的顺序一致,不能调整顺序,否则会引起异常。

必须使用@Structure.FieldOrder或者重载FieldOrder方法,并返回一个字符串数组,数组中的成员是结构体的成员属性列表。

关于内存对齐,如果结构体中使用了内存对齐,那么在java中也必须声明一个无参构

造函数,并调用父类方法指定内存对齐模式,否则有可能引发内存访问异常。

如果需要用到结构体指针,则需要在结构体类的实现中实现静态内部类ByReference,如代码中所示,VALUE_ITEM.ByReference 就等同于 VALUE_ITEM*

如果需要用到结构体对象数组,则需要在结构体类的实现中实现静态内部类ByValue,如上述代码所示, VALUE_ITEM.ByValue 就等同于 VALUE_ITEM[]

二、回调函数Callback以及复杂结构体参数传递

C语言中的定义

typedef struct HISTORY_ITEM

{

int _nDate = 0; //日期 yyyymmdd

int _nTime = 0; //时间 hhmmss

double _dYClose = 0; //前收盘价 分时-均价

double _dOpen = 0; //开

double _dHigh = 0; //高

double _dLow = 0; //低

double _dClose = 0; //收

double _dVol = 0; //成交量

double _dAmount = 0; //成交金额

int _nAdvance = 0; //周期上涨家数. 分笔 B/S

int _nDecline = 0; //周期下跌家数.

//期货

double _dPosition = 0; //持仓

double _dSettle = 0; //结算价

char _exData[32] = { 0 }; //预留数据

} HQCHART_KDATA;

typedef struct tagHQChartKDataResult

{

HQCHART_KDATA* _pData; //K线数据指针

int _lCount; //k线数据数量

wchar_t* _pszName; //股票名称

bool _bResult;

wchar_t* _pszError;

int _lPeriod; //周期

int _lRight; //复权

int _exData[4];

}HQCHART_KDATA_RESULT, * PHQCHART_KDATA_RESULT;

typedef bool(HQ_CALL *pHQChart_LoadKData)(const wchar_t* lpSymbol, long lPeriod, long lRight, HQCHART_KDATA_RESULT* pResult, const wchar_t* pstrGuid);

参数列表说明: lpSymbol,lPeriod,lRight,pstrGuid这几个是输入参数,pResult是输出参数

函数说明: 这是一个函数指针,主要用于加载K线数据.需要在Java中实现取K线数据的逻辑,并使用输出参数pResult传递给C语言的类库中使用。

在java中的模拟

C语言中的回调函数指针在Java中是使用一个集成了StdCallLibrary.StdCallCallback类的接口实现的。

// 结构体模拟实现

@Structure.FieldOrder({"_pData", "_lCount", "_pszName", "_bResult","_lPeriod", "_lRight"})

class HQCHART_KDATA_RESULT extends Structure {

public HQCHART_KDATA.ByReference _pData;

public int _lCount;

public WString _pszName; //股票名称

public int _lPeriod; //周期

public int _lRight; //复权

public HQCHART_KDATA_RESULT() {

super(ALIGN_NONE);

}

public static class ByReference extends HQCHART_KDATA_RESULT implements Structure.ByReference {

public ByReference() {

}

}

}

@Structure.FieldOrder({"tDate", "_nTime", "_dYClose", "_dOpen", "_dHigh",

"_dLow", "_dClose", "_dVol", "_dAmount"})

class HQCHART_KDATA extends Structure {

public int tDate = 0; //日期 yyyymmdd

public int _nTime = 0; //时间 hhmmss

public double _dYClose = 0; //前收盘价 分时-均价

public double _dOpen = 0; //开

public double _dHigh = 0; //高

public double _dLow = 0; //低

public double _dClose = 0; //收

public double _dVol = 0; //成交量

public double _dAmount = 0; //成交金额

public HQCHART_KDATA() {

super(ALIGN_NONE);

}

public static class ByReference extends HQCHART_KDATA implements Structure.ByReference {

public ByReference() {

}

}

}

//回调函数接口定义

interface pHQChart_LoadKData extends StdCallLibrary.StdCallCallback {

boolean invoke(WString lpSymbol,

long lPeriod, long lRight, HQCHART_KDATA_RESULT.ByReference pResult,

WString pstrGuid);

}

//回调函数接口类实现

package com.zealink.hqchart.hqdata;

import com.sun.jna.WString;

public class HQChartLoadKDataImpl implements HQChart.pHQChart_LoadKData {

@Override

public boolean invoke(WString lpSymbol, long lPeriod, long lRight, HQChart.HQCHART_KDATA_RESULT.ByReference pResult, WString pstrGuid) {

System.out.println("pHQChart_LoadKData" + "\t" + lpSymbol + "\t" + lPeriod + "\t" + lRight + "\t" + pstrGuid);

pResult.read();

pResult._lCount = 20;

pResult._lPeriod = (int) lPeriod;

pResult._lRight = (int) lRight;

pResult._pszName = new WString("智慧能源");

//结构体中嵌套结构体指针,指向一个连续的结构体数组并且需要动态申请内存,在此模拟申请20个结构体对象的数组,需要通过如下方式动态申请连续的内存块,否则因为内存不连续而报错。

pResult._pData = new HQChart.HQCHART_KDATA.ByReference();

HQChart.HQCHART_KDATA[] cdata = (HQChart.HQCHART_KDATA[]) pResult._pData.toArray(20);

for (int i = 0; i < pResult._lCount; i++) {

cdata[i].tDate = 20210401 + i;

cdata[i]._nTime = 0;

cdata[i]._dOpen = 13.01 + 0.5 * i;

cdata[i]._dHigh = 14.01 + 0.5 * i;

cdata[i]._dLow = 12.5 + 0.5 * i;

cdata[i]._dClose = 13.05 + 0.5 * i;

if (i >= 1) {

cdata[i]._dYClose = cdata[i - 1]._dClose;

} else {

cdata[i]._dYClose = 13.0;

}

cdata[i]._dVol = 100000000;

cdata[i]._dAmount = 1000000000;

}

//必须调用write方法将数据写入到堆内存中,否则写入的数据都为空。

pResult.write();

return true;

}

}

总结说明

如果结构体中嵌套结构体指针,且指向的是需要动态申请内存的结构体数组,那么该指针需要被定义为public HQCHART_KDATA.ByReference

如果是输入参数的结构体指针,则在函数定义的时候可以定义为public HQCHART_KDATA.ByValue 类型,然后遍历数组取值

动态申请连续的内存时需要通过ByReference(结构体Structure)对象的toArray(数组大小)来申请内存,不能通过Structure[] obj = new Structure[size]这种方式申请,否则办法赋值给ByReference对象。

ByReference对象数据写完后,需要调用write()方法将数据写入到堆内存中,否则数据传递到外层就变成了空数据。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值