在C中声明Pascal样式的字符串

在C中,有一个很好的方法来首先定义长度,Pascal样式的字符串作为常量,所以它们可以放在ROM中吗? (我正在使用一个带有非GCC ANSI C编译器的小型嵌入式系统)。

例如,C字符串被终止。 { 'f''o''o'0 }。

Pascal字符串的长度在第一个字节中,例如。 { 3'f''o''o' }。

我可以声明一个C字符串放在ROM中:

 const char *s = "foo"; 

对于Pascal字符串,我可以手动指定长度:

 const char s[] = {3, 'f', 'o', 'o'}; 

但是,这很尴尬。 有没有更好的办法? 也许在预处理器中?

我认为以下是一个很好的解决方案,但不要忘记启用打包的结构:

 #include  #define DEFINE_PSTRING(var,str) const struct {unsigned char len; char content[sizeof(str)];} (var) = {sizeof(str)-1, (str)} DEFINE_PSTRING(x, "foo"); /* Expands to following: const struct {unsigned char len; char content[sizeof("foo")];} x = {sizeof("foo")-1, "foo"}; */ int main(void) { printf("%d %s\n", x.len, x.content); return 0; } 

一个问题是,它在你的字符串之后添加了一个额外的NUL字节,但它可能是理想的,因为那时你也可以将它用作普通的c字符串。 您还需要将其强制转换为外部库所期望的任何类型。

GCC和clang(可能还有其他人)接受-fpascal-strings选项,它允许你通过让字符串中出现的第一个东西为\p来声明pascal样式的字符串文字,例如"\pfoo" 。 不完全可移植,但肯定比时髦的宏或它们的运行时构造更好。

有关详细信息,请参见此处

您仍然可以使用const char * literal和转义序列作为指示长度的第一个字符:

 const char *pascal_string = "\x03foo"; 

它仍然是空终止的,但这可能无关紧要。

我的方法是创建处理Pascal字符串的函数:

 void cstr2pstr(const char *cstr, char *pstr) { int i; for (i = 0; cstr[i]; i++) { pstr[i+1] = cstr[i]; } pstr[0] = i; } void pstr2cstr(const char *pstr, char *cstr) { int i; for (i = 0; i < pstr[0]; i++) { cstr[i] = pstr[i+1]; } cstr[i] = 0; } 

然后我可以这样使用它:

 int main(int arg, char *argv[]) { char cstr[] = "ABCD", pstr[5], back[5]; cstr2pstr(cstr, pstr); pstr2cstr(pstr, back); printf("%s\n", back); return 0; } 

这似乎简单,直接,不易出错,并且不是特别尴尬。 它可能不是您的问题的解决方案,但我建议您至少考虑使用它。

您也可以将sizeof应用于字符串文字。 这样可以减少一点尴尬

 const char s[] = {sizeof "foo" - 1u, 'f', 'o', 'o'}; 

请注意,字符串文字的大小包括终止NUL字符,这就是你必须减去1的原因。但是,它仍然是很多打字和混淆:-)

这可能听起来有点极端但是如果你有许多需要频繁更新的字符串,你可以考虑编写自己的小工具(可能是perl脚本吗?)在主机系统上运行,用自定义格式解析输入文件你可以根据自己的喜好设计并输出.c文件。 您可以将它集成到您​​的makefile或其他任何东西,并在以后幸福地生活:)

我在谈论一个将转换此输入(或您喜欢的其他语法)的程序:

 s = "foo"; x = "My string"; 

对于此输出,这是一个.c文件:

 const char s[] = {3, 'f', 'o', 'o'}; const char x[] = {9, 'M', 'y', ' ', 's', 't', 'r', 'i', 'n', 'g'}; 

一种选择可能是滥用预处理器。 通过声明一个正确大小的结构并在初始化时填充它,它可以是const

 #define DECLARE_PSTR(id,X) \ struct pstr_##id { char len; char data[sizeof(X)]; }; \ static const struct pstr_##id id = {sizeof(X)-1, X}; #define GET_PSTR(id) (const char *)&(id) #pragma pack(push) #pragma pack(1) DECLARE_PSTR(bob, "foo"); #pragma pack(pop) int main(int argc, char *argv[]) { const char *s = GET_PSTR(bob); int len; len = *s++; printf("len=%d\n", len); while(len--) putchar(*s++); return 0; } 

这就是为什么在C99中引入可变长度数组(并且为了避免使用“struct hack”)IIRC,Pascal字符串被限制为最大长度255。

 #include  #include  #include  #include  // For CHAR_BIT struct pstring { unsigned char len; char dat[]; }; struct pstring *pstring_new(char *src, size_t len) { struct pstring *this; if (!len) len = strlen(src); /* if the size does not fit in the ->len field: just truncate ... */ if (len >=(1u << (CHAR_BIT * sizeof this->len))) len = (1u << (CHAR_BIT * sizeof this->len))-1; this = malloc(sizeof *this + len); if (!this) return NULL; this->len = len; memcpy (this->dat, src, len); return this; } int main(void) { struct pstring *pp; pp = pstring_new("Hello, world!", 0); printf("%p:[%u], %*.*s\n", (void*) pp , (unsigned int) pp->len , (unsigned int) pp->len , (unsigned int) pp->len , pp->dat ); return 0; } 

您可以按照自己喜欢的方式定义数组,但请注意,此语法不够用:

 const char *s = {3, 'f', 'o', 'o'}; 

你需要一个数组而不是一个指针:

 const char s[] = {3, 'f', 'o', 'o'}; 

请注意, char只会存储最多255个数字(考虑到它没有签名),这将是您的最大字符串长度。

但是,不要指望这可以在其他字符串的情况下工作。 预期AC字符串不仅由编译器以空字符终止,而是由其他所有字符终止。

这是我的答案,完成了一个使用alloca()进行自动存储的追加操作。

 #include  #include  #include  struct pstr { unsigned length; char *cstr; }; #define PSTR(x) ((struct pstr){sizeof x - 1, x}) struct pstr pstr_append (struct pstr out, const struct pstr a, const struct pstr b) { memcpy(out.cstr, a.cstr, a.length); memcpy(out.cstr + a.length, b.cstr, b.length + 1); out.length = a.length + b.length; return out; } #define PSTR_APPEND(a,b) \ pstr_append((struct pstr){0, alloca(a.length + b.length + 1)}, a, b) int main() { struct pstr a = PSTR("Hello, Pascal!"); struct pstr b = PSTR("I didn't C you there."); struct pstr result = PSTR_APPEND(PSTR_APPEND(a, PSTR(" ")), b); printf("\"%s\" is %d chars long.\n", result.cstr, result.length); return 0; } 

您可以使用c字符串和strlen完成相同的操作。 因为alloca和strlen都喜欢短字符串,所以我认为这会更有意义。