#define vs const

Bonjour
Il me semble avoir lu qu'il fallait mieux préférer le
const TYPE Nom = Valeur;
que le #define Nom Valeur

Mais dans les docs, on trouve toujours des #define

exemple (ça vient d'un exemple d'adafruit) sur le DHT

//#define DHTTYPE DHT11 //Si vous utiliser le DHT 11
#define DHTTYPE DHT22 //Si vous utiliser le DHT 22 (AM2302)
//#define DHTTYPE DHT21 //Si vous utiliser le DHT 21 (AM2301)

Mais que si on utilisait le #define c'était géré au niveau du pré compilateur...

Du coup, il en est quoi au juste ? Et dans le cas sus cité, le type faudrait mettre quoi ?

Qu'elle est le meilleur codage en quelques sortes ?

D'avance merci,

un define est remplacé syntaxiquement dans le code avant compilation, comme si dans un traitement de texte vous faisiez un "rechercher et remplacer"

ça simplifie certaines écritures et la lecture du code mais la validation sémantique n'est pas effectuée puisque souvent les éléments ne sont pas typés.

par exemple si vous faites

#define MS 1000 // 1000 microsecondes dans une milli-seconde 
#define S 1000 * MS // 1000 millisecondes dans une seconde
const unsigned long tempsAttente = 60 * S; // une minute d'attente

le compilateur ne sait pas quel type utiliser et à la compilation le remplacement textuel va dégager les #define et vous vous retrouverez avec

unsigned long tempsAttente = 60 * 1000 * 1000; // une minute d'attente

à compiler et là c'est la cata car le compilateur quand il n'a pas d'Information prend un int par défaut pour les valeurs numériques et donc va effectuer (sur un UNO) le calcul 60 * 1000 * 1000 sur des entiers signés à 2 octets dont la valeur max est 32767... on voit bien que ça ne va pas marcher, même si on met le résultat dans un unsigned long, le calcul lui même sera faux.

si vous aviez défini

const unsigned long MS = 1000;
const unsigned long S = 1000 * S;
const unsigned long tempsAttente = 60 * S;

alors le compilateur va effectuer les calculs pour vous mais comme il connait le type des données il sait qu'il faut travailler sur des unsigned long et tout va bien se passer.

biens sûr si vous êtes précis vous pourriez dire

#define MS (1000ul) // 1000 microsecondes dans une milli-seconde 
#define S (1000ul * MS) // 1000 millisecondes dans une seconde
const unsigned long tempsAttente = 60 * S; // une minute d'attente

et forcer le type des constantes numériques pour le remplacement. ce serait une bonne pratique mais bien souvent le programmeur n'y pense pas

c'est pour cela que je préfère des enum et des const on peut définir le type qui sous tend la donnée représentée.

Cela dit, avec un #define on peut aussi simplifier des écritures répétitives plus complexes (même si maintenant on peut utiliser des inline) par exemple vous pouvez définir de manière générique comment trouver le max de 2 éléments par

#define max(a,b) ((a)>(b)?(a):(b))

votre code sera plus lisible si vous employez max()

Vous en verrez plein définis ainsi dans Arduino.h, par exemple

#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))
#define round(x)     ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))
#define radians(deg) ((deg)*DEG_TO_RAD)
#define degrees(rad) ((rad)*RAD_TO_DEG)
#define sq(x) ((x)*(x))

#define interrupts() sei()
#define noInterrupts() cli()

#define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )
#define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() )
#define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() )

le define vient du temps où les inline n'existaient pas et qu'un appel de fonction ou la lecture d'une variable en mémoire coûtait cher mais de nos jours les compilateurs savent optimiser tout cela pour vous.

Merci beaucoup pour ces informations.