Гибкий элемент массива (c99) внутри структуры

Я использую этот код некоторое время, и он отлично работает, но у меня возникла головная боль при его реализации. Он использует гибкий элемент массива (FAM) aka Struct Hack. Теперь, когда C99 имеет возможность использовать массив переменной длины (VLA), мне интересно, как я могу воспользоваться этим преимуществом?

typedef struct nO
{
    int oper;              /* some operator */
    int nops;              /* number of args */
    struct nO *ptn[1];     /* expansible array <=== HERE */
} nodoOper;

nodoOper *operator(int o, int n, ...)
{
    va_list ap;
    nodoOper *tn;
    size_t tam;
    int i;

    tam = sizeof(nodoOper) + (n - 1) * sizeof(nodoOper *); /* <=== AND HERE */

    if((tn=malloc(tam))==NULL)
        yyerror("Memory fault (cod. 4)");

    tn->oper = o;
    tn->nops = n;
    va_start(ap, n);
    for(i=0; i<n; i++)
        tn->ptn[i] = va_arg(ap, nodoOper*);
    va_end(ap);
    return tn;
}

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

Теперь определение структуры находится в файле заголовка (.h), а код создает дерево синтаксиса для компилятора. Как я могу изменить массив, чтобы использовать переменную длину?

Спасибо! Beco.

Отредактировано: откат последней правки к элементу гибкого массива.

2-е издание. Предположим, я добавляю внутрь функции этот фрагмент кода:

struct nOp
{
    int oper;             /* some operator */
    int nops;             /* number of args */
    struct nOp *ptn[n];   /* Variable Length Array (VLA) */
};
struct nOp tnop;
struct nOp *tn2;

tn2 = &tnop;
return tn2;

Первая проблема, которую я вижу, я возвращаю указатель на локальную переменную. Но, кроме того, плодотворен ли этот путь? Спасибо


person DrBeco    schedule 29.03.2011    source источник
comment
Теперь, когда я узнал, что член гибкого массива (FAM) является синонимом Struct Hack, и что это уже было то, что я уже сделал, мне стало ясно, что цель этого вопроса - найти другой способ обойтись с помощью другого инструмента C99 дал нам название Variable Length Array (VLA). Если это невозможно, ну ... хорошо, мы сохраняем старый способ, который уже был реализован.   -  person DrBeco    schedule 30.03.2011
comment
Если вы решите сохранить старый способ, вы можете использовать offsetof(nodoOper, ptn) + n * sizeof(nodoOper *) для расчета размера. Таким образом, вам не нужно помнить о вычитании 1. Таким образом, вы вообще не зависите от того, что указано между [] в объявлении массива.   -  person AnT    schedule 30.03.2011


Ответы (2)


На самом деле, здесь вы хотите использовать не массивы переменной длины, а хак struct, он же «неполные типы», он же «гибкий член массива»:

typedef struct nO
{
    int oper;
    int nops;
    struct nO *ptn[];  // <== look ma, no index!
} nodoOper;

// skip a bit
// no more (n-1) --------\
tam = sizeof(nodoOper) + n * sizeof(nodoOper *);

Только последний член struct может быть «гибким».

Другой особенностью являются массивы переменной длины:

void foo(int size)
{
    float a[size];               // run-time stack allocation
    printf("%lu\n", sizeof(a));  // and run-time sizeof (yuck)
}

(О, и эти вещи называются массивами, а не матрицами.)

person Fred Foo    schedule 29.03.2011
comment
Ох, и эти штуки называются массивами, а не матрицами - ха-ха - person Sadique; 30.03.2011
comment
:) В порядке! Пост поправлю. Как сказал @pmg, имя важно для облегчения поиска в сети. - person DrBeco; 30.03.2011
comment
Спасибо larsmans, но ему все еще нужен malloc. Мое намерение состояло в том, чтобы вообще исключить malloc. Есть другой вариант? - person DrBeco; 30.03.2011
comment
@ Доктор Беко: нет, таким образом от malloc не избавиться. Вы можете избавиться от malloc, если все-таки используете VLA, но это не сработает в struct. - person Fred Foo; 30.03.2011
comment
@larsman, tam размер должен быть маллокирован? Это немного неправильно ... sizeof(nodoOper) + n * sizeof(struct nO*) или даже лучше offsetof(nodoOper, ptn) + n * sizeof(struct nO*) или даже лучше действительно правильная формула max этого и sizeof(nodoOper). - person Jens Gustedt; 30.03.2011
comment
Что не так с struct s { int n; struct s *d[m]; }; struct s si;? - person DrBeco; 30.03.2011
comment
@Jens: извини, я не понимаю. Вы учли typedef struct nO nodoOper (без *)? - person Fred Foo; 30.03.2011
comment
Да, tam - это общее количество, которое нужно очистить. - person DrBeco; 30.03.2011
comment
@ Доктор Беко: иногда это срабатывает, но откуда взялся m? Это не входит в область действия, если вы не объявите struct в функции. Но тогда вы не можете использовать struct вне этой функции. - person Fred Foo; 30.03.2011
comment
Можно ли tam переписать как tam = sizeof(nodoOper) + sizeof(struct nO *[m]);? Или даже лучше: tam = sizeof(nodoOper) + sizeof(nodoOper *[m]);? - person DrBeco; 30.03.2011
comment
Что касается проблемы с областью видимости, теперь я понимаю, почему вы решили оставить malloc. Здесь небольшая территория. Я помещу еще один фрагмент кода, который пришел мне в голову, в конце вопроса. Надеюсь, мы сможем прояснить эту часть. Спасибо большое за вашу помощь. - person DrBeco; 30.03.2011
comment
@ Доктор Беко: прочтите. Вы сами сигнализируете о первой проблеме: возвращаете указатель на что-то в стеке, что ведет прямо к 9 кругам неопределенного поведения. Вторая проблема заключается в том, что вы возвращаете nOp, а не nO. Просто используйте malloc. - person Fred Foo; 30.03.2011
comment
@larsman, ах, я не заметил, что struct nO это то же самое, что и nodoOper, у меня плохо. Но я думаю, что вторая часть действительна, но, вероятно, не имеет отношения к различным размерам и смещениям, которые могут быть здесь. - person Jens Gustedt; 30.03.2011

Имя C для этой конструкции - «гибкий элемент массива» (может помочь в поиске), или «struct hack» до C99.

Посмотрите на 6.7.2.1 в стандарте; в тексте есть пример использования.


Вас также может заинтересовать «Массивы переменной длины». См. 6.7.5.2 в Стандарте.

person pmg    schedule 29.03.2011
comment
Возможно, я допустил ошибку при редактировании. Я думаю, что сейчас правильным является массив переменной длины. - person DrBeco; 30.03.2011
comment
Также в индексе под struct hack :) - person Fred Foo; 30.03.2011
comment
Определенно, это не случай гибкого элемента массива. - person DrBeco; 30.03.2011
comment
Я думаю, у вас есть FAM, @DrBeco. Пример в ответе larsmans должен помочь вам легко начать. - person pmg; 30.03.2011
comment
Прошу прощения за такое количество выпусков. Я закончил редактировать заголовок вопроса. Я прошу помощи в чем-то, даже названия не знаю. Еще раз извините, ребята. - person DrBeco; 30.03.2011
comment
Да, это случай FAM, а не VLA. VLA служит совершенно другой цели, о которой я также объяснил в своем посте. @ Доктор Беко: все в порядке. Вы здесь, чтобы учиться, как и все мы :) - person Fred Foo; 30.03.2011