This is not just MSVC.
GCC accepts it, if your function definition is below the call site and there is no prototype. C has always allowed calling an undeclared function. It infers the prototype from the call-site. So I think the behavior is related to that aspect (though when I move the function above the call-site in GCC, it changes to an error, which makes sense for C99). This should be Undefined Behavior, nonetheless (different number of args than parameters).
int main()
{
foo(1,2,3);
}
void foo(int a, int b)
{
}
f.c:6:6: warning: conflicting types for ‘foo’ [enabled by default]
void foo(int a, int b)
^
f.c:3:4: note: previous implicit declaration of ‘foo’ was here
foo(1,2,3);
I found this
6.7.5.3p15:
[...] If one type has a parameter type list and the other type is
specified by a function definition that contains a (possibly empty)
identifier list [this is your situation], both shall agree in the
number of parameters, and the type of each prototype parameter shall
be compatible with the type that results from the application of the
default argument promotions to the type of the corresponding
identifier. [...]
.... but this paragraph is not part of a constraint. Violating a
"shall" outside a constraint section is undefined behavior, not
must-be-diagnosed behavior (4p2).
I quoted this from : http://compgroups.net/comp.lang.c/why-is-this-not-an-error-in-visual-c/732881
Your mileage may vary.
In short, apparently the only requirement is that the compiler barks at you for some definition of bark. In VS 2013 it is treated as an error.
As far as what happens to the argument, for the same reason that variable argument lists work in C, the call site should push the extra argument, yet the callee will not be aware of it (just guessing here). Though it works, it does not mean it is defined behavior.