函数调用中的堆栈使用情况和堆栈重用

时间:2019-07-12 09:47:52

标签: c optimization

我正在从事一个物联网项目,其中内存管理很重要。

我遇到了堆栈使用问题:如果直接将返回结构的函数(或语句表达式或Lambda)作为另一个函数的参数调用,则会在堆栈上添加新结构,并且不会重用内存。

下面的示例代码在堆栈中添加了4种结构

struct TestStruct
{
    uint32_t field1;
    uint32_t field2;
    uint32_t field3;
    uint32_t field4;
} ;

struct TestStruct initStructure(uint32_t f1, uint32_t f2, uint32_t f3, uint32_t f4)
{
    struct TestStruct myStruct;
    myStruct.field1 = f1;
    myStruct.field2 = f2;
    myStruct.field3 = f3;
    myStruct.field4 = f4;
    return myStruct;
}

void doStuff(struct TestStruct myStruct)
{
    printf("f1 = %d, f2 = %d, f3 = %d, f4 = %d", myStruct.field1, myStruct.field2, myStruct.field3, myStruct.field4);
}

int main(void)
{
    doStuff(initStructure(1,2,3,4));
    doStuff(initStructure(11,22,33,44));
    doStuff(initStructure(11,12,13,14));
    doStuff(initStructure(21,22,23,24));
}

我会认为,由于结构的行为就像在单独范围内的自动临时变量一样,因此内存将被重用。在每个函数调用周围添加作用域不会执行任何操作,因此在您输入主函数时,结构会保留在堆栈中。

我正在使用ARM GCC,并且-fstack-reuse = all和-Os,-Og,-O1,-O2会发生这种情况。有没有办法强迫这些变量重用堆栈?

谢谢

更新:碰巧的是,我们的工作环境已设置为无论如何都使用-x c++,这是导致堆栈问题的原因。看来c ++ 14和c ++ 17在堆栈重用方面并不是很积极,这对我们来说是个问题。

另一点是,上面的代码的行为确实与我在环境中所解释的相同,但不是在干净的环境中。要观察堆栈问题,可以initStructure函数进行如下修改:

struct TestStruct initStructure(uint32_t f1, uint32_t f2, uint32_t f3, uint32_t f4)
{
    struct TestStruct myStruct;
    myStruct.field1 = f1;
    myStruct.field2 = f2;
    myStruct.field3 = f3;
    myStruct.field4 = f4;

    printf("Temporary address %x", &myStruct);
    return myStruct;
}

在没有-x c++的情况下,堆栈可以重用,但是堆栈没有被重用。

为什么c ++无法重用堆栈?我们在编译时使用了一些非常有趣的字符串计算,这在C语言中是不可用的,因此对我们而言,同时进行字符串处理和内存优化非常重要。

1 个答案:

答案 0 :(得分:1)

也许您的代码应该看起来像这样。通过指针传递结构,这样,您无需将所有结构都复制到堆栈中,而仅将其地址复制到堆栈中。就您而言,当您调用doStuff(initStructure(1,2,3,4))时,堆栈中同时具有3个复制的结构。 函数initStructure()在堆栈中为2个结构保留了内存,因为您将其返回类型声明为结构,并且在函数内部创建了另一个局部结构。函数doStuff在堆栈中为您传递给它的参数分配1个结构。由于不需要通过调用函数在堆栈上分配那么多的内存,因此按指针传递在性能上也将是最佳的。

typedef struct 
{
    uint32_t field1;
    uint32_t field2;
    uint32_t field3;
    uint32_t field4;
}TestStruct;

TestStruct myStruct;

void initStructure(uint32_t f1, uint32_t f2, uint32_t f3, uint32_t f4, TestStruct* myStruct)
{
    //struct TestStruct myStruct;
    myStruct->field1 = f1;
    myStruct->field2 = f2;
    myStruct->field3 = f3;
    myStruct->field4 = f4;
    //return &myStruct;
}

void doStuff(TestStruct* myStruct)
{
    printf("f1 = %d, f2 = %d, f3 = %d, f4 = %d", myStruct->field1, myStruct->field2, myStruct->field3, myStruct->field4);
}

int main(void)
{
    initStructure(1,2,3,6,&myStruct);
    doStuff(&myStruct);
}