2

I'm using a nice GCC extensions which allow us to declare VLAs inside structures. For now I found a way to pass VLAs to functions (by value) this way. I also find a way to return one but in a very limited context.

The function code of this example is this:

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

The above example is designed for test purposes (specifically to compare binary code compiled of it).

However this is quite limited as the size of returned array doesn't vary between different calls of the function.

How could I make the returned array size equal either to one of the function parameters or some other local in this function.

I don't think alloca could help me in the case as the memory it allocates is immediately destroyed at function exit (IRC).

I want to write something like this:

/*???*/ 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
}

In other words what could be the type inside the question marks? Or maybe there is other solution (but without using malloc)?

Theoretical usage of such function will theoretically need another type to store the returned value as the size of the returned structure won't be available to the caller(unless there is someway to avoid this?). But on first sight it should be something like this:

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

If we do something like this:

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

It wouldn't work because the VM return-type of func5 will either depend on it's arguments or local variables. However that's all theoretical for the moment as we still can't define this function.

AnArrayOfFunctions
  • 3,452
  • 2
  • 29
  • 66
  • 1
    just let the caller allocate the array first and the called just fill in the values. – user3528438 Mar 04 '16 at 21:58
  • @user3528438 I'm not interested in alternative solutions. I want the VLA allocation to be held in the called function (and without using `malloc`). – AnArrayOfFunctions Mar 04 '16 at 21:59
  • I trying to see how this is would be coded. Like `{type_TBD func5(); type_TBD y = func5(); // use y }` ? Could you post some theoretical code that uses the result of `func5()`? – chux - Reinstate Monica Mar 04 '16 at 23:02
  • 1
    I would say the tricky bit is that the size of the array needs to be known _before_ `func5()` is called so that the `y` of `y = func5()` can be declared. – chux - Reinstate Monica Mar 04 '16 at 23:10
  • I think I answered your question (look at my edit). Calling such function will be tricky - the same way as passing VLAs to one. Because of VM types. – AnArrayOfFunctions Mar 04 '16 at 23:23
  • 1
    You claim to want the VLA "to be held in the called function", but you also want to return it. That must make a copy, which means you need a VLA in the calling function to receive the copy. (However you do it, the copy is necessary because you cannot return a pointer to a local variable.) So the only solution is the one @user3528438 suggests, except of course that won't work either because you'd need to have different versions of the function for different sizes of the struct. In short, use malloc. That's what it's for. – rici Mar 05 '16 at 00:40
  • Are you aware that both nested functions and `__typeof__` are GCC extensions? Note that C functions cannot return arrays; they can return pointers to arrays, but those arrays have to be statically allocated (and then cannot be variably modified) or dynamically allocated (via `malloc()` et al). Neither of those meets your requirements, it seems; you are without a remotely portable solution to the problem you wish to solve. – Jonathan Leffler Mar 05 '16 at 05:06

3 Answers3

2

[..] I want the VLA allocation to be held in the called function (and without using malloc).

There are only two sources for dynamic memory storage in (common hosted implementations of) C programs: The heap and the stack.

You don't want to use the first, but the second is managed automatically: Whatever you allocate "inside" of some function will be "gone" when that function returns.

The only exception is - of course - the return value. That doesn't help much, though, because in order to stay on the stack the memory for it (if returned via the stack) is "allocated beneath" the parameters of the function call. Thus, it's size must be known before calling the function (otherwise one wouldn't know where to store the parameters, local variables of the function, and so on).

Since "allocation on the stack" is essentially the same as "advancing some pointer by a known amount of bytes" there's a contradiction here: You want to allocate inside the function, but need to know how much before even entering the function.

This won't work.

Daniel Jour
  • 15,896
  • 2
  • 36
  • 63
  • Well maybe it will if I could get the size from the parameters (the same way as passing a VLA). However for now that is syntactically impossible because function parameters are not visible at the time the function return type is specified. But maybe there is some other way who knows. – AnArrayOfFunctions Mar 04 '16 at 23:55
2

How to return VLA with size varying on each function instance?

Returning a VLA is one thing and not really do-able unless it was passed in. (then what it the point of returning it). See no way for the calling code to receive it unless its size was determined prior.


Perhaps this is close enough to OP's goal.

Use a VLA that is allocated after the size is known, but before func5() is called.

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

Output

0
1
2
3
4
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • IMHO this is the correct way to approach this, though you probably should get rid of that struct and just pass in the pointer and size. – Daniel Jour Mar 05 '16 at 23:08
0

The closest I've gotten so far is exploiting the calling conventions (works under GCC) - so we have the generator function which will generate the array size - first half and second half fill the returned array:

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

Then if you wish to call it you would need to generate a stamp with the function signature and return value like this (example main function):

(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]);

}

Here is some live example

AnArrayOfFunctions
  • 3,452
  • 2
  • 29
  • 66