在循环外声明变量和在循环内声明静态变量有什么区别?


9

这是我可以在循环(或任何函数)之外保存变量的两种方式。

首先,我可以在循环外使用全局范围声明它:

void setup()
{
    Serial.begin(9600);
}

int count = 0;

void loop()
{
    Serial.println(count);
    count++;

    delay(250);
}

我还可以在循环内将其声明为静态的:

void setup()
{
    Serial.begin(9600);
}

void loop()
{
    static int count = 0;

    Serial.println(count);
    count++;

    delay(250);
}

这有什么区别(如果有)?

Answers:


10

最基本的区别是范围。

在第一种情况下,您要声明一个全局变量。在定义之后,它是在每个范围内均可访问的变量。

void setup()
{
    Serial.begin(9600);
}

void inc();
int count = 0;

void loop()
{
    Serial.println(count);
    count++;

    inc();

    delay(500);
}

void inc() //Can edit the value of count
{
  count=count+1;
};

在第二种情况下,您将声明一个具有局部作用域的静态变量。该变量将在整个程序运行时持久保存,类似于全局变量,但只能在声明它的代码块中访问。这是相同的示例,只有一个更改。count现在已在内部声明为静态变量loop

void inc();

void loop()
{
    static int count = 0;
    Serial.println(count);
    count++;

    inc();

    delay(500);
}

由于该函数inc()无权访问,因此无法编译count

全局变量尽管看似有用,但也有一些陷阱。在编写可以与周围环境交互的程序时,这些甚至可能导致损坏。这是一个非常基本的示例,说明一旦程序开始变大,很可能会发生这种情况。函数可能会无意间更改全局变量的状态。

void setup()
{
    Serial.begin(9600);
}
void another_function();
int state=0;

void loop()
{
    //Keep toggling the state
    Serial.println(state);
    delay(250);
    state=state?0:1;

    //Some unrelated function call
    another_function();
}

void another_function()
{
  //Inadvertently changes state
  state=1;

}

这种情况很难调试。这种类型的问题然而,可以很容易地通过简单地使用一个静态变量检测。

void setup()
{
    Serial.begin(9600);
}
void another_function();

void loop()
{
    static int state=0;

    //Keep toggling the state
    Serial.println(state);
    delay(250);
    state=state?0:1;

    //Some unrelated function call
    another_function();
}

void another_function()
{
  //Results in a compile time error. Saves time.
  state=1;

}

5

从功能的角度来看,两个版本都产生相同的结果,因为在这两种情况下,的值count都存储在的执行之间loop()(因为它是全局变量,或者因为它被标记为static并因此保留其值)。

因此,选择的决定取决于以下参数:

  1. 一般情况下,在计算机科学,鼓励,让您的变量本地尽可能来讲范围。这通常可以使代码更清晰,副作用更少,并减少其他人使用该全局变量破坏逻辑的机会。例如,在您的第一个示例中,其他逻辑区域可能会更改该count值,而在第二个示例中,只有该特定函数loop()才能更改该值。
  2. 全局变量和静态变量始终占用内存,只有局部变量在范围内时才这样做。在上面的示例中,这没有什么区别(因为在其中一个示例中使用了全局变量,在另一个示例中使用了静态变量),但是在更大,更复杂的程序中,它可能并且可以使用非静态局部变量来节省内存。 但是:如果在经常执行的逻辑区域中有一个变量,请考虑使其成为静态变量或全局变量,因为否则每次输入该逻辑区域时都会失去一点性能,因为这样做会花费一些时间。为该新变量实例分配内存。您需要在内存负载和性能之间找到平衡。
  3. 其他方面,例如更好的静态分析布局或由编译器进行优化,也可能会起作用。
  4. 在某些特殊情况下,静态元素的不可预测的初始化顺序可能会出现问题(不确定那一点,不过请比较此链接)。

来源:arduino.cc上的类似线程


由于Arduino不支持并发性,因此重入永远不会成为Arduino的问题。
彼得·布鲁姆菲尔德

真正。这更多的是一般性观点,但实际上与Arduino不相关。我删除了那一点。
菲利普·阿尔盖尔2014年

1
在范围内声明的静态变量将始终存在,并与全局变量使用相同的空间!在OP代码中,唯一的区别是哪些代码可以访问变量。在scipe中,可以在同一范围内访问静态。
jfpoilpret 2014年

1
@jfpoilpret当然,这是对的,我发现我回答的各个部分都有误导性。固定的。
菲利普·阿尔盖尔2014年

2

这两个变量都是静态的-它们在整个执行会话中都保持不变。如果全局函数声明了(但未定义)全局变量,或者该函数在同一编译单元(文件+包含文件)中遵循该定义,则它对于任何函数都是可见的。

将的定义移动count到函数内部不仅将其可见性范围限制到最接近的{}es 封闭集合,而且赋予其函数调用生存期(在函数进入和退出时创建和销毁它)。声明它static也使它具有执行会话生存期,从执行会话的开始到结束一直存在,并在函数调用之间保持不变。

顺便说一句:谨慎使用函数中的初始化静态变量,因为我已经看到某些版本的gnu编译器会出错。应该在每个函数条目上创建并初始化带有初始化程序的自动变量。具有初始化程序的静态对象仅应在执行设置过程中初始化一次,然后才对main()进行控制(就像全局变量一样)。我已经在每个函数项上重新初始化了局部静态变量,就好像它们是自动的一样,这是不正确的。请确保测试您自己的编译器。


我不确定我是否理解您对声明全局函数的含义。你的意思是extern
彼得·布卢姆菲尔德

@ PeterR.Bloomfield:我不确定您要问的帖子的哪一部分,但是我指的是OP的两个示例-第一个是固有的全局定义,第二个是局部静态值。
JRobert

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.