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

59

Lors de l' examen de mon code ici, la question de l'utilisation du constmot clé a été soulevée. Je comprends qu’il est utilisé pour implémenter un comportement en lecture seule sur des variables.

Je suis confus quant aux différentes situations où cela peut être utile.

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

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

  • Quand devrait être le constmot - clé utilisé dans la programmation C?
  • Quels sont les différents types d'avantages pouvant être obtenus en utilisant ce mot clé en C?
  • Y a-t-il des inconvénients à utiliser un constmot clé?


Il a été souligné que cette question était peut-être trop large en raison de toutes ces questions dans le détail de ma question. Je voulais juste préciser que ces questions ne font que clarifier la confusion concernant la question principale.

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

Il peut aussi être reformulé comme

L'utilisation appropriée du constmot clé en C` avec les avantages et les inconvénients de la même chose.

Aseem Bansal
la source
Pour ceux qui votent en faveur de sa fermeture, veuillez expliquer pourquoi il ne relève pas de la catégorie Specific issues with software development. Je suis assez spécifique.
Aseem Bansal
Je suppose que c’est parce que vous avez posé plusieurs questions dans le même message et que, par conséquent, votre question relève de la catégorie Trop large: «Il y a trop de réponses possibles ou bien de bonnes réponses seraient trop longues pour ce format. Ajoutez des détails. pour restreindre l’ensemble des réponses ou pour isoler un problème qui peut être résolu en quelques paragraphes. "
Robert Harvey
1
@RobertHarvey Toutes ces questions ne sont que pour expliquer ma confusion. Ma seule question reste le titre de cette question. Une bonne réponse à cette question couvrirait toutes ces questions connexes. Donc ça reste un specific issue. L'utilisation appropriée du constmot clé en C` avec les avantages et les inconvénients de la même chose.
Aseem Bansal
@RobertHarvey Est-ce mieux maintenant?
Aseem Bansal

Réponses:

65

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.

William Morris
la source
Cela n'a pas vraiment deux significations. Cela signifie simplement, comme vous le dites, " je ne vais pas changer cela." Par exemple, il est parfaitement valide d'avoir une const volatilevariable.
Détly
2
Cela est vrai des paramètres de fonction, mais pour les constantes à la portée du fichier, par exemple, une constvariable est en lecture seule: constdit "Ceci ne peut pas être modifié". A const volatiledit en revanche "Cela ne peut pas être modifié, mais cela pourrait changer". Je vais ajouter une mention de volatiles à ma réponse.
William Morris
Je ne suis pas en désaccord avec votre commentaire, juste avec l'idée que c'est une instruction au compilateur de le mettre en ROM. Cela implique une sorte de garde d'exécution contre les comportements indéfinis (c'est-à-dire la modification d'un const via), ce qui peut conduire à des malentendus du type "pourquoi puis-je changer cette constvariable par un non- constpointeur" (une question courante sur SO et tous les autres forums C !) Globalement , je comme si cette réponse :)
detly
Vous avez raison, "mettre ceci en ROM" est inexact (bien que succinct :-) - je le changerai en "ceci ne peut pas être modifié", ce qui est plus précis. Merci pour vos commentaires.
William Morris
C'est un bon aperçu. J'aime formuler ce terme en constdéclarant à la fois les données en lecture seule et les vues en lecture seule - la différence étant «cela ne peut pas être modifié» par rapport à «vous ne pouvez pas modifier cela».
Jon Purdy
8

En règle générale, dans tout langage de programmation, il est recommandé d’utiliser constle modificateur équivalent car

  • Il peut clarifier à l'appelant que ce qu'ils ont 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
TheLQ
la source
1

Oui, c'est essentiellement la réponse de TheLQ.

Il s’agit d’une mesure de sécurité pour le programmeur afin de ne pas modifier une variable et d’appeler des fonctions susceptibles de 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 que même le compilateur ne vous le permettra pas. Cependant, vous pouvez toujours changer facilement la valeur de la variable avec seulement un transtypage.

Dans ce que j'ai l'habitude de voir, c'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 afin de créer une copie de la variable, puis de la transmettre à function. Si ce n’est pas le cas, vous n’avez évidemment pas besoin de la copie. Par exemple, vous pouvez changer,

int foo(Structure s);

à

int foo(const Structure * s);

et ne pas obtenir la copie overhead.

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 un fichier. Dans le deuxième cas, le pointeur est constant mais son contenu ne l'est pas. Le compilateur vous permettra de dire * a = 3;sans erreur du compilateur, mais vous ne pouvez pas faire aréférence à autre chose.

lgarcia
la source
0

En accord avec les déclarations de TheLQ:

Lorsque vous travaillez avec une équipe de programmeurs, vous déclarez que constc’est un bon moyen d’indiquer que cette variable ne doit pas être modifiée, ou tout simplement pour vous en rappeler dans les grands projets. C'est utile dans ce sens, et cela peut éviter beaucoup de maux de tête.

David Otano
la source