Возвращает пустой указатель на постоянный объект в C

StackOverflow https://stackoverflow.com/questions/567098

  •  05-09-2019
  •  | 
  •  

Вопрос

Я пишу функцию доступа, которая возвращает указатель на внутренний буфер, и я хотел бы намекнуть пользователям моей функции, что им не следует обновлять объект, на который указано.Очень надуманным примером может быть:

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);

Да, я знаю, что это выглядит неубедительно.

То, что я хочу предотвратить, это:

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

Я знаю, что не могу полностью предотвратить это, но я бы по крайней мере хотел, чтобы предупреждение компилятора намекало пользователю, что они не должны этого делать.Если они приводят мой указатель, то это их проблема.Существует ли какая-то комбинация const и void и *, которая сделает это?Я попробовал что-то вроде:

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

но это, казалось, избавило от пустоты указателя, поэтому вызывающий должен был сделать:

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

или

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

и не смог сделать:

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

Наконец - то я пришел в себя, чтобы:

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

и если пользователь делает:

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

компилятор (по крайней мере, GCC) действительно жалуется на то, что отбрасывает квалификатор.

Это было полезно?

Решение

Было бы лучше всего сделать что-то вроде:

const void * myAccessFunc();

Когда вы возвращаете указатель на внутренние компоненты, это немного более естественно, чем передавать его в качестве параметра out .

Если бы вы передали его в качестве параметра out, вы бы хотели:

void myAccessFunc(const void **p);

что не позволяет им сделать это случайно:

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

Другие советы

У тебя есть две вещи, которые ты можешь сделать:

  • Возвращает постоянную пустоту*
  • Напишите какую-нибудь документацию для вашего API, в которой пользователям будет указано не изменять возвращаемое значение

С учетом сказанного, любой пользователь API, который решит попробовать что-то испортить с помощью такого указателя, заслуживает того, что он получает: P.

Предотвратить это невозможно, так как вы можете отбросить постоянство.Если вы зададите параметру вашей функции значение const, вам придется самому отбросить его внутри функции.Вы также солгали бы любому, кто использует вашу функцию, и могли бы вызвать всевозможные забавные ошибки.

Вместо этого вы могли бы попробовать вернуть указатель.Тогда вы, по крайней мере, не нарушали бы свой собственный const.Однако в данном случае это может быть неуместно.

Возврат указателя был бы лучшим, но если вы абсолютно нужно сделать это параметром out, вы могли бы использовать struct в качестве посредника:

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);

Это хулиганство, но оно должно сработать

Итак, вы хотите предотвратить модификацию с помощью возвращаемого вами указателя?Попробуй это:

const void* myAccessFunc(bool string);

Что в этом плохого?Если вы собираетесь проголосовать против правильного ответа, пожалуйста, оставьте комментарий.

Если вы пишете библиотеку C, в которой вы не хотите раскрывать какую-либо внутреннюю структуру данных, может быть полезно использовать дополнительную маршрут и скрыть реализацию с помощью неуказанной структуры typedef.Это также называется непрозрачные типы

Пример:

В 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__ */



В 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 */
 }

Это C или C ++?Почему в случае int вы передаете указатель на статический const?Интерфейс вызывает подозрение.

Перегрузите и избавьтесь от логического значения в интерфейсе:

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;
}

Избавляется и от теста тоже.Поскольку пользователь знает true или false для исходной функции, он знает, какую из них использовать.Строгий набор текста - ваш друг.Избавляется от класса ошибок, в которых тип и указатель несовместимы...

Кстати, передача таких управляющих флагов в сравнении с перегрузкой нежелательна, особенно в небольших методах.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top