Sou relativamente novo em programação e muitas das práticas recomendadas de codificação que estou lendo efetivamente afirmam que existem muito poucas razões para usar uma variável global (ou que o melhor código não possui globais).
Eu fiz o meu melhor para manter isso em mente, ao escrever um software para criar uma interface do Arduino com um cartão SD, conversar com um computador e executar um controlador de motor.
Atualmente, tenho 46 globais para cerca de 1100 linhas de código "nível iniciante" (nenhuma linha com mais de uma ação). Essa é uma boa proporção ou devo reduzir mais? Além disso, que práticas posso usar para reduzir ainda mais o número de globais?
Estou perguntando isso aqui porque estou especificamente preocupado com as práticas recomendadas para codificação de produtos Arduino, em vez de programação de computadores em geral.
fonte
Respostas:
Eles não são maus por si só, mas tendem a ser usados em excesso onde não há boas razões para usá-los.
Muitas vezes as variáveis globais são vantajosas. Especialmente porque a programação de um Arduino é muito diferente da programação de um PC.
O maior benefício para variáveis globais é a alocação estática. Especialmente com variáveis grandes e complexas, como instâncias de classe. A alocação dinâmica (o uso de
new
etc) é desaprovada devido à falta de recursos.Além disso, você não recebe uma única árvore de chamada como em um programa C normal (
main()
função única chamando outras funções) - em vez disso, você efetivamente recebe duas árvores separadas (setup()
chamando funções e depoisloop()
chamando funções), o que significa que às vezes variáveis globais são as única maneira de atingir seu objetivo (ou seja, se você quiser usá-lo em ambossetup()
eloop()
).Portanto, não, eles não são maus, e em um Arduino eles têm mais e melhores usos do que em um PC.
fonte
loop()
(ou em várias funções chamadasloop()
)? seria melhor configurá-los de uma maneira diferente do que defini-los no início?static
se precisasse deles para manter seu valor entre as iterações) e os passaria pelos parâmetros de função na cadeia de chamadas.void foo(int &var) { var = 4; }
efoo(n);
-n
agora é 4.É muito difícil dar uma resposta definitiva sem ver seu código real.
Variáveis globais não são ruins e geralmente fazem sentido em um ambiente incorporado, onde você normalmente faz muito acesso a hardware. Você tem apenas quatro UARTS, apenas uma porta I2C, etc. Portanto, faz sentido usar globais para variáveis vinculadas a recursos de hardware específicos. E, de fato, a biblioteca núcleo Arduino faz isso:
Serial
,Serial1
, etc, são variáveis globais. Além disso, uma variável que representa o estado global do programa geralmente é global.Não é sobre os números. A pergunta certa que você deve se perguntar é, para cada um desses globais, se faz sentido tê-lo no escopo global.
Ainda assim, 46 globais parecem um pouco altos para mim. Se alguns deles mantiverem valores constantes, qualifique-os como
const
: o compilador geralmente otimizará seu armazenamento. Se alguma dessas variáveis for usada apenas em uma única função, torne-a local. Se você deseja que seu valor persista entre as chamadas para a função, qualifique-o comostatic
. Você também pode reduzir o número de globais "visíveis" agrupando variáveis em uma classe e tendo uma instância global dessa classe. Mas faça isso apenas quando fizer sentido juntar as coisas. Fazer umaGlobalStuff
classe grande por ter apenas uma variável global não ajudará a tornar seu código mais claro.fonte
static
se tenho uma variável que é usada apenas em uma função, mas recebo um novo valor toda vez que uma função é chamada (comovar=millis()
), devo fazer issostatic
?static
É apenas para quando você precisa manter o valor. Se a primeira coisa que você faz na função é definir o valor da variável para o horário atual, não há necessidade de manter o valor antigo. Tudo o que a estática faz nessa situação está desperdiçando memória.O principal problema com variáveis globais é a manutenção de código. Ao ler uma linha de código, é fácil encontrar a declaração de variáveis passadas como parâmetro ou declaradas localmente. Não é tão fácil encontrar uma declaração de variáveis globais (geralmente requer e IDE).
Quando você tem muitas variáveis globais (40 já são muitas), fica difícil ter um nome explícito que não seja muito longo. Usar o espaço para nome é uma maneira de esclarecer o papel das variáveis globais.
Uma maneira ruim de imitar namespaces em C é:
No processador intel ou arm, o acesso a variáveis globais é mais lento que outras variáveis. Provavelmente é o oposto no arduino.
fonte
Embora eu não os usasse na programação para um PC, para o Arduino eles têm alguns benefícios. Mais se já foi dito:
Além disso, em alguns casos, especialmente em termos de desempenho, pode ser bom usar variáveis globais, em vez de criar elementos quando necessário:
fonte
Como em tudo (exceto os gotos que são verdadeiramente maus), os globais têm seu lugar.
por exemplo, se você possui uma porta serial de depuração ou um arquivo de log no qual precisa gravar de qualquer lugar, geralmente faz sentido torná-lo global. Da mesma forma, se você tiver algumas informações críticas sobre o status do sistema, torná-las globais geralmente é a solução mais fácil. Não faz sentido ter um valor que você precisa passar para todas as funções do programa.
Como outros já disseram, o 46 parece muito por apenas mais de 1000 linhas de código, mas sem saber os detalhes do que você está fazendo, é difícil dizer se você os está usando ou não.
No entanto, para cada global, faça a si mesmo algumas perguntas importantes:
O nome é claro e específico para que eu não tente usar acidentalmente o mesmo nome em outro lugar? Caso contrário, mude o nome.
Isso precisa mudar sempre? Caso contrário, considere torná-lo uma const.
Isso precisa estar visível em qualquer lugar ou é apenas global para que o valor seja mantido entre as chamadas de função? Portanto, considere torná-lo local para a função e usar a palavra-chave estática.
As coisas realmente vão estragar muito se isso for alterado por um pedaço de código quando eu não estiver sendo cuidadoso? por exemplo, se você tiver duas variáveis relacionadas, como nome e número de identificação, que são globais (consulte a observação anterior sobre o uso global quando você precisar de informações em quase todos os lugares), se uma delas for alterada sem que outras coisas desagradáveis possam acontecer. Sim, você pode ter cuidado, mas às vezes é bom impor um pouco de cuidado. por exemplo, coloque-os em um arquivo .c diferente e, em seguida, defina funções que definem os dois ao mesmo tempo e permitem que você os leia. Você então inclui apenas as funções no arquivo de cabeçalho associado, para que o restante do seu código possa acessar apenas as variáveis através das funções definidas e, portanto, não possa atrapalhar as coisas.
- update - Acabei de perceber que você havia perguntado sobre as melhores práticas específicas do Arduino, em vez da codificação geral, e isso é mais uma resposta de codificação geral. Mas, honestamente, não há muita diferença, boas práticas são boas práticas. A estrutura
startup()
eloop()
do Arduino significa que você precisa usar globals um pouco mais do que outras plataformas em algumas situações, mas isso não muda muito, você sempre acaba buscando o melhor que pode fazer dentro das limitações da plataforma, não importa o que a plataforma é.fonte
goto
é romper os loops aninhados, é muito mais limpo e fácil de entender do que as alternativas.goto
é mau, confira o código do Linux. Indiscutivelmente, eles são tão maus quantotry...catch
blocos quando usados como tais.Eles são maus? Talvez. O problema com os globais é que eles podem ser acessados e modificados a qualquer momento, por qualquer função ou parte do código sendo executada, sem restrições. Isso pode levar a situações difíceis, digamos, de rastrear e explicar. Minimizar a quantidade de globais, se possível, trazer a quantidade de volta a zero, é, portanto, desejável.
Eles podem ser evitados? Quase sempre sim. O problema com o Arduino é que eles o forçam a essa abordagem de duas funções, na qual eles assumem você
setup()
e vocêloop()
. Nesse caso específico, você não tem acesso ao escopo da função de chamada dessas duas funções (provavelmentemain()
). Se você tivesse, seria capaz de se livrar de todos os globais e usar os habitantes locais.Imagine o seguinte:
Provavelmente, isso é mais ou menos o que parece a principal função de um programa Arduino. As variáveis necessárias na função
setup()
e naloop()
função seriam preferencialmente declaradas dentro do escopo damain()
função e não no escopo global. Eles poderiam então ser disponibilizados para as outras duas funções, passando-os como argumentos (usando ponteiros, se necessário).Por exemplo:
Observe que, nesse caso, você também precisa alterar a assinatura de ambas as funções.
Como isso pode não ser viável nem desejável, vejo realmente apenas uma maneira de remover a maioria dos globais de um programa Arduino sem modificar a estrutura forçada do programa.
Se bem me lembro, você é perfeitamente capaz de usar C ++ durante a programação do Arduino, em vez de C. Se você ainda não está familiarizado com OOP (Programação Orientada a Objetos) ou C ++, pode levar algum tempo para se acostumar. leitura.
Minha proposta seria criar uma classe de programa e criar uma única instância global dessa classe. Uma classe deve ser considerada o blueprint para objetos.
Considere o seguinte programa de exemplo:
Voilà, nos livramos de quase todos os globais. As funções nas quais você começaria a adicionar a lógica do aplicativo seriam as funções
Program::setup()
eProgram::loop()
. Essas funções têm acesso às variáveis de membro específicas da instânciamyFirstSampleVariable
e,mySecondSampleVariable
enquanto as funçõessetup()
e tradicionaisloop()
não têm acesso, pois essas variáveis foram marcadas como classe privada. Esse conceito é chamado de encapsulamento ou ocultação de dados.Ensinar a você OOP e / ou C ++ está um pouco fora do escopo da resposta a esta pergunta, então vou parar por aqui.
Resumindo: os globais devem ser evitados e quase sempre é possível reduzir drasticamente a quantidade de globais. Além disso, quando você está programando para o Arduino.
Mais importante ainda, espero que minha resposta seja um pouco útil para você :)
fonte
Program::instance().setup()
invés deglobalProgram.setup()
. Colocar variáveis globais relacionadas em uma classe / struct / namespace pode ser benéfico, especialmente se elas forem necessárias apenas para algumas funções relacionadas, mas o padrão singleton não adiciona nada. Em outras palavras,static Program p;
possui armazenamento global estatic Program& instance()
acesso global, o que equivale a simplesmenteProgram globalProgram;
.Variáveis globais nunca são más . Uma regra geral contra eles é apenas uma muleta para permitir que você sobreviva o tempo suficiente para ganhar experiência e tomar melhores decisões.
O que é uma variável global é uma suposição inerente de que existe apenas uma coisa (não importa se estamos falando de uma matriz ou mapa global que pode conter várias coisas, que ainda contém a suposição de que há apenas uma lista ou mapeamento, e não várias listas independentes).
Portanto, antes de usar um global, você quer se perguntar: é concebível que eu queira usar mais de uma dessas coisas? Se isso acontecer, você terá que modificar o código para não globalizar essa coisa e provavelmente descobrirá ao longo do caminho que outras partes do seu código dependem dessa suposição de exclusividade. terá que corrigi-los também, e o processo se torna tedioso e propenso a erros. O "não use globais" é ensinado porque geralmente é um custo muito pequeno evitar os globais desde o início e evita o potencial de ter que pagar um grande custo mais tarde.
Mas as suposições simplificadoras permitidas pelas globais também tornam seu código menor, mais rápido e usam menos memória, porque ele não precisa passar uma noção de qual coisa está usando, não precisa ser indireto, não precisa considere a possibilidade da coisa desejada talvez não exista, etc. No incorporado, é mais provável que você fique restrito ao tamanho do código e / ou tempo e / ou CPU de memória do que em um PC, portanto essas economias podem importar. E muitos aplicativos incorporados também têm mais rigidez nos requisitos - você sabe que seu chip possui apenas um de um periférico, o usuário não pode simplesmente conectar outro a uma porta USB ou algo assim.
Outro motivo comum para querer mais do que algo que parece único é o teste - testar a interação entre dois componentes é mais fácil quando você pode simplesmente passar uma instância de teste de algum componente para uma função, enquanto tentar modificar o comportamento de um componente global é uma proposição mais complicada. Mas os testes no mundo incorporado tendem a ser muito diferentes de outros lugares, portanto, isso pode não se aplicar a você. Até onde eu sei, o Arduino não tem nenhuma cultura de teste.
Então vá em frente e use globais quando parecerem valiosos. A polícia de código não irá buscá-lo. Saiba que a escolha errada pode levar a muito mais trabalho para você no caminho, por isso, se você não tiver certeza ...
fonte
nada é inerentemente mau, incluindo variáveis globais. Eu o caracterizaria como um "mal necessário" - isso pode facilitar sua vida, mas deve ser abordada com cautela.
use funções de wrapper para acessar suas variáveis globais. pelo menos você o está gerenciando da perspectiva do escopo.
fonte