Почему bsearch возвращает пустоту *? - PullRequest
10 голосов
/ 29 октября 2011
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 * результат?

Ответы [ 3 ]

8 голосов
/ 29 октября 2011

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

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

3 голосов
/ 29 октября 2011

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

Прототип 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);

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

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

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

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

0 голосов
/ 13 ноября 2011

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

...