Дурака включаешь ты и реально не догоняешь даже. Смотри, вариант 1: https://godbolt.org/z/b7fLgY вариант 2: https://godbolt.org/z/ju9yry
В варианте 2 временные переменные (bb) далеко от вершины стека (+20, +24, +28), в варианте 1 -- сразу кладутся в [sp] и/или передаются в регистры, без записи в стек. В одном случае (вариант 1) в стеке резервируется 28 байт, в другом (вариант 2) -- 36 байт. В первом варианте вызов функции f() может попортить данные на вершине стека и сломать работу sprintf (испортить её аргумент), вот здесь:
bl f
ldr r4, .L3+4
str r0, [sp]
в варианте 2 такое невозможно, там если даже байт 20 затрёшь с вершины, всё пройдёт гладко:
ldr r4, .L3+4
ldr r1, [sp, #28]
ldr r3, [sp, #24]
str r1, [sp]
ldr r2, [sp, #20]
ldr r1, .L3+8
movs r0, r4
bl sprintf
Даже если компилятор и резервирует место под все переменные в начале функции, то это не значит, что они окажутся именно в этом месте относительно SP, а не в другом или вообще не окажутся в регистрах (временные переменные созданные компилятором, аргументы сразу передаваемые в другую функцию скорей окажутся на вершине стека, а переменные созданные руками -- далеко от вершины).
И хуже того, это не так: в ряде случаев место выделяется динамически... (variable length arrays, передача аргументов в variadic функции в частности, всё что не помещяется в регистрах идёт через стек, место выделяется -- вот сразу туда и можно класть, даже если в стеке что-то зарезервировано ещё).
[ZX]
-
- И чего я не так сказал? - POV_(10.12.2019 12:55, )