...
Код в main.cpp знает (исходя из доступной декларации функции), что у неё 1 параметр типа int.
Он ложит его в стек и выполняет инструкцию call по переходу на адрес функции (при этом в стек дополнительно ложится адрес возврата).
Функция уже в своём теле знает, что у неё 3 локальных переменных типа int - она просто берет и (сама) добавляет их к стеку.
При возврате функция убирает свои локальные переменные со стека сама.
Далее возможны варианты - если это "классический си", то функция выполняет инструкцию ret, которая убирает со стека адрес возврата и переходит по нему, чтобы продолжить выполнение вызвавшего функцию кода, а уже этот код еще раз убирает со стека параметр функции и продолжает работу.
Если же мы имеем дело с соглашениями по вызову функций адаптированными под архитектуру Intel 386, то сама функция может выполнить инструкцию ret X, где X это размер параметров, которые уберутся при возврате из функции автоматически, это меньше инструкций в ассемблере получится.
Как видно нигде в этой схеме вызывающему коду не надо знать сколько и каких у функции внутри локальных переменных. Всё что его интересует - это параметры и возвращаемое значение.
Именно поэтому чтобы вызвать функцию эти вещи и нужно явно компилятору прописать.