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

时间:2011-10-04 13:50:08

标签: c string pascal

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

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

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

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

const char *s = "foo";

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

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

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

10 个答案:

答案 0 :(得分:20)

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

#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字符串。您还需要将其强制转换为外部库所期望的任何类型。

答案 1 :(得分:11)

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

有关详细信息,请参阅here

答案 2 :(得分:5)

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

const char *pascal_string = "\x03foo";

它仍然会以空值终止,但这可能无关紧要。

答案 3 :(得分:3)

我的方法是创建处理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;
}

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

答案 4 :(得分:3)

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

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

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

答案 5 :(得分:3)

这可能听起来有点极端但是如果你有很多需要频繁更新的字符串,你可以考虑编写自己的小工具(可能是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'};

答案 6 :(得分:2)

一种选择可能是滥用预处理器。通过声明一个正确大小的结构并在初始化时填充它,它可以是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;
} 

答案 7 :(得分:2)

这就是为什么在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)
{
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;
}

答案 8 :(得分:1)

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

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

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

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

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

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

答案 9 :(得分:1)

这是我的答案,完成了一个使用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字符串和strlen完成相同的操作。因为alloca和strlen都喜欢短字符串,所以我认为这会更有意义。