为什么 C/C++ 语言的标准库不做成 Java 那样可安装的运行时?

8 天前
 w568w

如题。现在的平台/编程语言运行时,按兼容性大致可以分成几类:

  1. 几乎不变动,程序可以动态链接/调用:POSIX API ,Win32 API ;
  2. 经常变动,但可以自由安装:Python ,Java ,NodeJS ,C# .NET……;
  3. 经常变动,但直接嵌入到编译后的程序:Go ,Rust……。

唯独 C 和 C++,在 Linux 下不仅经常变动,并且不向前兼容(即旧版 libc(++) 不能运行新版 libc(++) 的程序)、不互相兼容( musl-libc 的程序不能用 glibc 运行),我在 Arch 上编译的程序几乎没法拿到 Ubuntu LTS 上运行,只能开个 Docker 容器来编译。

这就导致两个奇怪的现象:

  1. 在面向旧发行版开发 C/C++ 的时候,很多人宁愿自己搭古老开发环境来编译,也不愿/不能升级目标环境。而在 Python 、Java 之类的语言里,第一反应是直接给目标环境装个新版 Python/JRE 。
  2. 很多 C/C++ 软件不得不发布一堆变体来保证兼容性(此外,C++ 还有 CXX11ABI 的兼容性问题),或者干脆让用户自己编译(然而用户并不是何时都有空、有能力搭环境,尤其是一些编译依赖非常复杂的软件)。

但似乎很少有人会专门去下载标准库实现。除了 Conda ,好像也没人关心怎么打包标准库。

为什么 C/C++ 不像 JRE 那样,发布一个 C/C++ Runtime 呢,这样分发软件不是方便得多吗?

3233 次点击
所在节点    C++
31 条回复
InkStone
8 天前
C/C++也可以像 Go 和 Rust 一样静态链接 Runtime 的啊,只是习惯上不那么做而已。
w568w
8 天前
@InkStone 是的,但对于静态编译,glibc 是 strongly discouraged ,musl libc 也会有各种问题(例如不兼容 glibc 的一些代码、部分实现性能低),其他 libc 就更不用说了。

至于 libc++/libstdc++,我似乎都没看到有人静态编译过……
hwdq0012
8 天前
linus 好像有要推一款 linux ,全静态链接的,就是为了解决 linux abi 兼容不好的问题
yanqiyu
8 天前
静态链接 libstdc++/libc++我觉得很正常啊,只是这时候要保证引用到/会 dlopen 的的所有动态链接库别再拉一个 libstdc++/libc++进来就行。

并且向后兼容相对容易,给符号标记上版本然后保证老版本符号永远不会被扔掉就行,但是向前兼容就基本上意味着不能加任何新的符号(因为老版本没有),除非这个语言/运行时永远不演化,不然向前兼容不现实

(其实你可以 LD_LIBRARY_PATH 塞一个自己编译的新版本的库来让程序能跑起来的,科学计算集群全员 CentOS7 的时候我经常这么干)

考虑到和 rust 的区别,rust 你编译什么东西默认都是静态链接,但是你要是真的想要用 rust 做整个操作系统的 userspace ,大范围用上了动态链接(节约磁盘之类的理由),到时候 C++遇到的 ABI 的鸡毛蒜皮一个也跑不掉。

或者说你要是能接受编译 C/C++项目的时候像编译 rust/go 项目一样把整个项目的所有依赖全部编译一遍然后静态链接,那 ABI 也不是问题。大家不习惯的原因大概是 C++生态出现在集中的包管理之前,没有包管理、和系统里面依赖 C++的东西太多了,以至于用系统的库已经成了最经济的选择了吧。
yanqiyu
8 天前
@hwdq0012 这个取舍,就是哪天 libc 出现 CVE 你就会惊讶地发现所有软件都弹出来了安全更新。😂
greygoo
8 天前
生态问题,C++不缺少一个新 C/C++ Runtime,但是别人会不会用又是另一个问题,本身大家都是各玩各的.其实 docker 也是挺好的一个解决方案.
cwcc
8 天前
古老的代价,有很多库在现在的操作系统中仍然依赖,但它们已经没有任何官方维护了,取而代之的是一堆各种第三方、高校、企业给老库打的 patch 。久而久之形成了一个非常难受的依赖。

另外对底层兼容也要花费大量心思,更何况静态编译。Linux 上自己都不统一:glibc 对静态链接不友好、有发行版不依赖 glibc 、glibc 版本依赖问题等等。

> 这我就要夹带我的私货了:全静态构建的 PHP 运行时( https://static-php.dev )中 linux 的部分我就是花了很大的精力去 patch 各种依赖库从而实现构建独立的二进制,甚至专门编译了两套专门的工具链。还有一套另一位贡献者在研究接入 zig 。
pursuer
8 天前
我觉得这不能完全算是 C/C++的锅,Windows 的 COM 组件也是 C++的,但 Windows 的兼容是公认做的比较好的。再看 Linux 阵营,虽然很多库是用 C 写的,但升起来也费劲,glibc 更是重量级。
w568w
8 天前
@yanqiyu

> 想要用 rust 做整个操作系统的 userspace ,大范围用上了动态链接(节约磁盘之类的理由),到时候 C++遇到的 ABI 的鸡毛蒜皮一个也跑不掉

我觉得比较好的形式可能是 Windows 的 Visual C++ Distributable 那样,把标准库按年代打包成运行时,然后让用户安装。这样既可以保证不需要向前兼容,又能兼容较新的程序,还能节省硬盘空间。
yanqiyu
8 天前
@w568w #9 还是会绕不开统一分发的问题,比如 A 程序依赖 B/C 两个库,但是 B 是编译链接到 2024 版本的 stdlib 但是 C 是链接到 2025 版本的 stdlib ,这么下来依旧会出问题

结果就是整套动态库都得有多个版本。
xtreme1
8 天前
因为一旦错失了时间窗口, 再想统一已经不现实了
unused
8 天前
没见过 VC++ runtime redist ? C/C++ 生态是碎片化的,不像另外几个话语权完全在一两个组织手里。标准库用什么形式分发都可以,只是大家习惯了现在主流的方式。
unused
8 天前
另外你说的另外几种语言,运行时基本也都依赖 libc 的,有人帮它们负重前行。
w568w
8 天前
@pursuer @unused #12 我也越来越觉得这个更是 Linux 的问题了。就像包管理和 init 的选择,永恒的碎片化……

@unused #13 是的,但作为最终用户和开发者其实都不用考虑这些,相当于有人做兼容了。而且只要有一个人做好兼容,其他用户都能获益。
fcten
8 天前
打包运行时只是方便了开发者。对于用户来说可是深恶痛绝。谁也不想自己电脑里同时跑十几个不同版本的运行时把?
w568w
8 天前
@fcten 比现状好吧,现状是「只允许同时用一个运行时,兼容性问题用户自己想办法」。在「让用户装多个运行时」和「让用户自己学习编译知识从头编译,或者为了你的软件升级整个系统」,我觉得还是前者方便点。
bli22ard
8 天前
C/C++ 静态链接 musl-libc 和其他依赖, 不就达到 0 依赖了? java 不会向前兼容, 老版本的 jre ,不能运行新版本 jdk 编译的 class
w568w
8 天前
@bli22ard #17 理论上是这样,但实际上有很多依赖根本无法静态链接(作者写 Makefile 就没考虑到,比如 GNUStep ),或者依赖极其繁杂(例如 libcurl ),或者使用了 musl-libc 不支持的扩展(例如 onnx )。感觉一切的坏头都是 glibc 起的……
bli22ard
8 天前
@w568w #18 所以 linux 上的软件分发要依靠包管理器, 你要发行到哪个发行版的具体版本都是一定的。不然动态链接的软件,基本等于不能用。这依赖确实挺烦的。我搞 rust , 就遇到过动态链接,要访问 https api ,依赖 openssl 搞的编译焦头烂额
julyclyde
8 天前
libstdc++不就是可安装的吗?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://ex.noerr.eu.org/t/1141180

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX