V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
phpIsNumberOne
V2EX  ›  程序员

99.9RMB 悬赏一技术支持

  •  
  •   phpIsNumberOne · 2021-02-26 11:00:27 +08:00 · 7107 次点击
    这是一个创建于 1372 天前的主题,其中的信息可能已经有所发展或是发生改变。
    概述:

    使用 C 打包为 Android 的 C-shar(so)动态链库,然后使用 Fltter 的 ffi 调用。现在我实现了函数为 int 类型的相互调用,但是 string(*char)始终有问题。

    要求:

    将 C 程序打包为 so 动态链库,并能在 Flutter 项目上正常使用,仅 Android 即可,使用 CMake ; C 程序:

    #include <stdio.h>
    #include <stdlib.h>
    int Hello(){
        return 233;
    }
    
    char *World(){
        char *r = "1234";
        return r;
    }
    
    char *Test(char *str)
    {
        return str;
    }
    
    说明:
    • 单纯使用 Dart 调用动态链库是完全没问题的,包括函数返回类型是*char,这是在 Windows 上测试的。
    • 将动态链库使用到 Flutter 上时,只有返回为 int 等数值类型的才没问题,调用返回*char 类型的函数时会闪退;这是你需要为我解决的。
    • 先到先得,可以加点价。
    相关资料:
    • C 交叉编译为 Android C-share 库

    • Dart ffi 调用动态链接库官方 Demo

    • 我自己写的 ffi 类,可供参考:链接: https://pan.baidu.com/s/1z-ooHzgkbM2SkzWrkJs4eA 提取码: 9awt

    • 在 Flutte 上使用 C

    • 我打包的动态链库,如果相信没问题可以直接用:链接: https://pan.baidu.com/s/16lZ8NwUexXJ3rpFcCxZwLw 提取码: c8s7

      打包命令:
      D:\Desktop\test>E:\android-ndk-r22-windows-x86_64\android-ndk-r22\toolchains\llvm\prebuilt\windows-x86_64\bin\aarch64-linux-android24-clang -shared -o mylib.so -Xlinker -soname=mylib.so -lm  mylib.c
      
      D:\Desktop\test>E:\android-ndk-r22-windows-x86_64\android-ndk-r22\toolchains\llvm\prebuilt\windows-x86_64\bin\aarch64-linux-android-nm.exe ./mylib.so
      00000000000014f4 t __atexit_handler_wrapper
                       U __cxa_atexit
                       U __cxa_finalize
      00000000000025b0 d __dso_handle
      00000000000025b0 d __dso_handle_const
      00000000000014ec t __emutls_unregister_key
      00000000000004dc r __FRAME_END__
      00000000000014e0 t __on_dlclose
      00000000000014f0 t __on_dlclose_late
                       U __register_atfork
      00000000000025c8 d _DYNAMIC
      0000000000001500 t atexit
      0000000000001528 T Hello
      0000000000000290 r ndk_build_number
      0000000000000250 r ndk_version
      0000000000000238 r note_android_ident
      000000000000024c r note_data
      00000000000002d0 r note_end
      0000000000000244 r note_name
      000000000000151c t pthread_atfork
      000000000000154c T Test
      0000000000001530 T World
      
    40 条回复    2021-02-27 17:55:56 +08:00
    kosgug
        1
    kosgug  
       2021-02-26 11:02:42 +08:00
    建议把悬赏金额调低
    liprais
        2
    liprais  
       2021-02-26 11:03:04 +08:00
    单纯的提问都比你这 99.9 好
    20015jjw
        3
    20015jjw  
       2021-02-26 11:05:37 +08:00 via Android   ❤️ 1
    这就跟求包养
    一个月只要两包泡面一样...
    pekki
        4
    pekki  
       2021-02-26 11:10:07 +08:00
    你是不是把程序员的时薪想的太低了。。。
    youthfire
        5
    youthfire  
       2021-02-26 11:14:28 +08:00 via iPhone   ❤️ 4
    传说中的伤害性不大,侮辱性极强
    heyjei
        6
    heyjei  
       2021-02-26 11:17:15 +08:00   ❤️ 4
    一杯星巴克或者一杯瑞幸的外卖都比你这个 99.9 更有吸引力啊
    hanxiV2EX
        7
    hanxiV2EX  
       2021-02-26 11:17:16 +08:00   ❤️ 2
    试试这样?记得 free 它

    char *World(){
    char *tmp = "1234";
    char *r = malloc(strlen(tmp)+1);
    strcpy(r, tmp);
    return r;
    }

    void FreeWorld(char *){
    free(r)
    }
    wzb0909
        8
    wzb0909  
       2021-02-26 11:17:51 +08:00
    99 元让人看完你的字都不够啊
    hanxiV2EX
        9
    hanxiV2EX  
       2021-02-26 11:23:03 +08:00   ❤️ 1
    建议 @phpIsNumberOne 重新学习 C 语言吧,内存没搞清楚写出来的东西会挂的。
    danhahaha
        10
    danhahaha  
       2021-02-26 11:27:19 +08:00
    建议改成 100.99 或者 0.1K 这样看起来比较多
    vone
        11
    vone  
       2021-02-26 11:30:14 +08:00
    没八百这活干不了。
    hanssx
        12
    hanssx  
       2021-02-26 11:30:48 +08:00   ❤️ 3
    99.9 不少了,而且题主提问的方式挺正规的吧?
    话说我记得*和后面的之间是不是加个空格更 c ?
    mm163
        13
    mm163  
       2021-02-26 11:40:04 +08:00
    Flutter 没用过,不清楚。
    java 虚拟环境与 c 不是一个空间的,字符串处理应该使用 jni,不能直接返回指针。
    pkookp8
        14
    pkookp8  
       2021-02-26 11:50:06 +08:00 via Android
    我觉得需要开辟一块共享内存或者普通内存用来操作字符串。world 的 1234 是在字符常量区,可读不可写。test 的内存不知道哪来的,不知道可不可读写

    char*不行 int 行,显然是内存问题。但是不懂 flutter,无能为力。试试对入参或出参进行申请内存,使用后释放。或者 socket 交互
    anonydmer
        15
    anonydmer  
       2021-02-26 11:51:38 +08:00
    亲,你这样用 C 返回字符串是不行的哦,内存管理错了
    hantsy
        16
    hantsy  
       2021-02-26 11:53:19 +08:00
    9 。9 包邮
    Chenamy2017
        17
    Chenamy2017  
       2021-02-26 12:48:54 +08:00
    好好学学 C 语言吧,指针内存什么的。
    你这个 World 函数返回的指针有问题。
    phpIsNumberOne
        18
    phpIsNumberOne  
    OP
       2021-02-26 13:53:59 +08:00
    @hanxiV2EX
    @Chenamy2017
    见笑了,C 就大学学了下,后面就没深入了
    wangjunling
        19
    wangjunling  
       2021-02-26 14:02:44 +08:00
    还不如直接求帮助, 99 悬赏真是侮辱程序员这个行业呀, 理解问题, 解决问题, 至少你的来个三位数呀, 999 或许真有人接
    hanxiV2EX
        20
    hanxiV2EX  
       2021-02-26 14:10:09 +08:00
    楼主提问表述确实很清楚,有经验的一眼也就能看到问题所在。然后提供的资料也还全面,这样的提问还是值得称赞的。
    oxromantic
        21
    oxromantic  
       2021-02-26 14:27:41 +08:00   ❤️ 2
    楼上一群冷嘲热讽的不知道有没有认真看代码, 至少楼主的例子我用 flutter 跑了下是可以传到 flutter 端的

    楼主的例子是 literals string, 本来就不需要栈上分配内存, 直接 return 又如何?

    不知道楼主 flutter 端是怎么写的, 至少我是这样可以取到的:

    #include <stdint.h>
    #include <string.h>

    extern "C" __attribute__((visibility("default"))) __attribute__((used))
    char* native_add(int32_t x, int32_t y) {
    return "12345";
    }

    final Pointer<Utf8> Function(int x, int y) nativeAdd = nativeAddLib
    .lookup<NativeFunction<Pointer<Utf8> Function(Int32, Int32)>>(
    "native_add")
    .asFunction();

    Text('Running on: $_platformVersion ${Utf8.fromUtf8(nativeAdd(3, 3))}\n')
    Visitor233
        22
    Visitor233  
       2021-02-26 14:31:24 +08:00
    @hanxiV2EX 确实,学习了。
    Anshi
        23
    Anshi  
       2021-02-26 14:32:51 +08:00   ❤️ 4
    悬赏 99.9 为什么会收到冷嘲热讽,是属于一个文化界限问题,要么一杯咖啡,对方当交个朋友,而选中帮你,要么完全不提供回馈,直接真诚提问,对方这是公益性质的帮你,如果选择一个实际的数字量化,那就需要好好讨论,帮你这个事情变得复杂,它到底值多少钱,多了,显得势力,少了,不划算,变成了人性的问题。
    Ritter
        24
    Ritter  
       2021-02-26 15:14:23 +08:00
    100 块不少了 可惜我不会
    NasirQ
        25
    NasirQ  
       2021-02-26 15:31:39 +08:00
    抛开其他,我觉得楼主出价有问题。首先所谓 "99.9" "9.9" 这样子定价,是商家为了满足顾客的省钱心里做的定价策略。然而楼主是在寻求技术支持。这里建议改为 0.1k 或者 0.1011k,这样二进制形式的数字更能吸引人 [dog]
    daoyouma
        26
    daoyouma  
       2021-02-26 15:47:35 +08:00
    10 分钟能解答 我不介意 关键 我不会
    yolee599
        27
    yolee599  
       2021-02-26 20:34:54 +08:00 via Android
    char *r = "1234"; 要改成 const char *r = "1234";
    yolee599
        28
    yolee599  
       2021-02-26 20:38:07 +08:00 via Android
    @yolee599 说错了要改成 static const char *r = "1234";
    aheadlead
        29
    aheadlead  
       2021-02-26 21:00:48 +08:00
    ………………………楼上这

    我来回答一下吧。楼主你在 World() 里定义的变量 char *r 是在 World() 这次函数调用的栈帧里的。
    World() 函数返回后,栈帧弹出,char *r 本身所占据的 8 字节内存就不应该访问了。

    虽然你仍然有可能能访问到,但这属于 undefined behavior 。
    我对 Flutter 一无所知,但有可能是就是你访问了不该访问的内存(比如提示 memory access violation 、segmentation fault 之类的)。

    至于你说 Test() 函数也会闪退。Test() 函数本身没有任何问题(只是没啥意义),可能和你的用法有关。楼主要是不嫌麻烦的话,把代码放到 github 或者 gist/pastebin 上,我可以一看。
    aheadlead
        30
    aheadlead  
       2021-02-26 21:04:23 +08:00
    补充一下,char *r 所指向的内存是在 rodata 段,这块区域的内存可以随意访问。
    导致访问出错的不是 r 指向的内存,而是访问 r 本身。

    FYR: https://en.wikipedia.org/wiki/Data_segment
    aheadlead
        31
    aheadlead  
       2021-02-26 21:08:22 +08:00
    hanssx
        32
    hanssx  
       2021-02-27 09:37:13 +08:00
    @aheadlead 请教个问题,我对 c 语言也不熟,我 World()函数返回的应该是"1234"的内存地址吧?"1234"是在堆上分配的吗?如果是的话,World()函数返回值应该还能索引到"1234"这块内存吧?
    aheadlead
        33
    aheadlead  
       2021-02-27 09:46:58 +08:00
    @hanssx #31 你这么一说还真是。。。看来好几年没写 C 还犯错了,糗大了

    你说的是正确的,昨天我发的那个代码还多此一举了,抱歉浪费各位时间。

    准确的说,楼主位的代码是不会造成我说的那种内存错误。楼主可以抓个 bugreport 看下 native 的 log 是什么。
    realpg
        34
    realpg  
       2021-02-27 11:11:31 +08:00
    提问良好,字多并不啰嗦,值得鼓励
    realpg
        35
    realpg  
       2021-02-27 11:15:00 +08:00
    @NasirQ #25
    真的 99.9 这种会遭人反感的

    但是 0.0001M 估计会有码农开心的愿意的
    cz5424
        36
    cz5424  
       2021-02-27 11:51:32 +08:00 via iPhone
    建议楼主看一下 C 语言的指针部分,函数内分配的内存不要再返回出去,容易内存泄露
    caiyue1993
        37
    caiyue1993  
       2021-02-27 14:30:06 +08:00
    阴阳怪气的人真多啊,“请尽量让自己的恢复能够对别人有帮助”
    taogen
        38
    taogen  
       2021-02-27 17:47:18 +08:00
    99.9 充满资本的铜臭味道
    phpIsNumberOne
        39
    phpIsNumberOne  
    OP
       2021-02-27 17:54:49 +08:00   ❤️ 2
    找到问题了,主要是我更新代码后使用得是热重载(Hot reload),和想象的不太一样,so 文件好像并没有更新,致使我写了正确的代码也没有正确的反馈;将 APP 卸载重新编译即可。

    至于 C 内存的问题,并没有考虑那么多,只想先跑通再说;而且我实际也不是用的 C,而是用的 go(CGO);使用 C 尝试就是因为没有给我正常的反馈,还以为是 CGO 的问题。

    再补充两点:
    动态库的名称应该以 lib 开头,否则项目以 release 运行时会找不到;
    go 函数中不能写 defer C.free(unsafe.Pointer(r))释放内存,应该 dart 取到值后再释放。
    phpIsNumberOne
        40
    phpIsNumberOne  
    OP
       2021-02-27 17:55:56 +08:00
    @hanxiV2EX
    @oxromantic
    @aheadlead
    感谢你们的热心解答
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2559 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 01:31 · PVG 09:31 · LAX 17:31 · JFK 20:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.