5

I know that C++ doesn't specify the order in which parameters are passed to a function. But if we write the following code:

void __cdecl func(int a, int b, int c)
{
       printf("%d,%d,%d", a,b,c);
}
int main()
{
   int i=10;
   func(++i, i, ++i);
}

Can we reliably say the output would be 12,11,11 since the __cdecl ensures that argument-passing order is right to left?

  • Since I would never write code like this, I am curious to know what prompted this question. What is the constraint? – Android Eve Dec 09 '10 at 19:00
  • http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points – Steve Jessop Dec 09 '10 at 19:25
  • Additionally have you not read the million and one post about not modifying a variable more than once in a statement. – Martin York Dec 09 '10 at 19:25
  • [The order that function parameters are evaluated](http://stackoverflow.com/questions/367633/what-are-all-the-common-undefined-behaviour-that-a-c-programmer-should-know-abo/367663#367663) – Martin York Dec 09 '10 at 19:27

4 Answers4

13

As per the Standard, there are two things you need to understand and differentiate:

  1. C++ doesn't specify the order in which parameters are passed to a function (as you said yourself, that is true!)

  2. C++ doesn't specify the order in which the function arguments are evaluated [expr.call].

Now, please note, __cdecl ensures only the first, not the second. Calling conventions decide only how the functions arguments will be passed, left-to-right or right-to-left; they can still be evaluated in ANY order!

Hope this clarifies your doubts regarding the calling conventions.

However, since these conventions are Microsoft compiler extension to C++, so your code is non-portable. In that case, you can see how MSVC++ compiler evaluates function arguments and be relax IF you don't want to run the same code on other platform!


func(++i, i, ++i);

Note that this particular code invokes undefined behavior, because i is incremented more than once without any intervening any sequence point.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • low level calling convention isn't the same thing as the order of evaluation for expressions. – seand Dec 09 '10 at 19:01
  • 1
    Not only is it not portable between platforms, it's not portable between different compilers on the same platform, or even between different versions of the same compiler on the same platform because it's **undefined behavior**. – Adam Rosenfield Dec 09 '10 at 19:09
  • @Adam: I would even add that two subsequent calls to `func` are not guaranteed to yield the same order either. – Matthieu M. Dec 09 '10 at 19:45
1

No, you cannot assume that.
An optimizing compiler will inline short functions. All that __stdcall will garantee is that the __stdcall version of function will be generated, but this does not mean that compiler cannot also inline it in the same time.
If you really want to be sure it's not inlined, you have to declare it in another compilation unit, alough linker optimizations could inline even in this case.

Also, the order of parameters on the stack have nothing to do with the order they are evaluated. For example, for a function call fn(a, b, c) GCC usually won't do

push c
push b
push a 
call fn

but rather

sub esp, 0xC  
mov [esp+8], c
mov [esp+4], b
mov [esp], a
call fn

Note that in the second case it has no restrictions on the order.

ruslik
  • 14,714
  • 1
  • 39
  • 40
1

It is possible for a particular C implementation to define what a compiler will do in certain cases which would, per the standard, be "undefined behavior". For example, setting an int variable to ~0U would constitute undefined behavior, but there's nothing in the C standard that wouldn't allow a compiler to evaluate the int as -1 (or -493, for that matter). Nor is there anything that would forbid a particular compiler vendor from stating that their particular compiler will in fact set the variable to -1. Since __cdecl isn't defined in the C standard, and is only applicable to certain compilers, the question of how its semantics are defined is up to those vendors; since the C standard lists it as undocumented behavior, it will only be documented to the extent particular vendors document it.

supercat
  • 77,689
  • 9
  • 166
  • 211
0

You are changing the same variable more than once between sequence points (function argument evaluation is one sequence point), which cause undefined behaviour regardless of calling convention.

a1ex07
  • 36,826
  • 12
  • 90
  • 103