3

As far as I know, only the caller-clean-stack convention can use variable arguments.
By the way, the WinApi StringCchPrintfW is declared like this.(I removed the SAL)

__inline HRESULT __stdcall
StringCchPrintfW(
STRSAFE_LPWSTR pszDest, size_t cchDest, STRSAFE_LPCWSTR pszFormat, ...
);

Can stdcall have a variable arguments either?

Benjamin
  • 10,085
  • 19
  • 80
  • 130

1 Answers1

10

No. The stdcall calling convention has the callee clean the stack. Since the callee is cleaning the stack there is no way for it to know at compile time how much to pop off, therefore it cannot have variable arguments.

In order to have variable number of function arguments you need to use cdecl, which has the caller clean the stack. This all the compiler to determine how many arguments are being passed and since the caller is cleaning up the stack it also knows how much to pop off the stack when the call to the function returns.

In the case mentioned above, the function is declared to use __stdcall, which as previously mentioned does not support variable arguments. In this case, the compiler makes the decision to ignore the calling convention defined and revert back to __cdecl. This behavior is alluded to in the description for stdcall, mentioned above. I quote:

The callee cleans the stack, so the compiler makes vararg functions __cdecl.

This can be observed if the following code is compiled and a call to the function disassembled.

int __stdcall Bar(int a, int b, ...)
{
  return b * a;
}

The resulting code will be treated as __cdecl. As to the reason this is defined that way, I do not know.

linuxuser27
  • 7,183
  • 1
  • 26
  • 22
  • Yeah sorry about that. Got confused with the old `pascal` calling convention. – linuxuser27 Sep 01 '10 at 06:09
  • I don't think the point is left-to-right or right-to-left order, by the way. The point is who has a responsiblity to clean-up the stack. – Benjamin Sep 01 '10 at 06:14
  • Exactly. I just updated my answer. Before `stdcall`, there was the `pascal` calling convention, you can find that in the link you referenced. That had the left to right argument pushing. – linuxuser27 Sep 01 '10 at 06:17
  • It(inline) makes a sense. But why they did write the stdcall explicitly. It gives me a confusion. – Benjamin Sep 01 '10 at 06:37
  • Your answer has been modified to a nice one! Thanks. – Benjamin Sep 01 '10 at 10:04
  • @Benjamin: Right to left vs. left to right also plays a part. With R2L, the first argument is at the top of the stack so you can use that to figure out how many more arguments are on the stack. With L2R, the *last* argument is on the stop of the stack. Since that's a variable argument of some unknown type, it's not much use in figuring out what else was passed. – Jerry Coffin Sep 01 '10 at 15:40
  • @Jerry Coffin: What does some unknown type mean? Give me more hints. Thanks. – Benjamin Sep 18 '10 at 05:39
  • 4
    @Benjamin: Consider printf as an example. The first argument tells the types of the rest of the arguments. With arguments pushed R2L, that's at the top of the stack so `printf` can use it to determine the types of the others. If the arguments pushed L2R, the TOS would be the *last* argument pushed, which could be an int or a long or a double, or whatever, but doesn't tell `printf` anything about the other arguments. – Jerry Coffin Sep 19 '10 at 22:23