Почему параметр endptr для strtof и strtod указывает на указатель не const char?
Стандартные функции библиотеки C strtof
и strtod
имеют следующие подписи:
float strtof(const char *str, char **endptr);
double strtod(const char *str, char **endptr);
Каждый из них разбивает входную строку str
на три части:
- Исходная, возможно, пустая последовательность пробелов
- "последовательность объектов" символов, которые представляют значение с плавающей запятой
- "конечная последовательность" символов, которые не распознаются (и которые не влияют на преобразование).
Если endptr
не NULL
, тогда *endptr
устанавливается на указатель на символ, следующий за последним символом, который был частью преобразования (другими словами, начало трейлинг-последовательности).
Мне интересно: почему endptr
, а затем указатель на указатель не const
char
? Является ли *endptr
указателем на строку const
char
(входная строка str
)?
Ответы
Ответ 1
Причина проста в использовании. char *
может автоматически преобразовываться в const char *
, но char **
не может автоматически преобразовать в const char **
, а фактический тип указателя (адрес которого передается), используемого вызывающей функцией, скорее всего будет char *
чем const char *
. Причина, по которой это автоматическое преобразование невозможна, заключается в том, что существует неочевидный способ, с помощью которого можно удалить квалификацию const
с помощью нескольких этапов, где каждый шаг выглядит абсолютно корректным и само по себе. Стив Джессоп привел пример в комментариях:
если вы можете автоматически преобразовать char**
в const char**
, тогда вы могли бы сделать
char *p;
char **pp = &p;
const char** cp = pp;
*cp = (const char*) "hello";
*p = 'j';.
Для const-безопасности одна из этих строк должна быть незаконной, а поскольку остальные - абсолютно нормальные операции, она должна быть cp = pp;
Лучше всего было бы определить эти функции вместо char **
вместо void *
. Оба char **
и const char **
могут автоматически преобразовываться в void *
. (Исправленный текст на самом деле был очень плохой идеей; он не только предотвращает проверку типов, но C фактически запрещает объекты типа char *
и const char *
в псевдоним.) В качестве альтернативы эти функции могли принимать аргумент ptrdiff_t *
или size_t *
, в котором для хранения смещения конца, а не указателя на него. Это часто более полезно.
Если вам нравится последний подход, не стесняйтесь писать такую оболочку вокруг стандартных функций библиотеки и вызывать свою обертку, чтобы сохранить остальную часть вашего кода const
-clean и cast-free.
Ответ 2
Юзабилити. Аргумент str
помечен как const
, потому что входной аргумент не будет изменен. Если endptr
были const
, то это будет указывать вызывающему, что он не должен изменять данные, на которые ссылается endptr
на выходе, но часто вызывающий хочет сделать именно это. Например, мне может понадобиться нуль-конец строки после получения из нее float:
float StrToFAndTerminate(char *Text) {
float Num;
Num = strtof(Text, &Text);
*Text = '\0';
return Num;
}
Совершенно разумная вещь, чтобы хотеть делать, в некоторых обстоятельствах. Не работает, если endptr
имеет тип const char **
.
В идеале endptr
должен быть constness, соответствующий фактической входной константе str
, но C не дает возможности указывать это через свой синтаксис. ( Андерс Хейлсберг рассказывает об этой при описании того, почему const
было исключено из С#.)