extern C 透彻理解

2023-05-16

一、背景

一直以来对extern C的理解都停留在表面,只知道为了C/C++混合编程。今天来透彻理解下这个概念。

 

二、整体的项目结构。

jni

  ---Android.mk

LOCAL_PATH := $(call my-dir)
  
include $(CLEAR_VARS)  
  
LOCAL_MODULE    := lesson2
  
LOCAL_SRC_FILES := Lesson2.cpp MyTest.cpp module.c

LOCAL_LDLIBS    := -llog -ldl

LOCAL_CPPFLAGS += -O3

LOCAL_CFLAGS += -fvisibility=hidden
        
include $(BUILD_SHARED_LIBRARY)

  ---Application.mk

APP_PLATFORM := android-21
APP_ABI := armeabi-v7a

  ---com_example_ndkreverse2_Lesson2.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_ndkreverse2_Lesson2 */

#ifndef _Included_com_example_ndkreverse2_Lesson2
#define _Included_com_example_ndkreverse2_Lesson2
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_ndkreverse2_Lesson2
 * Method:    main
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_ndkreverse2_Lesson2_main
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

  ---Lesson2.cpp

#include "com_example_ndkreverse2_Lesson2.h"
#include <android/log.h>
#include <stdlib.h>
#include <string.h>
#include <sys/auxv.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include "MyTest.h"
extern "C" {
    #include "module.h"
}


JNIEXPORT void gettime() {
    struct tm *info;
    time_t rawtime;
    char buffer[64];
    char final_time[128];
    time( &rawtime );
    info = localtime( &rawtime );

    struct timeval tv;
    gettimeofday(&tv,NULL);
    strftime(buffer,64,"%Y.%m.%d.%H:%M:%S.%%u", info);
    snprintf(final_time, 0x80u, buffer, tv.tv_usec / 1000);


    ALOGD("info:%s\n", final_time);
}


JNIEXPORT void JNICALL Java_com_example_ndkreverse2_Lesson2_main
  (JNIEnv * env, jobject jobject) {
    gettime();
    MyTest* pTest = new MyTest();
    pTest->Print();
    foo(1,2);
    foo1(2,1);
}

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    ALOGD("JNI_OnLoad");
    return JNI_VERSION_1_4;
}

  ---module.c

#include "module.h"

int foo( int x, int y ) {
    return x + y;
}

  ---module.h

#ifndef NDKREVERSE2_MODULE_H
#define NDKREVERSE2_MODULE_H
int foo( int x, int y );
#endif //NDKREVERSE2_MODULE_H

  ---MyTest.cpp

#include "MyTest.h"
#include <android/log.h>
#define LOG_TAG "lesson2"

#define ALOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))

int foo1(int x, int y) {
    MyTest myTest;
    myTest.Print();
    return x - y;
}

MyTest::MyTest()
{
    ALOGD("MyTest\n");
}

void MyTest::Print()
{
    ALOGD("Print\n");
}

  ---MyTest.h

#ifndef NDKREVERSE2_MYTEST_H
#define NDKREVERSE2_MYTEST_H

int foo1(int x, int y);

class MyTest {
    public:
        MyTest();
        void Print();
};



#endif //NDKREVERSE2_MYTEST_H

 

三、extern C的第一种用法--告诉C++编译器按照C语言编译器的规则来链接

使用ndk-build,首先会生成几个.o文件:Lesson2.o,module.o,MyTest.o。我们分别使用ida查看下module.o的导出符号。

module.o是C语言编译器编译生成的,所有导出符号是foo。而调用module的地方是在Lesson2.cpp文件中,Lesson2.o是使用C++编译器生成的。由于C++语法支持重载,所以使用C++编译器生成的函数和使用C语言编译器生成的函数是不一样的(后面可以看到哪里不一样)。为了让module.o和Lesson2.o能够链接到一起,我们在Lesson2.cpp加入了

extern "C" {
    #include "module.h"
}

这是告诉编译器在链接时,按照C语言的编译风格寻找module.o中的foo函数。

extern C 是C++语法,不能在C语言中使用。如果要在C语言中使用,需要如下定义:

#ifdef __cplusplus
extern "C" {
#endif
int foo(int x, int y)

#ifdef __cplusplus
}
#endif

在C++语言包含时,会加上extern C。在C语言包含时,会去掉extern C。

 

四、extern C的第二种用法---定义.cpp中说明函数要按照C语言的风格编译

上面工程中MyTest.h定义了foo1这个函数,而这个函数实现在MyTest.cpp中,编译生成的MyTest.o。

我们可以看到是按照C++的风格编译的函数名为_Z4foo1ii。

如果MyTest.h加上extern C。

#ifndef NDKREVERSE2_MYTEST_H
#define NDKREVERSE2_MYTEST_H

extern "C" int foo1(int x, int y);

class MyTest {
    public:
        MyTest();
        void Print();
};



#endif //NDKREVERSE2_MYTEST_H

编译的结果如下:

我们可以看到是按照C语言的风格编译的函数名为foo1。

同时也告诉了Lesson2.o和MyTest.o中链接的时候按照C语言的风格寻找foo1函数。

 

五、so导出函数

我们在Android.mk中定义了

LOCAL_CFLAGS += -fvisibility=hidden

这个CFLAGS的含义是所有在so中的符号默认不导出,只有加上了

#define JNIEXPORT  __attribute__ ((visibility ("default"))) 才是导出函数。

我们回看下Lesson2.cpp,Lesson2.h:

Java_com_example_ndkreverse2_Lesson2_main是有extern C包含的,所以导出函数就是Java_com_example_ndkreverse2_Lesson2_main。

还有个疑问,JNI_Onload为什么按照C语言的风格导出呢?

答案在jni.h中

#ifdef __cplusplus
extern "C" {
#endif
/*
 * VM initialization functions.
 *
 * Note these are the only symbols exported for JNI by the VM.
 */
jint JNI_GetDefaultJavaVMInitArgs(void*);
jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*);

#define JNIIMPORT
#define JNIEXPORT  __attribute__ ((visibility ("default")))
#define JNICALL

/*
 * Prototypes for functions exported by loadable shared libs.  These are
 * called by JNI, not provided by JNI.
 */
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved);

#ifdef __cplusplus
}
#endif

那么这里为什么要按照C原因的风格导出呢?

因为加载so的时候会使用dlsym,dlsym传入的函数名称,就是导出的函数名称,这样调用dlsym(handle,"JNI_Onload")就能调用对应的方法了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

extern C 透彻理解 的相关文章

  • keil中的extern的使用

    C51头文件使用extern的目的是外部变量或函数声明 使用时要注意 1 extern最好写在 h文件中 xff0c 方便后期的可读性 2 在使用extern进行外部变量声明时 xff0c 不能重新给变量赋值 例如 xff1a extern
  • Android NDK 为什么要 extern “C”

    由于C 43 43 函数支持重载 xff0c 就是一个C 43 43 函数 xff0c 可以有不同的参数个数和类型 xff0c 编译后函数名会变 为了避免ndk load 的C C 43 43 库的时候找不到这个函数 xff0c 索性都用
  • 关于function declared implicitly的正确解法以及extern的用法

    一直以来以为function declared implicitly这个问题都是很容易的解决的 xff0c 所以没有在意 xff0c 没想到昨天查了下 xff0c 网上竟然有好多种说法是不合适的 xff0c 所以解答下 首先这句话是函数没有
  • 浅析extern “C”的作用

    浅析extern C 的作用 关于extern C 的作用和意思 xff0c 网上资料已经有很多了 xff08 我也参考了几篇 xff09 xff0c 不过我还是觉得有必要自己总结一下 xff0c 毕竟 好记性不如烂笔头 嘛 到C标准函数库
  • extern “C"作用

    1 在cpp文件中调用c文件中实现的函数的时候 xff0c 需要用extern 34 C 34 声明该函数 xff0c 否则cpp会按名字改编后的 函数名去找该函数而找不到 这是作用 xff09 c文件中有一函数 xff1a void Tr
  • virtual,override,new ,extern,abstract修饰符

    方法定义中可以使用的修饰符有 a new b public c protected d internal e private f static g virtual h override i extern j abstract 对于virtu
  • 为什么“外部”存储类的功能不同?

    下面的代码片段工作正常 extern int i int i int main return 0 我得到的是 i 被声明然后定义 由于只有一个定义 所以完全没问题 int main extern int i int i return 0 现
  • 使用 extern 全局变量的正确方法是什么?

    文件 a cc int a 0 文件 b cc include a cc 文件 main cc include b cc extern int a int main g c a cc g c b cc g main cc a o b o e
  • C、硬件抽象层中“extern”类型的变量

    我正在研究硬件抽象层 该 HAL 的目的是在 Linux 驱动程序和 MCU 驱动程序之间轻松切换 我正在研究SPI接口 下面是 打开 SPI接口的HAL函数的签名 哈尔 spi h spi handle t spi open spi po
  • 访问另一个 .cpp 文件中的 .cpp 文件中定义的全局变量[重复]

    这个问题在这里已经有答案了 考虑以下场景 我的文件 cpp const int myVar 0 全局变量 另一个文件 cpp void myFun std cout lt lt myVar compiler error Undefined
  • Objective-C 中的全局变量 - extern 和 .m 文件顶部声明的差异

    我知道你可以使用 extern 在 Objective C 中定义一个全局变量 但我刚刚意识到我在第一个方法之前在 m 文件顶部声明的变量也意外地是全局的 这导致了一些问题 问题 我将它们移至头文件的 interface 部分 我认为这正确
  • C隐式extern用于全局变量,什么时候发生,它是如何工作的

    我试图了解 C 全局变量在多个文件 编译单元 之间共享的方式 我已经阅读了精彩的问答here https stackoverflow com questions 1433204 what are extern variables in c
  • 在定义中使用 static 关键字与在 C 中使用声明

    以下编译良好 使用static仅在函数声明期间 include
  • 关于静态数据成员和成员函数定义的 Extern 关键字,C++

    C 标准是否允许extern静态数据成员和成员函数定义上的关键字 假设链接匹配 例如 struct A static int a external linkage void f external linkage extern int A a
  • 块范围内没有链接?

    块中声明的所有变量是否都 无链接 例如 1 如果我声明一个静态变量 void foo static int i 它有内部联系还是没有联系 如果没有链接 那为什么要使其静态呢 2 如果我使用 extern 会发生什么 global scope
  • 模板外部链接?谁能解释一下吗?

    模板名称具有链接 3 5 非成员函数模板可以有内部链接 任何其他模板名称应具有外部链接 从具有内部链接的模板生成的实体与在其他翻译单元中生成的所有实体不同 我知道使用关键字的外部链接 extern C EX extern C templat
  • 为什么模板不能位于外部“C”块内?

    这是一个后续问题一个答案 https stackoverflow com questions 4866433 is it possible to typedef a pointer to extern c function type wit
  • 函数模板:外部模板与显式专业化

    考虑以下函数模板声明 template
  • 外部关键字的使用

    我正在使用三个程序extern关键词 我无法理解结果 下面是三个例子 示例 1 我期望下面的代码会给出编译错误 即多个声明k 但效果还好吗 int k works fine extern int k 10 void main cout lt
  • 块作用域链接 C 标准

    以下标识符没有链接 声明为对象或函数之外的任何标识符 声明为函数参数的标识符 未使用存储类说明符 extern 声明的对象的块作用域标识符 static int a no linkage 对于在该标识符的先前声明可见的范围内使用存储类说明符

随机推荐

  • 2018-07-25-github-如何在Github上面创建Release

    github release 看别的Github项目都有一条类似timeline 时间线 的版本列表 xff0c 如下图 xff0c 所以在Github上面摸索了一下 xff0c 弄好了记录一下 创建一个Release TestBefore
  • SSH登录卡在‘Last login‘提示界面的一种原因

    以前解决过SSH登录卡顿的问题 xff0c 它一般来源于 xff1a GSSAPIAuthentication UseDNS 以上设置项被默认打开或意外打开 而这次遇到的问题不是卡顿 xff0c 而是卡在 Last login xff0c
  • 【云原生之Docker实战】使用Docker部署openwrt软路由

    云原生之Docker实战 使用Docker部署openwrt软路由 一 openwrt介绍 二 检查本地docker状态 1 查看docker版本 2 查看docker信息 3 查看本地docker网络 三 安装docker compose
  • 值得收藏:图解算法——动态规划系列

    个人博客导航页 xff08 点击右侧链接即可打开个人博客 xff09 xff1a 大牛带你入门技术栈 小浩 xff1a 宜信科技中心攻城狮一枚 xff0c 热爱算法 xff0c 热爱学习 xff0c 不拘泥于枯燥编程代码 xff0c 更喜欢
  • TensorFlow之循环神经网络&自然语言处理 学习总结

    作者 xff1a jliang https blog csdn net jliang3 junliang 20190303 说明 xff1a 以下所有代码使用版本TensorFlow1 4 0或1 12 0版本 import tensorf
  • Docker镜像容器的迁移问题

    本文是本人项目踩坑经验 xff0c 如有错漏请见谅 xff01 背景需求 xff1a 一个已经配置好的容器 xff08 无build文件 xff09 需要部署到生产环境中 xff0c 容器内有程序绑定了宿主环境的硬件信息 需求部署后能让第三
  • 视觉里程计1 高翔

    小白 xff08 我 xff09 本着学习 xff0c 提高自我 xff0c 增加知识的想法 xff0c 决定认真分析高翔博士slam 在此立下一个flag xff1a 每周进行知识总结 xff08 CSDN xff09 xff1b 每周要
  • InvokeHelper函数的用法

    ActiveX控件的方法和属性操作与生成的C 43 43 类成员函数相关联都是通过InvokeHelper函数的调用来完成的 xff0c InvokeHelper函数的第一个参数是由Component Gallery xff08 控件提供者
  • 前向声明! struct netif; —— 只声明,无具体内部细节

    今天在看到 Linux阅码场 的 宋宝华 xff1a Linux内核编程广泛使用的前向声明 Forward Declaration xff0c 非常感谢 xff01 前向声明 编程定律 先强调一点 xff1a 在一切可能的场景 xff0c
  • MCU初始化流程——从上电到main()之间

    说明 xff1a 以下介绍示例的MCU地址空间如下 xff1a ROM空间为 xff1a 0x0000 0000 0x0000 8000 RAM空间为 xff1a 0x2000 0000 0x2000 2000 堆栈 SP 生长方向为 递减
  • FreeRTOS 启动第一个任务 prvStartFirstTask vPortSVCHandler

    asm void prvStartFirstTask void asm void prvStartFirstTask void PRESERVE8 Use the NVIC offset register to locate the sta
  • 组播知识 - IGMP

    https zhuanlan zhihu com p 258619129 组播初识 一 为什么要启用组播 xff1f 1 节省不必要的数据发送 2 需要发送相同的数据去往多个不同的接收者 3 减少带宽的占用 4 优化网络设备的处理进程 5
  • copy_from_user函数详细分析

    copy from user函数的目的是从用户空间拷贝数据到内核空间 xff0c 失败返回没有被拷贝的字节数 xff0c 成功返回0 这么简单的一个函数却含盖了许多关于内核方面的知识 比如内核关于异常出错的处理 从用户空间拷贝数据到内核中时
  • linux驱动中的宏 _IOC_NR, _IOC_TYPE, _IOC_SIZE, _IOC_DIR

    转载自 xff1a http blog csdn net u010245383 article details 29391805 虽然排版都点点乱 xff0c 但是内容还是较全面的 在驱动程序里 xff0c ioctl 函数上传送的变量 c
  • 【转载】更进一步的了解Keil Flash的下载算法

    转载自 xff1a https jingyan baidu com article 414eccf64f03be6b431f0af8 html 前面提到了通用算法的选择 xff0c 那么问题来了 xff0c 这个算法文件如何来的呢 xff1
  • 自己动手写操作系统-经典书籍

    汇编语言 xff0c 王爽编写 王爽老师这本书 xff0c 绝对是经典中的经典 xff0c 比其他介绍汇编语言的书强很多 这本书以例子贯穿整本书 不像其他书罗列一堆指令 这本书只介绍了常用指令 xff0c 而且每个指令都有例子 xff0c
  • golang 闭包 函数作为参数和返回值

    一 函数闭包 package main import 34 fmt 34 func adder func int int 函数作为返回值 sum 61 0 自由变量 xff0c 每次调用都保留上次的结果 return func v int
  • go routine channel select

    一 go routine channel package main import 34 fmt 34 34 time 34 func worker id int c chan int for n 61 range c 读取channel f
  • 我的vimrc配置文件

    34 vundle begin set nocompatible 34 与vi不一致 filetype off filetype plugin on 34 检测插件 set rtp 43 61 vim bundle vundle 34 载入
  • extern C 透彻理解

    一 背景 一直以来对extern C的理解都停留在表面 xff0c 只知道为了C C 43 43 混合编程 今天来透彻理解下这个概念 二 整体的项目结构 jni Android mk LOCAL PATH 61 call my dir in