Причина проста: аргументы функции помещаются в стек вызывающей функцией (единственной, которая может это сделать, потому что только она обладает необходимой информацией; в конце концов, весь смысл в том, чтобы передать этоинформация для вызываемой функции).Адрес возврата помещается в стек с помощью механизма вызова функции.Функция вызывается после вызывающая функция установила параметры, потому что после вызова выполняется вызываемая функция, а не вызывающая.
ОК, так что теперь вы можете поспоритьчто вызывающая функция может поставить параметры за пределы текущего используемого стека, и вызываемая функция может затем просто соответствующим образом настроить указатель стека.Но это не сработает, потому что в любой момент может быть прерывание или сигнал, который помещает текущее состояние в стек, чтобы восстановить его позже (я не удивлюсь, если переключатель задач тоже так сделает),Но если вы установите параметры за пределами текущего стека, эти асинхронные события перезапишут его, и поскольку вы не можете предсказать, когда они произойдут, вы не можете этого избежать (кроме отключения, которое может иметь другие недостатки или даже быть невозможным, в случаепереключателя задач).По сути, все, что находится за пределами текущего стека, должно рассматриваться как изменчивое.
Также обратите внимание, что это не зависит от вопроса о том, кто очищает параметры.В принципе, вызываемая функция может вызывать деструкторы вызова аргументов, даже если физически они лежат в кадре стека вызывающей стороны.Кроме того, многие процессоры (включая x86) имеют инструкции, которые автоматически возвращают дополнительное пространство над адресом возврата при возврате (например, компиляторы Pascal обычно делают это, потому что в Pascal нет очистки после возврата памяти, и, по крайней мере, дляПроцессоры того времени было более эффективно очищать с помощью этой инструкции процессора (я понятия не имею, верно ли это для современных процессоров). Однако C не использовал этот механизм из-за списков аргументов переменной длины:этот механизм неприменим, потому что вам нужно знать во время компиляции, сколько дополнительного пространства нужно освободить, а K & R C не требуется предварительно объявлять функции с переменными значениями (C89 делает, но лишь немногие, если какие-либо компиляторы используют это в силусовместимости со старым кодом), поэтому вызывающая функция не могла узнать, следует ли очищать аргументы, если она не должна делать это всегда.