1、C语言执行的流程
编译:形成目标代码(.obj)
连接:将目标代码与C函数库连接合并,形成最终的可执行文件
预编译(预处理),为编译做准备工作,完成代码文本的替换工作
例如我们的导入文件时
void main(){
#include "my.txt"
printf("%s\n", "I am a little boy!");
getchar();
}
当我们在vs中写一个txt文件,当我导入时这就相当于一个预编译的过程
完成了一个代码文本的替换过程
一般我们的预编译的文件在
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include目录下
头文件告诉编译器有这样一个函数,连接器负责找到这个函数的实现
2、宏定义、宏替换、预编译指令
define指令
1.定义标示
#ifdef __cplusplus 标识支持C++语法
#ifdef ANDROID //是否支持安卓
2防止文件重复引入
//如果没有定义AH,定义AH
#ifndef AH
#define AH
#include "B.h"
void printfA();
#endif//有if就需要有endif
或者这样写
//该头文件只被包含一次,让编译器自定处理好循环包含问题
#pragma once
#include "B.h"
void printfA();
3.定义常数(便于修改与阅读)
#define MAX 100
void main(){
int i = 90;
if (i < MAX){
printf("比MAX小..");
}
getchar();
}
4定义宏函数
void ms_com_jni_read(){
printf("read\n");
}
void ms_com_jni_write(){
printf("write\n");
}
//NAME是参数
#define jni(NAME) ms_com_jni_##NAME();
//webrtc JNI函数名称很长,也是JOW宏函数缩短函数名称
void main(){
jni(write);//替换:ms_com_jni_write();
//替换成:printf("INFO:"); printf("%s%d","大小:",89);
getchar();
}
上边是函数无参的,当有参数的时候
//日志输出
//...是很多参数的意思
//__VA_ARGS__可变参数
//#define LOG(FORMAT,...) printf(##FORMAT,__VA_ARGS__);
日志会有级别
//#define LOG_I(FORMAT,...) printf("INFO:"); printf(##FORMAT,__VA_ARGS__);
//#define LOG_E(FORMAT,...) printf("ERRO:"); printf(##FORMAT,__VA_ARGS__);
//升级版本
#define LOG(LEVEL,FORMAT,...) printf(##LEVEL); printf(##FORMAT,__VA_ARGS__);
#define LOG_I(FORMAT,...) LOG("INFO:",##FORMAT,__VA_ARGS__);
#define LOG_E(FORMAT,...) LOG("ERROR:",##FORMAT,__VA_ARGS__);
#define LOG_W(FORMAT,...) LOG("WARN:",##FORMAT,__VA_ARGS__);
//在Android中
//#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"jason",FORMAT,##__VA_ARGS__);
//LOGI("%s","fix");
//替换
//__android_log_print(ANDROID_LOG_INFO, "jason", "%s", "fix");
void main(){
LOG_E("%s%d","大小:",89);
getchar();
}
3、JNI开发流程
JNI全称是Java Native Interface(Java本地接口)单词首字母的缩写,本地接口就是指用C和C++开发的接口。由于JNI是JVM规范中的一部份,因此可以将我们写的JNI程序在任何实现了JNI规范的Java虚拟机中运行。同时,这个特性使我们可以复用以前用C/C++写的大量代码。
开发JNI程序会受到系统环境的限制,因为用C/C++语言写出来的代码或模块,编译过程当中要依赖当前操作系统环境所提供的一些库函数,并和本地库链接在一起。而且编译后生成的二进制代码只能在本地操作系统环境下运行,因为不同的操作系统环境,有自己的本地库和CPU指令集,而且各个平台对标准C/C++的规范和标准库函数实现方式也有所区别。这就造成使用了JNI接口的JAVA程序,不再像以前那样自由的跨平台。如果要实现跨平台,就必须将本地代码在不同的操作系统平台下编译出相应的动态库。
简单来说就是让C去访问Java 或者让java取访问我们的C的代码,就是为了代码提升效率,还有让我们的代码更加的安全
JNI开发流程主要分为以下8步:
1.编写native方法
public class JniTest {
public native static String getStringFromC();
//这里的native修饰的方法,表示可以被C调用
public static void main(String[] args) {
}
}
2.javah命令,生成.h头文件
打开cmd命令,javah 包名加完整的类名生成.h文件
3.复制.h头文件到CPP工程中
在我们的vs中添加头文件,点击添加现有项,然后刷新
4.复制jni.h和jni_md.h文件到CPP工程中
这两个文件在我们的jdk的目录下搜索
最后就是这样的
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_ms_jni_JniTest */
#ifndef _Included_com_ms_jni_JniTest
#define _Included_com_ms_jni_JniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_ms_jni_JniTest
* Method: getStringFromC
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_ms_jni_JniTest_getStringFromC
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
//这是我们生成的.h文件
5.实现.h头文件中声明的函数
#include "com_ms_jni_JniTest.h"
//函数实现
//这里我们的方法名的命名要求:Java_包名_类名_方法名,必须这样去写,
//不这样写,这个方法就调用不到
JNIEXPORT jstring JNICALL Java_com_ms_jni_JniTest_getStringFromC
(JNIEnv *env, jclass jcls){
//JNIEnv 结构体指针
//env二级指针
//代表Java运行环境,调用Java中的代码
//简单的实现
//将C的字符串转为一个java字符串
return (*env)->NewStringUTF(env,"C String");
}
6.生成dll文件
加载我们的dll动态库
public class JniTest {
public native static String getStringFromC();
public static void main(String[] args) {
}
//加载动态库
static{
System.loadLibrary("jni_study");
}
}
7.配置dll文件所在目录到环境变量
配置环境变量到path
8.重启Eclipse
public class JniTest {
public native static String getStringFromC();
public static void main(String[] args) {
String text = getStringFromC();
System.out.println(text);
}
//加载动态库
static{
System.loadLibrary("jni_study");
}
}
5、动态库和静态库的区别
动态库.dll :共享代码
静态库.a
6、JNIEnv
在C中:
JNIEnv 结构体指针别名
env二级指针
在C++中:
JNIEnv 是一个结构体的别名
env 一级指针
1.为什么需要传入JNIEnv,函数执行过程中需要JNIEnv
2.C++为什么没有传入?C++中进行了封装,传入了this所以不用再写
3.C++只是对C的那一套进行的封装,给一个变量赋值为指针,这个变量是二级指针
C/C++中为什么有区别?