You compiled it with g++
to get those errors, if you compile it with gcc
you don't get them.
$ gcc test.c
$ g++ test.c
test.c:3:5: warning: first argument of 'int main(void*)' should be 'int' [-Wmain]
int main(void *framep)
^~~~
test.c:3:5: warning: 'int main(void*)' takes only zero or two arguments [-Wmain]
This is important because C doesn't consider the argument types (or number!) to be part of the type of the function (whereas C++ does). There are various reasons, among them being that in C, the caller cleans up the arguments, so if he specifies too many, he also cleans them up. In C++, the callee cleans up arguments, so if he cleans up the wrong number, you end up with a corrupted stack.
On to why you might choose to use int main(void *framep)
: In the calling convention for C, the arguments are pushed onto the stack, and then the call is made, which places the return address next. The callee will then typically push the old value of EBP, then move the stack pointer into EBP as the "base pointer" for the new stack frame. Then the stack pointer is moved to allocate space of any automatic (local) variables in the callee. I.e., the stack looks like this:
Arg n
Arg n-1
...
Arg 1
Return Addr
Old EBP
Callee locals
Now assume that we'd like to inspect the return address for our function, or read the prior frame pointer (Old EBP
) that was pushed. If we were writing in assembly, we'd just dereference relative to the current frame pointer (EBP
). But we're writing in C. One way to get a reference would be to take the address of the first argument. That is, &framep
, which is the place where Arg1
lives on the stack. Thus (&framep)[-2]
should be a void *
pointing to the stored prior frame pointer (Old EBP
).
(Note: I am assuming Intel architecture, where all pushes to the stack are extended to pointer size by the hardware.)