На прошлой неделе я был отладкой кода, и подошла странная ситуация: gdb проходит через два различных пункта возврата. Я сделал простой пример, который иллюстрирует ситуацию:
#include <iostream>
using namespace std;
int test() {
string a = "asd";
string b = "asd";
while (true) {
if (a == b) {
return 0;
}
}
return -1;
}
int main() {
int result = test();
cout << "result: " << result << endl;
}
При отладке кода я добрался:
(gdb) b main
Breakpoint 1 at 0x1d4c: file example.cpp, line 19.
(gdb) r
Starting program: /Users/yuppienet/temp/a.out
Reading symbols for shared libraries +++. done
Breakpoint 1, main () at example.cpp:19
19 int result = test();
(gdb) s
test () at example.cpp:7
7 string a = "asd";
(gdb) n
8 string b = "asd";
(gdb) n
11 if (a == b) {
(gdb) n
12 return 0;
(gdb) n
15 return -1;
(gdb) n
16 }
(gdb) n
main () at example.cpp:20
20 cout << "result: " << result << endl;
(gdb) n
result: 0
21 }
(gdb) n
0x00001ab2 in start ()
Я отметил, что, даже если gdb показывает строку 15, возвращаемое значение 0 ( finish
команда подтверждает это также).
Таким образом, вопрос: почему делает выставочную строку gdb 15: return -1
, даже если функция действительно не возвращает это значение?
Спасибо!
Править: Я забыл упоминать, что я скомпилировал со следующей строкой:
g++ -Wall -pedantic -g -pg example.cpp
Я подозреваю, что вы видите эпилог функции. У ваших двух строк есть деструкторы, которые неявно вызываются при возврате. Проверьте, что говорит дизассемблер, чтобы быть уверенным, но я подозреваю, что оба оператора return отображают что-то вроде:
stash return_value;
goto epilogue;
и, соответственно:
epilogue:
destroy a; // on the stack, so destructor will be called
destroy b;
really_return(stashed value);
Эпилог, похоже, исходит из строки 15 как побочный эффект того, как g ++ выполняет нумерацию строк - довольно простой формат, на самом деле просто список тегов в форме «адрес X происходит из строки с номером Y» - и поэтому он сообщает 15 как самое близкое совпадение. В данном случае это сбивает с толку, но часто поправляет.
Возможно, потому что регистр программного счетчика проходит через инструкции, которые лучше всего соответствуют окончательному возврату, т.е. последовательности выхода функции. Фактическое значение возврата, вероятно, хранится в регистре, поэтому первый return
просто загружает нужное значение и переходит в конец функции, а затем этот адрес "привязывается" к строке исходного кода финального return
.
Вы не говорите, но если вы скомпилировали с оптимизацией, это в точности то поведение, которое вы могли бы увидеть в gdb. Вы видите, что первая строка устанавливает возвращаемое значение, а затем она переходит к реальной инструкции возврата, но в C ++ вы видите все, включая возвращаемое значение.