未在递归函数中创建调用栈

时间:2018-07-11 11:15:43

标签: c++ recursion callstack

#include<iostream>
#include<vector>
#include<stack>
#include<utility>
#define P(i, j) all[i-r0][j-c0]
#define C(i, j) check[i-r0][j-c0]

int r0, c0, r1, c1;
std::stack<std::pair<std::pair<int, int>, int>> s;
std::vector<int> mov = {-1, 0, 1};

int move(std::vector<std::vector<bool>> all, std::vector<std::vector<bool>> check){
    auto p = s.top();
    if(p.first.first==r1&&p.first.second==c1)
        return p.second;
    while(!s.empty()){
        for(int i=0; i<3; i++)
            for(int j=0; j<3; j++){
                auto r = p;
                r.first.first += mov[i];
                r.first.second += mov[j];
                r.second++;
                if(r.first.first>=r0&&r.first.first<=r1&&r.first.second>=c0&&r.first.second<=c1&&P(r.first.first, r.first.second)&&!C(r.first.first, r.first.second)){
                    s.push(r);
                    C(r.first.first, r.first.second) = 1;
                    return move(all, check);
                }
            }
        s.pop();
    }
}

int main(){
    std::cin>>r0>>c0>>r1>>c1;
    s.push({{r0, c0}, 0});
    int n;  std::cin>>n;
    std::vector<std::vector<bool>> all(r1-r0+1, std::vector<bool>(c1-c0+1));
    std::vector<std::vector<bool>> check(r1-r0+1, std::vector<bool>(c1-c0+1));
    C(r0, c0)=1;
    for(int i=0; i<n; i++){
        int tempx;
        std::cin>>tempx;
        int tempy1, tempy2;
        std::cin>>tempy1>>tempy2;
        for(int j=tempy1; j<=tempy2; j++)
            if(j<=c1&&j>=c0&&tempx<=r1&&tempx>=r0)
                P(tempx, j) = 1;
    }
    std::cout<<move(all, check)<<'\n';
}

在上述程序中,当我提供以下输入时

5 7 6 11
3
5 3 8
6 7 11
5 2 5

,然后使用调试器分析代码,这很奇怪,在第6次调用时,当代码到达return move(all, check)时,它没有被调用,也没有为它创建堆栈。相反,它只是被跳过,s.pop()函数被顺序调用。有任何正当的理由吗?

如果要将其放在调试器上,请在return move(all, check)s.pop()处使用断点。

1 个答案:

答案 0 :(得分:1)

  

有什么正当理由吗?

是的,这些原因是编译器优化,特别是tail call optimization。只要程序的可观察的行为不变,编译器就可以生成所需的任何代码。

在这种情况下,尾部调用优化使编译器只需重用当前堆栈即可消除创建新堆栈帧的开销。由于您的调试会话(和堆栈框架)不被视为可观察到的行为的一部分,因此完全可以在编译器内部处理这样的调用堆栈。

内联函数也会发生类似的情况:由于内联代码已替换了函数调用,因此您也不会获得新的堆栈框架。

尽管如此,大多数编译器不会在调试版本中进行这些优化。因此,如果您希望使用“真正的”调用堆栈进行调试,请切换到调试版本(并希望该错误仍然出现在此位置)。