2

我正在编写一个访问函数,它返回一个指向内部缓冲区的指针,我想向我的函数的用户提示他们不应该更新指向的对象。一个非常人为的例子是:

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)确实抱怨丢弃了限定符。

4

7 回答 7

10

最好执行以下操作:

const void * myAccessFunc();

在将指针返回到内部的地方,这比将其作为输出参数传递更自然一些。

如果要将其作为输出参数传递,则需要:

void myAccessFunc(const void **p);

这可以防止他们意外地这样做:

void *p;  /* error: must be 'const void *p;' */
myAccessFunc(&p);
于 2009-02-19T21:03:58.733 回答
3

你有两件事可以做:

  • 返回一个 const void*
  • 为您的 API 编写一些文档,告诉用户不要修改返回的值

话虽如此,任何决定尝试用这样的指针弄脏的 API 用户都应该得到他们得到的东西:P。

于 2009-02-19T21:06:35.250 回答
2

预防是不可能的,因为你可以抛弃 constness。如果您将函数参数设为 const,则必须自己在函数中将其丢弃。你也会对任何使用你的函数的人撒谎,并可能导致各种有趣的错误。

您可以尝试返回一个指针。那么你至少不会违反你自己的 const。不过,在这种情况下,这可能不合适。

于 2009-02-19T21:02:17.117 回答
2

返回一个指针是最好的,但如果你绝对需要让它成为一个输出参数,你可以使用一个结构作为中介:

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

这很狡猾,但它应该可以解决问题

于 2009-02-19T21:14:44.953 回答
1

所以你想防止通过你返回的指针进行修改?试试这个:

const void* myAccessFunc(bool string);

这有什么问题?如果您要对有效答案投反对票,请发表评论。

于 2009-02-19T21:06:28.917 回答
1

如果您正在编写一个不想公开某些内部数据结构的 C 库,那么最好走一条额外的路线并使用未指定的 struct 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 */
 }
于 2009-02-22T23:58:18.677 回答
0

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

也摆脱了测试。由于用户知道原始功能的真假,因此他知道要使用哪一个。强打字是你的朋友。摆脱类型和指针不一致的错误类...

顺便说一句,传递此类控制标志而不是重载是不可取的,尤其是在小型方法中。

于 2009-04-25T23:31:40.270 回答