what 什麼是JNI
- JNI java native interface native本地 java本地接口
- 通過JNI可以實現java和本地代碼之間相互調用
- jni可以看做是翻譯 實際上就是一套協議
why 爲什麼要用JNI
- Java 一處編譯到處運行
- ①java運行在虛擬機上 JNI可以擴展java虛擬機的能力 讓java代碼可以調用驅動
- ②java是解釋型語言 運行效率相對較低 C/C++的效率要高很多 通過jni把耗時操作方法C/C++可以提高java運行效率
- ③ java代碼編譯成的.class 文件安全性較差, 可以通過jni 把重要的業務邏輯放到c/c++去實現,c/c++反編譯比較困難 安全性較高
- C歷史悠久 1972年C 通過JNI可以調用優秀的C開源類庫
How 怎麼用JNI
- java
- c/c++ 能看懂 會調用
- JNI開發流程 NDK native develop kit
C基本語法
CHelloWorld
#include<stdio.h> // 相當於 java的import .h c的頭文件 stdio.h standard io 標準輸入輸出
#include<stdlib.h> // stdlib standard library 標準函數庫 java.lang
/**
*/
main(){ // public static void main(String[] args)
printf("helloworld!\n"); //System.out.println(); "\n"換行符
system("javac Hello.java");
system("java Hello");
system("notepad");
system("pause"); //system執行windows的系統命令
}
C的基本數據類型
- java基本數據類型
boolean 1
byte 1
char 2 char 1個字節
short 2 short 2
int 4 int 4
long 8 long 4
float 4 float 4
double 8 double 8
char, int, float, double, long, short, signed, unsigned, void
* signed 有符號數 最高位是符號位 可以表示負數 但是表示的最大值相對要小
* unsigned 無符號數 最高位是數值位 不可以表示負數 表示的最大值相對要大
* signed unsigned 只能用來修飾整形變量 char short int long
* C沒有 boolean byte C用0和非0表示false true
C的輸出函數
%d - int
%ld – long int
%lld - long long
%hd – 短整型
%c - char
%f - float
%lf – double
%u – 無符號數
%x – 十六進制輸出 int 或者long int 或者short int
%o - 八進制輸出
%s – 字符串
- 佔位符不要亂用 要選擇正確的對應類型 否則可能會損失精度
C字符串
- C沒有String類型 C的字符串實際就是字符數組
- C數組定義 [ ]只能再變量名之後
C字符串兩種定義方式
char str[] = {'h','e','l','l','o','\0'};//注意'\0'字符串結束符 char str[] = "你好"; //這種定義方式不用寫結束符 可以表示漢字
C的輸入函數
- scanf(“佔位符”, &地址);
- & 取地址符
- C字符串不檢查下標越界 使用時要注意
內存地址的概念
- 聲明一個變量,就會立即爲這個變量申請內存,一定會有一個對應的內存地址
- 沒有地址的內存是無法使用的
- 內存的每一個字節都有一個對應的地址
- 內存地址用一個16進制數來表示
- 32位操作系統最大可以支持4G內存
- 32位系統的地址總線爲32位,也就是說系統有2^32個數字可以分配給內存作爲地址使用
指針入門 **
int i = 123;
//一般計算機中用16進制數來表示一個內存地址
printf("%#x\n",&i);
//int* int類型的指針變量 pointer指針 指針變量只能用來保存內存地址
//用取地址符&i 把變量i的地址取出來 用指針變量pointer 保存了起來
//此時我們可以說 指針pointer指向了 i的地址
int* pointer = &i;
printf("pointer的值 = %#x\n",pointer);
printf("*pointer的值%d\n",*pointer);
*pointer = 456;
printf("i的值是%d\n",i);
system("pause");
* 指針常見錯誤
* 聲明瞭指針變量後 未初始化直接通過*p 進行賦值操作 運行時會報錯
* * 未賦值的指針稱爲野指針
* 指針類型錯誤 如int* p 指向了double類型的地址, 通過指針進行讀取操作時,讀取值會出錯
指針的練習
值傳遞和引用傳遞(交換兩個數的值)
- 引用傳遞本質是把地址傳遞過去
所有傳遞其實本質都是值傳遞,引用傳遞其實也是傳遞一個值,但是這個值是一個內存地址
void swap(int* p, int* p2){ int temp = *p; *p = *p2; *p2 = temp; } main(){ int i = 123; int j = 456; //將i, j的地址傳遞過去 swap(&i,&j); printf("i = %d, j = %d", i, j); }
- 返回多個值
- 把地址作爲參數傳入函數中,當函數執行完畢時,參數的值就已經被修改了
多級指針
- int* p; int 類型的一級指針 int** p2; int 類型的二級指針
- 二級指針變量只能保存一級指針變量的地址
- 有幾個* 就是幾級指針 int*** 三級指針
通過int類型三級指針 操作int類型變量的值 ***p
int i = 123; //int類型一級指針 int* p = &i; //int 類型 二級指針 二級指針只能保存一級指針的地址 int** p2 = &p; //int 類型 三級指針 三級指針只能保存二級指針的地址 int*** p3 = &p2; //通過p3 取出 i的值 printf("***p3 = %d\n", ***p3);
多級指針案例 取出子函數中臨時變量的地址
數組和指針的關係
- 數組佔用的內存空間是連續的
- 數組變量保存的是第0個元素地址,也就是首地址
- *(p + 1):指針位移一個單位,一個單位是多少個字節,取決於指針的類型
指針的長度
- 不管變量的類型是什麼,它的內存地址的長度一定是相同的
- 類型不同只決定變量佔用的內存空間不同
- 32位環境下,內存地址長度都是4個字節,所以指針變量長度只需4個字節即可
- 區分指針類型是爲了指針位移運算方便
堆棧概念 靜態內存分配 動態內存分配
- 棧內存
- 系統自動分配
- 系統自動銷燬
- 連續的內存區域
- 向低地址擴展
- 大小固定
- 棧上分配的內存稱爲靜態內存
- 靜態內存分配
- 子函數執行完,子函數中的所有局部變量都會被銷燬,內存釋放,但內存地址不可能被銷燬,只是地址上的值沒了
- 堆內存
- 程序員手動分配
- java:new
- c:malloc
- 空間不連續
- 大小取決於系統的虛擬內存
- C:程序員手動回收free
- java:自動回收
- 堆上分配的內存稱爲動態內存
- 程序員手動分配
結構體
- 結構體中的屬性長度會被自動補齊,這是爲了方便指針位移運算
- 結構體中不能定義函數,可以定義函數指針
- 程序運行時,函數也是保存在內存中的,也有一個地址
- 結構體中只能定義變量
- 函數指針其實也是變量,它是指針變量
- 函數指針的定義 返回值類型(*變量名)(接收的參數);
- 函數指針的賦值: 函數指針只能指向跟它返回值和接收的參數相同的函數
聯合體
- 長度等於聯合體中定義的變量當中最長的那個
- 聯合體只能保存一個變量的值
- 聯合體共用同一塊內存