现在我有一个char数组,用于存储字体数据:
const char * const FONT[] = {
"\x48" "a44448", //0
"\x27" "m\x48" "m\x40", //1
"\x06" "a46425" "m\x00" "m\x80", //2
"\x06" "a46425" "a42425", //3
"\x83" "m\x03" "m\x68" "m\x60", //4
"\x88" "m\x08" "m\x04" "m\x44" "a42424" "m\x00", //5
"\x02" "a42428" "a84842", //6
"\x08" "m\x88" "m\x20", //7
"\x44" "A46428" "a42428", //8
"\x86" "a46428" "m\x60", //9
...
是否有一种方法可以更容易阅读,但是仍然在编译时进行了计算? 例如,类似:
#define start(x,y) //somehow create '\x<x><y>'. start(3,4) -> '\x34'
#define arc(x,y,rx,ry,a) //evaluate to {'a','<x>','<y>','<rx>','<ry>','<a>'}. arc(1,2,3,4,5) -> {'a','1','2','3','4','5'}
const char * const FONT[] = {
start(4,8) arc(4,4, 4,4, 8) "", //somehow concatenate them
...
此外,为什么我可以使用字符串文字而不使用char数组文字: (这不起作用)
const char * const FONT[] = {
{'\x48','a','4','4','4','4','8','\0'}, //0
但这可行:
const char X[] = {'\x48','a','4','4','4','4','8','\0'};
const char * const FONT[] = {
X,
...
答案 0 :(得分:2)
这组宏应该执行您想要的操作:
#define str(s) #s
#define start(px,py) str(\x##px##py)
#define arc(x,y,rx,ry,pa) str(a##x##y##rx##ry##pa)
const char * const FONT[] = {
start(4,8) arc(4,4, 4,4, 8),
}
这利用了#
and ##
operators(又名字符串化级联运算符)。
并产生以下预编译器输出:
const char * const FONT[] = {
"\x48" "a44448",
}
答案 1 :(得分:1)
一种更好的方法是将所有内容都以十六进制表示,即使对于可打印字符也是如此。但是考虑到不同的长度,那仍然是一团糟。
在运行时从文件加载字体或将其作为二进制blob链接到文件中怎么样?确实没有一种使二进制数据在源代码中看起来不错的好方法。
答案 2 :(得分:1)
您几乎有原因。您声明指针数组,因为您有不同大小的行。因此,在const char * const FONT[] = {...
中,FONT
是指向const
数组的const char
指针的数组。一个乱七八糟的字符串是一个const char数组,因此它将衰减为一个指针,该指针将用于FONT
的初始化。如果您首先声明一个char数组并使用其名称,那么事情也会顺利进行,因为在这里数组再次衰减为指针。
但是在C {'\x48','a','4','4','4','4','8','\0'}
中,它本身不是数组,而只是一个初始化列表,只能用于初始化字符数组。例如:
char arr_ok[] = { '1', '2', '3', '\0' }; // correct initialization of a char[4]
char *ptr_ko = { '1', '2', '3', '\0' }; // wrong initialization of a char* (should not compile)
这意味着初始化列表不是数组,并且不能衰减到指针。
对于2D数组,情况将有所不同
char arr2D[][9] = { { '1', '2', '3' }, { '4', '5', '6' }, { '7', '8', '9'} };
此行用resp初始化3个子数组。 '1','2','3''4','5','6和'7','8','9'。但是它不能用于指针数组
答案 3 :(得分:1)
有没有一种方法可以用更可读的方式编写,但是仍然可以使用 在编译时计算?例如,类似:
#define start(x,y) //somehow create '\x<x><y>'. start(3,4) -> '\x34' #define arc(x,y,rx,ry,a) //evaluate to {'a','<x>','<y>','<rx>','<ry>','<a>'}. arc(1,2,3,4,5) -> {'a','1','2','3','4','5'} const char * const FONT[] = { start(4,8) arc(4,4, 4,4, 8) "", //somehow concatenate them ...
您可以使用预处理器的字符串化(start()
)和令牌粘贴(#
)运算符来实现##
宏。但是,您需要对它们稍加注意,以说明以下事实:它们的参数没有首先进行宏扩展。在需要宏扩展的地方,可以通过插入额外的宏层来实现。例如:
// Stringify the argument (without expansion)
#define STRINGIFY(x) #x
// Expand the argument and stringify the result
#define STRINGIFY_EXPANSION(x) STRINGIFY(x)
// Assumes that the arguments should not themselves be expanded
#define MAKE_HEX(x, y) \ ## x ## y
#define start(x,y) STRINGIFY_EXPANSION(MAKE_HEX(x,y))
类似地,您可以将arc()
宏实现为
// No macro expansion wanted here, neither at this level nor before stringifying
#define arc(x,y,rx,ry,a) STRINGIFY(x ## y ## rx ## ry ## a)
(从技术上讲,它创建一个字符串文字标记,表示一个空终止符,而不是您描述的未终止的char
数组,但这仍然是您真正想要的。)
此外,为什么我可以使用字符串文字而不使用char数组文字:(这 不起作用)
const char * const FONT[] = { {'\x48','a','4','4','4','4','8','\0'}, //0
但这可行:
const char X[] = {'\x48','a','4','4','4','4','8','\0'}; const char * const FONT[] = { X, ...
很大程度上是因为这些不是不是数组文字。它们是简单的初始化器。初始化程序提供用于初始化要声明的对象的一系列值;当该对象是复合对象(例如数组或结构)时,其初始化程序中提供的多个值将为其某些或所有成员提供初始值。数组FONT
的成员的类型为char *
,这些指针是初始化器为其提供值的指针。而且,它们没有更深的结构,因此不需要嵌套的括号。
数组文字可能看起来像这样:
(const char[]) {'\x48','a','4','4','4','4','8','\0'}
而且,由于数组也衰减到初始化器中的指针,就像它们在其他任何地方所做的一样,您确实可以使用数组文字来初始化指针数组:
const char * const FONT[] = {
(const char[]) {'\x48','a','4','4','4','4','8','\0'},
// ...
};