Интерпретация уродливых макросов (всего 1 строка) - PullRequest
1 голос
/ 11 января 2010
#define STRING(s) (((String*)s)-1)

что в мире есть (((String*)s)-1)?

typedef 
struct String {
    int length;
    int capacity;
    unsigned check;
    char ptr[0];
} String;

Ответы [ 4 ]

8 голосов
/ 11 января 2010

Вы кастуете s на String *. Затем вы вычитаете одно из этого (что бы оно ни указывало на предыдущее).

Что-то более конкретное должно знать определение String - но (WILD SPECULATION) Я бы предположил, что приложение использует двойные строки в стиле VB / C (null завершается, ему предшествует длина), и эта функция меняет его из формы, подходящей для функций C (указатель на первый символ), в форму, пригодную для другого типа (указатель на длину).

5 голосов
/ 11 января 2010

Механически макрос работает так, как его уже описали другие. С семантической точки зрения, вы можете думать об этом как о форме приведения от char * s до String * s.

Структура String является заголовком подсчитанной строки, т. Е. Той, в которой вы знаете общую длину без необходимости поиска байта NUL. Эта конкретная версия также хранит общую сумму. Вы создали бы его следующим образом:

struct String *str = malloc(sizeof(*s) + maxlen);
str->length = 0;
str->capacity = maxlen;
str->checked = /* ??? */;

Где-то должно быть несколько различных функций, чтобы манипулировать этими подсчитанными строками.

Макрос сам по себе является хаком для перехода от простого char *, который, как предполагается, указывает на первый символ a String, как указано выше, обратно на String *. Было бы использовано что-то вроде этого:

/* allocate str as above */
char *s = str->p;

Теперь, через цепочку вызовов или возвратов функций, вы каким-то образом теряете след структуры String, содержащей s, и вам нужно найти ее снова. Вы пишете:

String *str2 = STRING(s);

Это не очень хороший способ реализации подсчитанной строки в C, но он демонстрирует технику, которую время от времени видят.

2 голосов
/ 11 января 2010

Другие ответили на ваш вопрос. Техника объявления ptr внутри struct String с нулевым размером называется " struct hack ", а не была переносимой до C99 (хотя она широко использовалась еще до C99 и, кажется, работает везде). Идея состоит в том, что ptr использует 0 байтов, поэтому, если у вас есть указатель на ptr и вы хотите получить указатель на исходную структуру, вы должны использовать макрос STRING. Вы вычитаете размер struct из адреса члена ptr и, таким образом, получаете начальный адрес struct.

Лучший способ получить начальный адрес struct с указателем на любой из его членов - это использовать макрос offsetof(), определенный в stddef.h. offsetof(struct type, member), как следует из названия, дает смещение member в struct type:

#define STRING(x) ((String *)(((char *)(x) - offsetof(struct String, ptr))))

Тогда вы можете сделать:

#include <stddef.h>
#include <stdlib.h>
#include <assert.h>

typedef struct String {
    int length;
    int capacity;
    unsigned check;
    char ptr[0];
} String;

#define STRING(x) ((String *)(((char *)(x) - offsetof(struct String, ptr))))

int main(void)
{
    String *s = malloc(sizeof *s + 100);
    String *t;
    char *mystring = s->ptr;
    t = STRING(mystring);
    assert(t == s);
    return EXIT_SUCCESS;
}

offsetof() определено в stddef.h.

Обратите внимание, что в C99 "struct hack" объявляет ptr внутри struct как:

char ptr[];

т.е. без размера.

0 голосов
/ 11 января 2010
  • (String *) = приведение типа к указателю на объект String,
  • s = строка,
  • -1 = указывать на длину одного объекта String ранее в блоке памяти

Не знаю, почему макрос сделан таким образом. Возможно, определение String требует этого, но это просто дикое предположение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...