web-dev-qa-db-fra.com

Quand et à quelles fins le mot-clé const doit-il être utilisé en C pour les variables?

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.

  • Doit-il être utilisé pour plus de clarté dans les prototypes de fonction?
  • Doit-il être utilisé comme mesure de sécurité lors du développement du code?
  • Doit-il être utilisé dans le cadre de diverses fonctions pour déclarer des constantes d'exécution?
  • Doit-il être utilisé du tout?

Ces questions ne sont que des exemples de la confusion à laquelle je fais face. La confusion générale est

  • Quand doit être le mot clé const utilisé dans la programmation C?
  • Quels sont les différents types d'avantages qui peuvent être obtenus en utilisant ce mot-clé en C?
  • Y a-t-il des inconvénients à utiliser le mot clé 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.

65
Aseem Bansal

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.

74
William Morris

Généralement dans n'importe quel langage de programmation, il est recommandé d'utiliser const ou le modificateur équivalent depuis

  • Cela peut clarifier à l'appelant que ce qu'il a transmis ne va pas changer
  • Améliorations potentielles de la vitesse puisque le compilateur sait avec certitude qu'il peut omettre certaines choses qui ne sont pertinentes que si le paramètre peut changer
  • Protection contre vous-même en modifiant accidentellement la valeur
10
TheLQ

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.

1
lgarcia

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.

0
David Otano