二进制资源包含 (自 C23 起)
#embed 是一个预处理指令,用于将(二进制)资源包含在构建中,其中资源定义为从翻译环境访问的数据源。
内容 |
[编辑] 语法
#embed < h-char-sequence > embed-parameter-sequence (可选) new-line |
(1) | ||||||||
#embed " q-char-sequence " embed-parameter-sequence (可选) new-line |
(2) | ||||||||
#embed pp-tokens new-line |
(3) | ||||||||
__has_embed ( " q-char-sequence " embed-parameter-sequence (可选) ) __has_embed ( < h-char-sequence > embed-parameter-sequence (可选) ) |
(4) | ||||||||
__has_embed ( string-literal pp-balanced-token-sequence (可选) ) __has_embed ( < h-pp-tokens > pp-balanced-token-sequence (可选) ) |
(5) | ||||||||
new-line | - | 换行符 |
h-char-sequence | - | 一个或多个 h-char 的序列,其中出现以下任何内容会导致未定义的行为
|
h-char | - | 源字符集 中的任何成员,除了换行符和 > |
q-char-sequence | - | 一个或多个 q-char 的序列,其中出现以下任何内容会导致未定义的行为
|
q-char | - | 源字符集 中的任何成员,除了换行符和 " |
pp-tokens | - | 一个或多个 预处理标记 的序列 |
string-literal | - | 一个 字符串文字 |
h-pp-tokens | - | 一个或多个 预处理标记 的序列,除了 > |
embed-parameter-sequence | - | 一个或多个 pp-parameter 的序列。请注意,与 attribute-list 不同,此序列不是逗号分隔的。 |
pp-parameter | - | 一个 attribute-token(参见:属性),但由预处理标记而不是标记组成。 |
pp-balanced-token-sequence | - | 一个 balanced-token-sequence(参见:属性),但由预处理标记而不是标记组成 |
[编辑] 解释
embed
后的预处理标记的处理方式与普通文本相同(即,当前定义为宏名称的每个标识符都将替换为其预处理标记的替换列表)。所有替换后产生的指令应匹配之前两种形式中的一种。将 < 和 > 预处理标记对或一对 " 字符之间的预处理标记序列组合成单个头文件名预处理标记的方法是实现定义的。__has__embed
表达式计算为 __STDC_EMBED_FOUND__;如果资源为空且所有参数都受支持,则计算为 __STDC_EMBED_EMPTY__;如果搜索失败或传递的参数之一不受实现支持,则计算为 __STDC_EMBED_NOT_FOUND__。如果未找到资源或参数之一不受实现支持,则程序格式错误。
__has_embed
可以在 #if 和 #elif 的表达式中扩展。它由 #ifdef、 #ifndef、 #elifdef、 #elifndef 和 defined 视为已定义的宏,但不能在其他任何地方使用。
资源具有一个实现资源宽度,它是位于资源的实现定义大小(以位计)。它的资源宽度是实现资源宽度,除非被 limit
参数修改。如果资源宽度为 0,则该资源被视为为空。嵌入元素宽度等于 CHAR_BIT,除非被实现定义的参数修改。资源宽度必须能被嵌入元素宽度整除。
#embed 指令的扩展是由下面描述的整数 常量表达式 列表形成的标记序列。列表中每个整数常量表达式的标记组在标记序列中与列表中前一个整数常量表达式的标记组之间用逗号隔开。该序列既不以逗号开头也不以逗号结尾。如果整数常量表达式的列表为空,则标记序列为空。该指令将被其扩展替换,并且,在存在某些嵌入参数的情况下,会添加或替换标记序列。
扩展序列中整数常量表达式的值由对资源数据的实现定义映射确定。每个整数常量表达式的值都在范围内 [
0,
2embed element width)
。如果
- 整数常量表达式列表用于初始化与 unsigned char 类型兼容的数组,或者如果 char 不能保存负值,则与 char 兼容的数组,并且
- 嵌入元素宽度等于 CHAR_BIT,
那么,数组中已初始化元素的内容就像资源的二进制数据在翻译时被 fread 到数组中一样。
鼓励实现考虑翻译时的位和字节顺序以及执行时的位和字节顺序,以便更恰当地表示指令中的资源二进制数据。这最大限度地增加了以下可能性:如果通过 #embed 指令在翻译时引用的资源与通过执行时方式访问的资源相同,那么例如 fread 或类似方式读入连续存储区的的数据将逐位与从 #embed 指令的扩展内容初始化的字符类型数组相等。
[编辑] 参数
标准定义了参数 limit
、prefix
、suffix
和 if_empty
。指令中出现的任何其他参数必须是实现定义的,否则程序格式不正确。实现定义的嵌入参数可能会改变指令的语义。
[编辑] limit
limit( 常量表达式 ) |
(1) | ||||||||
__limit__( 常量表达式 ) |
(2) | ||||||||
limit
嵌入参数最多可以在嵌入参数序列中出现一次。它必须有一个参数,该参数必须是一个整数(预处理器)常量表达式,该表达式计算结果为非负数,并且不包含标记 defined。资源宽度设置为整数常量表达式乘以嵌入元素宽度与实现资源宽度中的最小值。
[编辑] suffix
suffix( pp-balanced-token-sequence (可选) ) |
(1) | ||||||||
__suffix__( pp-balanced-token-sequence (可选) ) |
(2) | ||||||||
suffix
嵌入参数最多可以在嵌入参数序列中出现一次。它必须有一个(可能是空的)预处理器参数子句。如果资源非空,则参数子句的内容将放置在指令展开后的紧后面。否则,它没有效果。
[编辑] prefix
prefix( pp-balanced-token-sequence (可选) ) |
(1) | ||||||||
__prefix__( pp-balanced-token-sequence (可选) ) |
(2) | ||||||||
prefix
嵌入参数最多可以在嵌入参数序列中出现一次。它必须有一个(可能是空的)预处理器参数子句。如果资源非空,则参数子句的内容将放置在指令展开后的紧前面。否则,它没有效果。
[编辑] if_empty
if_empty( pp-balanced-token-sequence (可选) ) |
(1) | ||||||||
__if_empty__( pp-balanced-token-sequence (可选) ) |
(2) | ||||||||
if_empty
嵌入参数最多可以在嵌入参数序列中出现一次。它必须有一个(可能是空的)预处理器参数子句。如果资源为空,则参数子句的内容将替换指令。否则,它没有效果。
[编辑] 示例
#include <stdint.h> #include <stdio.h> const uint8_t image_data[] = { #embed "image.png" }; const char message[] = { #embed "message.txt" if_empty('M', 'i', 's', 's', 'i', 'n', 'g', '\n') ,'\0' // null terminator }; void dump(const uint8_t arr[], size_t size) { for (size_t i = 0; i != size; ++i) printf("%02X%c", arr[i], (i + 1) % 16 ? ' ' : '\n'); puts(""); } int main() { puts("image_data[]:"); dump(image_data, sizeof image_data); puts("message[]:"); dump((const uint8_t*)message, sizeof message); }
可能的输出
image_data[]: 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 00 00 00 01 00 00 00 01 01 03 00 00 00 25 DB 56 ... message[]: 4D 69 73 73 69 6E 67 0A 00
[编辑] 参考文献
- C23 标准 (ISO/IEC 9899:2024)
- 6.4.7 头文件名称 (p: 69)
- 6.10.1 条件包含 (p: 165-169)
- 6.10.2 二进制资源包含 (p: 170-177)