在C中的函数调用之间保存值

时间:2017-10-25 23:28:49

标签: c function static

这两段代码之间的功能有什么不同吗?主要区别在于使用'静态'在第一个例子中,每次调用function1时都要保存x的值。但第二个例子消除了使用静态'静音的需要。通过在for循环的每次迭代中将i的值从main传递给function1。两者具有完全相同的输出。使用单向另一种方式有什么隐藏的优势吗?

注意:第一个例子是我见过的一段代码的简化版本。只是想知道为什么使用它而不是替代方案。

第一个例子:

void function1()
{
    static int x = 0;
    printf("function1 has now been called %d times\n", ++x);
}

int main(void)
{
    for (int i = 0; i < 5; i++)
        function1();
    return 0;
}

第二个例子:

void function1(int i)
{
    printf("function1 has now been called %d times\n", ++i);
}

int main(void)
{
    int i;
    for (i = 0; i < 5; i++)
        function1(i);
    return 0;
}

我很欣赏任何共享的知识。

1 个答案:

答案 0 :(得分:0)

正如人们在评论中所说,每种方法都有利弊。您选择哪一个取决于您所处的情况,以及您愿意做出哪些权衡。以下是一些让你滚动的想法。

静态变量方法

void function1(void)
{
    static int x = 0;
    printf("function1 has now been called %d times\n", ++x);
}

优点:

  • 降低资源使用率:您没有在堆栈上传递x,因此如果内存溢价,您可以使用更少的内存。此外,您还可以保存一些将参数移动到堆栈中的指令。地址也是固定的,因此您无需在代码中存储或操作它。

  • 良好的位置:作为一个静态的,x保持最低限度的目的,这意味着代码更容易理解和调试。如果x的范围增加到整个文件,那么理解起来会更加困难(谁在修改x,如何更改等等)。

  • 更具灵活性(简单界面):使用此功能确实没有错误 - 只需调用它即可。不用担心验证输入。如果你需要移动它,只需放入标题就可以了。

缺点:

  • 功能不太灵活:如果您需要更改x的值,例如,需要重置它,您就没有办法那。您需要以某种方式改变您的设计(使x成为全局,向函数添加一些重置参数等)以使其工作。需求一直在变化,而且可以根据需要进行最低限度的改变,这是良好设计的标志。

  • 难以测试:如上所述,您将如何对此功能进行单元测试?如果你想测试一些低数字和一些高数字(典型的边界测试),你需要遍历整个空间,如果函数需要很长时间才能运行,这可能是不可行的。

参数方法

void function1(int x)
{
    printf("function1 has now been called %d times\n", x);
}

void caller(int *x) /* could get x from anywhere */
{                   /* showing it as a pointer from outside here */
    *x = *x + 1;
    function1(*x);
}

优点:

  • 功能更灵活:如果您的设计要求发生变化,则更改起来相对简单。如果您需要更改序列或具有特殊条件,例如重复第一个值或跳过特定值,那么从调用代码中可以很容易地做到这一点。你已经把事情分开了,所以你可以用一个新的驱动程序,不再需要触摸这个功能了。事情更模块化。

  • 更可测试:该功能可以更轻松地进行测试,让您更有信心实际工作。您可以进行边界测试,测试您担心的输入,或轻松重建故障情景。

  • 易于理解:此格式的功能使能力更容易理解。如果函数为给定的一组输入产生相同的输出,则认为它是纯的。给出的示例并不纯粹,因为它正在执行IO(打印到屏幕上)。但是,一般来说,纯函数更容易推理,因为它没有任何内部状态,所以你真正关心的唯一事情就是输入。就像1 + 1 = 2一样,纯函数具有相同的简化属性。

  • (可能)性能更高:在纯函数的情况下,编译器可以利用函数的引用透明度(一个奇特的单词意味着你可以替换{{1使用add(1,1))并安全地缓存先前调用的结果。如果您已经完成了同样的工作,为什么还要这样做呢?如果调用该函数特别昂贵并且坐在一个用类似参数调用它的紧密循环中,那么你只需要节省大量的周期。同样,这个功能并不纯粹,但即使是这样,你也不会获得任何性能提升,因为它是一个顺序计数器。当计数器换行时会看到任何好处,或者再次使用相同的参数调用该函数。

缺点:

  • 更多资源使用:如果你被挤压内存,那么在传递变量时会占用更多的堆栈空间。您还可以使用更多说明来跟踪它的地址并将其移动。

  • 更容易搞砸:如果你只支持数字1-10,有人会传递0或-1。现在你需要决定如何处理它。

  • (潜在地)性能较差:您还可能陷入代码处理案例 永远不会以防御性编程的名义发生(这是一件好事)事情!)。但总的来说,防御性编程编程不是为速度而构建的。速度来自仔细考虑过的假设。如果您保证您的输入在一定范围内,您可以尽可能快地保持移动,而不必仔细检查您的管道。您通过暴露此界面获得的灵活性会带来性能成本。

  • 架构上不太灵活(更多残忍):如果您从很多地方调用此函数,现在需要使用此参数字符串来为其提供信息。如果您处于特别深的调用堆栈中,则可以有20个或更多函数传递此参数。如果设计发生变化并且您需要将此函数调用从一个地方移动到另一个地方,您可以从现有的调用堆栈中删除参数,更改所有调用者以符合新签名,将参数插入到新的调用堆栈,并更改所有它的调用者以符合新签名!你的另一个选择是单独留下旧的调用栈,这会导致更难的可维护性和更高的&#34;呵呵&#34;当有人从以前的日子里读出额外的行李时。