Por que usar uma variável int para um pino quando const int, enum ou #define faz muito mais sentido


24

Por que as pessoas usam uma variável para especificar um número de pino quando é improvável que o pino mude durante a execução do código?

Muitas vezes, vejo um intser usado para uma definição de pino,

int led = 13;

quando o uso de um const int

const int led = 13;

ou enumou#define

#define LED 13

faz muito mais sentido.

É mesmo em tutoriais no site do Arduino, por exemplo, o primeiro tutorial que a maioria das pessoas executa, o Blink .

Eu li em algum lugar que const inté preferido #define. Por que isso não é incentivado desde o início, em vez de permitir que as pessoas desenvolvam maus hábitos, desde o início? Eu notei isso há um tempo, mas recentemente isso começou a me irritar, daí a questão.

Memória / processamento / computação sábio é um const int, enumou para que o assunto #define, melhor do que uma planície int, ou seja, ocupa menos memória, armazenados em memória diferente (Flash, EEPROM, SRAM), uma execução mais rápida, mais rápida para compilar?


Pode parecer uma duplicata de É melhor usar #define ou const int para constantes? , mas estou abordando a questão de por que as pessoas usam variáveis ​​e como o desempenho melhora quando não usam, em vez de que tipo de constante é melhor.


9
Porque terrível gera terrível. A maioria dos entusiastas não são programadores experientes e, portanto, ensinam maus hábitos a outros entusiastas.
Ignacio Vazquez-Abrams

11
Com pinos em particular, a forma simplista das funções básicas arduino API como digitalWrite não encorajam projeto adequado incorporado, ou seja, usando máscaras e um endereço de memória único para todo o porto
crasic

Respostas:


20
const int led = 13;

Esse é o método correto. Ou até:

const byte led = 13;

Quantos pinos você tem?

Alguns dos tutoriais não passaram pelo controle de qualidade que poderiam ter.

O desempenho será melhor usando const byte, compare com into compilador, no entanto, pode ser inteligente o suficiente para perceber o que você está fazendo.

O que você pode fazer é incentivar gentilmente as pessoas a usar técnicas mais eficientes, usando-as em seu próprio código.


Respostas aos comentários

  1. Um comentarista sugeriu que bytenão é o padrão C. Isso está correto, no entanto, este é um site do Arduino StackExchange, e acredito que o uso de tipos padrão fornecidos pelo IDE do Arduino seja aceitável.

    No Arduino.h existe esta linha:

    typedef uint8_t byte;

    Observe que isso não é exatamente o mesmo que unsigned char. Consulte uint8_t vs char não assinado e Quando uint8_t char char não assinado? .

  2. Outro comentarista sugeriu que o uso de byte não melhorará necessariamente o desempenho, porque números menores do que intserão promovidos para int(consulte Regras de Promoção Inteira, se você quiser saber mais sobre isso).

    No entanto, no contexto de um identificador const , o compilador irá gerar código eficiente em qualquer caso. Por exemplo, desmontar "piscar" fornece isso na forma original:

    00000086 <loop>:
      86:   8d e0           ldi r24, 0x0D   ; 13
      88:   61 e0           ldi r22, 0x01   ; 1
      8a:   1b d1           rcall   .+566       ; 0x2c2 <digitalWrite>

    De fato, gera o mesmo código se 13:

    • É um literal
    • É um #define
    • É um const int
    • É um const byte

O compilador sabe quando pode caber um número em um registro e quando não pode. No entanto, é uma boa prática usar codificação que indique sua intenção . Tornar constclaro que o número não será alterado e deixar claro byte(ou uint8_t) deixa claro que você espera um número pequeno.


Mensagens de erro confusas

Outro motivo importante para evitar #definesão as mensagens de erro que você recebe se cometer um erro. Considere este esboço "intermitente" que possui um erro:

#define LED = 13;

void setup() {
  pinMode(LED, OUTPUT);      // <---- line with error
}

void loop() {
  digitalWrite(LED, HIGH);   // <---- line with error 
  delay(1000);             
  digitalWrite(LED, LOW);    // <---- line with error
  delay(1000);              
}

Na superfície, parece bom, mas gera as seguintes mensagens de erro:

Blink.ino: In function ‘void setup()’:
Blink:4: error: expected primary-expression before ‘=’ token
Blink:4: error: expected primary-expression before ‘,’ token
Blink:4: error: expected `;' before ‘)’ token
Blink.ino: In function ‘void loop()’:
Blink:8: error: expected primary-expression before ‘=’ token
Blink:8: error: expected primary-expression before ‘,’ token
Blink:8: error: expected `;' before ‘)’ token
Blink:10: error: expected primary-expression before ‘=’ token
Blink:10: error: expected primary-expression before ‘,’ token
Blink:10: error: expected `;' before ‘)’ token

Você olha para a primeira linha destacada (linha 4) e nem vê o símbolo "=". Além disso, a linha parece bem. Agora é bastante óbvio qual é o problema aqui ( = 13está sendo substituído LED), mas quando a linha está 400 linhas mais abaixo no código, não é óbvio que o problema está na maneira como o LED é definido.

Já vi pessoas se apaixonarem por isso muitas vezes (inclusive eu).


Quantos pinos você tem? é um ponto muito bom, Nick, como a maioria das placas tem apenas o intervalo das dezenas, não centenas (ou seja, maior que 255), portanto, inté um exagero ... isto é, até o Arduino finalmente lançar a placa Tera ... :-)
Greenonline 14/08/2015

2
C não tem um bytetipo . Você quer dizer unsigned char.
Kevin

O desempenho não será necessariamente melhor com, em bytevez de int, uma vez que , na maioria dos contextos, o valor inteiro com tipos menores do que intos promovidos int.
Pete Becker

11
C doesn't have a byte type. You mean unsigned char.- Minha resposta foi no contexto do Arduino, que tem isso typedef uint8_t byte;. Portanto, para um Arduino, usar byteestá OK.
Nick Gammon

Performance won't necessarily be better with byte instead of int- ver post alterado.
Nick Gammon

19

Como Ignacio afirma com razão, é basicamente porque eles não sabem melhor. E eles não sabem melhor porque as pessoas que os ensinaram (ou os recursos que eles usaram ao aprender) não sabiam melhor.

Grande parte do código e dos tutoriais do Arduino são escritos por pessoas que nunca tiveram nenhum treinamento em programação e são muito "autodidatas" a partir de recursos por pessoas que são elas mesmas autodidatas, sem treinamento adequado em programação.

Muitos dos trechos de código do tutorial que eu vejo em todo o lugar (e especialmente aqueles que estão disponíveis apenas nos vídeos do YouTube - urgh) seriam uma falha se eu os estivesse marcando em um exame.

Sim, a consté preferível a um não-const e até mesmo a #define, porque:

  • A const(como um #define, diferente de um não-const) não aloca nenhuma RAM
  • A const(como um não-const, mas diferente de a #define) fornece ao valor um tipo explícito

O segundo ponto é de particular interesse. A menos que seja especificado de outra forma com conversão de tipo incorporada ( (long)3) ou sufixo de tipo ( 3L) ou a presença de ponto decimal ( 3.0), a #definede um número sempre será um número inteiro e toda a matemática realizada nesse valor será como se fosse uma inteiro. Na maioria das vezes, isso não é um problema, mas você pode encontrar cenários interessantes ao tentar #definearmazenar um valor maior que um número inteiro, como por exemplo #define COUNT 70000executar uma operação matemática com outros intvalores. Ao usar a, constvocê pode dizer ao compilador "Este valor deve ser tratado como esse tipo de variável" - então você usaria: const long count = 70000;e tudo funcionaria conforme o esperado.

Ele também tem o efeito indireto de verificar o tipo ao passar o valor ao redor do local. Tente passar a const longpara uma função que espera uma inte ela se queixaria de restringir o intervalo de variáveis ​​(ou até mesmo falhar completamente na compilação, dependendo do cenário). Faça isso com um #definee ele continuaria silenciosamente dando a você os resultados errados e o deixaria coçando a cabeça por horas.


7
Vale a pena notar que uma constvariável pode exigir RAM, dependendo do contexto, por exemplo, se for inicializada usando o valor de retorno de uma função não constexpr.
Peter Bloomfield

Da mesma forma, const int foo = 13; bar(&foo);definitivamente exigirá que o compilador aloque memória real para foo.
Ilmari Karonen

3
Se você definir uma macro que se expande para um valor que não se encaixa em um intcompilador, o valor será o menor do tipo em que ele se ajustará (regras de módulo sobre assinado versus não assinado). Se você estiver em um sistema com int16 bits, #define count 70000parecerá countum long, como se tivesse sido definido como const long count = 70000;. Além disso, se você passar uma dessas versões countpara uma função esperada int, qualquer compilador são tratá-las da mesma forma.
Pete Becker

11
Concordo com @PeteBecker - uma construção como #define COUNT 70000não trunca em um int, mas o compilador a trata como um tipo grande o suficiente para armazenar esse número. É verdade que pode não ser óbvio quando você usa COUNTque não é um int, mas você pode dizer o mesmo sobre um de const longqualquer maneira.
Nick Gammon

2
"um # define sempre será um número inteiro" Isso não é verdade. Você está pegando as regras de literais inteiros e aplicando-as às macros do pré-processador. É como comparar maçãs e música pop. A expressão COUNTno seu exemplo é substituído antes da compilação com a expressão 70000, que tem um tipo definido pelas regras de literais, como 2ou 13Lou 4.0são definidas pelas regras de literais. O fato de você usar #definecomo alias essas expressões é irrelevante. Você pode usar #definecomo alias pedaços arbitrários de código C, se desejar.
Corridas de leveza com Monica

2

Como um novato de duas semanas no Arduino, eu continuava com a ideia geral de o Arduino ser ocupado por não programadores. A maioria dos esboços que examinei, incluindo os do site do Arduino, mostra uma total falta de ordem, com esboços que não funcionam e quase nenhum comentário coerente à vista. Os fluxogramas são inexistentes e as "Bibliotecas" são uma confusão não moderada.


0

Minha resposta é ... eles fazem isso porque funciona. Estou tendo dificuldades para não fazer uma pergunta na minha resposta, como "por que tem que estar 'errado'?"


3
Uma característica marcante de um bom programador é que o código sempre reflete suas intenções.
Ignacio Vazquez-Abrams

11
Ainda estamos falando de Arduinos, certo? ;)
linhartr22

3
O Arduino já tem um péssimo representante na comunidade maior de EE por causa dos designs de hardware medíocres a terríveis apresentados pela comunidade. Não deveríamos tentar dar a mínima para alguma coisa ?
Ignacio Vazquez-Abrams

2
"A maioria dos projetos não envolve risco de vida ou finanças ..." Não há surpresa por lá. Quem iria querer envolver Arduino onde há qualquer possibilidade de risco depois de olhar para a comunidade em geral.
Ignacio Vazquez-Abrams

2
Está 'errado' não porque não funciona em uma situação específica, mas porque, comparado a fazê-lo 'certo', há mais situações em que não funciona. Isso torna o código frágil; alterações no código podem causar falhas misteriosas que consomem tempo de depuração. A verificação de tipo e as mensagens de erro do compilador existem para ajudá-lo a detectar esses tipos de erros mais cedo ou mais tarde.
Curt J. Sampson
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.