在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'};
但是,这很尴尬。有没有更好的办法?也许在预处理器中?
答案 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都喜欢短字符串,所以我认为这会更有意义。