Question

J'écris une fonction d'accès qui renvoie un pointeur vers une mémoire tampon interne et je voudrais faire allusion aux utilisateurs de ma fonction qu'ils ne devraient pas mettre à jour l'objet qui est pointé. Un exemple très artificiel serait:

void myAccessFunc(bool string, void* p, size_t* len)
{
  static const char* s = "aha!";
  static const int i = 123;

  if (string) {
     *(char**)p = &s;
     *len = strlen(s);
  }
  else {
     *(int**)p = &i;
     *len = sizeof(i);
  }
}

char* s;
size_t bytes;
myAccessFunc(true,&s, &bytes);
printf("Got '%s'\n", s);

Oui, je sais que regarde flakey.

Ce que je veux éviter est:

char* s;
size_t bytes;
myAccessFunc(true,&s,&bytes);
s[4] = '?';

Je sais que je ne peux pas empêcher complètement, mais je au moins comme l'avertissement du compilateur de faire allusion à un utilisateur qu'ils ne devraient pas faire cela. S'ils jettent mon pointeur, puis c'est leur problème. Y at-il une combinaison de const et non avenue et * qui le fera? J'ai essayé quelque chose comme:

void myAccessFunc(bool string, const void** p, size_t* len);

mais il semblait en finir avec le pointeur de la vacuité si un appelant devait faire:

const void* p;
size_t bytes;
myAccessFunc(true, &p, &bytes);

ou

const char* s;
size_t bytes;
myAccessFunc(true, (const void**)&s, &bytes);

et ne pouvait pas faire:

const int * ip;
const char* s;
size_t bytes;
myAccessFunc(true, &s, &bytes);
myAccessFunc(false, &i, &bytes);

Je suis finalement arrivé autour de:

const void* myAccessFunc(bool string, size_t* len);

et si l'utilisateur fait:

char* p = myAcccessFunc(true,&bytes);

le compilateur (GCC, au moins), ne se plaignent de jeter le qualificatif.

Était-ce utile?

La solution

Il serait préférable de faire quelque chose comme:

const void * myAccessFunc();

Si vous retournez le pointeur sur les entrailles, c'est un peu plus naturel que ce passage comme un paramètre de sortie.

Si vous deviez passer comme paramètre, vous ne voudriez:

void myAccessFunc(const void **p);

ce qui les empêche de le faire accidentellement:

void *p;  /* error: must be 'const void *p;' */
myAccessFunc(&p);

Autres conseils

Vous avez deux choses que vous pouvez faire:

  • Retour vide const *
  • Rédiger la documentation pour votre API disant aux utilisateurs de ne pas modifier la valeur retournée

Cela dit, tout utilisateur API qui décide d'essayer de muck avec un pointeur comme ça mérite ce qu'ils obtiennent. P

La prévention est impossible, puisque vous pouvez jeter constness loin. Si vous faites votre paramètre de fonction const, vous devrez vous jeter loin dans la fonction. Vous auriez également mentez à toute personne utilisant votre fonction et pourrait causer toutes sortes de bugs amusants.

Vous pouvez essayer de retourner un pointeur à la place. Ensuite, vous auriez au moins ne violez votre propre const. Cela peut ne pas être approprié dans ce cas cependant.

De retour un pointeur serait préférable, mais si vous absolument besoin de faire un paramètre, vous pouvez utiliser un struct d'intermédiaire:

typedef struct _ptr_holder {
   const void* ptr;
} ptr_holder;

void myAccessFunc( bool string, ptr_holder* ptr ) {
...
}

ptr_holder s;
myAccessFunc(true,&s);
printf("Got '%s'\n", s.ptr);

Il est hokey, mais il devrait faire l'affaire

Vous voulez éviter la modification via le pointeur de votre retour? Essayez ceci:

const void* myAccessFunc(bool string);

Qu'est-ce qui ne va pas avec ça? Si vous allez voter en bas d'une réponse valable, s'il vous plaît laisser un commentaire.

Si vous écrivez une bibliothèque C où vous ne voulez pas exposer certaines données internes structure, il peut être bon d'aller une route supplémentaire et cacher la mise en œuvre avec un typedef struct non précisée. Ceci est également appelé types opaques

Exemple:

Dans my_interface.h

 #ifndef __MYINTERFACE_H__
 #define __MYINTERFACE_H__

 /* The unspecified struct statement */
 typedef struct s_my_data t_my_data;

 t_my_data  *new_my_data(int someInitializer);
 void        free_my_data(t_my_data *data);
 int         enter_my_data(t_my_data *data, int someDataToEnter);

 const char *return_str_my_data(t_my_data *data);

 #endif /* __MYINTERFACE_H__ */



Dans my_interface.c

 #include <my_interface.h>

 /* Keep struct in .c file */
 struct s_my_data {
     int    length;
     char  *string;
 };

 t_my_data *new_my_data(int someInitializer)
 {
     /* the exercise */
 }

 void free_my_data(t_my_data *data)
 {
     /* the exercise */
 }

 int enter_my_data(t_my_data *data, int someDataToEnter)
 {
     /* the exercise */
 }

 const char *return_str_my_data(t_my_data *data)
 {
     /* the exercise */
 }

Est-ce C ou C ++? Pourquoi dans le cas int vous passez le pointeur sur un const statique? L'interface est suspect.

surcharge et de se débarrasser du booléen dans l'interface:

void myAccessFunc( const char* * const p, size_t* len){
   *p = "blahblahblah";
   *len = 12;
}

void myAccessFunc( const int* * const ppI, size_t* len){
   static const int i = 123;
   *ppI = &i;
   *len = 4;
}

se débarrasse de l'épreuve aussi. Étant donné que l'utilisateur sait vrai ou faux pour la fonction d'origine, il sait que l'on à utiliser. Le typage fort est votre ami. Se débarrasser de la classe d'erreurs où le type et le pointeur ne sont pas compatibles ...

passant BTW ces indicateurs de contrôle par rapport à la surcharge est indésirable, surtout si dans les petites méthodes.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top