Как реализовать 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.