16

I'd like to write a function that has return type of va_list.

example: va_list MyFunc(va_list args);

is this safe and portable?

Hayri Uğur Koltuk
  • 2,970
  • 4
  • 31
  • 60
  • 4
    If your intent is to pass in a `va_list`, modify it, then use that modified `va_list` upon return from the function, it might be better to consider passing a pointer to a `va_list` to `MyFunct()` and have it act on the list via the pointer. The standard specifically mentions that technique being permissible in a footnote. – Michael Burr May 18 '12 at 07:38
  • this is exactly my intention. Could you point where in standard it is permitted? – Hayri Uğur Koltuk May 18 '12 at 07:49
  • 2
    In my opinion, `va_list` are a bad idea. There are usually better solutions that are more safe. So I would have a hard think and get a better solution to the design and implementation of the function. – Ed Heal May 18 '12 at 07:51
  • 3
    @Michael Burr i saw an answer (http://stackoverflow.com/questions/3369588/pass-va-list-or-pointer-to-va-list) and I think the footnote: 215) It is permitted to create a pointer to a va_list and pass that pointer to another function, in which case the original function may make further use of the original list after the other function returns – Hayri Uğur Koltuk May 18 '12 at 07:54
  • 2
    Passing a pointer to another function is quite different from returning that pointer though. Many/most implementations store the actual variable arguments in a stack frame that is destroyed when the vararg function returns. (i.e. returning a va_list or a pointer to one, will leave you with pointers to local variables that's destroyed). – nos May 18 '12 at 11:45
  • well in my case i'd be returning va_list back but thanks for warning – Hayri Uğur Koltuk May 18 '12 at 12:49
  • @Ali Veli Like I explained, returning a va_list makes little sense. Don't do it. – nos Jun 02 '12 at 20:56

4 Answers4

7

va_list might (but is not guaranteed to) be an array type, so you can't pass or return it by value. Code that looks as if it does, might just be passing/returning a pointer to the first element, so you can use the parameter in the callee but you might be acting on the original.

Formally you can probably say that va_list is an entity type, not a value type. You copy it with va_copy, not with assignment or via function parameters/return.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
1

While you can definitely return such a value, I am not sure if the return value can be used in a useful way.

As the handling of va_lists requires special treatment (va_end() required after va_start() and va_copy()), and va_start/copy and va_end macros are even allowed to contain { } to enforce this pairing, you cannot call one without the other.

glglgl
  • 89,107
  • 13
  • 149
  • 217
  • 3
    `va_copy` is used to create a *second* `va_list` object that could be read independently of the original one. As this is not the case here, I would say that it is OK to pass and return the `va_list` object. Also, the standard does not mention that `va_start()` and `va_end()` must be in the same scope, hence I don't see how they could be allowed to contain `{`:es. – Lindydancer May 18 '12 at 07:44
  • 2
    @Lindydancer Oh, didn't know about that. [Here](http://linux.die.net/man/3/va_start) I read "Each invocation of va_start() must be matched by a corresponding invocation of va_end() in the same function" and "Each invocation of va_copy() must be matched by a corresponding invocation of va_end() in the same function.", so I thought it cones from the standard... – glglgl May 18 '12 at 07:50
  • @glglgl: "in the same function" doesn't necessarily mean "in the same scope", so doesn't imply that the macros can use unmatched braces. But it does allow `va_copy` to use tricks like `alloca`, since the new list doesn't live beyond function return. – Steve Jessop May 18 '12 at 11:19
1

Whatever the language standard says, this is unlikely to work in practice. A va_list is likely to be a pointer to a call record placed on the stack by a caller for the benefit of the callee. Once the callee returns, that memory on the stack is fair game for reuse.

Returning the type va_list is unlikely to actually copy the content of the list back to the caller. Although that would be a valid implementation of C, if the standard requires it be done so, that would be a defect in the specification.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • The reason why the `va_end` macro exists is exactly because the `va_list` is not necessarily just a pointer to something on the stack. Otherwise, `va_end` would always be a no-op. There exist implementations where `va_list` objects are allocated on the heap. – Dietrich Epp May 18 '12 at 12:01
  • @DietrichEpp Good info, I thought `va_end` had something to do with register windows or old implementations that only allowed one iterator at a time. Adjusted my answer, but it's just a matter of relative emphasis… most architectures don't use the heap for varargs. – Potatoswatter May 18 '12 at 12:04
0

Passing a pointer to another function is quite different from returning that pointer though. Many/most implementations store the actual variable arguments in a stack frame that is destroyed when the vararg function returns. (i.e. returning a va_list or a pointer to one, will leave you with pointers to local variables that's destroyed). – nos

well in my case i'd be returning va_list back but thanks for warning – Hayri Uğur Koltuk

If you pass a pointer to a va_list to the function MyFunc(va_list *args), you don't need to pass the modified (by va_arg(*args, type)) argument list back, since MyFunc modifies the original list.

Armali
  • 18,255
  • 14
  • 57
  • 171
  • I don't think it's specified whether `MyFunc` modifies the original list. I've certainly encountered implementations that work both ways. – supercat Sep 25 '17 at 19:13
  • @supercat - If a pointer to any object is passed to a function, and the function modifies the pointed-to object, it is inherent that the original object is modified. – Armali Sep 26 '17 at 08:11
  • If a va_list is a simple pointer (as would be most efficient on many platforms), passing it to a function would give a function a copy of the pointer, and va_arg would increment that copy of the pointer without affecting the original. – supercat Sep 26 '17 at 14:13
  • @supercat - We (possibly except you) are talking about passing _a pointer to a `va_list` to the function_, so also _If a va_list is a simple pointer_, _a pointer to_ that pointer _is passed_, and _the function modifies the pointed-to_ original pointer. – Armali Sep 27 '17 at 09:54
  • 1
    Okay, I see where I was confused. I've gotten bitten by the fact that on some systems a `va_list` is an array, an argument of that type *inherently* gets passed as a pointer, whereas on other systems it does not, but I now see that in your particular case you're including the `*` in the argument type so it will be passed as a single-indirect pointer (to whatever is in the `va_list`, which likely contains another pointer) regardless. – supercat Sep 27 '17 at 14:11