Вопрос об исключении оператора возврата в объявлении функции - PullRequest
0 голосов
/ 23 января 2019

Рассмотрим следующий код для создания списка чисел от 0 до 9 вместе со значениями 2 и -3, возведенными в степень соответствующего числа из списка:

#include <stdio.h>

int power(int m, int n);

main()
{
   int i;

   for (i = 0; i <= 10; ++i)
      printf("%d %d %d\n", i, power(2, i), power(-3, i));
   return 0;
}

int power(int base, int n)
{
   int i, p;

   p = 1;
   for (i = 1; i <= n; ++i)
      p = p * base;
   // return statement purposefully omitted. //
}

Конечно, программане работает должным образом без оператора return для функции power, однако, запустив написанный код, я получаю следующий вывод:

0 1 1
1 2 2
2 3 3
3 4 4
4 5 5
5 6 6
6 7 7
7 8 8
8 9 9
9 10 10

И мне интересно, где находятся числа во втором и третьем столбцевыход из?При отсутствии действительного возвращаемого значения мощности элемент управления возвращается обратно к вызывающей функции, но почему он выводит эти числа?

1 Ответ

0 голосов
/ 23 января 2019

Как указали @dyukha и @Daniel H, возвращаемое значение равно «что есть в вашем EAX регистре».Ваша функция завершается с for -циклом, следовательно, последняя инструкция, выполненная перед возвратом (завершением функции), вероятно, была тестом ветвления, чтобы проверить, если i <= n (ваше условие цикла).На самом деле вы можете проверить, какая переменная установлена ​​в вашем регистре EAX, используя ваш компилятор для генерации версии сборки вашего кода (опция -S).Вы можете попытаться проследить, какие значения установлены в вашем регистре, перед вызовом

    popq    %rbp
    retq

в конце вашей функции.

На моем компьютере я пытался использовать Apple LLVM версии 9.0.0 (clang-900.0.39.2), который генерирует следующее для моей функции:

    movl    %edi, -8(%rbp)
    movl    %esi, -12(%rbp)
    movl    $1, -20(%rbp)
    movl    $1, -16(%rbp)
LBB2_1:                                 ## =>This Inner Loop Header: Depth=1
    movl    -12(%rbp), %eax
    cmpl    -16(%rbp), %eax
    jl  LBB2_4
## BB#2:                                ##   in Loop: Header=BB2_1 Depth=1
    movl    -20(%rbp), %eax
    imull   -8(%rbp), %eax
    movl    %eax, -20(%rbp)
## BB#3:                                ##   in Loop: Header=BB2_1 Depth=1
    movl    -16(%rbp), %eax
    addl    $1, %eax
    movl    %eax, -16(%rbp)
    jmp LBB2_1
LBB2_4:
    movl    -4(%rbp), %eax
    popq    %rbp
    retq

Как видите, у меня есть 4 замечательных адреса: -8(%rbp), -12(%rbp), -16(%rbp) и -20(%rbp).Учитывая порядок объявления в коде C и порядок инициализации, -8(%rbp) равно base, -12(%rbp) равно n, -16(%rbp) равно i и -20(%rbp) равно p.

LBB2_1 - это состояние вашей петли.Инструкции для проверки: * переместить значение n в %eax * - это значение на %eax ниже значения i, сохранить результат в %eax *, если %eax говорит, что оно былониже, перейдите к метке LBB2_4 иначе перейдите к следующей инструкции

Три инструкции после BB # 2 - это фактическое умножение.Три инструкции после BB # 3 - это ваш прирост i, за которым следует безусловный переход к условию вашего цикла в метке LBB2_1.

Окончание функции power состоит в том, чтобы взять все, что находится вадрес памяти -4(%rbp), положить его в %eax, а затем оставить функцию (сбросить указатель стека, поместить значение в %eax в соответствующую переменную в предыдущем кадре стека).

В кодесозданный моим компилятором, я не вижу того же результата, что и вы, так как я получаю каждый раз, когда последние два столбца равны 0 (-4(%rbp) никогда не устанавливается на что-либо).За исключением случаев добавления вызова к другой функции foo, принимая два целых числа в качестве параметров, имея две локальные целочисленные переменные (чтобы гарантировать, что мой новый кадр стека будет иметь тот же размер, что и степенная функция).Эта функция фактически устанавливает адрес -4(%rbp).При вызове моей функции непосредственно перед входом в цикл я нахожу значение из -4(%rbp), установленное в моей функции foo, возвращаемое в моей функции power.

Как только что сказал мне коллега, играя снеопределенное поведение опасно, так как вашему компилятору разрешено обращаться с ним любым удобным для него способом.Это может быть вызов демона во что бы то ни стало.

В окончательном, или TL; DR, это неопределенное поведение обрабатывается компилятором в некоторым образом .Независимо от того, перемещено ли какое-либо значение из одной локальной переменной, определено или нет, или ничего особенного не перемещено в регистр %eax, зависит от компилятора.В любом случае, все, что там висело, возвращалось, когда вызывали retq.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...