C語言基礎(chǔ)及指針⑩預(yù)編譯及jni.h分析

接續(xù)上篇C語言基礎(chǔ)及指針⑨聯(lián)合體與枚舉

在上篇中我們了解了 , 多類型集合的聯(lián)合體 , 固定值集合的枚舉 , 內(nèi)容相對比較簡單 , 今天我們談?wù)勵(lì)A(yù)編譯 , 也是本系列最后一個(gè)知識(shí)點(diǎn) , C語言基礎(chǔ)系列就要告一段落了 , 要開始我們的jni系列了 , JNI(Java Native Interface) 是java與C/C++進(jìn)行通信的一種技術(shù) , 使用JNI技術(shù),可以java調(diào)用C/C++的函數(shù)對象等等,Android中的Framework層與Native層就是采用的JNI技術(shù) 。

預(yù)編譯

預(yù)編譯(預(yù)處理,宏定義,宏替換)這種叫法 , 關(guān)鍵字#define , 其本質(zhì)是替換文本。

首先我們了解一下C語言的執(zhí)行過程:

  1. 編譯 --> 生成目標(biāo)代碼(.obj)
  2. 連接 --> 將目標(biāo)代碼與C函數(shù)庫合并 , 生成最終可執(zhí)行文件
  3. 執(zhí)行

預(yù)編譯 , 主要在編譯時(shí)期完成文本替換工作 , 常見的預(yù)編譯指令有: #include,ifndef,#endif,define,#pragma once等等 。

我們在jni.h頭文件中 , 可以看到較多的預(yù)編譯指令 , 例如:

#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif

如果編譯環(huán)境是C++, 則使用:

typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;

C語言編譯環(huán)境 , 則使用:

typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;

這里的定義就是我們后需要結(jié)束的JNIEnv指針 , 在C++環(huán)境中JNIEnv是一個(gè)一級(jí)指針 , 但是在C語言環(huán)境中 , 他是一個(gè)二級(jí)指針 ,這個(gè)我們將在jni系列中 , 再詳細(xì)說明 。

預(yù)編譯示例

// 定義一個(gè)常數(shù)
#define MAX 100 

void main() {

    int i = 99;
    if (i < MAX) // 在編譯時(shí)期, 會(huì)將MAX替換成100
    {
        printf("i 小于 MAX\n");
    }
}

定義一個(gè)宏常量MAX他的代表100 , 宏常數(shù)沒有類型 , 只做替換。

宏函數(shù)

#define 預(yù)編譯指令 , 不光可以定義常量 , 還可以定義函數(shù) , 因?yàn)槠浔举|(zhì)是替換 , 所有可以簡化很多很長的函數(shù)名稱 。

在jni.h中 , 也可以看到宏函數(shù) , 如下:

#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都是替換標(biāo)識(shí) , 替換_jtype,##_jname##

宏函數(shù)示例

// 普通函數(shù)
void _jni_define_func_read() {
    printf("read\n");
}

void _jni_define_func_write() {
    printf("write\n");
}

// 定義宏函數(shù)
#define jni(NAME) _jni_define_func_##NAME() ;

void main() {

    // 宏函數(shù)
    //jni(read); 可以簡化函數(shù)名稱
    jni(write) ;

    system("pause");
}

宏函數(shù)的核心就是替換 , 只要記住這一點(diǎn)就夠了 。下面我們就來做另一個(gè)實(shí)例 , 打印類似android Log日志的形式的函數(shù)。

// 模擬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", "我是錯(cuò)誤日志...\n");

    system("pause");
}

輸出:

INFO:自定義日志。。。。
ERROR:我是錯(cuò)誤日志...

我們可以看到輸出信息前面帶有類型標(biāo)識(shí) 。在這里做幾點(diǎn)代碼說明:

  1. 上述代碼中LEVE,FORMAT都是自定義的 , 可以是任意名稱 , 只有后面替換的名稱一致即可。
  1. LOG(LEVE,FORMAT,...)中的...表示可變參數(shù) , 替換則是使用__VA_ARGS__這種固定寫法 。
  2. 宏函數(shù)的核心就是——替換

預(yù)編譯指令就介紹到這里 , 其中心點(diǎn)就是替換 。 學(xué)到這里 , C語言的基礎(chǔ)也介紹得七七八八了 , 下面一起來分析分析 , 我們編寫JNI代碼的一個(gè)比較重要的頭文件jni.h 。

簡要分析jni.h

jni.h我們編寫NDK的時(shí)候需要引入的一個(gè)頭文件 , 它位于android-ndk-r10e\platforms\android-21\arch-arm\usr\include\jni.h不同的平臺(tái)都有相應(yīng)的jni.h 。

在文件開始部分 , 定義了很多類型別名 , 區(qū)分C++與C的編譯環(huán)境 , 如下:

#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;

接著定義了對象引用的枚舉 , 和方法簽名的結(jié)構(gòu)體:

typedef enum jobjectRefType {
    JNIInvalidRefType = 0,
    JNILocalRefType = 1,
    JNIGlobalRefType = 2,
    JNIWeakGlobalRefType = 3
} jobjectRefType;

typedef struct {
    const char* name;
    const char* signature;
    void*       fnPtr;
} JNINativeMethod;

接下來就是最重要的JNIEnv了 , 區(qū)分了C和C++環(huán)境

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是一個(gè)結(jié)構(gòu)體指針 , 里面定義了很多函數(shù) , 有調(diào)用java方法的函數(shù) , 轉(zhuǎn)換java類型的函數(shù) , 我可以看到 , C編譯環(huán)境中 , JNIEnv是struct JNINativeInterface*的指針別名,我們來看看JNINativeInterface結(jié)構(gòu)體是怎樣的:

/*
 * 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這個(gè)結(jié)構(gòu)體里面,定義了很多操作函數(shù) , 比較常見的字符處理函數(shù):

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. 將字符指針轉(zhuǎn)換成java的String類型
  1. 得到j(luò)ava的String類型長度
  2. 將java的String類型轉(zhuǎn)換成C的字符指針

值得注意的是 , C++的JNIEnv結(jié)構(gòu)體指針 , 并沒有重新實(shí)現(xiàn) , 而生將C中的JNINativeInterface結(jié)構(gòu)體 , 重新打了一次包 , 如下:

/*
 * 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); }

因?yàn)镃++是面向?qū)ο笳Z言 , 所有有上下文環(huán)境變量this , 簡化了C語言中調(diào)用函數(shù)需要傳遞本身結(jié)構(gòu)體指針的操作 。

分析了部分jni.h的源碼 , 后續(xù)的源碼 , 我相信看完C語言系列, 也可以看得懂 , 這里就不再做分析了 , 讀者可自行查看源碼分析一遍 ,對寫jni代碼還是有好處的 , 至少對JNIEnv結(jié)構(gòu)體指針中的函數(shù)有一個(gè)大致的印象 。

結(jié)語

C語言基礎(chǔ)系列 , 就宣告完結(jié)了 , 還有很多知識(shí)點(diǎn)沒講到 , 還有很多需要學(xué)習(xí) , 但我們不是要把C語言學(xué)透了學(xué)精了 , 再去學(xué)JNI 和NDK, 這樣 , 不知要到何年何月了 。 所以 , 先學(xué)基礎(chǔ)了解概貌 , 將java與C結(jié)合起來 , 著手做些東西出來 , 然后再繼續(xù)深入 。

語言都是相通的 , 關(guān)鍵是解決問題的思路 。

本文由老司機(jī)學(xué)院(動(dòng)腦學(xué)院)特約提供

做一家受人尊敬的企業(yè),做一位受人尊敬的老師

Android程序員學(xué)C系列:
C語言基礎(chǔ)及指針①
C語言基礎(chǔ)及指針②之指針內(nèi)存分析
C語言基礎(chǔ)及指針③函數(shù)與二級(jí)指針
C語言基礎(chǔ)及指針④函數(shù)指針
C語言基礎(chǔ)及指針⑤動(dòng)態(tài)內(nèi)存分配
C語言基礎(chǔ)及指針⑥字符操作
C語言基礎(chǔ)及指針⑦結(jié)構(gòu)體與指針
C語言基礎(chǔ)及指針⑧文件IO
C語言基礎(chǔ)及指針⑨聯(lián)合體與枚舉
C語言基礎(chǔ)及指針⑩預(yù)編譯及jni.h分析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評(píng)論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,538評(píng)論 3 417
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評(píng)論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評(píng)論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,761評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,207評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,419評(píng)論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,959評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,782評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,983評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,222評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評(píng)論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,678評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,978評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容