【问题标题】:Concatenate compile time constant strings with characters whose character code comes from a #define将编译时常量字符串与字符代码来自 #define 的字符连接起来
【发布时间】:2022-01-03 07:32:19
【问题描述】:

我想组装一个 char 数组,其中包含一个带有编译时间常数值的字节和一个字符串,该字符串也是编译时间常数。解决方案是:

char packet[] = "\42" __DATE__;

这可行,但可读性和可维护性不强,因为 0x42 是在其他地方使用的消息操作码,这使它成为一个神奇的数字。现在,我可以将一个虚拟 x 放入字符串中,然后按照这个定义进行如下赋值:

#define OPCODE 0x42
char packet[] = "x" __DATE__;
packet[0] = OPCODE;

但我觉得可以用纯常量字符串文字来完成,我只是找不到如何去做。有什么想法吗?

【问题讨论】:

  • 请注意,#define OPCODE 42十进制 42(八进制 52,十六进制 2a,ascii "),而字符串转义 "\42"八进制 42(十进制34,十六进制22,ascii *),所以问题本身是不一致的。
  • 好的,谢谢,我将42 更改为0x42。这应该使它保持一致。

标签: c string c99 avr-gcc compile-time-constant


【解决方案1】:

__DATE__ 应该大部分都具有 Mmm dd yyyy 格式,所以 11 个字符。你可以这样做:

char packet[] = {
  OPCODE,
  __DATE__[0],
  __DATE__[1],
  __DATE__[2],
  __DATE__[3],
  __DATE__[4],
  __DATE__[5],
  __DATE__[6],
  __DATE__[7],
  __DATE__[8],
  __DATE__[9],
  __DATE__[10],
  __DATE__[11],
  0,
};
   

【讨论】:

  • 考虑在__DATE__的大小上添加一个静态断言
  • 虽然 ISO C 要求预定义宏 __DATE__ 使用 11 个字符加 NUL 格式“mmm dd yyyy”,但使用 _Static_assert(sizeof(__DATE__)==12, "unexpected size of __DATE__"); 仍然显得谨慎。
  • 看起来初始化列表中的最后一个字符常量是0'\0' 是不必要的。 clang 和 gcc 都会自动生成一个尾随 NUL 字符。不过,我找不到指定的位置或是否指定。
  • 请注意,只有在 OPCODE value 0 未在任何地方使用时,对任何数据包的运行时 strlen() 调用才会按预期工作。但是,静态编译时间 sizeof 将起作用。
【解决方案2】:

由于 C 预处理器无法提供帮助,因此可能需要在构建系统中添加额外的预处理器。

您是否选择第二阶段的 C 预处理器、m4、shell 或 python 脚本或其他任何由您决定。

或者你走另一条路,将数据包类型从 char 数组更改为具有灵活数组成员的 struct,如下所示:

/* foo.c
 * Compile with something like
 *    avr-gcc -mmcu=atmega328 -Os -Wall -Wextra -Werror \
 *        -save-temps=obj -Wa,-adhlns=foo.lst,-gstabs -c foo.c
 */

#include <stddef.h>
#include <stdint.h>

#include <avr/pgmspace.h>

#define OPCODE_DATE 0x42

struct packet_descr {
  uint8_t  opcode;
  uint16_t size;
};

struct string_packet {
  struct packet_descr descr;
  char                string[];
};

#define STRING_PACKET_P(IDENTIFIER, OPCODE, STRING)   \
  const struct string_packet IDENTIFIER PROGMEM = {   \
    { (OPCODE),                                       \
      sizeof(STRING)-1                                \
    },                                                \
    (STRING)                                          \
  }

STRING_PACKET_P(packet_date_P, OPCODE_DATE, __DATE__);

void uart_send_char(const char ch);
void uart_send_char(const char ch)
{
  UDR0 = ch;
}

/* Send string packet in the following format:
 *      uint8_t  opcode;
 *      uint16_t len;         // in AVR endianness
 *      char     string[len]; // string of "len" characters, unterminated
 */
extern
void string_packet_send_P(const struct string_packet *string_packet_P);
void string_packet_send_P(const struct string_packet *string_packet_P)
{
  const uint8_t opcode = pgm_read_byte(&string_packet_P->descr.opcode);
  uart_send_char(opcode);

  size_t        len    = pgm_read_word(&string_packet_P->descr.size);
  for (PGM_P byte_P = (PGM_P)&string_packet_P->string; len > 0; len--, byte_P++) {
    uart_send_char(pgm_read_byte(byte_P));
  }
}

int main(void)
{
  string_packet_send_P(&packet_date_P);
  return 0;
}

具有灵活数组成员的struct 的一个明显缺点是sizeof(packet) 只会产生非数组部分的大小。但是,根据您的实际数据包格式(数据包的接收方也需要知道数据包何时开始和结束,对吧?),单独记录大小可能是可行的。

【讨论】:

    猜你喜欢
    • 2017-04-17
    • 2020-06-20
    • 2020-06-21
    • 1970-01-01
    • 1970-01-01
    • 2021-04-12
    • 1970-01-01
    • 1970-01-01
    • 2011-05-13
    相关资源
    最近更新 更多