Qual é a diferença entre declarar uma variável fora do loop e declarar o loop interno estático?

9

Estas são duas maneiras pelas quais posso manter uma variável fora do loop (ou qualquer outra função).

Primeiro, posso declarar com escopo global fora do loop:

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

int count = 0;

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

    delay(250);
}

Também posso declarar estático dentro do loop:

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

void loop()
{
    static int count = 0;

    Serial.println(count);
    count++;

    delay(250);
}

Que diferença, se houver, isso fará?

Cybergibbons
fonte

Respostas:

10

A diferença mais básica é de escopo.

No primeiro caso, você está declarando uma variável global. É uma variável que é acessível em todos os escopos após sua definição.

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;
};

No segundo caso, você está declarando uma variável estática com escopo local. A variável persistirá para que o programa inteiro seja executado de maneira semelhante às variáveis ​​globais, mas estará acessível apenas no bloco de código em que está declarado. Este é o mesmo exemplo, com apenas uma alteração. countagora é declarado como uma variável estática dentro loop.

void inc();

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

    inc();

    delay(500);
}

Isso não será compilado, pois a função inc()não tem acesso count.

As variáveis ​​globais, por mais úteis que sejam, vêm com algumas armadilhas. Eles podem até causar danos quando se trata de escrever programas que podem interagir com o ambiente físico. Este é um exemplo muito básico de algo que provavelmente acontecerá, assim que os programas começarem a ficar maiores. Uma função pode alterar inadvertidamente o estado de uma variável global.

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;

}

Esses casos são muito difíceis de depurar. Esse tipo de problema, no entanto, pode ser facilmente detectado, simplesmente usando uma variável estática.

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;

}
asheeshr
fonte
5

De uma perspectiva funcional, ambas as versões geram o mesmo resultado, pois, em ambos os casos, o valor de counté armazenado entre as execuções de loop()(por ser uma variável global ou por estar marcado como statice, portanto, mantém seu valor).

Portanto, a decisão que escolher se resume aos seguintes argumentos:

  1. Geralmente, na ciência da computação, é recomendável manter suas variáveis ​​o mais local possível em termos de escopo . Isso geralmente resulta em um código muito mais claro, com menos efeitos colaterais e reduz as chances de alguém usar essa variável global estragando sua lógica). Por exemplo, no seu primeiro exemplo, outras áreas lógicas podem alterar o countvalor, enquanto no segundo, somente essa função específica loop()pode fazê-lo).
  2. Variáveis ​​globais e estáticas sempre ocupam memória , enquanto os locais apenas o fazem quando estão no escopo. Nos exemplos acima, isso não faz diferença (já que em um você usa uma variável global e a outra estática), mas em programas maiores e mais complexos, isso pode e você pode economizar memória usando locais não estáticos. No entanto : se você tiver uma variável em uma área lógica que é executada com muita frequência, considere torná-la estática ou global, pois, caso contrário, você perderá um pouquinho de desempenho cada vez que a área lógica for inserida, pois leva um pouco de tempo para aloque a memória para essa nova instância variável. Você precisa encontrar um equilíbrio entre a carga da memória e o desempenho.
  3. Outros pontos, como melhor layout para análise estática ou otimização pelo compilador, também podem entrar em jogo.
  4. Em alguns cenários especiais, pode haver problemas com a ordem de inicialização imprevisível dos elementos estáticos (não tenho certeza sobre esse ponto, compare este link ).

Fonte: Tópico semelhante no arduino.cc

Philip Allgaier
fonte
A reentrada nunca deve ser um problema no Arduino, pois não suporta simultaneidade.
Peter Bloomfield
Verdade. Esse foi um argumento mais geral, mas de fato não relevante para o Arduino. Eu removi esse pedaço.
Philip Allgaier
11
Uma variável estática declarada dentro de um escopo sempre existirá e usará o mesmo espaço que uma variável global! No código OP, a única diferença é qual código pode acessar a variável. No scipe, a estática estará acessível no mesmo escopo.
Jfpoilpret #
11
@jfpoilpret Isso, é claro, é verdade, e vejo que a parte respectiva na minha resposta foi um pouco enganadora. Corrigido isso.
Philip Allgaier
2

Ambas as variáveis ​​são estáticas - elas persistem por toda a sessão de execução. O global é visível para qualquer função se declarar - não define - o global, ou se a função seguir a definição na mesma unidade de compilação (arquivo + inclui).

Mover a definição de countpara dentro de uma função limita seu escopo de visibilidade ao conjunto de {}es envolvente mais próximo e fornece a vida útil da chamada de função (ela é criada e destruída à medida que a função é inserida e encerrada). A declaração statictambém fornece a vida útil da sessão de execução, que existe desde o início até o final da sessão de execução, persistindo nas invocações de funções.

BTW: tenha cuidado ao usar estática inicializada em uma função, como já vi algumas versões do compilador gnu entenderem isso errado. Uma variável automática com um inicializador deve ser criada e inicializada em cada entrada de função. Uma estática com um inicializador deve ser inicializada apenas uma vez, durante a configuração de execução, antes que main () tenha controle (da mesma forma que uma global seria). Eu tive a estatística local sendo reinicializada em cada entrada de função como se fossem automáticas, o que está incorreto. Teste seu próprio compilador para ter certeza.

JRobert
fonte
Não sei se entendi o que você quer dizer com uma função que declara global. Você quer dizer como um extern?
Peter Bloomfield
@ PeterR.Bloomfield: Não tenho certeza de que parte do meu post você está perguntando, mas estava me referindo aos dois exemplos do OP - o primeiro, uma definição inerentemente global e o segundo, uma estática local.
precisa saber é o seguinte
-3

De acordo com a documentação da Atmel: "Se uma variável global for declarada, um endereço exclusivo na SRAM será atribuído a essa variável no momento do link do programa".

A documentação completa está aqui (Dica nº 2 para variáveis ​​globais): http://www.atmel.com/images/doc8453.pdf

Void Main
fonte
4
Os dois exemplos não terão um endereço exclusivo na SRAM? Ambos precisam persistir.
Cybergibbons
2
Sim, na verdade, você pode encontrar essa informação no mesmo documento na ponta # 6
jfpoilpret