Возвращение пустого указателя на постоянный объект в C - PullRequest
2 голосов
/ 19 февраля 2009

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

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) жалуется на то, что выбросил классификатор.

Ответы [ 7 ]

10 голосов
/ 20 февраля 2009

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

const void * myAccessFunc();

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

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

void myAccessFunc(const void **p);

, который мешает им сделать это случайно:

void *p;  /* error: must be 'const void *p;' */
myAccessFunc(&p);
3 голосов
/ 20 февраля 2009

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

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

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

2 голосов
/ 20 февраля 2009

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

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

Это хоккей, но это должно сработать

2 голосов
/ 20 февраля 2009

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

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

1 голос
/ 23 февраля 2009

Если вы пишете библиотеку 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 */
 }
1 голос
/ 20 февраля 2009

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

const void* myAccessFunc(bool string);

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

0 голосов
/ 26 апреля 2009

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

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

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 для исходной функции, он знает, какую из них использовать. Строгая печать - твой друг. Избавляет от класса ошибок, где тип и указатель несовместимы ...

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...