android基礎--JNI基礎:C/C++語言



JNI簡介
什麼是JNI
JNI(Java Native Interface) java本地開發接口
JNI 是一個協議, 有了這個協議可以使Java代碼和C/C++代碼相互調用.
--C語言調用java是使用反射技術,C反射java.


爲什麼用JNI
* JNI擴展了java虛擬機的能力, 如wifi熱點共享功能, wifi需要使用到硬件,而硬件需要驅動程序, 只有c語言可以開發驅動程序 
  遊戲外掛就是用C寫的,拿到別的玩家的某個參數的地址值並進行修改。
* c/c++代碼執行效率高. c可以直接操控內存. c指針可以直接根據某個地址操控內存. 執行的效率和速度提升。
  在實時渲染的遊戲上,音視頻處理 (極品飛車,opengl,ffmpeg)上可以用C將數據直接推送到顯卡內存顯示,速率高,不會出現屏幕卡,用java寫慢,可能會卡。
  windows系統中顯卡內存範圍是固定的,C用指針可以訪問
* C語言開源庫比java開源庫還多。如ffmpeg(萬能解碼器,可以對90%以上的視頻格式,如avi/wma等進行編碼),7zip等。
* 複用代碼 (文件壓縮,人臉識別…)
* 特殊的業務場景

C語言中System()方法的作用:
System("pause"); 給命令行一個暫停的命令,用於觀察程序執行的結果。
---System方法會暫停程序運行,阻塞主程序,當前運行完成後,纔會繼續往下走。
System("mspaint");打開畫畫板。
System("java HelloWorld"); 運行java程序,HelloWorld.class需要在C程序當前目錄中。

# C/C++語言

- 基本數據類型
- java的八大數據類型
 - byte 1個字節
 - boolean 1個字節
 - char 2個字節,是一個unicode,可以表示漢字
 - short 2個字節
 - int 4個字節
 - long 8個字節
 - float 4個字節
 - double 8個字節

- c語言的基本數據類型
 - char 1個字節,不能表示漢字
 - int 4個字節,
 - float 4個字節
 - double 8個字節
 - long 4個字節
 - short 2個字節
 - signed 有符號,表示的數值比較小,但是可以表示 負數
 - unsigned 無符號,表示 的數值較少。
 - void 任意類型, 類似於java中Object

 - 和java對比缺少兩種類型
  - byte c中沒有此類型
  - boolean 0代表是false, 非0代表是true.

---sizeof(int/long/char),獲取基本數據類型佔用的字節數。
---C語言中long佔用4個字節,java中long佔用8個字節,C與java中long數據傳遞時使用C中的long long類型,佔用8個字節。

輸出函數
printf,使用佔位符輸出數據。
佔位符:
   
%d     -  int
%hd    – 短整型 short
%ld    -  long int
%c     -  char
%f     -  float
%lf    – double
%u     – 無符號數
%x     – 十六進制輸出 int 或者long int 或者short int
%o     -  八進制輸出
%s     – 字符串
---浮點數在輸出時, 默認是以小數點後6位輸出. 多出的6位會四捨五入.
---在%的後面加上.4(%.4f),指定輸出小數點後四位
---對應的類型, 就使用對應的佔位符,否則可能損失精度。
---如int數據用%hd輸出時只會輸出後面2個8位:
   %hd: 24910
      二進制:  00000000  00000000  01100001  01001110
   %d: 12345678
      二進制:  00000000   10111100   01100001     01001110
   比較:
      24910:              00000000  00000000  01100001  01001110
      12345678:           00000000  10111100  01100001  01001110

---c語言中沒有string類型, 用來表示字符串類型的是char數組
---c語言中定義數組時, 中括號不允許寫在類型的後面, 應該寫變量名的後面
---%x,%o,一般用來輸出地址,輸出結果中不帶前綴0x/0,要想顯示前綴,需要在中間加#號,%#x,%#o。

輸入函數
--在變量名前邊加一個&, 就是取地址
--scanf(佔位符, 地址);


指針
內存,內存編號就是地址,內存分很多單元,每個單元對應一個編號.
地址,內存單元的編號。

爲什麼4G內存只顯示3G?
地址是從0開始的非負整數,0000~FFFF
XP操作系統 爲神馬只能顯示3G內存:
常用的XP系統都是32位的系統,就是說在所有程序(包括)系統本身運行的時候,最多能使用2的32次方的地址,
可以自己算一下2的32次方就是4G,但問題是,系統裏面除了內存還有其它設備啊,顯卡硬盤之類的都是需要地址的,
所以,留給內存使用的地址只有3G多一點,剩下的要保留給其它設備。

指針說明
--當說一個指針中存儲的是值時, 當前所說的指針就是: 內存地址.
--當說一個指針中存儲的xx變量的地址時, 當前所說的指針就是: 指針變量.

*號的三種含義;
1. 作爲數學運算符: 乘號
2. 當*號寫在某個類型的後面時, 這時作爲聲明指針變量的類型出現.
3. 當*號寫在指針變量的前面時, 這時作爲取出指針變量中存儲的地址對應在內存中的值.

變量i和*p的關係
 int i = 335;
 int* p;
 p = &i; // 把i變量的地址賦值給了指針變量p
 // 修改p的值會影響i的值嗎 不會
 //printf("修改前i的值爲: %d\n", i);
 //int j = 56;
 //p = &j;
 //printf("修改後i的值爲: %d\n", i);
 // 修改*p的值會影響i的值嗎 會
 printf("修改前i的值爲: %d\n", i);
 *p = 8888; // 把p變量中存儲的i的地址在內存中對應的值修改爲: 8888
 printf("修改後i的值爲: %d\n", i);

多級指針.
 /*
 多級指針 
 一級指針變量的地址, 只能由二級指針變量地址存儲. 依此類推.  
 多級指針時, 加*號取值時, 不要以變量名去查找上一個變量.  
 */
 main() {
    int i = 168;    
    int* p1 = &i;          
    int** p2 = &p1;    
    int*** p3 = &p2;    
    int**** p4 = &p3;   
    // 通過p4取i的值   
    printf("****p4=%d\n", ****p4);    
    system("pause");      
 }

數組
指針變量就是數組, 數組就是指針變量
 // 使用指針的方式定義數組,cPoint中存儲的是首地址.
 char* cPoint = "nihaoa!!!";
 // cArray中存儲的也是首地址.
 char cArray[] = {'n', 'i', 'h', 'a', 'o', 'a', '!', '!'};

C中數組其實是一塊連續的內存區域,每一個元素佔用着自己的類型的字節長度。
首地址(第0個元素的地址)
         1. 數組變量名的地址就是首地址
         2. 數組變量中存儲的也是首地址
         3. 數組中第0個元素的地址也是首地址.

如果採用指針變量的方式定義數組: char* cPoint = "nihaoya!!!!";
                                指針變量中存儲的地址就是首地址.

指針的長度和運算.
--不管什麼類型的指針都是4個字節.
--C語言爲了方便指針運算, 定義各種基本類型的指針, 每種類型的指針運算時所偏移量的值是根據類型的長度決定的.

注:
---C中不存在數組角標越界,超出數組長度時會取出後面地址中的內容。
---C語言中for循環中的循環變量不能定義在for()語句內,只能定義在外面,否則違反C99標準。
   C99是C語言的一種語法約束標準。


指針和指針變量的關係
--指針就是地址,地址就是指針
--地址就是內存單元的編號
--指針變量是存放地址的變量
--指針和指針變量是兩個不同的概念
--但是要注意: 通常我們敘述時會把指針變量簡稱爲指針,實際它們含義並不一樣
--我的指針裏邊的值爲: 123;   指針(地址)
--我的指針裏邊存的是xxx變量的地址, 指針(指針變量)

指針的重要性
--直接訪問硬件 (opengl 顯卡繪圖)
--快速傳遞數據(指針表示地址)
--返回一個以上的值(返回一個數組或者結構體的指針)
--表示複雜的數據結構(結構體)
--方便處理字符串
--指針有助於理解面向對象


C語言內存,棧內存,堆內存
1. 從靜態存儲區域分配。內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。例如全局變量,static變量。
2. 在棧上創建。在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集中,效率很高,但是分配的內存容量有限。
3. 從堆上分配,亦稱動態內存分配。程序在運行的時候用malloc或new申請任意多少的內存,程序員自己負責在何時用free或delete釋放內存。動態內存的生存期由我們決定,使用非常靈活,但問題也最多.
堆和棧的區別:
1. 申請方式
棧:
由系統自動分配.例如,聲明一個局部變量intb;系統自動在棧中爲b開闢空間.例如當在調用涵數時,需要保存的變量,最明顯的是在遞歸調用時,要系統自動分配一個棧的空間,後進先出的,而後又由系統釋放這個空間.
堆:
需要程序員自己申請,並指明大小,在c中用malloc函數
如char*  p1=(char*) malloc(10);//14byte
但是注意p1本身是在棧中的.
2. 申請後系統的響應
棧:只要棧的剩餘空間大於所申請空間,系統將爲程序提供內存,否則將報異常提示棧溢出。
堆:首先應該知道操作系統有一個記錄空閒內存地址的鏈表,當系統收到程序的申請時,會遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閒結點鏈表中刪除,並將該結點的空間分配給程序,另外,對於大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本內存空間。另外,由於找到的堆結點的大小不一定正好等於申請的大小,系統會自動的將多餘的那部分重新放入空閒鏈表中。

3.申請大小的限制
棧:在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在WINDOWS下,棧的大小是2M(vc編譯選項中可以設置,其實就是一個STACK參數,缺省2M),如果申請的空間超過棧的剩餘空間時,將提示overflow。因此,能從棧獲得的空間較小。
堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由於系統是用鏈表來存儲的空閒內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。

4.申請效率的比較:
棧:由系統自動分配,速度較快。但程序員是無法控制的。
堆:由malloc/new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便.

5.堆和棧中的存儲內容
棧:在函數調用時,第一個進棧的是主函數中後的下一條指令(函數調用語句的下一條可執行語句)的地址,然後是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,然後是函數中的局部變量。注意靜態變量是不入棧的。
當本次函數調用結束後,局部變量先出棧,然後是參數,最後棧頂指針指向最開始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。
堆:一般是在堆的頭部用一個字節存放堆的大小。堆中的具體內容有程序員安排。

6.內存的回收
棧上分配的內存,編譯器會自動收回;堆上分配的內存,要通過free來顯式地收回,否則會造成內存泄漏。
堆和棧的區別可以用如下的比喻來看出:
使用棧就像我們去飯館裏吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等準備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。
使用堆就像是自己動手做喜歡吃的菜餚,比較麻煩,但是比較符合自己的口味,而且自由度大。

靜態內存和動態內存
靜態內存, 默認情況下就是靜態內存. 當某個方法執行完畢之後, 此方法內聲明的變量都是採用靜態內存分配得來的, 當方法執行完畢之後, 就會把方法裏聲明的變量的內存回收了.
--靜態內存回收機制, 在回收的時候有一定的過程, 在另一個方法中立刻的取一下值, 還是可以取到的.

 /*
 pAddress中存儲的是: main函數中p的地址.
 */
 func(int** pAddress) {
     int a = 543;     
     printf("a的地址爲: %#x\n", &a);     
     // 把a地址賦值給main函數中p變量
     *pAddress = &a;
 }
 
 main() {
     int* p; // 定義一個指針變量p, 用於接收子函數func中a變量的地址.     
     func(&p);     
     printf("*p=%d\n", *p);
     printf("*p=%d\n", *p); // 第二次取值,就取不到了,被回收掉了.
     printf("p中存儲的地址是: %#x\n", p);
     system("pause");      
 }

--動態內存, 可以長期在另一個方法中保留子方法中的變量.
  malloc(申請的長度);  申請一塊內存空間, 返回的是申請出來的地址.
  free(地址); 把給定的地址所在的內存給回收掉.
  realloc,重新內存空間
 func(int** pAddress) {
     int i = 168;           
     // 手動申請一塊內存, 並且把i變量的值, 拷貝進去.     
     int* temp; // 定義一個臨時指針變量temp
     // temp中存儲的是申請出來的地址
     temp = malloc(sizeof(int)); // 申請一塊內存空間: 4個字節, 此方法返回的是申請出來那塊內存空間的地址.     
     *temp = i;    
     // 把main中的p變量中的值, 賦值爲申請出來的地址
     *pAddress = temp;
 }
 
 /*
 動態內存分配: 手動申請一塊內存空間. 需要手動釋放掉.    
 free(地址); 把給定的地址的內存給回收掉.
 */
 main() {
     int* p;    
     func(&p);
     printf("*p=%d\n", *p);
     printf("*p=%d\n", *p);
     printf("*p=%d\n", *p);    
     free(p); // 把手動申請出來的內存空間回收掉
     printf("回收後: *p=%d\n", *p);
     printf("回收後: *p=%d\n", *p);
     printf("回收後: *p=%d\n", *p);    
     system("pause");      
 }

注:要使用malloc函數,
--必須添加malloc.h這個頭文件
--malloc函數只能返回第一個字節的地址
--int * p = (int *)malloc(4);這句代碼分配了8個字節, p變量佔4個字節, p所指向的內存也佔4個字節
--p本身所佔的內存是靜態分配的, p所指向的內存是動態分配的  

案例: 用戶手動創建數組, 再擴展數組.

  // 1. 讓用戶輸入一個長度.
  printf("請輸入數組的長度: ");
  int length;
  scanf("%d", &length);
  printf("長度爲: %d\n", length);

  // 2. 根據用戶輸入的長度, 申請一塊內存空間.  長度 * sizeof(int) = 總內存空間. 
  int* iArray; // 定義一個指針變量 
  iArray = malloc(length * sizeof(int)); // 把數組的首地址給了iArray 

  // 3. 依次讓用戶把每一個元素的值填進去.
  int i;
  for(i = 0; i < length; i ++) {
     printf("請輸入第%d個元素的值: ", i);
     scanf("%d", iArray + i);
  }

  // 4. 再讓用戶輸入一個擴展的長度
  printf("請輸入擴展的長度: ");
  int supportLength;
  scanf("%d", &supportLength);
  printf("擴展的長度爲: %d\n", supportLength); 

  // 5. 重新申請內存空間: (第一次輸入的長度 + 擴展的長度) * sizeof(int) = 總內存空間.
  iArray = realloc(iArray, (length + supportLength) * sizeof(int));

  // 6. 把擴展後新增的幾個元素一個一個賦值. 
  for(i = length; i < (length + supportLength); i ++) {
     printf("請輸入第%d個元素的值: ", i);
     scanf("%d", iArray + i);
  }

  // 7. 把數組打印輸出了. 
  for(i = 0; i < (length + supportLength); i ++) {
     printf("第%d個元素的值: %d\n", i, *(iArray + i));      
  }
  // 8. 回收內存
  free(iArray);


函數指針

 int increment(int x, int y) {
  return x + y;
 }
 
 main() {
     int (*funcPoint)(int x, int y); // 定義一個函數指針: 接收兩個int的參數, 返回是int的類型.
     
     funcPoint = increment; // 把方法賦值給函數指針. 
     
     int result = funcPoint(10, 25);  // 使用函數指針調用方法. 
     
     printf("結果爲: %d\n", result);
     
     system("pause");       
 } 


結構體

 struct Student {
     int age;
     float score;
     char sex;       
 }; 
 
 main() {
     struct Student stu = {20, 99.5, 'M'};
     
     printf("stu.age=%d\n", stu.age);
     printf("stu.score=%.1f\n", stu.score);
     printf("stu.sex=%c\n", stu.sex);
     
     stu.age = 25;
     stu.score = 100.00;
     stu.sex = 'W';
     printf("stu.age=%d\n", stu.age);
     printf("stu.score=%.1f\n", stu.score);
     printf("stu.sex=%c\n", stu.sex);
     
     // 定義一個結構體指針變量
     struct Student* stuPoint;
     stuPoint = &stu; // 把結構體stu的地址賦值給stuPoint結構體的指針變量
     
     printf("(*stuPoint).age=%d\n", (*stuPoint).age );
     printf("(*stuPoint).score=%.1f\n", (*stuPoint).score);
     printf("(*stuPoint).sex=%c\n", (*stuPoint).sex);
     
     (*stuPoint).age = 30;
     (*stuPoint).score = 110.5;
     (*stuPoint).sex = 'M';
     
     printf("(*stuPoint).age=%d\n", (*stuPoint).age );
     printf("(*stuPoint).score=%.1f\n", (*stuPoint).score);
     printf("(*stuPoint).sex=%c\n", (*stuPoint).sex);
     
     //  stuPoint->age  等價於 (*stuPoint).age
     
     stuPoint->age = 50;
     printf("stuPoint->age=%d\n", stuPoint->age);
     
 
     system("pause");       
 }

聯合體

 /*
 只有一遍變量會被賦值. 特點: 多個變量採用的是同一塊內存空間. 
 後賦值的會把先賦值的給覆蓋掉. 
 */
 union Result {
    long l;
    int i;
    char c;
 };
 
 
 main() {
     
     union Result result; 
     result.l = 123;
     result.i = 888;
     
     printf("result.l=%ld\n", result.l);
     printf("result.i=%d\n", result.i);
     
     system("pause");       
 }

枚舉

 enum WeekDay {
   Monday = 10,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
 };
 
 main() {
     enum WeekDay day; // 定義枚舉對象
     
     day = Sunday;
     
     printf("day=%d\n", day);
     
     system("pause");       
 } 
typedef 給某個類型起個別名

typedef double db; // 給某個類型起個別名:  給double起個別名: db 
 typedef int i;
 typedef float ft;

   db d = 3.14159234234;
   printf("%lf\n", d);
   
   ft f = 3.1415;
   printf("%f\n", f);

int數組的指針方式定義

C中可隨意調用sleep().

services.msc,打開系統中所有服務

msconfig,打開系統配置。

C中註釋與java中相同。


代碼優化,int數據換成byte.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章