Я сталкивался с соглашениями о вызовах во время изучения состояний для создания игр на C ++.
В предыдущем вопросе кто-то заявил, что MSDN не очень хорошо объясняет _stdcall — я согласен.
Каковы основные цели для соглашений о вызовах, таких как _stdcall? Имеет ли значение, в каком порядке аргументы размещаются в стеке? Как это уменьшает размер кода в X86 (как кто-то еще сказал)?
Причина того, что немного Соглашение о вызове довольно простое: так, чтобы вызывающий абонент и вызываемый абонент договорились о том, как все будет работать. Без этого вызывающая сторона не знает, куда поместить аргументы при вызове определенной функции.
Что касается того, почему Microsoft определилась с конкретными деталями _stdcall
, это во многом исторические. В MS-DOS все вызовы основывались на регистре, поэтому для всех вызовов ОС требовался язык ассемблера или странные расширения для большинства языков более высокого уровня.
Когда они впервые сделали Windows, они использовали cdecl
соглашение о вызовах, главным образом потому, что это то, что компилятор сделал по умолчанию. По крайней мере, согласно слухам, незадолго до того, как они подготовились к выпуску Windows 1.0, они перешли на соглашение о вызовах Pascal, потому что оно было достаточно более эффективным, что (среди прочего) позволяло Windows помещаться на одном меньшем количестве дискет. Независимо от точный Более того, соглашение о вызовах Паскаля сделало код немного меньше, потому что вызываемая функция очищала аргументы из стека, вместо того, чтобы очищать их везде, где была вызвана функция. Для любой функции, которая была вызвана как минимум из двух разных мест, это победа (и если она связана где-либо еще).
Затем они начали работу над OS / 2 и изобрели еще одно соглашение о вызовах (syscall).
Затем, конечно же, вышел Win32. С технической точки зрения на самом деле не так уж и много проблем с syscall, но (я полагаю) все, что связано с OS / 2, считалось испорченным, поэтому syscall пришлось уйти. Результатом было нечто совершенно иное, чтобы оправдать новое имя. Честно говоря, это немного преувеличение: они действительно добавили одно действительно полезное дополнение: они закодировали число байтов аргументов в каждое имя функции, поэтому, если (например) вы предоставили неправильный прототип для функции, код не не связывать, а не приводить к несоответствию между вызывающим и вызываемым абонентами, что может привести к гораздо более серьезным проблемам.
По большей части, это действительно возвращает нас к исходному пункту: точные детали соглашения о вызовах не имеют большого значения, пока вы не напутаете. В большинстве случаев важно, чтобы вызывающая сторона и вызываемая сторона согласовывали одно и то же, поэтому, если компилятор знает, какие параметры принимает функция, он знает, как сгенерировать код, чтобы правильно передать эти параметры в функцию (и, аналогично, они оба согласны как выполняется очистка стека и т. д.)
Других решений пока нет …