Иногда при анализе сборки может быть полезно напрямую указать, что происходит, как C, а затем упростить:
int fun(int n) {
int s0 = 0;
int s1 = 1;
int t0 = n; // n is in a0
loop:
if (s0 == t0) {
goto loop_exit;
}
s1 = s1 + s1;
s0 = s0 + 1;
goto loop;
loop_exit:
return s1; // Returns in a0
}
Мы видим, что копирование в t0
не требуется, мы можем используйте n
напрямую. Кроме того, эта конструкция goto
очень похожа на while
l oop, с проверкой условий для цикла в начале.
int fun(int n) {
int s0 = 0;
int s1 = 1;
while (s0 != n) {
s1 += s1;
s0 += 1;
}
return s1;
}
Теперь мы видим, что s1
начинается с 1, а затем удвоить n
раз. Мы можем переписать, дав описательные имена и заменив while
на for
:
int fun(int n) {
int value = 1;
for (int counter = 0; counter != n; counter += 1) {
value += value;
}
return value;
}
Благодаря красоте компиляторов эти три набора кода фактически генерируют то же самое. сборка (на этом конкретном компиляторе c, на момент написания этой статьи).
Когда n
меньше точности int
, эта функция ведет себя как сдвиг влево 1 на n
бит.
В этом неграничном случае fun
является эквивалентно:
int fun(int n) {
return 1 << n;
}
Если n
имеет точность не менее int
, тогда вы столкнетесь с переполнением целого числа со знаком, что приводит к поведению, определяемому реализацией.