乐于分享
好东西不私藏

移植一个 20 多年前的老软件,竟然栽在了 enum 上

移植一个 20 多年前的老软件,竟然栽在了 enum 上

最近我在做一个老软件的移植。

这个软件有多老?应该有二十几年了。它使用 Borland C++ Builder 6 开发,编译器是 BCC32,生成的是 Win32 可执行文件。

在做了大量代码修改之后,这个软件终于能够在 Linux 上运行起来了。🎉

不过,新的问题也随之出现了:新软件生成的数据文件,在老软件上却无法打开。

经过一番排查,最终发现,问题出在一个很不起眼的地方——枚举类型的大小。

原来,BCC32 和 GCC 在处理 enum 类型时,行为并不相同。

在常见的 GCC 默认配置下,enum 通常使用 4 字节的整数类型来存储。而 BCC32 则会根据枚举值的范围,选择能够容纳这些值的较小整数类型。

比如,一个枚举类型的所有值都能够用 1 字节整数表示,BCC32 就可能使用 1 字节;如果需要 2 字节才能表示,则使用 2 字节,以此类推。

这就产生了一个很隐蔽的问题。

如果程序直接将包含枚举类型的数据结构写入文件,那么枚举类型占用的字节数,就会直接成为数据文件格式的一部分

于是,GCC 编译的新程序写入文件时,一个枚举值可能占 4 个字节;而 BCC32 编译的老程序读取这个文件时,却按照 1 字节或 2 字节来读取。

这样一来,后面的数据自然就全部“错位”了。😅

找到原因之后,解决办法反而很简单:给 GCC 加上 -fshort-enums 参数。

这个参数会让 GCC 为 enum 选择能够容纳所有枚举值的最小整数类型,从而让它的行为更接近 BCC32。

加上这个参数重新编译之后,新程序生成的数据文件终于可以被老程序正常读取了。

老软件移植时,真正麻烦的往往不是代码本身,而是那些隐藏在编译器和 ABI 里的细节。

一个看起来普通的 enum,只要大小发生变化,就可能让两个程序之间的二进制数据格式彻底对不上。🙂

(完)