Как вернуть VLA с разным размером для каждого экземпляра функции?

Я использую хорошие расширения GCC, которые позволяют нам объявлять VLA внутри структур. На данный момент я нашел способ передать VLA в функции (по значению) таким образом. Я также нахожу способ вернуть один, но в очень ограниченном контексте.

Функциональный код этого примера такой:

extern void func3()
{
    size_t size;

    scanf("%zu", &size);

    struct tx{int _[size];} fn()
    {
        struct tx rt;

        for(size_t i=0; i < size; ++i)
            scanf("%d", &rt._[i]);

        return rt;
    }

    volatile __typeof__(fn) *pf = fn;
}

Приведенный выше пример предназначен для тестирования (в частности, для сравнения скомпилированного из него двоичного кода).

Однако это довольно ограничено, так как размер возвращаемого массива не изменяется между различными вызовами функции.

Как я могу сделать размер возвращаемого массива равным одному из параметров функции или некоторому другому локальному в этой функции.

Я не думаю alloca может помочь мне в этом случае, поскольку память, которую он выделяет, немедленно уничтожается при выходе из функции (IRC).

Я хочу написать что-то вроде этого:

/*???*/ func5()
{
    size_t size;

    scanf("%zu", &size);

    struct {int _[size];} rt;

    for(size_t i=0; i < size; ++i)
        scanf("%d", &rt._[i]);

    return rt; //ok - return the structure
}

Другими словами, какой может быть тип внутри вопросительных знаков? Или, может быть, есть другое решение (но без использования malloc )?

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

size_t size;

//scanf("%zu", &size);

struct {int _[size];} tmp; //create locally VM type 
                            //compatible with the one
                            //returned by our theoretical func5

                            //we can't directly initialize tmp here (gcc complains)


tmp = ((__typeof__(tmp) (*)())func5)(); //direct assignment between VM structures 
                                        //works here on the other hand

                                        //as function return value is rvalue and we can't
                                        //take its pointer and cast it to our local VM structure type
                                        //we instead cast the function pointer

Если мы сделаем что-то вроде этого:

__typeof__(func5()) tmp = func5();

Это не сработает, потому что тип возврата виртуальной машины func5 будет зависеть от его аргументов или локальных переменных. Однако на данный момент это все теоретически, так как мы до сих пор не можем определить эту функцию.

3 ответа

Как вернуть VLA с разным размером для каждого экземпляра функции?

Возврат VLA - это одна вещь, которую невозможно выполнить, если она не была передана. (Тогда какой смысл возвращать ее). Не вижу способа, чтобы вызывающий код мог его получить, если только его размер не был определен ранее.


Возможно, это достаточно близко к цели ОП.

Используйте VLA, который выделяется после того, как размер известен, но до func5() называется.

typedef struct {
  size_t size;
  int *a;
} va;

void func5(va *p) {
  for (size_t i = 0; i < p->size; ++i) {
    // error handling not shown
    // scanf("%d", &p.a[i]);
    p->a[i] = i;
  }
}

int main(void) {

  // create
  size_t size = 5;
  // scanf("%zu", &size);
  int v[size];
  va t = { size, v };

  // populate
  func5(&t);

  // use 
  for (size_t i = 0; i < size; i++) {
    printf("%d\n", t.a[i]);
  }

  // automatic "free"
  return 0;
}

Выход

0
1
2
3
4

[..] Я хочу, чтобы распределение VLA проводилось в вызываемой функции (и без использования malloc).

Есть только два источника для динамического хранения памяти в (распространенных размещенных реализациях) программах на C: куча и стек.

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

Единственное исключение - конечно - возвращаемое значение. Однако это не очень помогает, потому что для того, чтобы оставаться в стеке, память для него (если она возвращается через стек) "выделяется" под параметрами вызова функции. Таким образом, его размер должен быть известен до вызова функции (иначе нельзя было бы знать, где хранить параметры, локальные переменные функции и т. Д.).

Поскольку "распределение в стеке" по сути то же самое, что и "продвижение некоторого указателя на известное количество байтов", здесь есть противоречие: вы хотите выделить внутри функции, но вам нужно знать, сколько еще, прежде чем войти в функцию.

Это не сработает.

Самое близкое, что я получил до сих пор, это использование соглашений о вызовах (работает в GCC) - поэтому у нас есть функция генератора, которая будет генерировать размер массива - первая половина и вторая половина заполняют возвращаемый массив:

returnVLAgenerator(vlaout)
    char vlaout[];
{
    size_t szvla = 3; //calculate vlaout size here

    if(vlaout == 0)
        return szvla;

    while(szvla--) vlaout[szvla] = 'x'; //fill vlaout here

    return;
}

Затем, если вы хотите вызвать его, вам нужно сгенерировать штамп с сигнатурой функции и возвращаемым значением, как это (пример основной функции):

(main())
{
    struct { char ar[returnVLAgenerator(0)]}(*returnVLAstamp)() = returnVLAgenerator, vlaOut;

    vlaOut = returnVLAstamp();

    for(size_t i = 0; i < sizeof(vlaOut.ar); ++i)
        printf("%c", vlaOut.ar[i]);

}

Вот живой пример

Другие вопросы по тегам