void * bsearch ( const void * key,
const void * base,
size_t num,
size_t size,
int ( * comparator ) ( const void *, const void * ) );
如果我传入const void * base
,bsearch
也不应该返回const void *
个结果?
答案 0 :(得分:8)
当您搜索某些内容时,这是一个有效的请求,您可以在找到它后对其进行修改。如果搜索功能不允许您这样做,那将会限制太多。当然,这样的修改可以打破后续搜索,但这是另一回事。
参数是const作为bsearch 本身不会修改它们的承诺,这是合理的。
答案 1 :(得分:3)
将限定符添加到指向类型是隐式转换,而删除限定符则需要显式转换。
bsearch()
的原型编写方式允许以下两种用法而不进行显式转换:
int needle = 0xdeadbeef;
int foo[42] = { ... };
int *p = bsearch(&needle, foo, 42, sizeof *foo, cmpi);
const int bar[42] = { ... };
const int *q = bsearch(&needle, bar, 42, sizeof *bar, cmpi);
但是,这意味着可以使用bsearch()
- 以及许多其他libc函数 - 在没有警告的情况下删除const-qualification,例如,如果我们已经编写了
int *q = bsearch(&needle, bar, 42, sizeof *bar, cmpi);
这是完全合法的:只有在我们实际使用q
来修改bar
时才会出现未定义的行为。
您还应该记住,const限定指针参数仅影响在没有强制转换的情况下接受哪些参数,但不保证该函数不会修改指向对象。这仅仅是一种惯例,后面几乎是所有现有代码,但它并没有被语言语义强制执行。
特别是,编译器不能在调用代码中使用此信息进行优化 - 编译器需要查看函数体,因为从指针中删除const限定并修改指向对象是合法的本身未被宣布为const
。
在过去,我假设另外限制限定指针参数会强制执行不变性,但仔细重读第6.7.3.1节会让我相信情况并非如此:指向对象的约束 - 如果指针实际用于访问对象,则限制限定指针才会生效,但调用代码不能单独从原型中做出假设...
答案 2 :(得分:0)
我认为这是C类型系统中最大且最烦人的缺陷。另一个例子是strchr
,一个具有完全相同问题的函数:它返回一个指向用户传入的资源的指针。这个函数需要对const和非const输入参数都有用。你看到的是一种妥协。
我发现对于像这样的访问者来说这是最烦人的:
const struct list *list_next(const struct list *x) { return x->next; }
对于内部使用,宏是一个很好的“多态”实现
#define LIST_NEXT(x) ((x)->next)
但是对于外部使用,您必须使用bsearch
折衷方案或两个单独的函数list_next
和list_next_const
。