介绍
我的 Cortex-M4 设备上的 Flash 快用完了。我分析了代码,减少代码大小的最大机会就是在预定义的常量中。
- 例子
const Struct option364[] = {
{ "String1", 0x4523, "String2" },
{ "Str3", 0x1123, "S4" },
{ "String 5", 0xAAFC, "S6" }
};
问题
问题是我有(大量)(短)字符串要存储,但它们中的大多数用于表中 - const struct
s 数组具有指向const
与数字数据混合的字符串的指针。每个字符串的大小都是可变的,但是我仍然考虑更改struct
指针以保存一个简单的(最大)char
数组而不是指针 - 并没有太大区别。编译器希望在 4 字节边界上开始每个新字符串并没有帮助。这让我想到...
主意
如果我可以将 4 字节char
指针替换index
为字符串表中的 2 字节 - 一个预定义的链接器部分,它index
是一个偏移量 - 我会在那里为每条记录节省 2 个字节,但会以轻微的代码碰撞为代价。我也会避免内部填充,因为每个字符串都可以在前一个字符串的NUL
字节之后立即开始。如果我能聪明点,我可以为索引重用字符串——甚至是部分字符串。
但此外,我会将4 + 2 + 4 (+ 2)
对齐方式更改为2 + 2 + 2
- 节省更多空间!
- 考虑
当然,在源代码中,所有这些字符串以及字符串表本身的内务管理将是一场噩梦……除非我能让编译器帮忙?我想改变实际源代码的语法:如果我希望字符串出现在字符串表中,我会将其写为#"String"
,其中#
前缀会将其标记为字符串表候选者。一个普通的字符串不会有那个前缀,编译器会把它当作普通的。
执行
因此,要实现这一点,我必须编写一个预预编译器。只处理#""
字符串,用“神奇”的 16 位偏移量替换它们,然后将其他所有内容输出到真正的(预)编译器以进行实际编译。预编译器还必须编写一个C
包含完整字符串表的新文件(尽管有一个技巧 - 见下文),以便编译器解析并提供给链接器的专用部分。使用开关调用它会很容易-no-integrated-cpp
,调用我自己的预处理器,然后调用真正的预处理器。
- 问题
不要误会我的意思;我知道有问题。例如,它必须能够处理部分构建。我的解决方案是,对于每个修改过的C
文件,它会(如有必要)编写一个并行字符串表文件。“主”C
字符串表文件只不过是一系列s,如果其中一个s 已更改,或者实际上,如果添加了新#include
的,则构建将意识到需要重新编译。#include
#include
结果
结果将是一个可执行文件,它将所有(常量)字符串打包到一个不大于 64K 的内存块中(这不是问题!)。代码会知道这index
将是该 blob 的偏移量,因此会在正常使用它之前将索引添加到字符串表指针的开头。
问题
我的问题是:值得吗?
- 优点:
- 它将节省一吨空间。我没有在上面量化它,但假设节省了总 Flash 的 5%(!)。
- 缺点:
- 这将需要修改构建过程以包含定制的预处理器;
- 该预处理器必须作为工具链而不是项目的一部分来构建;
- 预处理器可能有错误或限制;
- 真正的源代码不会“开箱即用”编译。
现在...
我已经穿上了我的石棉套装,所以... GO!