安卓JNI基础知识

JNI基础知识

  • JNI简介
  • NDK
  • 配置开发环境
  • JNI实践
    • 配置CMake
    • JNI编码
    • JNI注册
      • 1.静态注册
      • 2.动态注册
    • 编译方式
      • CMakeLists编译
      • Makefile编译
      • 命令编译
  • JNI和C/C++代码分离
  • Java调用C/C++
      • 查看so中包含的方法
  • C/C++调用Java
  • 打印C/C++的log
  • 生成多个共享库so
  • JNI调试

本文整理了JNI技术基础知识

JNI简介

JNI 是java原生接口(Java Native Interface),它定义了 Android 从受管理代码(使用 Java 或 Kotlin 编程语言编写)编译的字节码与原生代码(使用 C/C++ 编写)进行交互的方法,也就是安卓通过JNI技术提供Java调用C/C++或者C/C++调用Java的能力。JNI 不依赖于供应商,支持从动态共享库加载代码,虽然有时较为繁琐,但效率较高。
在这里插入图片描述

NDK

Android NDK(Native Development Kit),原生开发工具包,它是一组能将C或C++(“原生代码”)嵌入到Android 应用中的工具。可以帮助开发者快速开发C/C+的动态库,自动将so和java应用一起打包成apk。
NDK集成了一些交叉编译器,并提供了相应的mk文件,用于隔离CPU、平台、ABI等差异,开发人员通过配置mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以生成自己的so库。
原生共享库:NDK 从 C/C++ 源代码构建这些库或 .so 文件。so是shared object的缩写。
原生静态库:NDK 也可构建静态库或 .a 文件,而您可将静态库关联到其他库。

配置开发环境

  1. 下载NDK
    File>Settings>Android SDK>SDK Tools>勾选需要的版本号>apply>OK
  2. 配置项目NDK
    在这里插入图片描述
    如果NDK location无法编辑输入,可以在local.properties中新增ndk.dir进行设置:
    sdk.dir=D\:\\win10_program\\develop\\Android\\AndroidSDK
    ndk.dir=D\:\\win10_program\\develop\\Android\\AndroidSDK\\ndk\\26.1.10909125  // 已经过时的用法,可以直接删除此行配置,具体参考下面的操作。
    
    较新的项目,直接在app\build.gradle中直接配置ndk版本号即可。
    android {namespace 'com.xxx.xxx'compileSdk 33ndkVersion "25.2.9519653"...
    }
    
    命令查看ndk版本号:ndk-build --version

JNI实践

这里使用官方例子介绍。

配置CMake

配置CMake的目的是:告诉CMake改如何从源码编译生成目标库。

# 需要生成的目标库native-lib
# 也可以使用add_executable()生成可执行文件
add_library( # 指定要生成的目标库名称为native-libnative-lib# 将native-lib库设置默认为SHARED(原生共享库.so)STATIC(原生静态库.a)SHARED# 生成native-lib库所需源码的相对路径列表。包含.cpp和.hsrc/main/cpp/native-lib.cppNativeImpl.cpp)   # NativeImpl.cpp在后面代码分离部分实现# 指定源文件关联的头文件(适用于头文件和源文件分离的情况,但是也可以不写,因为.cpp中已经include了)           
include_directories(src/main/cpp/include/)# 在已有库中查找需要的库,并将它的路径存储在变量xxx-lib中。类似用法的函数
# find_file()find_path()find_program()find_package()
find_library( # 自定义变量的名称xxx-libxxx-lib#在ndk开发包中查找需要的libyyy.so,存储到xxx-lib变量中yyy ) # 将依赖的库文件链接到此目标库上
target_link_libraries(# 指定目标库native-lib# 将下面的库列表全部连接到目标库上${xxx-lib}  # 获取find_library找到的yyy库android     # 获取android库log)        # 获取log库

注意:
1、如果对库文件有修改变动,请务必在Gradle之前清理一下项目 Build > Clean Project在这里插入图片描述
2、如果需要生成多个共享库,可以在CMakeLists.txt文件中增加多个成对的add_librarytarget_link_libraries函数。

JNI编码

  1. 在Java侧声明调用方法。如stringFromJNI
    // 应用启动时,调用此函数会加载原生共享文件sodemo.sostatic {System.loadLibrary("sodemo");  //官方推荐使用:ReLinker.loadLibrary}/*** 声明此方法在原生端(共享文件sodemo.so)中实现,它与该应用程序打包在一起。*/public native String stringFromJNI();
  1. 在C侧实现具体方法Java_com_wingtech_sodemo_JNIUtils_stringFromJNI
#include <jni.h>
#include <string>extern "C" JNIEXPORT jstring JNICALL
Java_com_wingtech_sodemo_JNIUtils_stringFromJNI(JNIEnv *env, jobject thiz) {std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());
}
方法名称:Java_包名_类名_方法名
方法参数:JNIEnv* 是指向虚拟机环境的指针。jobject 是指向从 Java 端传递的隐式 this 对象的指针。

重要:C/C++和Java通过此方法名称建立了一对一映射关系。

JNI注册

1.静态注册

如果只有一个类具有原生方法,建议使用静态注册。使用标准 System.loadLibrary 从共享库加载原生代码。
从静态类初始化程序中调用 System.loadLibrary(或 ReLinker.loadLibrary)。具体静态注册同前面JNI编码部分所述。

2.动态注册

如果有多个类有原生方法,可以使用RegisterNatives注册,也可以让运行时使用dlsym动态查找它们。可以从 Application进行调用,这样始终加载该库,而且总是会提前加载。当执行到System.loadLibrary()函数时,会回调JNI组件中的JNI_OnLoad()函数;当释放该组件时会回调JNI_OnUnload()函数。

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {JNIEnv* env;// 通过调用了GetEnv函数获取JNIEnv结构体指针env(JNI环境变量),JNIEnv结构体是指向一个函数表的,// 该函数表又指向了一些列对应的JNI函数。所以可以通过env和java交互,如GetObjectClass,CallVoidMethod等if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {return JNI_ERR;}// Find your class. JNI_OnLoad is called from the correct class loader context for this to work.jclass c = env->FindClass("com/example/app/package/MyClass");if (c == nullptr) return JNI_ERR;// 将所有方法装进数组中。这里数组中每个元素是结构体JNINativeMethod。// typedef struct {// const char* name;//Java层native方法的名字// const char* signature;//Java层native方法的描述符// void*       fnPtr;//对应JNI函数的指针// } JNINativeMethod;static const JNINativeMethod methods[] = {{"nativeFoo", "()V", reinterpret_cast<void*>(nativeFoo)},{"nativeBar", "(Ljava/lang/String;I)Z", reinterpret_cast<void*>(nativeBar)},};// 使用RegisterNatives注册所有原生方法int rc = env->RegisterNatives(c, methods, sizeof(methods)/sizeof(JNINativeMethod));if (rc != JNI_OK) return rc;return JNI_VERSION_1_6;
}

jni函数的指针
void regist(JNIEnv *env, jobject thiz, jobject jCallback) {
LOGD(“–动态注册调用成功–>”);
jstring pJstring = env->NewStringUTF(“动态注册调用成功”);
jclass pJclass = env->GetObjectClass(thiz);
jmethodID id = env->GetMethodID(pJclass, “beInjectedDebug”, “(Ljava/lang/String;)V”);
//执行函数
env->CallVoidMethod(thiz,id,pJstring);
}

编译方式

一般有两种编译方式:

  • 1、CMakeLists编译
  • 2、Makefile编译
  • 3、命令编译

CMakeLists编译

1、CMakeLists配置
具体配置如前面配置CMake的介绍,这里使用cpp目录下的CMakeLists.txt、native-lib.cpp文件生成.so库。
在这里插入图片描述
2、gradle配置
在app\build.gradle中设置库文件适配的CPU架构类型和CMakeLists.txt 文件路径。

android {namespace 'com.xxx.sodemo'compileSdk 33defaultConfig {applicationId "com.xxx.sodemo"minSdk 31targetSdk 33versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"ndk {// 生成so库类型abiFilters 'armeabi-v7a', 'arm64-v8a','x86','x86_64'}}externalNativeBuild {cmake {// 设置CMakeLists.txt文件路径path file('src/main/cpp/CMakeLists.txt')version '3.22.1'}}...}

3、编译库文件
点击Make按钮或Build->Make Project,运行结束后,会在 根目录/app/build/intermediates/cmake/debug/obj 路径下生成对应平台的.so库文件。

Makefile编译

使用MK文件编译,不需要编辑CMakeLists.txt,也不需要在build.gradle中配置,只要在Android.mk和Application.mk文件中配置好即可。一般在C/C++同目录下创建mk文件。
1、编写Android.mk文件

#设置当前编译路径为当前文件夹路径
LOCAL_PATH :=$(call my-dir)#清空编译环境的变量(由其他模块设置过的变量)
include $(CLEAR_VARS)
LOCAL_LDLIBS := -lm -llog#指定生成模块的名称(库引用名称),编译时会自动添加lib前缀
LOCAL_MODULE :=JNITest123#需要编译的源文件。如果存在多个.cpp文件时使用"\"隔开
LOCAL_SRC_FILES :=native-lib.cpp \NativeImpl.cpp  # NativeImpl.cpp在后面代码分离部分实现#生成动态库
include $(BUILD_SHARED_LIBRARY)

2、编写Application.mk文件

#模块名字,与Android.mk中保持一致
APP_MODULES := JNITest123#支持平台,这里支持所有平台
APP_ABI := all
APP_ALLOW_MISSING_DEPS=true

3、编译库文件
方式一:
1)检查编译环境
打开cmd窗口,运行ndk-build --version,如下输出,说明ndk配置正确。
在这里插入图片描述
2)在cmd中进入C/C++文件所在目录下,执行ndk-build命令编译。

NDK_PROJECT_PATH=.  # 当前项目
APP_PLATFORM=android-16 # 有默认值,可以不设置
APP_BUILD_SCRIPT=./Android.mk  # 当前目录下的Android.mk文件。注意:这里根据实际情况修改路径
NDK_APPLICATION_MK=./Application.mk  # 当前目录下的Application.mk文件。注意:这里根据实际情况修改路径
NDK_LOG=1   # 打印编译日志

整理成一行命令执行:

ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk NDK_APPLICATION_MK=./Application.mk NDK_LOG=1

3)执行结果:
在这里插入图片描述
说明:在哪个目录下执行ndk-build命令编译,就在此目录下生成库文件。

E:\work\Test\Andriod\SoDemo\app\src\main>ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./cpp/Android.mk NDK_APPLICATION_MK=./cpp/Application.mk APP_PLATFORM=android-16
Android NDK: WARNING: APP_PLATFORM android-16 is higher than android:minSdkVersion 1 in ./AndroidManifest.xml. NDK binaries will *not* be compatible with devices older than android-16. See https://android.googlesource.com/platform/ndk/+/master/docs/user/common_problems.md for more information.
[arm64-v8a] Compile++      : JNITest123 <= native-lib.cpp
[arm64-v8a] SharedLibrary  : libJNITest123.so
[arm64-v8a] Install        : libJNITest123.so => libs/arm64-v8a/libJNITest123.so
[x86_64] Compile++      : JNITest123 <= native-lib.cpp
[x86_64] SharedLibrary  : libJNITest123.so
[x86_64] Install        : libJNITest123.so => libs/x86_64/libJNITest123.so
[armeabi-v7a] Compile++ thumb: JNITest123 <= native-lib.cpp
[armeabi-v7a] SharedLibrary  : libJNITest123.so
[armeabi-v7a] Install        : libJNITest123.so => libs/armeabi-v7a/libJNITest123.so
[x86] Compile++      : JNITest123 <= native-lib.cpp
[x86] SharedLibrary  : libJNITest123.so
[x86] Install        : libJNITest123.so => libs/x86/libJNITest123.so

方式二:在Android Studio中,打开终端Terminal,cd进入C/C++文件所在目录的父目录下,执行ndk-build.cmd即可。只要代码没有问题,一般可以在同级目录下生成文件。

PS E:\work\Test\Andriod\SoDemo\app\src\main> D:\win10_program\develop\Android\AndroidSDK\ndk\26.1.10909125\ndk-build.cmd
Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-21.
[arm64-v8a] SharedLibrary  : libJNITest123.so
[arm64-v8a] Install        : libJNITest123.so => libs/arm64-v8a/libJNITest123.so
[x86_64] Compile++      : JNITest123 <= native-lib.cpp
[x86_64] SharedLibrary  : libJNITest123.so
[x86_64] Install        : libJNITest123.so => libs/x86_64/libJNITest123.so
[armeabi-v7a] Compile++ thumb: JNITest123 <= native-lib.cpp
[armeabi-v7a] SharedLibrary  : libJNITest123.so
[armeabi-v7a] Install        : libJNITest123.so => libs/armeabi-v7a/libJNITest123.so
[x86] Compile++      : JNITest123 <= native-lib.cpp
[x86] SharedLibrary  : libJNITest123.so
[x86] Install        : libJNITest123.so => libs/x86/libJNITest123.so

常见问题:

E:\work\Test\Andriod\SoDemo\app\src\main> D:\win10_program\develop\Android\AndroidSDK\ndk\21.0.6113669\ndk-build.cmd
Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-16.
Android NDK: Your APP_BUILD_SCRIPT points to an unknown file: 
D:\win10_program\develop\Android\AndroidSDK\ndk\21.0.6113669/jni/Android.mk  // 这里是路径问题jni
D:/win10_program/develop/Android/AndroidSDK/ndk/21.0.6113669/build//../build/core/add-application.mk:88: *** Android NDK: Aborting...    .  Stop.

因为这里的mk文件实际在src\main\cpp中,而NDK编译环境默认在jni目录下找mk文件,所以报错无法找到。这里可以将文件名称cpp修改为默认路径jni,也可以在ndk-build命令里指定mk的路径,具体修改如下。

ndk-build.cmd APP_BUILD_SCRIPT=./cpp/Android.mk NDK_APPLICATION_MK=./cpp/Application.mk
PS E:\work\Test\Andriod\SoDemo\app\src\main>  D:\win10_program\develop\Android\AndroidSDK\ndk\26.1.10909125\ndk-build.cmd APP_BUILD_SCRIPT=./cpp/Android.mk NDK_APPLICATION_MK=./cpp/Application.mk
Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-21.
[arm64-v8a] Compile++      : JNITest123 <= native-lib.cpp
[arm64-v8a] SharedLibrary  : libJNITest123.so
...

命令编译

JNI和C/C++代码分离

分离设计目的是希望在JNI文件中出现少量的C++代码。
1、编写.cpp文件
这里以NativeImpl.cpp为例

#include <jni.h>
#include "NativeImpl.h"NativeImpl::NativeImpl() {}NativeImpl::~NativeImpl() {}int NativeImpl::Clear_Zero() {LOGD("打印C++ LOGD");LOGE("打印C++ LOGE");LOGI("打印C++ LOGI");LOGW("打印C++ LOGW");return 0;
}

2、编写.h文件
这里以NativeImpl.h为例

#ifndef SODEMO_NATIVEIMPL_H
#define SODEMO_NATIVEIMPL_H
#include "LOG.h"#include <vector>
class NativeImpl {public:NativeImpl();virtual ~NativeImpl();virtual int Clear_Zero();
};#endif //SODEMO_NATIVEIMPL_H

3、在JNI文件中调用C++方法。

#include <jni.h>
#include "NativeImpl.h"NativeImpl nativeImpl;
NativeImpl*  getNativeImpl(){return &nativeImpl;
}extern "C"
JNIEXPORT jint JNICALL
Java_com_mytest_sodemo_JNIUtils_clrNumber(JNIEnv *env, jclass clazz) {// TODO: implement clrNumber()int zero = getNativeImpl()->Clear_Zero();return zero;
}

注意:代码分离后,需要将纯C++的源码添加到编译环境中,也就是在CMakeLists.txt的add_library方法中添加NativeImpl.cpp,或者在Android.mk的LOCAL_SRC_FILES中添加NativeImpl.cpp。具体参考上面的编译方式。

Java调用C/C++

Java调用C或C++程序,前提是给定了C或C++的动态库dll(Windows)或so(Linux)文件和函数头文件说明,这里介绍如何正确调用第三方so。
Java层调用C++函数主要通过建立的映射关系,这里jni函数调用java层的函数就要通过JNIEnv。
1、将第三方提供的so文件全部放进app\libs目录下,然后在app\build.gradle的sourceSets中配置libs,这样就会在打包时,自动把libs下的文件副本迁移到apk的lib目录下。
当然,这里的路径可以自定义,只要Gradle在打包时能通过你配置的路径,找到so的存放位置即可。

android {defaultConfig {...}sourceSets {main {jniLibs.srcDirs = ['libs']          // 打包时会把app\libs下的共享库.so的副本迁移到apk的lib目录下。// jniLibs.srcDirs = ['libs/test']  // 也可以在app\libs下新建各个公司或模块提供的库目录。// jniLibs.srcDirs = ['src/mylibs'] // 也可以自定义路径so的存储路径,只要能找到就行。}}...
}

在这里插入图片描述
2、根据已知的Java_xx包名_yy类名_方法名格式(也可以通过nm命令获取so库的方法),在自己的项目中app\src\main\java目录下新建xx包名,然后再创建一个和so中的yy类名相同的类,这里要确保包名、类名、方法名、库名(不带lib前缀)四者一致,最后在自己的项目中直接调用yy类中的方法即可。

extern "C" JNIEXPORT jstring JNICALL
Java_com_mytest_sodemo_JNIUtils_stringFromJNI(JNIEnv *env, jobject) {std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());
}

注意:这一步非常重要,通过如此设计的方法名,建立了java和C之间的映射关系,所以在其他应用中使用时,也需要建立这种映射关系,否则报错UnsatisfiedLinkError。
//todo 缺图
3、清理和检查
清理后重新打包:Build>Clean Project>等一会儿>Make Project或者Build APK(s)>等一会儿。
检查so是否打入包内:Build>Analyze APK>OK>打开目标APK的lib目录。
4、常见异常
如果检查没有发现错误,编译运行后还是出现UnsatisfiedLinkError异常,多半是因为apk中的so没有打入包内,请按照第3步处理.

查看so中包含的方法

需要使用nm工具,一般在sdk\ndk\xx版本\toolchains\x86-4.9\prebuilt\windows-x86_64\i686-linux-android\bin

nm -D "so文件路径" 

C/C++调用Java

打印C/C++的log

在CPP目录下新建head文件LOG.h

#ifndef SODEMO_LOG_H
#define SODEMO_LOG_H#include <android/log.h>#define TAG "haitao"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__);#endif //SODEMO_LOG_H

在需要使用的文件中#include "LOG.h"即可。

生成多个共享库so

如果需要生成多个共享库,可以在CMakeLists.txt再增加add_librarytarget_link_libraries
参考配置CMake

# 生成libtest-1.so
add_library(test-1SHAREDnative-lib.cpp)
target_link_libraries(test-2 android)
...# 生成libtest-2.so
add_library(test-2 SHAREDnative-lib.cpp)
target_link_libraries(test-2android)

JNI调试

在这里插入图片描述
在Debug模式下,有时候会出现这个Permission denied的提示。
解决方法:退出App重新debug运行。如果退出无法解决此问题,重新USB连接即可。

与君共勉人生自当扶摇上,揽星衔月逐日光。你只管去劈浪,与众生争锋芒,你举步是八万里宽广,你眼望是千江拍白浪,你平生这一趟,要让旁人想都不敢想!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/506505.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mac命令行下计算文件SHA-256散列值

源起 从国内的第三方网站下载了Android sutiod的zip包下载地址&#xff0c;为了安全起见还是得跟Android官网上的对应的zip包的SHA值做下对比。以前是经常使用md5命令的&#xff0c;所以理论在命令行下应该是有对应的命令行工具可以计算SHA值的。后来搜索到可以用 shasum命令来…

day02-JavaScript-Vue

文章目录 1 JavaScript1.1 介绍 1.2 引入方式1.3 基础语法1.3.1 书写语法1.3.2 变量1.3.3 数据类型和运算符 1.4 函数1.4.1 第一种定义格式1.4.2 第二种定义格式 1.5 JavaScript对象1.5.1 基本对象1.5.1.1 Array对象语法格式特点属性和方法 1.5.1.2 String对象语法格式属性和方…

竞争加剧下,登顶后的瑞幸该做什么?

瑞幸咖啡仅用短短18个月时间从品牌创立到纳斯达克上市&#xff0c;刷新全球最快上市记录。2020年因交易造假事件被勒令退市股价暴跌80%&#xff0c;有人说这个创造了赴美IPO奇迹的“巨婴”将是下一个倒下的ofo。2023年瑞幸咖啡以逆势超速增长领跑咖啡赛道有力回应了市场的质疑&…

消息中间件之RocketMQ源码分析(二十七)

Broker提交或回滚事务消息 当生产者本地事务处理完成并且Broker回查事务消息后&#xff0c;不管执行Commit还是Rollback,都会根据用户本地事务的执行结果发送一个End_transaction的RPC请求给Broker&#xff0c;Broker端处理该请求的类是EndTransactionProcessor 第一步&…

什么是张量?如何理解张量?

一、张量概念 张量&#xff08;tensor&#xff09;是一个多维数组&#xff0c;它是向量&#xff08;一维数组&#xff09;和矩阵&#xff08;二维数组&#xff09;的推广。在数学和物理学中&#xff0c;张量是一种广泛应用的概念&#xff0c;用来描述物理量在空间中的分布和变…

LLM 系列——BERT——论文解读

一、概述 1、是什么 是单模态“小”语言模型&#xff0c;是一个“Bidirectional Encoder Representations fromTransformers”的缩写&#xff0c;是一个语言预训练模型&#xff0c;通过随机掩盖一些词&#xff0c;然后预测这些被遮盖的词来训练双向语言模型&#xff08;编码器…

HTML~

HTML HTML是一门语言&#xff0c;所有的网页都是用HTML这门语言编写出来的HTML(HyperText Markup Language):超文本标记语言 超文本:超越了文本的限制&#xff0c;比普通文本更强大。除了文字信息&#xff0c;还可以定义图片、音频、视频等内容 标记语言:由标签构成的语言 …

下载github项目到pycharm

一、下载git 1.下载git链接 https://git-scm.com/ 2.一路点击next&#xff0c;最后finish 二、使用git 1.安装成功后在开始菜单栏会找到如下内容&#xff0c;其中常用的是Git Bash 2.点击Git Bash 3.这里就可以克隆github上的代码了 点击复制&#xff0c;在命令行输入…

【Linux常用命令——基础命令】

1.基础命令 pwd&#xff1a;显示当前位置下的绝对路径 man&#xff1a;帮助文档&#xff0c;退出方式是键盘输入q cd&#xff1a;cd 路径——表示切换到某路径下 .一个点是当前文件…代表上一层文件 任何时候&#xff0c;cd ~都会回到家目录 想要不停的在两个最近去过的两…

大数据与云计算、物联网、人工智能

云计算 云计算的概念 云计算的概念 通过网络、以服务的方式&#xff0c;为千家万户提供非常廉价的IT资源&#xff0c;一种商业模式。云计算是一种模型&#xff0c;用户可以方便地通过网络按需访问一个可配置计算资源&#xff08;如网络、服务器、存储、应用和服务&#xff0…

sizeof 和 strlen的区别

sizeof sizeof是单目操作符,sizeof计算变量所栈内存空间大小,单位是字节,如果操作数是类型的话,会计算类型所占大小,sizeof指在乎占用内存空间大小不在乎内容是什么. int main() {int a 0;printf("%zd\n", sizeof(a));printf("%zd\n", sizeof a );printf…

走进SQL审计视图——《OceanBase诊断系列》之二

1. 前言 在SQL性能诊断上&#xff0c;OceanBase有一个非常实用的功能 —— SQL审计视图(gv$sql_audit)。在OceanBase 4.0.0及更高版本中&#xff0c;该功能是 gv$ob_sql_audit。它可以使开发和运维人员更方便地排查在OceanBase上运行过的任意一条SQL&#xff0c;无论这些SQL是成…