Почему bsearch возвращает пустоту *?

void * bsearch ( const void * key,
                 const void * base,
                 size_t num,
                 size_t size,
                 int ( * comparator ) ( const void *, const void * ) );

Если я передаю a const void * base, не должен ли bsearch возвращать результат const void *?

Ответ 1

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

Параметры const являются обещанием, что сам bsearch не будет их модифицировать, что разумно.

Ответ 2

Добавление квалификатора к типу с указателем - это неявное преобразование, тогда как удаление квалификатора требует явного приведения.

Прототип 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-квалификацию без предупреждения, например, если бы мы написали

int *q = bsearch(&needle, bar, 42, sizeof *bar, cmpi);

Это совершенно легально: поведение undefined происходит, если мы действительно использовали q для modifiy bar.

Следует также иметь в виду, что const-qualifying параметр указателя влияет только на то, какие аргументы принимаются без приведения, но не гарантирует, что функция не будет изменять объект с указателем. Это просто соглашение, за которым следует практически весь существующий код, но он не применяется семантикой языка.

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

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

Ответ 3

Я думаю, что это самый большой и самый раздражающий дефект в системе типа C. Другим примером этого является strchr, функция с точно такой же проблемой: она возвращает указатель на ресурс, который передал пользователь. Эта функция должна быть полезна как для константных, так и для неконстантных входных параметров. То, что вы видите, является компромиссом.

Я нахожу это наиболее раздражающим для таких аксессуаров:

const struct list *list_next(const struct list *x) { return x->next; }

Для внутреннего использования макрос является хорошей "полиморфной" реализацией

#define LIST_NEXT(x) ((x)->next)

но для внешнего использования вы должны либо использовать компрометацию bsearch, либо две отдельные функции list_next и list_next_const.