6
votes

Design C-Contener avec `const` éléments?

si une interface de conteneur déclarer des pointeurs sur le contenu contenu éléments tels que const code>?

Tâche: Concevoir un conteneur en C (Remarque: Ceci est explicitement sur le CLAIR C, pas em> à propos de C ++, ni C #). Le conteneur doit être nourri avec des pointeurs vers articles, et devrait renvoyer des pointeurs à des éléments. P>

Un MWe quelque peu inutile, mais l'idée s'étend aux conteneurs utiles comme Eh bien: p> xxx pré>

jusqu'à présent, si bon. p>

Eh bien, le conteneur ne doit pas utiliser les pointeurs pour modifier la version stockée. Les données. J'aimerais rendre cela clair dans l'interface, par un petit Changement: p>

return (void *)c->data;


13 commentaires

Étant donné que y n'est pas const , vous pouvez modifier les octets, c'est pourquoi le compilateur se plaint. y = (int *) get (& c); si vous faites cela, quel est le bénéfice du const ?


Oui je sais. Je comprends complètement pourquoi le compilateur se plaint. Avantage de l'utilisation de Const est Twofold: 1: promis à la bibliothèque utilisateur que le conteneur ne modifiera pas les données. 2: Make Compiler Warn Bibliothèque MiseidLor À propos de la modification accidentelle des données gérées par le compilateur.


Et puis pourquoi voulez-vous une référence de const Const ?


Je suis désolé, peut-être que c'est parce que ma pauvre connaissances anglaise, mais je ne comprends pas il ne devrait pas arriver à l'intérieur du conteneur! Que voulez-vous dire avec ça? Est-ce que vous ne voulez pas qu'il ait accès aux membres struct ?


Il n'y a aucun moyen de sortir de cela, mais de casser le const lorsque vous renvoyez data par le getter ou dumping l'idée d'utiliser const du tout . J'irais pour ce dernier.


Veuillez ne pas appeler vos types quelque chose_t . Le suffixe _t est réservé par POSIX pour ses types.


Le qualificatif const d'un paramètre de fonction doit être interprété comme décrit le type acceptable de l'argument correspondant. C'est pas la même chose que ce que la fonction fera avec l'argument, bien que les deux sont lâchées. Si votre conteneur doit stocker des objets modifiables (et les renvoyer sans qualification de type ajouté), votre Met () ne doit pas const -qualifier ses paramètres.


Étant donné que seuls les pointeurs sont manipulés, la valeur de données d'origine doit toujours exister lorsque le pointeur est récupéré. Cependant, ce pointeur peut être constitué, car le pointeur des données est constitué. Toutefois, dans l'ensemble, le conteneur ne contient pas les données, seul un pointeur vers les données, IMO: si les données d'origine sont toujours disponibles, le conteneur pour un pointeur sur ces données est inutile.


Corrigez mon commentaire précédent: car l'utilisateur de la bibliothèque souhaite toujours modifier les données. Il faut juste modifier pas par le conteneur! - Désolé, tapé-toi


@John Bollinger: Une ligne de pensée intéressante, je suis sûr que je suis d'accord, bien que: f (const int *) soit appliqué à une classe d'arguments plus large, tandis que f (int * ) ne peut pas être appliqué aux arguments de type const int * . Donc, omettre const signifierait que la fonction demande la modification des données transmises?


@stefan no, omettre const signifierait uniquement que la fonction n'accepte pas const arguments. Il ne dit toujours rien de ce que la fonction fera avec ses arguments. Cette question même met en évidence la différence: les éléments du conteneur peuvent inclure des données const ou non, et cela doit être reflété dans les signatures des fonctions de getter d'élément et de réglage. Qu'un conteneur qui puisse contenir des données const peut aussi détenir non- const est hors de propos.


@John Bollinger: Je ne sais pas que je comprenne l'intention derrière deux interfaces: une pour const données, une pour non const data, est-ce que c'est? Mais dans ce cas, j'avais également besoin de deux types "distincts" pour le conteneur, afin de vous assurer que seules les fonctions correspondantes (const ou non-const) sont utilisées avec le conteneur. En outre, seule l'interface avec Const communiquerait à l'utilisateur que le conteneur ne fera pas violer les données.


@stefan, pour complète const -Correcness Vous avez besoin non seulement d'interfaces distinctes, mais des structures de données distinctes. Il s'agit de la nature du conteneur. Si vous dessinez un élément d'un conteneur pouvant contenir des éléments const , il est possible que cet élément est (destiné à être) const . C'est ce que const -qualifiant la fonction getter indique. Si un élément particulier réellement est const n'est pas très pertinent, car il n'y a aucun moyen dans C pour transmettre ces informations sur une base d'éléments par éléments.


4 Réponses :


1
votes

const * signifie que vous pouvez accéder et modifier les données de cette adresse mais ne peut pas modifier l'adresse que vous pointez.

Cela signifie que si l'utilisateur souhaite modifier votre contenu de données, vous pourrez le faire. Mais l'adresse restera la même chose.

Cependant, tout le monde est libre de définir leur pointeur à une adresse. Vous ne pouvez pas empêcher cela.

Pour plus d'informations, consultez Ce lien (le livre C - Const et volatils) Chapitre 8.4.1.


4 commentaires

const * n'est pas un type valide, laissant-moi incertain ce que l'on entend, mais cette réponse semble dirigée dans la mauvaise direction. Par exemple, les deux const char * et chartons * sont (modifiables) des pointeurs non modifiables char . Si vous voulez que le pointeur soit non modifiable, alors c'est orthographié char * cons .


vous avez raison. Dire const * je suppose que tout le monde voit ce que je voulais dire.


Comme je l'ai dit, I Je ne savais pas ce que vous vouliez dire. Je ne fais toujours pas, d'ailleurs. Je pourrais déduire que vous parlez de pointeurs non modifiables si cela était en fait pertinent pour la question, mais ce n'est pas le cas.


Ce n'est pas correct. Dans const * , le const ne fait jamais référence au * . Vous voulez dire * const .



1
votes

Si vous voulez la Constitution du pointeur Y , la bonne façon serait la suivante: int * const y = get (& c);

Si vous voulez détenir ce que vous points sur , alors, comme vous l'avez mentionné:

const int * y = get (& c);


1 commentaires

C'est vrai, mais cela ne répond pas à la question.



2
votes

Un type de conteneur est juste que - un conteneur. Il permet à l'utilisateur d'ajouter / supprimer / accéder à l'élément contenu. Ce que l'utilisateur fait avec elle est à l'utilisateur.

Si vous voulez toujours un pointeur à constante, votre 2e approche est ce qui fonctionne mieux. Mais si un utilisateur peut lancer const, le code du conteneur peut ainsi que la promesse de ne pas modifier les données est peu profonde.

Notez également que votre conteneur stocke le pointeur à vide. Que ce soit-il est une indication forte pour l'utilisateur que le code du conteneur ne peut pas faire grand chose parce qu'il n'a aucune idée du type de données que le pointeur pointera. Oui, il peut toujours écrire des ordures à cet emplacement de la mémoire.

étant donné que le const ne garantit pas la constante de qualité I.e. Le compilateur peut ne pas placer les données de Const dans une mémoire en lecture seule, l'utilisateur du code de conteneur peut toujours tenter d'utiliser Const. Maintenant, il est possible que les données de Cons-ce puissent réellement se retrouver dans une mémoire en lecture seule ou que l'utilisateur puisse fournir le conteneur l'adresse d'une mémoire en lecture seule. Autrement dit, l'utilisateur a assuré qu'aucun code ne peut modifier les données constantes. Dans ce cas, vous n'avez pas à vous soucier de définir un pointeur à des données constantes. Si l'utilisateur a vérifié que les données ne peuvent pas être faites en lecture seule, il peut toujours ajouter des trucs comme CRC aux données. De cette façon, quand il / il saisit les données S / il peut vérifier si les données sont fiables ou non.

résumer : si vous avez un pointeur à vide, ce qui en fait un pointeur pour Const Void est un peu inutile. En outre, il appartient à l'utilisateur de protéger ses données. Une structure de données ne devrait se soucier que de son objectif.


0 commentaires

2
votes

Votre interface fait un contrat, c'est-à-dire que l'objet qui est transmis ne sera jamais modifié à travers cet accès. Donc, en substance, vous auriez besoin de deux types de conteneurs, ou plus généralement d'interfaces, qui suppose que l'objet est mutable (a Void * version) et celui qui suppose qu'il peut être modifié (un VOID Const * version.

C'est un problème qui se produit dans de nombreux endroits, même dans la bibliothèque C. E.g MemChR est une telle interface qui en silence une conversion de const de l'objet. Cette trahison directe pourrait être contournée dans le C moderne C avec _generic .

Votre problème de stockage de la valeur puis de le reproduire à un endroit complètement indépendant dans le code ne peut pas être évité. Les normes C et POSIX ont ce problème pour tss_set / tss_get et pthread_setspecific / pthread_getspecific et résolvez le problème de manière incompatible. La variante C a void * pour les deux, la variante POSIX a void const * pour régler et vide * pour obtenir.


3 commentaires

Je ne serais pas d'accord pour dire que j'ai besoin de deux interfaces, voir ma réponse au commentaire de John Bollinger à ma question. Mais je choisis cette réponse, car il décrit le mieux le problème, et que C const ne fournit pas de solution, bien mis avec "stocker la valeur, puis la reproduise à un endroit complètement indépendant"


" ... dans le code ne peut pas. " Err, ne peut pas savoir quoi s'il vous plaît? " ... Soyez contourné ... "?


@alk, oui, c'était l'idée. J'ai essayé de clarifier, maintenant.