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?
As per the Standard, there are two things you need to understand and differentiate:
C++ doesn't specify the order in which parameters are passed to a
function (as you said yourself, that is true!)
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.
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.
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.
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.