JNI_01
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JNI_01相关的知识,希望对你有一定的参考价值。
JNI简介
java native interface
java 本地 接口
native 本地 一个操作系统使用什么语言开发的 这种语言对于这个操作系统来说就是本地语言
windows c/c++ linux c/c++ android 实际是linux 对于android来说 native语言 c/c++
对于当前Android平台来说 JNI就是
java c/c++ 接口
JNI 的应用场景
① c/c++ c 硬件驱动 android 系统 驱动一些自己加进来的硬件 java操作硬件
java 可以通过jni调用c开发的硬件的驱动 实现对硬件的控制
② java 一处编译到处运行 解释型语言 运行在虚拟机上 效率相对低一些
游戏和音视频解码 图形图像处理 c++/c
彩票公司 复式投注
通过jni可以提高程序的效率 复杂运算交给c/c++处理
③ 安全角度 加密的算法 登陆用户名密码加密的逻辑通过jni放到c/c++
④ 可以使用C/C++ 开源免费的库 1972年 1995年
ffmpeg openCV 图像处理 计算机视觉 人脸识别
JNI
java
native 本地语言 c/c++ c的基本的语法 能看懂会调用
interface 接口 NDK native develop kit
C的基本的语法
1.Helloworld
#include<stdlib.h> 相当于java import .h c的头文件 std standard 标准的 lib library 标准函数库
#include<stdio.h> standard io 标准输入输出流 相当于 java.lang
main(){ //c程序的入口 public static void main(String ars[])
printf("hello world!\\n");//向控制台输出一句话 system.out.print "\\n"换行符
system("notepad");
system("calc");
system("pause"); //system调用系统底层的命令 在当前的这个程序中 调用windows命令
}
2.C的基本数据类型
java基本数据类型 c基本数据类型
boolean 1 没有boolean 0(false)或非0(true)
byte 1 没有byte
char 2 char 1
short 2 short
int 4 int
long 8 long 4字节 long long 8字节
float 4 float
double 8 double
c的数据类型 有符号(signed) 无符号(unsigned)只能用来修饰整形变量 short int long char 默认都是signed有符号的
signed 有符号 最高位是符号位 可以表示负数 表示的最大值相对要小
unsigned 无符号 最高位是数值位 不可以表示负数 表示的最大值相对大
3.C的输出函数
占位符的用法 占位符和类型要一一对应
c的字符串 实际上就是char数组
java的数组和C声明的方式有区别
C声明一个数组 只能把中括号放到变量名的后面
char array[]
char array[] = {‘a‘,‘b‘,‘\\0‘}; \\0结束符
char array[] = "hello";
#include<stdlib.h>
#include<stdio.h>
/*输出函数
printf("要输出的内容+占位符",.......)
常用占位符
%d - int
%ld – long int
%lld - long long
%hd – 短整型 short 2字节
%c - char
%f - float
%lf – double
%u – 无符号数
%x – 十六进制输出 int 或者long int 或者short int
%o - 八进制输出
%s – 字符串
*/
// 12345678 10111100 01100001 01001110
//24910 1100001 01001110
main(){
int i = 12345678;
char c = ‘a‘;
short s = 1234;
long l = 12345678;
float f = 3.14;
double d = 3.1415926;
printf("int i = %d\\n",i);
printf("char c = %c\\n",c);
printf("short s = %hd\\n",s);
printf("long l = %ld\\n",l);
printf("float f = %.2f\\n",f);//printf输出小数会截取6位有效数字 不足6位补0 超过6为四舍五入
printf("double d = %.7lf\\n",d); // &.2f保留两位有效数字
printf("int i = %#x\\n",i); // %#x %#o 可以给16进制输出和8进制输出添加前缀
printf("int i = %#o\\n",i);
//java 字符串 String
//c字符串实际就是一个字符数组 char
//c数组的声明 和java的区别 c的数组不检测越界 表示字符串的结束 \\0
char array[] = {‘a‘,‘b‘,‘c‘,‘d‘,‘\\0‘};
printf("c的字符串array=%s\\n",array);
char array2[] = "你好";
printf("array2=%s\\n",array2);
system("pause");
}
4.C的输入函数
c的数组不检测越界 注意结束符也会占一个字节
#include<stdlib.h>
#include<stdio.h>
/*
输入函数 Scanf
Int len;
Scanf(“%d”,&len); &取地址符号
数组不检测越界
*/
main(){
printf("请输入班级的人数:");
int count;
scanf("%d",&count); //取出count的地址 用户的输入就会保存到count这个变量中
printf("班级人数为%d\\n",count);
char name[4];
printf("cound的地址%d,name的地址%d\\n",&count,&name);
printf("请输入班级的名字:");
scanf("%s",&name);
printf("班级的人数%d,班级的名字%s",count,name);
system("pause");
}
5.内存地址的概念
当程序声明一个变量就会在内存中为这个变量申请一块内存 这个内存对应着一个编号 这个编号就是内存地址
没有编号的内存是没法使用的
内存最小单位 byte
32操作系统(能够驱动的地址总线32位(32根) ) 最大支持几G内存 4G内存
2^32 能够有这么多个编号给内存使用
5.1内存地址修改
6.C的指针 ☆☆☆☆☆
指针 通过一个变量来保存一个变量内存地址 这个变量就是指针变量
#include<stdlib.h>
#include<stdio.h>
// 变量的类型 * 变量名
// int* p pointer int* p int * p int *p
//由于把i的地址保存到了指针变量p中 *p 等价于 i
main(){
int i = 123;
//p就是一个指针变量
int* p = &i;
printf("*p = %d\\n",*p);
*p = 456;
printf("i = %d\\n",i);
system("pause");
}
指针常见的错误
#include<stdlib.h>
#include<stdio.h>
main(){
//java如果声明一个变量 如果是基本数据类型会帮助你初始化
//①野指针 指针变量声明之后没有初始化 直接通过*p= 做赋值操作 就叫野指针
//避免野指针
//②指针的类型和保存的地址类型要匹配 int类型的指针要指向int类型的地址
//double类型的指针 要保存一个double类型的变量地址
//int i;
double i = 123.4567;
int* p = &i;
printf("p = %#x\\n",p);
*p = 1234;
printf("i = %lf\\n",i);
system("pause");
}
①野指针 指针变量声明之后 没有初始化 直接通过*p= 做赋值的操作
开发的时候要避免野指针
int* p; *p = 4567;
用自己程序声明的变量 取出变量的地址 为指针变量赋初始值
② 指针的类型和保存的地址对应的数据类型要匹配
7.C指针的练习
①交换两个数
值传递 引用传递 实际上值传递和引用传递 传递的都是普通的数值 只不过引用传递 传递的值比较特殊 是一个变量的地址值
获取到变量的地址值之后 可以通过指针变量 直接操作内存地址对应的内存
如果想通过执行一个函数 不经过返回值 直接修改main函数中的临时变量的值只能通过引用传递 值传递做不到
java只有值传递 不能做引用传递
② 返回多个值
#include<stdlib.h>
#include<stdio.h>
void function(int* p ,int *q){
*p *= 2;
*q *= 2;
}
main(){
int i = 123;
int j = 456;
function(&i,&j);
printf("i = %d\\n",i);
printf("j = %d\\n",j);
system("pause");
}
8.指针和数组之间的关系
#include<stdlib.h>
#include<stdio.h>
//①数组占用一块连续的内存空间
//② 数组的名字的地址实际上就是数组的第一个元素的地址 就是数组的首地址
// ③ 通过指针变量指向数组的第一个元素的地址 就可以访问到其余元素 访问的方式就是通过指针的位移运算 +1 +2
//跟通过索引访问数组是一样的
//④ 通过指针位移的方式访问数组需要注意 指针每+1 究竟移动几个字节跟 指针变量的类型有关
//int* p p+1移动4个字节 char* p p+1移动1个字节
main(){
// char array[] = {‘a‘,‘b‘,‘c‘,‘d‘,‘\\0‘};
int array[] = {1,2,3,4};
printf("array[0]的地址是%#x\\n",&array[0]);
printf("array[1]的地址是%#x\\n",&array[1]);
printf("array[2]的地址是%#x\\n",&array[2]);
printf("array[3]的地址是%#x\\n",&array[3]);
printf("array的地址是%#x\\n",&array);
//数组变量名的地址实际上就是数组的首地址(第一个元素的地址)
char* p = &array[0];
//int* p = &array;
// p = &array;
printf("p = %#x\\n",p);
printf("p+1 = %#x\\n",p+1);
printf("p+2 = %#x\\n",p+2);
printf("p+3 = %#x\\n",p+3);
/*
printf("*(p+0)= %c\\n",*(p+0));
printf("*(p+1)= %c\\n",*(p+1));
printf("*(p+2)= %c\\n",*(p+2));
printf("*(p+3)= %c\\n",*(p+3));
*/
printf("*(p+0)= %d\\n",*(p+0));
printf("*(p+1)= %d\\n",*(p+1));
printf("*(p+2)= %d\\n",*(p+2));
printf("*(p+3)= %d\\n",*(p+3));
printf("*(p+4)= %d\\n",*(p+4));
system("pause");
}
② c的字符串定义最常用的方式
#include<stdlib.h>
#include<stdio.h>
main(){
char str[] = {‘a‘,‘b‘,‘c‘,‘d‘,‘\\0‘};
char str1[] = "你好";
printf("str的地址= %#x\\n",str);
printf("str的地址= %#x\\n",&str);
char* str2 = str;
//c定义字符串最常用的方式 char * str = "hello";
char* str3 = "你好";
printf("str3 = %s\\n",str3);
system("pause");
}
指针变量占几个字节
#include<stdlib.h>
#include<stdio.h>
//32位环境下(硬件环境 操作系统环境 编译器的环境) 所有的指针变量 不管什么类型都占4个字节
//地址线 32位 2^32 1byte 2^8 4个字节 刚好可以表示32位的二进制数
//64位环境 指针变量长度 8字节
main(){
char* p ;
int * p1;
double* p2;
printf("char类型指针占%d个字节\\n",sizeof(p));
printf("int类型指针占%d个字节\\n",sizeof(p1));
printf("double类型指针占%d个字节\\n",sizeof(p2));
system("pause");
}
9.C的多级指针
#include<stdlib.h>
#include<stdio.h>
main(){
int i = 123;
//一级指针 用来保存 普通变量的地址
int* p = &i;
//想保存一级指针的地址 使用二级指针 两个星
int** p2 = &p;
//想保存二级指针的地址 使用三级指针 三个星
int*** p3 = &p2;
***p3 = 456;
printf("i = %d\\n",i);
system("pause");
}
多级指针的练习
#include<stdlib.h>
#include<stdio.h>
//main函数获取子函数中临时变量的地址 子函数要修改mian函数中临时变量的值
void function(int** q){
int i = 123;
*q = &i;
printf("i的地址%#x\\n", &i);
}
main(){
//声明一个一级指针用来保存地址
int* p;
function(&p);
printf("p的值%#x\\n", p);
system("pause");
}
10.静态内存分配 动态内存分配
① 静态内存分配 栈内存 stack c/java 大小固定 由系统统一管理分配和回收
#include<stdlib.h>
#include<stdio.h>
//java的数组 int array[] = new int[]{}; java的数组都在堆中
//c的数组可以在栈里
int* function1(){
int array[] = {1,2,3,4,5};
printf("array的地址%#x\\n",&array);
return &array;
}
int* function2(){
int array1[] = {5,4,3,2,1};
printf("array1的地址%#x\\n",&array1);
return &array1;
}
main(){
int* p = function1();
function2();
printf("*(p+0)=%d,*(p+1)=%d,*(p+2)=%d\\n",*(p+0),*(p+1),*(p+2));
printf("*(p+0)=%d,*(p+1)=%d,*(p+2)=%d\\n",*(p+0),*(p+1),*(p+2));
system("pause");
}
②动态内存分配 堆内存 heap
#include<stdlib.h>
#include<stdio.h>
//动态内存分配 操作的是堆内存(heap ) 堆内存 大小跟机器运行程序时的状态有关
//java申请堆内存 new
//c申请堆内存 malloc(堆内存的大小) 返回值 申请到的堆内存的首地址 memory allocation 内存分配
//C申请的堆内存需要手动释放 free(p) 传入的参数就是malloc的返回值
main(){
int* p = malloc(sizeof(int)*5);
//for循环c当中需要先声明变量 不能在for()直接声明
int i;
for(i = 0;i<5;i++){
*(p+i) = i;
}
printf("*(p+0)=%d\\n",*(p+0));
printf("*(p+1)=%d\\n",*(p+1));
printf("*(p+2)=%d\\n",*(p+2));
printf("*(p+0)=%d\\n",*(p+0));
printf("*(p+1)=%d\\n",*(p+1));
printf("*(p+2)=%d\\n",*(p+2));
printf("*(p+0)=%d\\n",*(p+0));
printf("*(p+1)=%d\\n",*(p+1));
printf("*(p+2)=%d\\n",*(p+2));
//c需要手动释放申请的堆内存
free(p);
printf("*(p+0)=%d\\n",*(p+0));
printf("*(p+1)=%d\\n",*(p+1));
system("pause");
}
练习 :学生学号管理系统
提示用户输入学生的人数 根据输入申请堆内存 用来保存学生的学号
提示用户输入学生的学号
来了插班生 申请新的内存用来保存新来学生的学号
提示用户输入新来学生的学号
回显所有学生的学号
#include<stdlib.h>
#include<stdio.h>
//realloc re- alloc 在malloc函数的基础上 重新分配足够大的内存
// realloc(malloc的返回值,一共要的申请内存大小) 返回申请到的足够大的内存首地址
// 如果malloc 申请到的内存 后面还有足够大的空间供使用 这个时候 直接在malloc申请到的内存后 继续申请足够大的内存
// 如果malloc 申请到的内存 后面没有足够大的空间供使用 这个时候 会开辟一块新的足够大的内存 并且把之前malloc 申请到的内存的
//内容复制过来 释放之前malloc的内存
main(){
printf("请输入学生的人数:");
int count;
scanf("%d",&count);
//申请足够大的堆内存 用来保存学生的学号
int* p = malloc(sizeof(int)*count);
int i;
for(i = 0;i<count;i++){
printf("请输入第%d个学生的学号:",(i+1));
scanf("%d",p+i);
}
for(i = 0;i<count;i++){
printf("第%d个学生的学号是%d\\n",(i+1),*(p+i));
}
//来了插班生
printf("请输入插班生的人数:");
int newcomer;
scanf("%d",&newcomer);
//申请新的足够大的内存
p = realloc(p,(newcomer+count)*sizeof(int));
for(i = count;i<count+newcomer;i++){
printf("请输入第%d个学生的学号:",(i+1));
scanf("%d",p+i);
}
for(i = 0;i<count+newcomer;i++){
printf("第%d个学生的学号是%d\\n",(i+1),*(p+i));
}
system("pause");
}
11.C的结构体 ☆☆☆☆☆
class Student{
char gender;
short age;
int score;
}
#include<stdlib.h>
#include<stdio.h>
/*
class Student{
char gender;
short age;
int score;
void study(){
System.out.println("好好学习");
}
}
Student stu = new Student();
stu.age;
*/
struct Student{
char gender; //1
short age; //2
int score; //4
void(*study)();
};
void study(){
printf("good good study,day day up!!!\\n");
}
//函数指针的定义 返回值(*函数指针的名字)(形参类型)
//结构体的大小 ① 大于或者等于所有成员的大小的和 ②是最大的成员的大小的整数倍
//C结构体中只能有变量的声明 不能有函数 可以通过函数指针 让一个函数指针变量指向一个具体的函数 使结构体实现有函数的功能
//结构体的指针
main(){
struct Student stu = {‘f‘,18,90,&study};
//让学生study指针变量指向具体函数 需要注意指向的这个函数 返回值和形参得跟函数指针保持一致
//stu.study = &study;
stu.study();
printf("学生的性别是%c\\n",stu.gender);
printf("学生的年龄是%hd\\n",stu.age);
//printf("学生的成绩是%d\\n",stu.score);
printf("学生结构体占%d个字节\\n",sizeof(stu));
struct Student* p = &stu;
(*p).study();
//间接引用运算符
p->study();
struct Student** p2 = &p;
(**p2).study();
(*p2)->study();
system("pause");
}
12.C的联合体
#include<stdlib.h>
#include<stdio.h>
union un{
short s;
int i;
}
// ①联合体所有的成员 使用同一块内存
//② 联合体的大小取决于最大的变量占的内存空间
//③对联合体中的成员多次赋值 只有最后一次是有意义的 之前的赋值都会被覆盖
main(){
union un u;
printf("union联合体占%d个字节\\n",sizeof u);
u.s = 1234;
u.i = 12345678;
printf("u.s = %hd\\n",u.s);
system("pause");
}
13.C的枚举
#include<stdlib.h>
#include<stdio.h>
enum Weekday{
SUN,MON,TUE,WEND,THUR,FRI=8,SAT
}
//枚举 定义了当前类型的取值范围只能从枚举值中取值
//默认枚举值从0开始 后面依次+1
main(){
enum Weekday day = SAT;
printf("day = %d\\n",day);
system("pause");
}
14.typedef 自定义类型
以上是关于JNI_01的主要内容,如果未能解决你的问题,请参考以下文章