<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                接續上篇[C語言基礎及指針⑨聯合體與枚舉](http://www.jianshu.com/p/0bd682066106) 在上篇中我們了解了 , 多類型集合的聯合體 , 固定值集合的枚舉 , 內容相對比較簡單 , 今天我們談談預編譯 , 也是本系列最后一個知識點 , C語言基礎系列就要告一段落了 , 要開始我們的jni系列了 , JNI(Java Native Interface) 是java與C/C++進行通信的一種技術 , 使用JNI技術,可以java調用C/C++的函數對象等等,Android中的Framework層與Native層就是采用的JNI技術 。 ### 預編譯 > 預編譯(預處理,宏定義,宏替換)這種叫法 , 關鍵字`#define` , 其本質是替換文本。 首先我們了解一下C語言的執行過程: 1. 編譯 --> 生成目標代碼(.obj) 2. 連接 --> 將目標代碼與C函數庫合并 , 生成最終可執行文件 3. 執行 > 預編譯 , 主要在編譯時期完成文本替換工作 , 常見的預編譯指令有: `#include`,`ifndef`,`#endif`,`define`,`#pragma once`等等 。 我們在jni.h頭文件中 , 可以看到較多的預編譯指令 , 例如: ```c #if defined(__cplusplus) typedef _JNIEnv JNIEnv; typedef _JavaVM JavaVM; #else typedef const struct JNINativeInterface* JNIEnv; typedef const struct JNIInvokeInterface* JavaVM; #endif ``` 如果編譯環境是C++, 則使用: ```c typedef _JNIEnv JNIEnv; typedef _JavaVM JavaVM; ``` C語言編譯環境 , 則使用: ```c typedef const struct JNINativeInterface* JNIEnv; typedef const struct JNIInvokeInterface* JavaVM; ``` 這里的定義就是我們后需要結束的`JNIEnv`指針 , 在C++環境中`JNIEnv`是一個一級指針 , 但是在C語言環境中 , 他是一個二級指針 ,這個我們將在jni系列中 , 再詳細說明 。 ### 預編譯示例 ```c // 定義一個常數 #define MAX 100 void main() { int i = 99; if (i < MAX) // 在編譯時期, 會將MAX替換成100 { printf("i 小于 MAX\n"); } } ``` 定義一個宏常量`MAX`他的代表`100` , 宏常數沒有類型 , 只做替換。 ### 宏函數 > `#define` 預編譯指令 , 不光可以定義常量 , 還可以定義函數 , 因為其本質是替換 , 所有可以簡化很多很長的函數名稱 。 在jni.h中 , 也可以看到宏函數 , 如下: ```c #define CALL_TYPE_METHODA(_jtype, _jname) \ __NDK_FPABI__ \ _jtype Call##_jname##MethodA(jobject obj, jmethodID methodID, \ jvalue* args) \ { return functions->Call##_jname##MethodA(this, obj, methodID, args); } ``` 其中`(_jtype, _jname) `里面的`_jtype` , `_jname`都是替換標識 , 替換`_jtype`,`##_jname##` 。 ### 宏函數示例 ```c // 普通函數 void _jni_define_func_read() { printf("read\n"); } void _jni_define_func_write() { printf("write\n"); } // 定義宏函數 #define jni(NAME) _jni_define_func_##NAME() ; void main() { // 宏函數 //jni(read); 可以簡化函數名稱 jni(write) ; system("pause"); } ``` 宏函數的核心就是替換 , 只要記住這一點就夠了 。下面我們就來做另一個實例 , 打印類似android Log日志的形式的函數。 ```c // 模擬Android日志輸出 , 核心就是替換 #define LOG(LEVE,FORMAT,...) printf(##LEVE); printf(##FORMAT,__VA_ARGS__) ; #define LOGI(FORMAT,...) LOG("INFO:",##FORMAT,__VA_ARGS__) ; #define LOGE(FORMAT,...) LOG("ERROR:",##FORMAT,__VA_ARGS__) ; #define LOGW(FORMAT,...) LOG("WARN:",##FORMAT,__VA_ARGS__) ; void main() { LOGI("%s", "自定義日志。。。。\n"); LOGE("%s", "我是錯誤日志...\n"); system("pause"); } ``` 輸出: ```c INFO:自定義日志。。。。 ERROR:我是錯誤日志... ``` 我們可以看到輸出信息前面帶有類型標識 。在這里做幾點代碼說明: > 1. 上述代碼中`LEVE`,`FORMAT`都是自定義的 , 可以是任意名稱 , 只有后面替換的名稱一致即可。 2. `LOG(LEVE,FORMAT,...)`中的`...`表示可變參數 , 替換則是使用`__VA_ARGS__`這種固定寫法 。 3. 宏函數的核心就是——替換 預編譯指令就介紹到這里 , 其中心點就是`替換` 。 學到這里 , C語言的基礎也介紹得七七八八了 , 下面一起來分析分析 , 我們編寫JNI代碼的一個比較重要的頭文件jni.h 。 ### 簡要分析jni.h jni.h我們編寫NDK的時候需要引入的一個頭文件 , 它位于`android-ndk-r10e\platforms\android-21\arch-arm\usr\include\jni.h`不同的平臺都有相應的jni.h 。 在文件開始部分 , 定義了很多類型別名 , 區分C++與C的編譯環境 , 如下: ```c #ifdef __cplusplus /* * Reference types, in C++ */ class _jobject {}; class _jclass : public _jobject {}; class _jstring : public _jobject {}; class _jarray : public _jobject {}; class _jobjectArray : public _jarray {}; #else /* not __cplusplus */ /* * Reference types, in C. */ typedef void* jobject; typedef jobject jclass; typedef jobject jstring; typedef jobject jarray; typedef jarray jobjectArray; ``` 接著定義了對象引用的枚舉 , 和方法簽名的結構體: ```c typedef enum jobjectRefType { JNIInvalidRefType = 0, JNILocalRefType = 1, JNIGlobalRefType = 2, JNIWeakGlobalRefType = 3 } jobjectRefType; typedef struct { const char* name; const char* signature; void* fnPtr; } JNINativeMethod; ``` 接下來就是最重要的JNIEnv了 , 區分了C和C++環境 ```c struct _JNIEnv; struct _JavaVM; typedef const struct JNINativeInterface* C_JNIEnv; #if defined(__cplusplus) typedef _JNIEnv JNIEnv; typedef _JavaVM JavaVM; #else typedef const struct JNINativeInterface* JNIEnv; typedef const struct JNIInvokeInterface* JavaVM; #endif ``` JNIEnv是一個結構體指針 , 里面定義了很多函數 , 有調用java方法的函數 , 轉換java類型的函數 , 我可以看到 , C編譯環境中 , JNIEnv是`struct JNINativeInterface*`的指針別名,我們來看看`JNINativeInterface`結構體是怎樣的: ```c /* * Table of interface function pointers. */ struct JNINativeInterface { void* reserved0; void* reserved1; void* reserved2; void* reserved3; jint (*GetVersion)(JNIEnv *); jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*, jsize); jclass (*FindClass)(JNIEnv*, const char*); jmethodID (*FromReflectedMethod)(JNIEnv*, jobject); jfieldID (*FromReflectedField)(JNIEnv*, jobject); /* spec doesn't show jboolean parameter */ jobject (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean); jclass (*GetSuperclass)(JNIEnv*, jclass); jboolean (*IsAssignableFrom)(JNIEnv*, jclass, jclass); ``` 大部分的操作都是在`JNINativeInterface`這個結構體里面,定義了很多操作函數 , 比較常見的字符處理函數: ```c jstring (*NewStringUTF)(JNIEnv*, const char*); jsize (*GetStringUTFLength)(JNIEnv*, jstring); /* JNI spec says this returns const jbyte*, but that's inconsistent */ const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*); ``` > 1. 將字符指針轉換成java的String類型 2. 得到java的String類型長度 3. 將java的String類型轉換成C的字符指針 值得注意的是 , C++的JNIEnv結構體指針 , 并沒有重新實現 , 而生將C中的`JNINativeInterface`結構體 , 重新打了一次包 , 如下: ```c /* * C++ object wrapper. * * This is usually overlaid on a C struct whose first element is a * JNINativeInterface*. We rely somewhat on compiler behavior. */ struct _JNIEnv { /* do not rename this; it does not seem to be entirely opaque */ const struct JNINativeInterface* functions; #if defined(__cplusplus) jint GetVersion() { return functions->GetVersion(this); } jclass DefineClass(const char *name, jobject loader, const jbyte* buf, jsize bufLen) { return functions->DefineClass(this, name, loader, buf, bufLen); } jclass FindClass(const char* name) { return functions->FindClass(this, name); } ``` 因為C++是面向對象語言 , 所有有上下文環境變量`this` , 簡化了C語言中調用函數需要傳遞本身結構體指針的操作 。 分析了部分jni.h的源碼 , 后續的源碼 , 我相信看完C語言系列, 也可以看得懂 , 這里就不再做分析了 , 讀者可自行查看源碼分析一遍 ,對寫jni代碼還是有好處的 , 至少對JNIEnv結構體指針中的函數有一個大致的印象 。 ### 結語 C語言基礎系列 , 就宣告完結了 , 還有很多知識點沒講到 , 還有很多需要學習 , 但我們不是要把C語言學透了學精了 , 再去學JNI 和NDK, 這樣 , 不知要到何年何月了 。 所以 , 先學基礎了解概貌 , 將java與C結合起來 , 著手做些東西出來 , 然后再繼續深入 。 > 語言都是相通的 , 關鍵是解決問題的思路 。 ### 本文由老司機學院([動腦學院](http://dongnaoedu.com/))特約提供 ### 做一家受人尊敬的企業,做一位受人尊敬的老師
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看