C语言 如何在编译时进行短字符串优化?

4smxwvx5  于 2023-10-15  发布在  其他
关注(0)|答案(2)|浏览(88)

我有一个基本的字符串结构:

typedef struct String {
    size_t size;
    union {
        char *ptr, buf[sizeof(char *)];
    };
} String;

我们的想法是能够从String中提取char *,如下所示:

extern String string;
char *str = string.size >= sizeof(char *) ? string.ptr : string.buf;

我有个问题如果我有一个conststatic/global String结构体,必须在编译时初始化,该怎么办?如果我不想硬编码char *是给定大小的假设,那会怎么样?

static const String string = {
    sizeof(CONSTANT_STRING)-1,
#if sizeof(CONSTANT_STRING) > sizeof(char *)
    .name
#else
    .buf
#endif
        = CONSTANT_STRING

但是我们知道sizeof() happens after preprocess time so cannot be used in an #if
如何在编译时有条件地将字符串的一个成员或另一个成员赋给给定的值,以便得到的表达式是编译时常量?

tkclm6bt

tkclm6bt1#

是的,这是可以做到的,但我不得不重新考虑String结构。我可以有一个命名的union成员,并使用?:操作符有条件地将其初始化为不同的复合文字,而不是使用一个未命名的嵌套联合:

typedef union Data {
    char *ptr, buf[sizeof(char *)];
} Data;
typedef struct String {
    size_t size;
    Data data;
} String;
#define CONSTANT_STRING "Hello"
static const String string = {
    .size = sizeof(CONSTANT_STRING)-1,
    .data = sizeof(CONSTANT_STRING) > sizeof(char *)
        ? (Data){.ptr = CONSTANT_STRING}
        : (Data){.buf = CONSTANT_STRING}
};

然而,为了使复合字面值被认为是编译时常量,要么它必须在全局范围内,要么我必须使用C23并将它们指定为constexpr和/或将它们的存储类指定为static

pnwntuvh

pnwntuvh2#

我想补充的是,struct中的空间比实际使用的空间要大,因为1-8之间的数值不需要size_t类型。
下面这个不完整的例子在64位系统上使用时,会为你的小字符串优化增加7个字节的空间:

typedef struct my_string_s {
   unsigned char len[sizeof(size_t)];
   char * ptr;
} my_string_s;

这里有一个关于“野生”的例子。
facil.io C STL String模块有更好的优化,但这是来自CLI模块,它显示了这种方法的工作。它是为64位系统设计的,可能会在32位系统上增加额外的4字节开销(但这4个字节将被短字符串使用):

/* Note: requires some macros and includes from the facil.io C STL core header */

/** string container */
typedef struct {
  uint8_t em;     /* embedded? const? how long? */
  uint8_t pad[3]; /* padding - embedded buffer starts here */
  uint32_t len;   /* if not embedded, otherwise see `em` */
  char *str;      /* if not embedded, otherwise see `pad` */
} fio_cli_str_s;

/* cli string free / destroy by context */
FIO_SFUNC void fio_cli_str_destroy(fio_cli_str_s *s) {
  if (!s || s->em || !s->str)
    return;
  FIO_LEAK_COUNTER_ON_FREE(fio_cli_str);
  FIO_MEM_FREE_(s->str, s->len);
  *s = (fio_cli_str_s){0};
}

/* cli string info */
FIO_IFUNC fio_buf_info_s fio_cli_str_buf(fio_cli_str_s *s) {
  fio_buf_info_s r = {0};
  if (s && (s->em || s->len))
    r = ((s->em) & 127) ? (FIO_BUF_INFO2((char *)s->pad, (size_t)s->em))
                        : (FIO_BUF_INFO2(s->str, (size_t)s->len));
  return r;
}

/* cli string copy */
FIO_SFUNC fio_cli_str_s fio_cli_str_copy(fio_buf_info_s s) {
  fio_cli_str_s r = {0};
  if (s.len < sizeof(r) - 2) {
    r.em = s.len;
    FIO_MEMCPY(r.pad, s.buf, s.len);
    return r;
  }
  r.len = (uint32_t)s.len;
  r.str = (char *)FIO_MEM_REALLOC_(NULL, 0, s.len + 1, 0);
  FIO_ASSERT_ALLOC(r.str);
  FIO_LEAK_COUNTER_ON_ALLOC(fio_cli_str);
  FIO_MEMCPY(r.str, s.buf, s.len);
  r.str[r.len] = 0;
  return r;
}

/* cli string tmp copy */
FIO_SFUNC fio_cli_str_s fio_cli_str(fio_buf_info_s s) {
  fio_cli_str_s r = {0};
  if (s.len < sizeof(r) - 2) {
    r.em = s.len;
    FIO_MEMCPY(r.pad, s.buf, s.len);
    return r;
  }
  r.em = 128; /* mark as const, memory shouldn't be freed */
  r.len = (uint32_t)s.len;
  r.str = s.buf;
  return r;
}

相关问题