From C textbooks you know that you can use function arguments just like local variables. Let's try.
First example:
#include <stdio.h>
void f()
{
int x, y;
for (x=0; x<3; x++)
for (y=0; y<3; y++)
printf ("%d %d\n", x, y);
};
void main()
{
f();
};
Looks innocent! Let's rewrite it:
void f(int x, int y)
{
for (x=0; x<3; x++)
for (y=0; y<3; y++)
printf ("%d %d\n", x, y);
};
void main()
{
f(0, 0);
};
Works just like the first function.
Compile both for 32-bit x86 (you may need to install additional GCC deps):
gcc -S -masm=intel -m32 fname.c
Compare (diff) two assembly outputs (some lines omitted):
% diff -u v1.s v2.s
...
mov ebp, esp
push ebx
- sub esp, 20
+ sub esp, 4
call __x86.get_pc_thunk.bx
add ebx, OFFSET FLAT:_GLOBAL_OFFSET_TABLE_
- mov DWORD PTR -16[ebp], 0
+ mov DWORD PTR 8[ebp], 0
jmp .L2
.L5:
- mov DWORD PTR -12[ebp], 0
+ mov DWORD PTR 12[ebp], 0
jmp .L3
.L4:
sub esp, 4
- push DWORD PTR -12[ebp]
- push DWORD PTR -16[ebp]
+ push DWORD PTR 12[ebp]
+ push DWORD PTR 8[ebp]
lea eax, .LC0@GOTOFF[ebx]
push eax
call printf@PLT
add esp, 16
- add DWORD PTR -12[ebp], 1
+ add DWORD PTR 12[ebp], 1
.L3:
- cmp DWORD PTR -12[ebp], 2
+ cmp DWORD PTR 12[ebp], 2
jle .L4
- add DWORD PTR -16[ebp], 1
+ add DWORD PTR 8[ebp], 1
.L2:
- cmp DWORD PTR -16[ebp], 2
+ cmp DWORD PTR 8[ebp], 2
jle .L5
...
The only difference is that x/y variables are located on another side of ESP (stack pointer)!
In x64 that picture will be slightly different and somewhat harder to understand. This is why I used 32-bit code here in my example.

Yes, I know about these lousy Disqus ads. Please use adblocker. I would consider to subscribe to 'pro' version of Disqus if the signal/noise ratio in comments would be good enough.