Arduino est un hybride étrange, où certaines fonctionnalités C ++ sont utilisées dans le monde embarqué, traditionnellement un environnement C. En effet, beaucoup de code Arduino ressemble beaucoup au C.
C a traditionnellement utilisé #define
s pour les constantes. Il ya un certain nombre de raisons à cela:
- Vous ne pouvez pas définir la taille des tableaux à l'aide de
const int
. - Vous ne pouvez pas utiliser
const int
comme étiquettes d'instructions de cas (bien que cela fonctionne dans certains compilateurs) - Vous ne pouvez pas initialiser un
const
avec un autreconst
.
Vous pouvez vérifier cette question sur StackOverflow pour plus de raisonnement.
Alors, que devrions-nous utiliser pour Arduino? Je tends vers #define
, mais je vois un peu de code en utilisant const
et certains en utilisant un mélange.
programming
c++
coding-standards
Cybergibbons
la source
la source
#define
le choix évident. Mon exemple est de nommer des broches analogiques - comme A5. Il n'y a pas de type approprié pour cela qui pourrait être utilisé comme unconst
donc le seul choix est d'utiliser un#define
et de laisser le compilateur le remplacer comme entrée de texte avant d'interpréter la signification.Réponses:
Il est important de noter que
const int
cela ne se comporte pas de la même manière en C et en C ++, donc en fait plusieurs des objections contre cela qui ont été évoquées dans la question d'origine et dans la réponse détaillée de Peter Bloomfields ne sont pas valides:const int
constantes sont des valeurs de temps de compilation et peuvent être utilisées pour définir des limites de tableau, comme des étiquettes de cas, etc.const int
les constantes n'occupent pas nécessairement de stockage. À moins que vous ne preniez leur adresse ou que vous ne les déclariez externes, ils auront généralement une existence au moment de la compilation.Cependant, pour les constantes entières, il peut souvent être préférable d'utiliser un (nommé ou anonyme)
enum
. J'aime souvent ça parce que:const int
(chaque bit est aussi sûr que le type en C ++ 11).Donc, dans un programme C ++ idiomatique, il n'y a aucune raison à utiliser
#define
pour définir une constante entière. Même si vous voulez rester compatible C (en raison d'exigences techniques, parce que vous vous démarquez à l'ancienne ou parce que les gens avec lesquels vous travaillez le préfèrent de cette façon), vous pouvez toujours utiliserenum
et devez le faire, plutôt que d'utiliser#define
.la source
const int
. Pour les types plus complexes, vous avez raison de penser que le stockage peut être alloué, mais même ainsi, il est peu probable que votre situation soit pire qu'avec un#define
.EDIT: microtherion donne une excellente réponse qui corrige certains de mes points ici, en particulier sur l'utilisation de la mémoire.
Comme vous l'avez identifié, il existe certaines situations où vous êtes obligé d'utiliser un
#define
, car le compilateur n'autorise pas uneconst
variable. De même, dans certaines situations, vous êtes obligé d'utiliser des variables, comme lorsque vous avez besoin d'un tableau de valeurs (c'est-à-dire que vous ne pouvez pas en avoir un#define
).Cependant, il existe de nombreuses autres situations où il n'y a pas nécessairement une seule réponse «correcte». Voici quelques directives que je suivrais:
Sécurité des types
Du point de vue de la programmation générale, les
const
variables sont généralement préférables (si possible). La principale raison en est la sécurité de type.Une
#define
(macro préprocesseur) copie directement la valeur littérale dans chaque emplacement du code, ce qui rend chaque utilisation indépendante. Cela peut hypothétiquement entraîner des ambiguïtés, car le type peut finir par être résolu différemment selon la façon dont il est utilisé.Une
const
variable n'est qu'un seul type, déterminé par sa déclaration et résolu lors de l'initialisation. Il nécessitera souvent une conversion explicite avant de se comporter différemment (bien qu'il existe diverses situations où il peut être implicitement promu par type en toute sécurité). À tout le moins, le compilateur peut (s'il est configuré correctement) émettre un avertissement plus fiable lorsqu'un problème de type se produit.Une solution de contournement possible consiste à inclure une distribution explicite ou un suffixe de type dans a
#define
. Par exemple:Cette approche peut cependant causer des problèmes de syntaxe dans certains cas, selon la façon dont elle est utilisée.
Utilisation de la mémoire
Contrairement à l'informatique à usage général, la mémoire est évidemment à un prix élevé lorsqu'il s'agit de quelque chose comme un Arduino. L'utilisation d'une
const
variable par rapport à a#define
peut affecter l'emplacement de stockage des données en mémoire, ce qui peut vous obliger à utiliser l'une ou l'autre.const
les variables seront (généralement) stockées dans SRAM, avec toutes les autres variables.#define
seront souvent stockées dans l'espace programme (mémoire Flash), à côté de l'esquisse elle-même.(Notez que diverses choses peuvent affecter exactement comment et où quelque chose est stocké, comme la configuration et l'optimisation du compilateur.)
SRAM et Flash ont des limitations différentes (par exemple 2 Ko et 32 Ko respectivement pour l'Uno). Pour certaines applications, il est assez facile de manquer de SRAM, il peut donc être utile de déplacer certaines choses dans Flash. L'inverse est également possible, bien que probablement moins courant.
PROGMEM
Il est possible de bénéficier des avantages de la sécurité de type tout en stockant les données dans l'espace programme (Flash). Cela se fait à l'aide du
PROGMEM
mot - clé. Cela ne fonctionne pas pour tous les types, mais il est couramment utilisé pour les tableaux d'entiers ou de chaînes.La forme générale donnée dans la documentation est la suivante:
Les tables de chaînes sont un peu plus compliquées, mais la documentation contient tous les détails.
la source
Pour les variables d'un type spécifié qui ne sont pas modifiées pendant l'exécution, l'une ou l'autre peut généralement être utilisée.
Pour les numéros de broches numériques contenus dans des variables, l'un ou l'autre peut fonctionner - tels que:
Mais il y a une circonstance où j'utilise toujours
#define
Il s'agit de définir des numéros de broches analogiques, car ils sont alphanumériques.
Bien sûr, vous pouvez coder en dur les numéros de broches que
a2
,a3
etc. tout au long du programme et le compilateur saura quoi faire avec eux. Ensuite, si vous changez d'épingles, chaque utilisation devra être modifiée.De plus, j'aime toujours avoir mes définitions de broches en haut en un seul endroit, donc la question devient quel type de
const
serait approprié pour une broche définie commeA5
.Dans ces cas, j'utilise toujours
#define
Exemple de diviseur de tension:
Toutes les variables de configuration sont tout en haut et il n'y aura jamais de modification de la valeur de
adcPin
sauf au moment de la compilation.Pas de soucis sur le type
adcPin
. Et aucune RAM supplémentaire n'est utilisée dans le binaire pour stocker une constante.Le compilateur remplace simplement chaque instance de
adcPin
par la chaîneA5
avant la compilation.Il existe un fil de discussion intéressant sur le forum Arduino qui discute d'autres façons de décider:
#define vs const variable (forum Arduino)
Excertps:
Substitution de code:
Code de débogage:
Définir
true
etfalse
comme booléen pour économiser de la RAMBeaucoup se résume à des préférences personnelles, mais il est clair que
#define
c'est plus polyvalent.la source
const
n'utilisera pas plus de RAM que a#define
. Et pour les broches analogiques, je les définirais commeconst uint8_t
, bien queconst int
cela ne fasse aucune différence.const
n'utilise pas réellement plus de RAM [...] jusqu'à ce qu'il soit réellement utilisé ». Vous avez manqué mon point: la plupart du temps, unconst
n'utilise pas de RAM, même lorsqu'il est utilisé . Ensuite, « ceci est un compilateur multipass ». Plus important encore, il s'agit d'un compilateur d' optimisation . Dans la mesure du possible, les constantes sont optimisées en opérandes immédiats .