在函数内部使用函数调用是一种好习惯吗?

时间:2019-05-01 16:26:32

标签: c

我有一个非常大的分子动力学C程序。它适用于N = 10000个粒子。当我通过增加变量大小将粒子增加到N = 100000时,会产生分割错误。

我执行了ulimit -s unlimited,问题已解决。

第一季度。 有什么方法可以检查我的代码要使用多少堆栈内存,代码的性能检查(优化),内存泄漏。

第二季度。 如果我的代码结构类似于嵌套函数,

int main() {
    function1();
    return 0;
}

void function1() {
    for(int i=0;i<1000;i++) {
        function2();
    }
}

void function2() { 
    double Var[100000];
}

在for循环中使用function2()会比只执行一次会占用更多的堆栈内存吗?

3 个答案:

答案 0 :(得分:3)

  

Q1。我想知道是否有任何方法可以检查我的代码要使用多少堆栈内存,代码的性能检查(优化),内存泄漏。

不是。 C标准甚至没有提到堆栈,任何C编译器都可以创建不使用堆栈但仍符合C标准的二进制文件。

但是,实际上,几乎总是使用堆栈,并且开销很小。只需对所有局部变量求和,您将得到一个很好的估计。

不应该预测性能。应该对其进行测量,然后在必要时进行优化。

即使不是不可能,编译器也很难以可靠的方式检测内存泄漏。您可以使用类似Valgrind的程序。

  

Q2。如果我的代码结构类似于嵌套函数,   由于在for循环内调用function2,它将使用比仅使用一次更多的堆栈内存吗?

不。每次调用function2()时,都会创建一个新的堆栈帧,该堆栈帧具有足够的空间来容纳100000个双打。但是函数返回时将立即释放它。这里的问题不是在循环中调用函数。问题是您在堆栈上分配了巨大的数组,这可能会成为问题。您应该考虑动态分配它们。基本上,它看起来像这样:

void function2
{
    double *var = malloc(100000*sizeof(*var));
    /* Code */
    free(var);
}

如果使用递归函数,则堆栈可能会成为问题。让我们考虑一下这个求和函数,它将所有自然数相加到num:

unsigned long long sum(unsigned long long num)
{
    if(num == 0) return num;
    return num + sum(n-1);
}

long long通常为8个字节,因此如果将此函数用于很大的数字(可能是100000或1000000),则可能会遇到堆栈问题。

答案 1 :(得分:2)

  

第一季度。我想知道是否有任何方法可以检查我的代码要使用多少堆栈内存,代码的性能检查(优化),内存泄漏。

您可以通过分析调用图和每个函数中声明的变量来估计程序需要多少堆栈空间,但这对于复杂的程序而言并不容易。如果涉及到任何递归,要困难得多,但是至少应该能够设定一个粗略的上限。但是简单的方法是测量在特征输入上运行时的使用情况。

绝对表现极其,人类很难预测。通常,我们通常能够可靠地做到的最好的事情就是表征性能如何随着问题的规模而扩展。比较效果应始终进行衡量。

对于内存泄漏,人类进行仔细的代码分析通常可以检测到内存泄漏,但是明智的做法是使用运行时内存使用情况分析工具(如Valgrind)进行补充。这样的工具即使在分析频率很高的代码中也能泄漏频率很高。

  

第二季度。如果我的代码结构类似于嵌套函数,   [...]   由于在for循环中使用function2,因此它将使用   比仅使用一次更多的堆栈内存?

不。当控件离开该函数时,将释放该函数的自动分配的资源。无论是循环还是以其他方式多次调用同一函数,都不会比最苛刻的单个调用使用更多的自动(堆栈)资源。

但是,当函数通过直接或间接调用自身而递归时,它可能确实会使用与递归深度成比例的其他资源。我之所以说“可能”,是因为在某些情况下,编译器可以在单个函数调用中将递归转换为迭代。

答案 2 :(得分:0)

最佳做法是不要在堆栈上创建大对象。相反,应该使用C中的rbenv local 2.3.1或C ++中的malloc动态分配它们。

如果分配请求被程序拒绝,您的程序即可恢复。

通过使用C ++中的新智能指针,可以帮助确保释放所有分配并且不发生泄漏,否则,您只需“仔细编写代码”,这是C编程的缺点。