Lors de l'examen du code, j'applique les règles suivantes:
Toujours utiliser const
pour les paramètres de fonction passés par référence lorsque la fonction ne modifie pas (ni ne libère) les données pointées.
int find(const int *data, size_t size, int value);
Toujours utiliser const
pour les constantes qui pourraient autrement être définies en utilisant un #define ou un enum. Le compilateur peut ainsi localiser les données dans une mémoire morte (ROM) (bien que l'éditeur de liens soit souvent un meilleur outil à cette fin dans les systèmes intégrés).
const double PI = 3.14;
N'utilisez jamais const dans un prototype de fonction pour un paramètre passé par
valeur . Cela n'a pas de sens et n'est donc que du "bruit".
// don't add const to 'value' or 'size'
int find(const int *data, size_t size, const int value);
Le cas échéant, utilisez-le const volatile
sur des emplacements qui ne peuvent pas être modifiés par le programme mais qui pourraient tout de même changer. Les registres de matériel constituent le cas d'utilisation typique ici, par exemple un registre d'état qui reflète un état de périphérique:
const volatile int32_t *DEVICE_STATUS = (int32_t*) 0x100;
Les autres utilisations sont facultatives. Par exemple, les paramètres d'une fonction dans l' implémentation de la fonction peuvent être marqués comme const.
// 'value' and 'size can be marked as const here
int find(const int *data, const size_t size, const int value)
{
... etc
ou la fonction renvoie des valeurs ou des calculs obtenus et qui ne changent jamais:
char *repeat_str(const char *str, size_t n)
{
const size_t len = strlen(str);
const size_t buf_size = 1 + (len * n);
char *buf = malloc(buf_size);
...
Ces utilisations de const
indiquent simplement que vous ne modifierez pas la variable; ils ne changent ni comment ni où la variable est stockée. Le compilateur peut bien sûr déterminer qu’une variable n’est pas modifiée, mais en l’ajoutant, const
vous lui permettez de l’appliquer. Cela peut aider le lecteur et ajouter un peu de sécurité (bien que si vos fonctions sont trop grandes ou assez compliquées pour que cela fasse une grande différence, vous avez sans doute d'autres problèmes). Modifier - par exemple. une fonction à codage dense de 200 lignes avec des boucles imbriquées et de nombreux noms de variable longs ou similaires, sachant que certaines variables ne changent jamais peut faciliter considérablement la compréhension. De telles fonctions ont été mal conçues ou maintenues.
Des problèmes avec const
. Vous entendrez probablement le terme "empoisonnement constant". Cela se produit lorsque l'ajout const
à un paramètre de fonction provoque la propagation de 'constness'.
Edit - empoisonnement constant: par exemple dans la fonction:
int function_a(char * str, int n)
{
...
function_b(str);
...
}
si nous passons str
à const
, nous devons alors nous assurer que cela fuction_b
prend également un const
. Et ainsi de suite si function_b
passe str
à function_c
, etc. Comme vous pouvez l'imaginer, cela pourrait être douloureux s'il se propageait dans de nombreux fichiers / modules distincts. S'il se propage dans une fonction qui ne peut pas être modifiée (par exemple une bibliothèque système), un transtypage devient alors nécessaire. Donc, asperger
const
dans le code existant, c'est peut-être une source de problèmes. Cependant, dans le nouveau code, il est préférable de const
qualifier systématiquement, le cas échéant.
Le problème le plus insidieux const
est que ce n'était pas dans la langue d'origine. En tant qu'add-on, cela ne convient pas vraiment. Pour commencer, il a deux sens (comme dans les règles ci-dessus, ce qui signifie "je ne vais pas changer cela" et "cela ne peut pas être modifié"). Mais plus que cela, cela peut être dangereux. Par exemple, compilez et exécutez ce code et (en fonction du compilateur / des options), il risque de planter en cas d'exécution:
const char str[] = "hello world\n";
char *s = strchr(str, '\n');
*s = '\0';
strchr
renvoie un char*
pas un const char*
. En tant que paramètre d'appel,
const
il doit convertir le paramètre d'appel en char*
. Et dans ce cas, cela élimine la véritable propriété de stockage en lecture seule. Edit: - ceci s’applique généralement aux vars de la mémoire en lecture seule. Par «ROM», j'entends non seulement la ROM physique, mais également toute mémoire protégée en écriture, comme cela arrive à la section de code des programmes exécutés sur un système d'exploitation typique.
De nombreuses fonctions de bibliothèque standard se comportent de la même manière, alors méfiez-vous: lorsque vous avez de vraies constantes (c'est-à-dire stockées dans une ROM), vous devez faire très attention à ne pas perdre leur constance.
Specific issues with software development
. Je suis assez spécifique.