在C中,有没有一个好的方法可以先定义长度,将Pascal风格的字符串作为常量,这样它们就可以放入ROM中? (我正在使用一个带有非 GCC ANSI C 编译器的小型嵌入式系统)。
C 字符串是
0
终止的,例如。 {'f'
,'o'
,'o'
,0
}.
Pascal 字符串的第一个字节的长度,例如。 {
3
,'f'
,'o'
,'o'
}.
我可以声明一个 C 字符串放置在 ROM 中:
const char *s = "foo";
对于 Pascal 字符串,我可以手动指定长度:
const char s[] = {3, 'f', 'o', 'o'};
但是,这很尴尬。有没有更好的办法?也许在预处理器中?
我认为以下是一个很好的解决方案,但不要忘记启用打包结构:
#include <stdio.h>
#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 *
文字和转义序列作为指示长度的第一个字符:
const char *pascal_string = "\x03foo";
它仍然会以空终止,但这可能并不重要。
这可能听起来有点极端,但如果您有许多此类需要频繁更新的字符串,您可以考虑编写自己的小工具(可能是 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'};
我的方法是创建处理 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。但是,这仍然需要大量输入和混淆:-)
一种选择可能是滥用预处理器。通过声明一个正确大小的结构体并在初始化时填充它,它可以是
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h> // For CHAR_BIT
struct pstring {
unsigned char len;
char dat[];
};
struct pstring* pstring_new(char* src, size_t len)
{
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;
}
struct pstring* 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 = 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 的数字(考虑到它没有签名),这将是您的最大字符串长度。
但是,不要指望这能在其他字符串中起作用。不仅编译器期望 C 字符串以空字符终止,其他一切都期望 C 字符串以空字符终止。
这是我的答案,完成了使用 alloca() 进行自动存储的附加操作。
#include <stdio.h>
#include <string.h>
#include <alloca.h>
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 strings 和 strlen 完成同样的事情。因为 alloca 和 strlen 都更喜欢短字符串,所以我认为这更有意义。