O Arduino é um híbrido ímpar, onde algumas funcionalidades do C ++ são usadas no mundo incorporado - tradicionalmente um ambiente C. De fato, muitos códigos do Arduino são muito parecidos com o C.
C tradicionalmente usa #define
s para constantes. Há várias razões para isso:
- Você não pode definir tamanhos de matriz usando
const int
. - Você não pode usar
const int
como rótulos de declaração de caso (embora isso funcione em alguns compiladores) - Você não pode inicializar um
const
com outroconst
.
Você pode verificar esta pergunta no StackOverflow para obter mais argumentos.
Então, o que devemos usar para o Arduino? Eu costumo #define
, mas vejo algum código usando const
e outros usando uma mistura.
programming
c++
coding-standards
Cybergibbons
fonte
fonte
#define
como a escolha óbvia. Meu exemplo é nomear pinos analógicos - como A5. Não existe um tipo apropriado para ele que possa ser usado como uma opção;const
portanto, a única opção é usar#define
ae deixar que o compilador a substitua como entrada de texto antes de interpretar o significado.Respostas:
É importante observar que
const int
não se comporta de maneira idêntica em C e em C ++; portanto, várias das objeções contra ele mencionadas na pergunta original e na extensa resposta de Peter Bloomfields não são válidas:const int
constantes são valores de tempo de compilação e podem ser usadas para definir limites de matriz, como rótulos de maiúsculas e minúsculas, etc.const int
constantes não ocupam necessariamente nenhum armazenamento. A menos que você escolha o endereço ou o declare externo, eles geralmente terão uma existência de tempo de compilação.No entanto, para constantes inteiras, pode ser preferível usar a (nomeado ou anônimo)
enum
. Costumo gostar disso porque:const int
(tão seguro quanto o tipo no C ++ 11).Portanto, em um programa C ++ idiomático, não há razão para usar
#define
para definir uma constante inteira. Mesmo se você quiser permanecer compatível com C (por causa de requisitos técnicos, porque você está começando na velha escola ou porque as pessoas com quem você trabalha preferem assim), você ainda pode usarenum
e deve fazê-lo, em vez de usar#define
.fonte
const int
. Para tipos mais complexos, você está certo de que o armazenamento pode ser alocado, mas, mesmo assim, é improvável que esteja pior do que com um#define
.EDIT: microtherion fornece uma excelente resposta que corrige alguns dos meus pontos aqui, particularmente sobre o uso de memória.
Como você identificou, há certas situações em que você é forçado a usar a
#define
, porque o compilador não permite umaconst
variável. Da mesma forma, em algumas situações, você é forçado a usar variáveis, como quando precisa de uma matriz de valores (ou seja, não pode ter uma matriz de#define
).No entanto, existem muitas outras situações em que não há necessariamente uma única resposta 'correta'. Aqui estão algumas diretrizes que eu seguiria:
Segurança do tipo
Do ponto de vista geral da programação, as
const
variáveis são geralmente preferíveis (sempre que possível). A principal razão para isso é a segurança do tipo.Uma
#define
(macro de pré-processador) copia diretamente o valor literal em cada local no código, tornando cada uso independente. Hipoteticamente, isso pode resultar em ambiguidades, porque o tipo pode acabar sendo resolvido de maneira diferente, dependendo de como / onde é usado.Uma
const
variável é apenas um tipo, que é determinado por sua declaração e resolvido durante a inicialização. Geralmente, exige uma conversão explícita antes de se comportar de maneira diferente (embora existam várias situações nas quais ele pode ser implicitamente promovido com segurança). No mínimo, o compilador pode (se configurado corretamente) emitir um aviso mais confiável quando ocorrer um problema de tipo.Uma solução possível para isso é incluir uma conversão explícita ou um sufixo de tipo em a
#define
. Por exemplo:Essa abordagem pode potencialmente causar problemas de sintaxe em alguns casos, dependendo de como é usada.
Uso da memória
Ao contrário da computação de uso geral, a memória é obviamente superior quando se lida com algo como um Arduino. O uso de uma
const
variável vs. a#define
pode afetar o local em que os dados são armazenados na memória, o que pode forçá-lo a usar um ou outro.const
variáveis (geralmente) serão armazenadas na SRAM, juntamente com todas as outras variáveis.#define
geralmente serão armazenados no espaço do programa (memória Flash), ao lado do próprio esboço.(Observe que existem várias coisas que podem afetar exatamente como e onde algo é armazenado, como configuração e otimização do compilador.)
SRAM e Flash têm limitações diferentes (por exemplo, 2 KB e 32 KB, respectivamente, para o Uno). Para alguns aplicativos, é muito fácil ficar sem SRAM, portanto, pode ser útil mudar algumas coisas para o Flash. O inverso também é possível, embora provavelmente menos comum.
PROGMEM
É possível obter os benefícios da segurança de tipo e também armazenar os dados no espaço do programa (Flash). Isso é feito usando a
PROGMEM
palavra - chave Não funciona para todos os tipos, mas é comumente usado para matrizes de números inteiros ou seqüências de caracteres.A forma geral fornecida na documentação é a seguinte:
As tabelas de strings são um pouco mais complicadas, mas a documentação possui detalhes completos.
fonte
Para variáveis de um tipo especificado que não são alteradas durante a execução, geralmente podem ser usadas.
Para números de pinos digitais contidos em variáveis, ambos podem funcionar - como:
Mas há uma circunstância em que eu sempre uso
#define
É para definir números de pinos analógicos, pois eles são alfanuméricos.
Claro, você pode codificar os números dos pinos como
a2
,a3
etc. durante todo o programa e o compilador saberá o que fazer com eles. Então, se você alterar os pinos, cada uso precisará ser alterado.Além disso, eu sempre gosto de ter minhas definições de pinos no topo, tudo em um só lugar, de modo que a pergunta se torna sobre que tipo de
const
seria apropriado para um pino definido comoA5
.Nesses casos, eu sempre uso
#define
Exemplo de divisor de tensão:
Todas as variáveis de configuração estão no topo e nunca haverá uma alteração no valor de
adcPin
exceto em tempo de compilação.Não se preocupe com o tipo
adcPin
. E nenhuma RAM extra é usada no binário para armazenar uma constante.O compilador simplesmente substitui cada instância de
adcPin
pela stringA5
antes de compilar.Há um tópico interessante no Fórum do Arduino que discute outras maneiras de decidir:
#define vs. const variable (fórum do Arduino)
Excertps:
Substituição de código:
Código de depuração:
Definindo
true
efalse
como Booleano para economizar RAMMuito disso se resume à preferência pessoal, porém é claro que
#define
é mais versátil.fonte
const
não utilizará mais RAM que a#define
. E para os pinos analógicos, eu os definiria comoconst uint8_t
, emboraconst int
não faça diferença.const
realmente não usa mais RAM [...] até que seja realmente usado ". Você não entendeu: na maioria das vezes, aconst
não usa RAM, mesmo quando usada . Então, “ este é um compilador multipass ”. Mais importante ainda, é um compilador de otimização . Sempre que possível, as constantes são otimizadas em operandos imediatos .