即将发布的C23标准中有一个新的预处理器指令:#embed
下面是一个简单的例子:
// Placing a small image resource.
#include <stddef.h>
void show_icon(const unsigned char *, size_t);
int main (int, char*[]) {
static const unsigned char icon_data[] = {
#embed "black_sheep.ico"
};
show_icon(icon_data, sizeof(icon_data));
return 0;
}
下面是一个更详细的例子,从二进制数据初始化非数组(不管这意味着什么):
int main() {
/* Braces may be kept or elided as per normal initialization rules */
int i = {
#embed "i.dat"
}; /* i value is [0, 2^(embed element width)) first entry */
int i2 =
#embed "i.dat"
; /* valid if i.dat produces 1 value, i2 value is [0, 2^(embed element width)) */
struct s {
double a, b, c;
struct { double e, f, g; };
double h, i, j;
};
struct s x = {
/* initializes each element in order according to
initialization rules with comma-separated list
of integer constant expressions inside of braces
*/
#embed "s.dat"
};
return 0;
}
在C语言中添加这个的目的是什么?
#embed
允许在程序可执行映像中轻松包含二进制数据,作为unsigned char
或其他类型的数组,而不需要从Makefile运行外部脚本。大多数编译器在解析此类数组时效率非常低,但有一个例外:tcc.
嵌入二进制甚至文本数据提供了从文件读取的好处:
- 可能没有文件系统
- 文件的路径可能不明显
- 文件可能丢失或无法访问
将此特性添加到C语言的主要原因似乎是将所有流行的c++特性都倾倒在C语言上,徒劳地试图将C收敛为两种语言的公共子集。c++委员会强烈支持这个扩展,而C委员会却不那么激动。
阅读详细信息:https://thephd.dev/_vendor/future_cxx/papers/C%20-%20embed.html
花了30年的时间才使strdup()
进入标准库,突然间,C23很高兴地在各个方向上扩展了50%的语言,没有任何悔意。
将其作为预处理器组件的基本原理非常值得怀疑,最后一个原因不言自明:
最后,Microsoft有一个关于最大字符串字面值大小的ABI问题,这个问题不能使用字符串字面值或任何被视为字符串字面值的东西来解决
#embed
的规范充满了怪癖和缺点。不愿意编写合适的脚本会导致如下令人讨厌的情况:
const unsigned char null_terminated_file_data[] = {
#embed "might_be_empty.txt"
prefix(0xEF, 0xBB, 0xBF, ) /* UTF-8 BOM */
suffix(,)
0 // always null-terminated
};
或者更糟:
int main () {
#define SOME_CONSTANT 0
return
#embed </dev/urandom> if_empty(0) limit(SOME_CONSTANT)
;
}
一个简单的数据描述和操作语言,将二进制文件组装成可链接的对象和资源,将会减少干扰,并且容易包含在所有语言的现有构建系统中,更重要的是所有现有的编译器。
论文列举了#embed
可能派上用场的有趣例子,但更通用的解决方案似乎是可能的。