V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
zjsxwc
V2EX  ›  程序员

C 语言为什么没有发展出类似依赖管理的框架?

  •  
  •   zjsxwc ·
    zjsxwc · 2018-01-17 09:26:38 +08:00 · 6790 次点击
    这是一个创建于 2537 天前的主题,其中的信息可能已经有所发展或是发生改变。

    就算浏览器上 es5 这种没有依赖注入容器,但也有 requirejs 这种管理依赖的东西,

    C 语言是怎么管理代码执行时依赖关系?

    这里我指的不是 makefile 这种只能算是文件层面的依赖管理。

    第 1 条附言  ·  2018-01-17 11:13:05 +08:00
    可能我没有说清楚,我指的是运行期间代码层面的依赖管理,不是包依赖管理。

    看了下别人的代码,好吧,我发现 C 语言是通过在.h 头文件里通过定义宏来处理代码层面依赖的,而且由于 C 语言里面的依赖只是函数与函数之间的依赖,C 语言的结构体也只是通过字面量方式初始化,于是也不存在面向对象语言那种对象实例初始化问题,所以根本没有通过代码初始化对象的需求,汗。。。所以简单的通过#include 解决依赖引入问题就够了,而且想在一个模块里不同函数都能访问到同一个数据只能每次调用函数时都传这个数据的指针。


    那么对于依赖需求,比如一个 cpu 监控模块的代码 cpu.c 依赖日志模块代码 log.c 与网络模块 net.c。
    开发者希望根据用户的配置文件初始化以及执行这个 cpu 监控代码(配置网络推送 ip 与端口、log 文件位置等),一般每次调用不同方法时都要传这同一个配置( conf )数据

    于是代码变成了在 cpu.h 里,这么引入( require )依赖以及暴露( export )自己的方法
    ```
    #ifndef NS_CPU_H
    #define NS_CPU_H

    #include "log.h"
    #include "net.h"

    bool cpuInit(struct conf_t* conf);
    bool cpuRun(struct conf_t* conf);

    #endif /* NS_CPU_H */
    ```

    总结就是 C 语言这种办法只能解决逻辑上函数之间的依赖,不能处理数据依赖,要处理数据依赖只能靠手动传结构体指针。
    34 条回复    2018-01-18 10:37:47 +08:00
    neoblackcap
        1
    neoblackcap  
       2018-01-17 09:29:16 +08:00
    动态库?不是系统默认的包管理器吗?
    crysislinux
        2
    crysislinux  
       2018-01-17 09:31:48 +08:00
    哈哈,对啊,包管理就是。C 这种比较麻烦的地方是有 ABI 兼容问题
    GuangXiN
        3
    GuangXiN  
       2018-01-17 09:35:13 +08:00 via Android   ❤️ 1
    谁说没有,RedHat 发展了 rpm,debian 发展了 apt,BSD 有 port
    Juggernaut
        4
    Juggernaut  
       2018-01-17 09:42:04 +08:00
    C 一直在做底层有差别硬件的无差别化服务支持,一门心思走到底了
    snnn
        5
    snnn  
       2018-01-17 09:56:39 +08:00
    有。
    去 google 的 github 里面找。
    wellsc
        6
    wellsc  
       2018-01-17 09:58:18 +08:00
    pacman 啊
    xd314697475
        7
    xd314697475  
       2018-01-17 10:24:30 +08:00   ❤️ 1
    如果你说的包管理的话,看看 Conan

    Conan, the C / C++ Package Manager for Developers

    https://conan.io/
    dychenyi
        8
    dychenyi  
       2018-01-17 10:27:18 +08:00
    还用管理?可能没看懂题目。一个 setenv LD_LIBRARY_PATH XXX 不是解决了所有问题吗? 而且这是操作系统管理的。
    mooncakejs
        9
    mooncakejs  
       2018-01-17 10:30:15 +08:00
    C 的跨平台是源码级跨平台,所以依赖就是源代码,动态库基本的依赖交给系统管理了。
    yum/apt install libxxx 安装动态库
    yum/apt install xxx-dev[el] 安装开发包
    rogerchen
        10
    rogerchen  
       2018-01-17 10:32:13 +08:00
    朋友,所有发行版的包管理器都是为了管理 C 系语言的依赖开发出来的,管理器多得一匹好吗。。。
    sinxccc
        11
    sinxccc  
       2018-01-17 10:41:27 +08:00
    UNIX 就是 C 的 runtime 啊ˊ_>ˋ
    zj299792458
        12
    zj299792458  
       2018-01-17 10:43:52 +08:00 via iPhone
    Apt-get ? brew ? macport ?
    feather12315
        13
    feather12315  
       2018-01-17 10:47:26 +08:00 via Android
    autoconf 算不算
    mengyaoss77
        14
    mengyaoss77  
       2018-01-17 11:12:20 +08:00
    -L -l 算是依赖吧
    包管理就用系统自带的啊 那么多!
    feverzsj
        15
    feverzsj  
       2018-01-17 11:18:20 +08:00
    因为 c/c++是应用平台最广的原生语言,无法在标准层面做出限制,即使 c++即将加入的 module,也不是和编译器绑定的
    GeruzoniAnsasu
        16
    GeruzoniAnsasu  
       2018-01-17 11:52:07 +08:00
    C 的代码是不可运行时改变的
    编译生成可执行文件时就需要把所有的依赖包含进来
    说白了 include 就是复制粘贴把所有用到的库的代码都粘贴进一个文件然后再开始编译

    编译完了再生成可执行文件

    作为静态语言的 C 压根就没有运行时代码注入和扩展的可能,自然不会有什么代码中间来一行 import,更不会有 try import raise,也更不可能在运行时来引入新的全局符号,静态语言的变量和代码符号都只是地址而已,当我们需要动态生成 /访问一个变量我们有指针,这是 C 的做法,跟有虚拟机的语言是很不一样的。

    这跟 O 不 OO 半点关系都没有,你用 C++也不会存在你概念中那种所谓依赖管理框架,对于静态语言来说,依赖管理即库文件管理,即包管理
    veelog
        17
    veelog  
       2018-01-17 11:56:11 +08:00 via iPhone
    pkg-config 算吗
    hitmanx
        18
    hitmanx  
       2018-01-17 12:00:28 +08:00
    @GeruzoniAnsasu

    > "编译生成可执行文件时就需要把所有的依赖包含进来 "
    动态库链接可以是 stub 的呀

    > "C 的代码是不可运行时改变的"
    如果是来自动态链接的库可以啊,一是可以热更新,二是可以用 LD_PRELOAD(intercept)之类的手段在运行时先于真正的符号库加载
    gnaggnoyil
        19
    gnaggnoyil  
       2018-01-17 12:00:55 +08:00
    然而讽刺的是,Makefile 等东西恰恰在 C 的所谓"依赖管理"中起到了主要作用——函数位置的确定都是靠 linker 来完成的,而 linker 的输入嘛……一般都是实现指定好的 object file.
    yksoft1
        20
    yksoft1  
       2018-01-17 12:03:41 +08:00
    毕竟 C 标准下要考虑到十几 K 内存的单片机的需求。
    northisland
        21
    northisland  
       2018-01-17 12:08:33 +08:00 via iPhone
    历史包袱。

    当年内存还是几十 k,硬盘百十 M 的时代。
    你生成个 20M 的 LAPACK 库比取经都难。
    所以所有执行文件,都链接这个宝贝库。

    不像现在,很多语言能随便打包模块。


    解决方法很多人都说了。
    northisland
        22
    northisland  
       2018-01-17 12:13:48 +08:00 via iPhone
    这点黑 C 不过分。

    C 系列里库的管理,很屎,比较吃经验。也叫技术门槛。
    但因为贴近底层,真心离不开这些"专家"
    所以对公司里某套东西现状比较熟的人比较不容易丢饭碗
    htfy96
        23
    htfy96  
       2018-01-17 12:22:31 +08:00
    conan + CMake
    northisland
        24
    northisland  
       2018-01-17 12:31:04 +08:00 via iPhone
    仔细看了一下你的描述,你需要了解

    1. 目标文件的代码段,数据段,只读数据段是干嘛的。

    2. 二进制文件是怎么动态装载,成为运行的程序。

    懂了以后你就会发现,你下的 C 要处理数据依赖只能靠手动传结构体指针,完全是胡扯。你学过 extern 关键字么?
    zjsxwc
        25
    zjsxwc  
    OP
       2018-01-17 12:35:06 +08:00
    @northisland #24

    我刚才看到的几个开源项目,基本上都是通过函数传指针来传数据的,很少用别的方法进行数据共享
    zhicheng
        26
    zhicheng  
       2018-01-17 12:42:13 +08:00
    看了问题和补充,楼主你是硬把编译型语言往解释型语言的概念上套。有些概念是互相通用的,有些则不是,解释型语言可以在 Runtime 检查依赖,但编译型不行,它必须在 Run 之前解决依赖 (Linker & Loader )。所以你说的那种东西目前在语言层是不存在的。

    动态库是可以减少内存使用,但它还有另一个主要作用,如果一个库有更新(比如 libc ),只需要更新这个库就可以了,不需要把所有依赖这个库的程序全部更新一遍。所以很多 OS 现在已经不提供静态库了。
    zjsxwc
        27
    zjsxwc  
    OP
       2018-01-17 12:54:40 +08:00
    @zhicheng #26

    只是 C 这种传统的编译型语言做不到吧,“新”出来的编译型语言,基本都有类似依赖注入的东西
    zhicheng
        28
    zhicheng  
       2018-01-17 13:10:47 +08:00
    @zjsxwc 这种方法就是把所有的代码全编译到一个 binary 里完全不使用动态库,可以看一下我回复里关于动态库的解释。这在 C 语言里是不可能的,因为现在很多新的 OS 里没有静态库,只提供动态库。
    GeruzoniAnsasu
        29
    GeruzoniAnsasu  
       2018-01-17 13:49:40 +08:00
    @hitmanx 动态链接库只是动态链接而已,但链接时的函数签名 ABI 都是固定不可改的,在编译的那一刻起就已经从附带的.h 里编译解析成导入符号写进了 binary 的符号表,说的不可改是指源码意义上无法存在 exec(void* code)这种东西,导入 dll 也好,热更新 dll 也好,源码编译后固定下来的仍然是那些

    没想较真所以也没怎么严谨,较真说的话汇编时代就有 smc 自修改用来保护软件,但那对于提升设计毫无帮助



    另外怎么说,依赖管理即库 /包管理是不会错的
    waruqi
        30
    waruqi  
       2018-01-17 14:24:53 +08:00 via Android
    用 xmake
    kimown
        31
    kimown  
       2018-01-17 18:51:44 +08:00 via Android
    所以说 c 一直没进步,一点没错,吃老本都能吃十几年,哪个语言赶得上
    akann
        32
    akann  
       2018-01-17 23:03:53 +08:00
    微软的 visual studio 有 nuget.
    akann
        33
    akann  
       2018-01-17 23:08:16 +08:00
    google 有 gclient , gyp 一大堆
    linux40
        34
    linux40  
       2018-01-18 10:37:47 +08:00 via Android
    你说的这个不就是加载吗?操作系统帮你把加载的事情做了,远程的加载的话有分布式系统支持远程传输共享库啊,你去补一下操作系统的知识吧。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2673 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 12:02 · PVG 20:02 · LAX 04:02 · JFK 07:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.