Подтвердить что ты не робот

Объявление строк в стиле Паскаля в C

В C, есть ли хороший способ определить длину сначала, строки в стиле Pascal как константы, чтобы их можно было помещать в ПЗУ? (Я работаю с небольшой встроенной системой с компилятором ANSI C, отличным от GCC).

С-строка 0 завершена, например. { 'f', 'o', 'o', 0}.

Строка Pascal имеет длину в первом байте, например. { 3, 'f', 'o', 'o'}.

Я могу объявить C-строку, которая будет помещена в ПЗУ, с помощью:

const char *s = "foo";

Для строки Pascal я могу вручную указать длину:

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

Но это неудобно. Есть ли способ лучше? Возможно, в препроцессоре?

4b9b3361

Ответ 1

Я думаю, что следующее является хорошим решением, но не забудьте включить упакованные структуры:

#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. Вам также необходимо передать его в любой тип, который ожидает ваша внешняя библиотека.

Ответ 2

GCC и clang (и, возможно, другие) принимают параметр -fpascal-strings, который позволяет объявлять строковые литералы в стиле pascal, имея первое, что появляется в строке, как \p, например. "\pfoo". Не совсем портативные, но, безусловно, приятнее, чем фанки-макросы или их исполнение.

Подробнее см. здесь.

Ответ 3

Вы все равно можете использовать литерал const char * и escape-последовательность в качестве своего первого символа, который указывает длину:

const char *pascal_string = "\x03foo";

Он по-прежнему будет иметь нулевое завершение, но это, вероятно, не имеет значения.

Ответ 4

Мой подход заключался бы в создании функций для работы с строками Паскаля:

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;
}

Это кажется простым, простым, менее подверженным ошибкам и не особо неудобным. Это может быть не решение вашей проблемы, но я бы рекомендовал вам по крайней мере подумать об использовании этого.

Ответ 5

Вы можете применить sizeof к строковым литералам. Это позволяет немного менее неудобно

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

Обратите внимание, что sizeof строкового литерала включает в себя завершающий символ NUL, поэтому вы должны вычесть 1. Но все же, это много набивки и обфускации: -)

Ответ 6

Это может показаться немного экстремальным, но если у вас много таких строк, которые требуют частого обновления, вы можете подумать о том, чтобы написать собственный небольшой инструмент (perl script, возможно?), который выполняется в главной системе, анализирует входной файл с пользовательским форматом, который вы можете создать по своему вкусу и выводит .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'};

Ответ 7

Одним из вариантов может быть злоупотребление препроцессором. Объявив структуру нужного размера и запустив ее при инициализации, она может быть 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;
} 

Ответ 8

Вот почему переменные длины массивов были введены в c99 (и, чтобы избежать использования "хачка структуры" ) 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;
}

Ответ 9

Вы можете определить массив так, как вам нравится, но обратите внимание, что этот синтаксис недостаточен:

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

Вам нужен массив вместо указателя:

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

Обратите внимание, что a char будет хранить номера до 255 (учитывая, что они не подписаны), и это будет ваша максимальная длина строки.

Не ожидайте, что это сработает там, где другие строки будут. Ожидается, что строка C завершится с нулевым символом не только компилятором, но и всем остальным.

Ответ 10

Здесь мой ответ, в комплекте с операцией append, которая использует 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 предпочитают короткие строки, я думаю, что это будет иметь больше смысла.