内存优化C中的递归调用

时间:2011-09-27 00:31:48

标签: c recursion

我有一个递归函数,可以写成:

void func(TypeName *dataStructure, LL_Node **accumulator) {
    func(datastructure->left, accumulator);
    func(datastructure->right, accumulator);

    {
        char buffer[1000];
        // do some stuff
    }

    return;
}        

我知道实际上缓冲区正在函数的开头分配,并将语句放在嵌套的范围块doesn't actually use a new stack frame中。但我不希望编译器一次分配一个指数数量的1000字节缓冲区,当每个级别返回时,它们可以一次分配和丢弃。

我应该使用外部全局变量吗?调用辅助函数强制在递归调用后分配缓冲区?我真正想要捕获的是关于强制这种行为的最干净,最常用的C方式的建议。

编辑:一个附加问题。如果完全相同的accumulator将被传递给func的每次调用,那么将accumulator指针留在全局变量中而不是在每次调用时将其推入堆栈是闻所未闻的吗?

5 个答案:

答案 0 :(得分:4)

由于它一次只能被一个调用使用,所以你可以预先分配它并通过操作数将它传递给所有调用:

void func(TypeName *dataStructure, LL_Node **accumulator, char *buffer) {
    func(datastructure->left, accumulator, buffer);
    func(datastructure->right, accumulator, buffer);

    {
        // do some stuff
    }

    return;
}  

答案 1 :(得分:3)

一个选项是将函数分解为非递归的“public”函数,该函数设置缓冲区并调用需要传入缓冲区的私有递归函数:

struct func_buffer {
    char buffer[1000];
};

static 
void func_private(TypeName *dataStructure, LL_Node **accumulator, struct func_buffer* buf)
{
    func_private(datastructure->left, accumulator, buf);
    func_private(datastructure->right, accumulator, buf);

    // do some stuff with *buf

    return;
}


void func(TypeName *dataStructure, LL_Node **accumulator) {
    struct func_buffer buffer;

    func_private( dataStructure, accumulator, &buffer);

    return;
} 

这样,函数的用户不需要关心如何管理函数的递归部分所使用的内存的细节。所以你可以很容易地改变它以使用全局或动态分配的缓冲区,如果很明显这样的改变是必要的或有意义的。

答案 2 :(得分:2)

您可以将引用传递给缓冲区或使用全局变量。

中使用参考时
void func(TypeName *dataStructure, LL_Node **accumulator, char buffer[]) {
    func(datastructure->left, accumulator, buffer);
    func(datastructure->right, accumulator, buffer);

    {
        char buffer[1000];
        // do some stuff
    }

    return;
}   

void main()
{
   char buffer[1000];

   func (structure, accum, buffer);
}

您正在传递引用,只是指向数组开头的指针,因此您必须记住它的长度。

如果选择使用全局变量,实际上并不是使用堆栈,而是分配程序存储器,即代码和数据共存的共享空间(代码是数据)。因此,如果你这样做,你永远不会在你的通话中使用单字节的额外内存:

char buffer[1000];

void func(TypeName *dataStructure, LL_Node **accumulator) {
        func(datastructure->left, accumulator);
        func(datastructure->right, accumulator);

    {

        // do some stuff
    }

    return;
}   

void main()
{

   func (structure, accum);
}

由您决定选择一个。第二个在每次递归调用时将较少的参数压入堆栈,但会扩大程序大小。第一个对某些人来说更优雅,但有点慢,甚至可能都不明显。

答案 3 :(得分:2)

我会在这种情况下亲自在堆上分配缓冲区,如下所示:

void func(TypeName *dataStructure, LL_Node **accumulator, char *buffer ) {
    bool alloced = false;
    if( buffer == 0 ){
        buffer = (char*) malloc( 1000 );
        alloced = true;
    }
    func(datastructure->left, accumulator, buffer);
    func(datastructure->right, accumulator, buffer);
    {

        // do some stuff
    }
    if( alloced )
        free( buffer );
    return;
}  

如果你不介意C ++语法,你可以让缓冲区默认为零,或者你可以修改函数名并添加

#define func(a,b) __mangledfunc__(a,b,0)

对您的应用来说,这似乎是最简单的。

答案 4 :(得分:0)

我相信您的普通编译器可以优化所谓的“尾递归函数”,其中函数中的最后一条指令基本上是对该函数的递归调用。在这种特殊情况下,函数将简单地重复堆栈帧与每次递归(因此所有堆栈分配的变量都不会被取消/重新分配!)如果你可以在递归调用之前推送所有指令,那么你有一个像样的编译器,它应该工作 - 否则,我只是将它作为参考变量传递。