如何在C ++中找到递归函数的深度

时间:2010-10-29 22:26:52

标签: c++ recursion

如何在不传递上一级的情况下在C ++中找到递归函数内的当前深度?即,是否可以知道调用函数的次数而不使用参数来跟踪级别并在每次调用函数时将该数字作为参数传递?

例如,我的递归函数如下所示:

DoSomething(int level)
{
  print level;
  if (level > 10)
    return;
  DoSomething(++level);
}

main
{
  DoSomething(0);
}

8 个答案:

答案 0 :(得分:13)

基于JoshD已经给出的答案:

void recursive() 
{ 
    static int calls = 0;
    static int max_calls = 0;
    calls++;
    if (calls > max_calls)
        max_calls = calls;

    recursive();

    calls--;
}

这会在递归函数完成后重置计数器,但仍会跟踪递归的最大深度。

除了快速测试之外,我不会像这样使用静态变量,不久之后就会被删除。如果你真的需要持续跟踪这个,那就有更好的方法。

答案 1 :(得分:6)

您可以在函数中使用静态变量...

void recursive()
{
 static int calls = 0;
 calls++;
 recursive();
}

当然,当你开始一个新的始发呼叫时,这将继续计算....

答案 2 :(得分:3)

如果您不关心线程安全性,可以使用本地静态变量。

虽然,这只会在您第一次运行递归例程时给出正确的计数。更好的技术是RAII保护类型,它包含一个内部静态变量。在递归例程开始时,构造保护类。构造函数会增加内部静态变量,析构函数会减少它。这样,当你创建一个新的堆栈帧时,计数器会增加1,当你从每个堆栈帧返回时,计数器会减1。

struct recursion_guard
{
    recursion_guard() { ++counter; }

    ~recursion_guard() { --counter; }

    static int counter;
};

int recursion_guard::counter = 0;

void recurse(int x)
{
    recursion_guard rg;
    if (x > 10) return;
    recurse(x + 1);
}

int main()
{
    recurse(0);
    recurse(0);
}

但请注意,这仍然不是线程安全的。如果需要线程安全性,可以使用boost::thread_specific_ptr或C ++ 0x线程本地工具将thread-local-storage变量替换为static-storage变量。

答案 3 :(得分:3)

如果您希望它是可重入且线程安全的,为什么不:

void rec(int &level)  // reference to your level var
{
   // do work

   rec(++level); // go down one level
}

main()
{
   //and you call it like
   int level=0;
   rec(level);

   cout<<level<<" levels."<<endl;
}

没有静态/全局变量来搞乱线程,你可以为不同的递归链使用不同的变量来解决重入问题。

答案 4 :(得分:1)

如果可以在编译时确定,也可以将级别作为模板参数传入。您还可以使用函数对象。这是迄今为止最好的选择 - 减少麻烦,应尽可能避免使用静态变量。

struct DoSomething {
    DoSomething() {
        calls = 0;
    }
    void operator()() {
        std::cout << calls;
        calls++;
        if (calls < 10)
            return operator()();
        return;
    }
    int calls;
};

int main() {
    DoSomething()(); // note the double ().
    std::cin.get();
}

答案 5 :(得分:0)

level转换为能够包含参数和(可能)函数的新对象(通常是模板)的实例变量。那么你可以重用递归累加器接口。

答案 6 :(得分:0)

您还可以尝试使用全局变量来记录深度。

var depth = 0;

DoSomething()
{
  print ++depth;
  if (depth > 10)
    return;
  DoSomething();
}

main
{
  DoSomething(0);
}

答案 7 :(得分:0)

当我感觉到需要某种递归时,我来到了这里,因为我正在实现一个可以验证证书链中信任链的函数。这不是 X.509 ,而是仅仅是证书的颁发者密钥必须与签名者的公开密钥匹配的基础知识。

bool verify_chain(std::vector<Cert>& chain,
              Cert* certificate,
              unsigned char* pOrigin = nullptr, int depth = 0)
{
    bool flag = false;

    if (certificate == nullptr) {
        // use first element in case parameter is null
        certificate = &chain[0];        
    }

    if (pOrigin == nullptr) {
        pOrigin = certificate->pubkey;            
    } else {
        if (std::memcmp(pOrigin, certificate->pubkey, 32) == 0) {
            return false; // detected circular chain
        }
    }

    if (certificate->hasValidSignature()) {
        if (!certificate->isRootCA()) {
            Cert* issuerCert = certificate->getIssuer(chain);
            if (issuerCert) {
                flag = verify_chain(chain, issuerCert, pOrigin, depth+1);
            }
        } else {
            flag = true; 
        }
    }

    if (pOrigin && depth == 1) {
        pOrigin = nullptr;
    }

    return flag;
}

我需要知道递归深度,以便可以正确清理pOrigin。在展开调用堆栈时,将其放在正确的堆栈帧上。

我使用pOrigin来检测循环链,没有循环链,递归调用将永远进行下去。例如,

  • cert0签署cert1
  • cert1签署cert2
  • cert2签署cert0

后来我意识到,对于只有一个公共链的简单情况,简单的for-loop就能做到。

bool verify_chain2(std::vector<Cert> &chain, Cert& cert)
{
    Cert *pCert = &cert;
    unsigned char *startkey = cert.pubkey;

    while (pCert != nullptr) {
        if (pCert->hasValidSignature()) {
            if (!pCert->isRootCA()) {
                pCert = pCert->getIssuer(chain);
                if (pCert == nullptr
                || std::memcmp(pCert->pubkey, startkey, 32) == 0) {
                    return false;
                }
                continue;
            } else {
                return true;
            }
        } else {
            return false;
        }
    }

    return false;
}

但是,如果没有一个公共链,而是每个证书中都有该链,则必须进行递归。我欢迎任何评论。谢谢。