Lors de l'examen du code, j'applique les règles suivantes:
Toujours utiliser constpour 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 constpour 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 volatilesur 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 constindiquent 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, constvous 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_bprend également un const. Et ainsi de suite si function_bpasse 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
constdans le code existant, c'est peut-être une source de problèmes. Cependant, dans le nouveau code, il est préférable de constqualifier systématiquement, le cas échéant.
Le problème le plus insidieux constest 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';
strchrrenvoie un char*pas un const char*. En tant que paramètre d'appel,
constil 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.