En obtenant mon code examiné ici le problème de l'utilisation du mot clé const
est survenu. Je comprends qu'il est utilisé pour implémenter un comportement en lecture seule sur les variables.
Je suis confus quant aux différentes situations où cela peut être utile.
Ces questions ne sont que des exemples de la confusion à laquelle je fais face. La confusion générale est
const
utilisé dans la programmation C?const
?Quand et à quelles fins le mot-clé const doit-il être utilisé en C pour les variables?
Il peut également être reformulé
La bonne utilisation du mot clé
const
en C` avec les avantages et les inconvénients de la même chose.
Lors de l'examen du code, j'applique les règles suivantes:
Utilisez toujours const
pour les paramètres de fonction passés par référence lorsque la fonction ne modifie pas (ou ne libère pas) les données pointées.
int find(const int *data, size_t size, int value);
Utilisez toujours const
pour les constantes qui pourraient autrement être définies à l'aide d'un #define ou d'un enum. Le compilateur peut ainsi localiser les données dans la mémoire morte (ROM) (bien que l'éditeur de liens soit souvent un meilleur outil à cet effet dans les systèmes embarqués).
const double PI = 3.14;
N'utilisez jamais const dans une fonction prototype pour un paramètre passé par valeur. Il n'a aucun 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 const volatile
sur des emplacements qui ne peuvent pas être modifiés par le programme mais qui peuvent encore changer. Les registres matériels sont 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;
D'autres utilisations sont facultatives. Par exemple, les paramètres d'une fonction dans la fonction implémentation 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 qui sont 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 comprendre qu'une variable n'est pas modifiée, mais en ajoutant const
vous lui permettez de l'imposer. Cela peut aider le lecteur et ajouter une certaine sécurité (bien que si vos fonctions sont suffisamment grandes ou compliquées pour que cela fasse une grande différence, vous avez sans doute d'autres problèmes). Modifier - par exemple. une fonction codée densément de 200 lignes avec des boucles imbriquées et de nombreux noms de variables longs ou similaires, sachant que certaines variables ne changent jamais pourrait faciliter considérablement la compréhension. De telles fonctions ont été mal conçues ou maintenues.
Problèmes avec const
. Vous entendrez probablement le terme "empoisonnement const". Cela se produit lorsque l'ajout de const
à un paramètre de fonction provoque la propagation de "constness".
Edit - empoisonnement const: par exemple dans la fonction:
int function_a(char * str, int n)
{
...
function_b(str);
...
}
si nous changeons str
en const
, nous devons alors nous assurer que fuction_b
prend également un const
. Et ainsi de suite si function_b
transmet le str
à function_c
, etc. Comme vous pouvez l'imaginer, cela peut être douloureux s'il se propage dans de nombreux fichiers/modules distincts. Si elle se propage dans une fonction qui ne peut pas être modifiée (par exemple une bibliothèque système), une conversion devient alors nécessaire. Donc, saupoudrer const
dans le code existant pose peut-être des problèmes. Cependant, dans le nouveau code, il est préférable de const
se qualifier de manière cohérente le cas échéant.
Le problème le plus insidieux de const
est qu'il n'était pas dans la langue d'origine. En tant qu'add-on, il ne convient pas tout à fait. Pour commencer, il a deux significations (comme dans les règles ci-dessus, signifiant "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 (selon le compilateur/les options), il peut bien se bloquer lors de l'exécution:
const char str[] = "hello world\n";
char *s = strchr(str, '\n');
*s = '\0';
strchr
renvoie un char*
pas un const char*
. Comme son paramètre d'appel est const
il doit cast le paramètre d'appel à char*
. Et dans ce cas, cela supprime la véritable propriété de stockage en lecture seule. Edit: - cela s'applique généralement aux variables en mémoire morte. Par "ROM", je veux dire non seulement physique ROM mais 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 faites attention: lorsque vous avez des constantes réelles (c'est-à-dire stockées dans la ROM), vous devez faire très attention de ne pas perdre leur constance.
Généralement dans n'importe quel langage de programmation, il est recommandé d'utiliser const
ou le modificateur équivalent depuis
Oui, c'est essentiellement la réponse de TheLQ.
Est une mesure de sécurité pour le programmeur afin de ne pas modifier une variable et de ne pas appeler les fonctions qui peuvent les modifier. Dans un tableau ou une structure, le spécificateur const indique que les valeurs de leur contenu ne seront pas modifiées, et même le compilateur ne vous permettra pas de le faire. Cependant, vous pouvez toujours facilement changer la valeur de la variable avec juste un cast.
Dans ce que j'ai l'habitude de voir, il est principalement utilisé pour ajouter des valeurs constantes dans le code et pour indiquer que le tableau ou la structure ne sera pas modifié si vous appelez une fonction particulière. Cette dernière partie est importante, car lorsque vous appelez une fonction qui modifiera votre tableau ou votre structure, vous souhaiterez peut-être conserver la version d'origine, vous créez donc une copie de la variable, puis la transmettez à la fonction. Si ce n'est pas le cas, vous n'avez évidemment pas besoin de la copie, donc par exemple vous pouvez changer,
int foo(Structure s);
à
int foo(const Structure * s);
et ne pas obtenir la surcharge de la copie.
Juste pour ajouter, notez que C a des règles particulières avec le spécificateur const. Par exemple,
int b = 1;
const int * a = &b;
n'est pas la même chose que
int b = 1;
int * const a = &b;
Le premier code ne vous permettra pas de modifier a. Dans le second cas, le pointeur est constant mais son contenu ne l'est pas, donc le compilateur vous permettra de dire * a = 3;
sans erreur de compilation, mais vous ne pouvez pas faire de a
une référence à autre chose.
En accord avec les déclarations de TheLQ:
Lorsque vous travaillez avec une équipe de programmeurs, déclarer const
est un bon moyen d'indiquer que ladite variable ne doit pas être modifiée, ou simplement pour vous en souvenir dans les grands projets. Il est utile dans ce sens et peut éviter de nombreux maux de tête.