如何更好地编写这些char数组?

时间:2018-06-19 13:51:58

标签: c arrays

现在我有一个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,
...

4 个答案:

答案 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'},
    // ...
};