Подтвердить что ты не робот

Почему параметр 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)?

4b9b3361

Ответ 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 было исключено из С#.)