我正在编写一个访问函数,它返回一个指向内部缓冲区的指针,我想向我的函数用户提示他们不应该更新指向的对象。一个非常人为的例子是:
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)确实抱怨丢弃限定符。
答案 0 :(得分:10)
最好做一些事情:
const void * myAccessFunc();
在返回指向内部的指针的地方,这比将其作为out参数传递更自然。
如果您将其作为out参数传递,则需要:
void myAccessFunc(const void **p);
阻止他们意外地这样做:
void *p; /* error: must be 'const void *p;' */
myAccessFunc(&p);
答案 1 :(得分:3)
你可以做两件事:
话虽如此,任何决定尝试用这样的指针进行捣乱的API用户都应该得到它们:P。
答案 2 :(得分:2)
预防是不可能的,因为你可以抛弃常量。如果你使你的函数参数为const,你将不得不在函数内部将它抛弃。你也会对任何使用你的功能的人说谎,并且会引起各种有趣的错误。
您可以尝试返回指针。那么你至少不会违反你自己的const。在这种情况下,这可能不合适。
答案 3 :(得分:2)
返回指针是最好的,但是如果你绝对需要使它成为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);
这很好,但它应该可以解决问题
答案 4 :(得分:1)
所以你想防止通过你返回的指针修改?试试这个:
const void* myAccessFunc(bool string);
这有什么问题?如果您要对有效答案进行投票,请发表评论。
答案 5 :(得分:1)
如果您正在编写一个不希望公开某些内部数据结构的C库,那么最好使用额外的路径并使用未指定的struct typedef隐藏实现。这也称为opaque types
示例:
在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 */
}
答案 6 :(得分:0)
这是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;
}
也摆脱了测试。由于用户知道原始功能的真或假,他知道使用哪一个。强打字是你的朋友。删除类型和指针不一致的错误类...
BTW传递这样的控制标志与过载是不可取的,尤其是在小方法中。