Why does this function call behave sensibly after calling it through a typecasted function pointer?

As others have pointed out, this is undefined behavior, so all bets are off about what may in principle happen. But assuming that you’re on an x86 machine, there’s a plausible explanation as to why you’re seeing this.

On x86, the g++ compiler doesn’t always pass arguments by pushing them onto the stack. Instead, it stashes the first few arguments into registers. If we disassemble the f function, notice that the first few instructions move the arguments out of registers and explicitly onto the stack:

    push    rbp
    mov     rbp, rsp
    sub     rsp, 16
    mov     DWORD PTR [rbp-4], edi  # <--- Here
    mov     DWORD PTR [rbp-8], esi  # <--- Here
    # (many lines skipped)

Similarly, notice how the call is generated in main. The arguments are placed into those registers:

    mov     rax, QWORD PTR [rbp-8]
    mov     edx, 30      # <--- Here
    mov     esi, 20      # <--- Here
    mov     edi, 10      # <--- Here
    call    rax

Since the entire register is being used to hold the arguments, the size of the arguments isn’t relevant here.

Moreover, because these arguments are being passed via registers, there’s no concern about resizing the stack in an incorrect way. Some calling conventions (cdecl) leave the caller to do cleanup, while others (stdcall) ask the callee to do cleanup. However, neither really matters here, because the stack isn’t touched.

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)