Как реализовать strstr() без отбрасывания const?

strstr является C99-совместимой функцией, сигнатурой типа которой является следующее:

char *strstr(const char *haystack, const char *needle);

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

Для справки, здесь реализация Apple, а вот реализация GNU. Оба отбрасывают const в конце.

Ответ 1

Вы не можете реализовать strstr(), не нарушая корректность const. Литье - это самый простой способ сделать это. Вероятно, вы можете скрыть нарушение (например, вы можете использовать memcpy() для копирования значения указателя), но при этом нечего делать.

Проблема заключается в том, что strstr() принимает const char*, который указывает на строку, и возвращает не-t21 > char*, который указывает на одну и ту же строку.

Например, эта программа:

#include <stdio.h>
#include <string.h>
int main(void) {
    const char s[] = "hello";
    char *result = strstr(s, "hello");
    *result = 'H';
    puts(result);
}

изменяет (или, по крайней мере, пытается изменить) объект const -qualified без использования указателя указателя или любой другой явно небезопасной конструкции.

Еще в 1989 году комитет ANSI C мог бы избежать этой проблемы, указав две разные функции, например:

const char *strcstr(const char *haystack, const char *needle);
      char *strstr (      char *haystack, const char *needle);

который возвращает указатель на const char с аргументами const, а другой, который возвращает указатель на изменяемый char с учетом модифицируемого аргумента. (С++, который наследует стандартную библиотеку C, делает это путем перегрузки.)

strstr() является одной из нескольких стандартных строковых функций, которые имеют эту проблему.

Ответ 2

То, что вы видите в случае strstr (и некоторых других стандартных функций), является идиоматическим решением, целью которого является поддержка немодулирующих операций как для константных, так и для неконстантных строк с помощью одной функции.

Да, для реализации такой функции необходимо явно отбросить константу от указателя ввода. Обратите внимание, что листинг сам по себе еще не физически нарушает что-либо (что означает: он не пытается изменять постоянные данные и не вызывает поведение undefined). Тем не менее, он открывает двери для таких нарушений, возвращая неконстантный указатель на константные данные вызывающему, то есть нарушает концептуальные правила const-correctness.

Альтернативным решением было бы предоставить две версии одной и той же стандартной функции - константной и неконстантной, но без перегрузки в стиле С++, это потребует двух отличительных имен функций, которые не обязательно выглядят как очень хорошая идея (и даже при перегрузке функции в стиле С++ у нее тоже есть свои проблемы).

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