前言
什么是JNI
- java native interface 的简称
- 在java和本地语言相互调用时,充当两者之间的翻译.
- 可以看作一种协议(规范),它提供了一套编程框架.
为什么需要JNI
- 可以很方便的操作底层硬件,例如手机上的传感器等.
- 提高程序的运行效率,例如2D,3D加速,音视频的解码.
- 提高程序的安全性,因为本地语言编译之后是二进制的可执行文件(不同的操作系统二进制的可执行文件不同),程序被反编译之后很难看出业务逻辑的具体实现.
- 方便复用本地开源项目(FFmpeg[多媒体播放器],sqlite,openSSL,openGL,openCV等).
- 降低程序的移植性等等.
使用需要知道哪些前提
- 熟悉使用java
- 了解基本c语言(指针)
- 熟悉JNI的规范
- 了解NDK的使用
C
我们应该了解的
- 两种语言相同的数据类型占用的字节相同时,在传递时不需要进行转换处理.
- c语言中没有byte,boolean,c语言中非0为真,0为假.(将所有非零真值中的1代表真).
- c语言中的char(1个字节)和long(4个字节)与java有所区别.
- c语言中整数类型前面有signed(默认)/ unsigned ,影响着这个整数类型是否能表示负数.
- sizeof()是一个运算符,计算数据类型大小,返回它参数的字节数.
- 内存特点
- 内存单元的大小是1 byte, 即 8 bits,也就是说:内存单元最大是255.
- 内存单元排列是线性连续.
- 进程内存单元个数(即内存大小) 在32位平台下,每个进程拥有4G的内存范围(实际用到的内存会映射到实际内存中).
- 内存单元编号:也就是内存单元在内存中的地址,是在内存中识别内存单元的唯一编号.
- 指针
- 将内存单元的地址(编号)成为指针.
- 是一个无符号整数(unsigned int),它是一个以当前系统寻址范围为取值范围的整数.
- 32位系统下寻址能力(地址空间)是4G Bytes(0~2^32-1)二进制表示长度为32bits(也就是4Bytes), unsigned int类型也正好如此取值.
- 指针变量
- 普通变量 : 保存数值数据
- 指针变量 : 专门保存地址的变量(只保存某个变量的第一个内存单元编号,其中变量是连续的地址空间)
- 我们约定这样表示一个指针变量 : 数据类型 * 指针变量名;
- 通常指针变量也被叫做指针
指针运算符*和&
- &取地址运算符,取变量的地址(编号)
- 在定义指针变量时使用的 * 是一个类型标志,代表定义的变量是一个指针变量
* 除了在定义指针变量和类型强转时以外的其他情况使用,都代表指针运算符,例如
int i = 0; //定义普通变量 int * p = &i;//定义指针变量,*是类型标志,不是指针运算符 // 也可以写成这样 int *p; p = &i; *p = 30; // *p <=> i, * 是指针运算符,指向谁就是访问谁 *&i = 20; // 通过指针访问 *&i <=> i
- 指针的大小
- 是确定的,在32位平台下,是4个字节
指针类型和指针的数据类型
//在这样一段代码中 int * p = &i; //int 是指针的数据类型,它不能影响指针的大小,它代表p所指向的变量的数据类型,决定指针在访问内存时一次存取多少字节 //int * 合起来才是真正的指针类型,代表p是一个指针变量.
二级指针
int main(){ int i = 0; int* p = &i; //一级指针保存普通变量的地址 int** pp = &p; //二级指针保存一级指针地址 i = 10;//变量名访问 printf("i = %d\n",i); *p = 20;//一级指针访问 *p <=> i printf("i = %d\n",i); **pp = 30;// 二级指针访问 **pp <=> *p <=> i printf("i = %d\n",i); return 0; }
指针的运算
- 指针的运算只有在一段连续的内存空间上才有意义.(即数组)
- 指针+整数N,指针p向高地址方向移动了N个对象,编号变化p+N*sizeof(对象类型);
因为编译器会自动乘.不需要我们画蛇添足. - 指针+整数N也是同样的道理.
数组
- 数组名:数组名代表数组首元素首地址,是一个常量. 例如int a[5]; a <=> &a[0]
- 数组元素个数计算方法 sizeof(a) / sizeof(a[0])
- 指针和数组的关系
- 根据数组和指针的运算, 我们可以得出: 若int a[5] , int * p = a; 则 a[i] <=> * (a + i) <=> * (p + i) <=> p[i].
- 因为在C语言中, a[5]会被转换成* (a+i)来编译. 简单来说, 当你看到a[?]时, 请自动脑补* (a+?), 而* (a+i)是不会出现越界的情况,因为无论任何时候, 都是可以加减的. 这样也就解释了为什么C语言不能检查索引越界.