【问题标题】:How can I write these char arrays in a nicer way?如何以更好的方式编写这些 char 数组?
【发布时间】:2018-06-19 13:51:57
【问题描述】:

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

【问题讨论】:

  • (这不起作用)是因为你想要char [][]而不是char *[]
  • @EugeneSh。我试过了,但它说我必须给第二个维度的大小。但是数组的长度都不同,所以我不能这样做。
  • 你可以给它最大可能。
  • 是的,"" 有点小技巧,因为编译器会为你分配内存,然后你定义一个指向字符串的指针数组。你不能用 {1,2,3} 符号来做到这一点。您必须分别定义每个字符,然后像使用 X[] 一样构建一个数组指针。
  • 请注意,'\x48'0x48 相同。这可以为您省去一些麻烦。

标签: c arrays


【解决方案1】:

这组宏应该做你想做的:

#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",
}

【讨论】:

    【解决方案2】:

    有没有办法以更易读的方式编写它,但仍然有它 在编译时计算?例如,类似:

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

    【讨论】:

      【解决方案3】:

      一个更好的方法是用十六进制表示,即使是可打印的字符。但考虑到不同的长度,这仍然是一团糟。

      如何在运行时从文件中加载字体或将其作为二进制 blob 从文件中链接?确实没有一种好的方法可以让二进制数据在源代码中看起来不错。

      【讨论】:

        【解决方案4】:

        你几乎找到了原因。您声明了一个指针数组,因为您有不同大小的行。所以在const char * const FONT[] = {... 中,FONT 是一个由const 组成的数组,指向const chars 的数组。 litteral 字符串是一个 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)
        

        也就是说初始化列表不是数组,不能衰减为指针。


        二维数组的情况会有所不同:

        char arr2D[][9] = { { '1', '2', '3' }, { '4', '5', '6' }, { '7', '8', '9'} };
        

        这一行分别初始化了 3 个子数组。 '1','2','3' '4','5','6 和 '7','8','9'。但它不能用于指针数组

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-05-31
          • 1970-01-01
          相关资源
          最近更新 更多