【问题标题】:C Macro to convert a string to a pascal string type将字符串转换为帕斯卡字符串类型的 C 宏
【发布时间】:2017-08-30 13:30:58
【问题描述】:

我想要一些关于宏的想法,将预处理器定义的字符串转换为帕斯卡类型的字符串,然后能够使用宏来初始化 const char 数组等。

这样的东西会很棒:

#define P_STRING_CONV(str) ...???...

const char *string = P_STRING_CONV("some string");

struct
{
    char str[30];
    ...
}some_struct = {.str = P_STRING_CONV("some_other_string")};

我已经尝试过这样的事情:

#define DEFINE_PASCAL_STRING(var, str, strlen) struct {uint8_t len; char content[strlen-1];} (var) = {sizeof(str)-1, (str)}

(strlen 参数可以去掉,但我需要定义大小。)

这很好用,但不能用于初始化结构中的元素。对于 const char 数组,我需要将其转换为其他变量。

有什么好主意吗?

【问题讨论】:

  • 1.你为什么要这样做?我认为我并不真正了解您真正想要实现的目标是什么。
  • 主要问题是您似乎认为类似 Pascal 的字符串与 C 字符串兼容(指向 char 的指针,或 char 的数组),这并不是很自然.使用类似 Pascal 的字符串会解决什么问题?为什么要使用它们?
  • 至少,字符串应该保留一个 NUL 终止符,因此将它们的最大长度减少到 254 个字符。至少你可以通过传递 [address+1] 将它们用作 const 参数。
  • 我需要与使用类似 Pascal 的字符串的系统进行通信,这是使用它的唯一原因。它也只是为了使代码更易于理解。现在必须有一个函数来初始化一些变量,对于 const 我需要类似const char string[30] = "\004test";
  • Hmm.. 我想我会选择 'PasStr' 类型和 'CtoPasStr()' 和 PasToCstr() 风格的 'conversion' 函数,即使这需要 malloc/free 风格的字符串内存管理以避免 1 字节溢出等。

标签: c macros


【解决方案1】:

将字符串转换为帕斯卡字符串类型

要转换字符串字面量_Generic复合字面量 将接近 OP 目标。

为了获得更好的解决方案,更多细节和示例用例将有助于说明 OP 的目标。

#define P_STRING_CONV(X) _Generic((X)+0, \
  char *: &((struct {char len; char s[sizeof(X)-1]; }){ (char)(sizeof(X)-1), (X) }).len \
  )

void dump(const char *s) {
  unsigned length = (unsigned char) *s++;
  printf("L:%u \"", length);
  while (length--) {
    printf("%c", *s++);
  }
  printf("\"\n");
}

int main(void) {
  dump(P_STRING_CONV(""));
  dump(P_STRING_CONV("A"));
  dump(P_STRING_CONV("AB"));
  dump(P_STRING_CONV("ABC"));
  return 0;
}

输出

L:0 ""
L:1 "A"
L:2 "AB"
L:3 "ABC"

@Jonathan Leffler 建议创建的类似pascal 的字符串也包含终止空字符。要使用上面的代码,只需将sizeof(X)-1 更改为sizeof(X)。然后通过访问pascal_like_string + 1,代码有一个指向有效C字符串的指针。


(X)+0将数组类型转换为指针

sizeof(X)-!!sizeof(X) 产生一个字符串字面量的大小,不包括它的 \0。至少 1 个。

struct {char len; char s[sizeof(X)-!!sizeof(X)]; } 是一个大小合适的类帕斯卡结构。

(struct {char len; char s[sizeof(X)-!!sizeof(X)]; }){ (char)(sizeof(X)-1), (X) } 是一个复合文字


以下内容会将 C 字符串 转换为类似帕斯卡的字符串。请注意,作为类似帕斯卡的字符串,没有'\0'

#include <limits.h>
#include <stdlib.h>
#include <string.h>
char *pstring_convert(char *s) {
  size_t len = strlen(s);
  assert(len <= UCHAR_MAX);
  memmove(s+1, s, len);
  s[0] = (char) (unsigned char) len;
  return s;
}

【讨论】:

  • 为了在 C 中有用,即使是 Pascal 类型的字符串也需要存储一个空终止符,以便可以将其传递给需要 C 字符串而不是 Pascal 字符串的函数。例如,系统调用之类的东西(open(),等)需要它。大多数 C 库都需要 C 字符串。必须复制 Pascal 字符串来创建 C 字符串将是不必要的繁重工作。您不必考虑长度字节中的空值(它就在那里;长度只是记录了直到空值的字符数)。您确实(IMNSHO)必须添加它。知道长度具有重要的优势。
  • @JonathanLeffler 够公平的。 OP 确实评论了“我需要与使用类似 Pascal 的字符串的系统进行通信”,所以这段代码就是这样做的。我怀疑 OP 需要一些 p-string 常量。如果 OP 请求了一个既可以像 Pascal 字符串又可以像 C 字符串一样工作的对象,那么对于更灵活的对象,您的想法肯定是有价值的。请注意,从 C 字符串转换为常见的 pascal_and_C 类字符串会产生额外的内存管理问题,而纯 C 到/从 pascal 转换则不需要。
  • 我不明白sizeof(X)-!!sizeof(X) 的意思。如果X 是字符串文字,则sizeof(X) 至少为1(即使X""),所以!!sizeof(X) 将始终为1。但这意味着如果X"" , 那么数组将被声明为s[0],这是无效的(尽管 gcc 接受它)。
  • 我认为它可以按需工作,但我的编译器仅支持 C89 和 C99,据我了解,泛型是在 C11 中引入的。如我错了请纠正我。但无论如何,它不适用于我的编译器。
  • @rici 同意sizeof(X)-1 就足够了。 !!sizeof(X) 是代码开发方式的影响。
【解决方案2】:

您可以将宏一分为二:

#define PASCAL_STRING_TYPE(size) struct { unsigned char len; char content[(size) - 1]; }
#define PASCAL_STRING_INIT(str) { .len = sizeof(str) - 1, .content = (str) }

然后像这样使用它:

static const PASCAL_STRING_TYPE(100) foo = PASCAL_STRING_INIT("foo");

struct bar {
   int answer;
   PASCAL_STRING_TYPE(100) question;
};
static const struct bar quux = {
    .answer = 42,
    .question = PASCAL_STRING_INIT("The Answer")
};

(未测试。)

【讨论】:

  • 好主意。但是我不能拥有extern PASCAL_STRING_TYPE(30) some_string;,因为编译器将其视为它认为不兼容的不同声明。但对于静力学,它可以按我的需要工作。
  • 没错,但你可以使用typedef PASCAL_STRING_TYPE(30) pstr30;extern pstr30 some_string;
猜你喜欢
  • 1970-01-01
  • 2014-09-23
  • 2014-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多