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

Являются ли strtol, strtod небезопасными?

Кажется, что strtol() и strtod() эффективно позволяют (и вынуждают) отбрасывать константу в строке:

#include <stdlib.h>
#include <stdio.h>

int main() {
  const char *foo = "Hello, world!";
  char *bar;
  strtol(foo, &bar, 10); // or strtod(foo, &bar);
  printf("%d\n", foo == bar); // prints "1"! they're equal
  *bar = 'X'; // segmentation fault
  return 0;
}

Выше, я не выполнял никаких бросков. Однако strtol() в основном меняет const char * на char * для меня без каких-либо предупреждений или чего-либо еще. (На самом деле, это не позволило бы вам набрать bar как const char * и тем самым вызвать небезопасное изменение типа.) Разве это не так опасно?

4b9b3361

Ответ 1

Я бы предположил, что, поскольку альтернатива была хуже. Предположим, что прототип был изменен, чтобы добавить const:

long int strtol(const char *nptr, const char **endptr, int base);

Теперь предположим, что мы хотим разобрать непостоянную строку:

char str[] = "12345xyz";  // non-const
char *endptr;
lont result = strtol(str, &endptr, 10);
*endptr = '_';
printf("%s\n", str);  // expected output: 12345_yz

Но что происходит, когда мы пытаемся скомпилировать этот код? Ошибка компилятора! Это довольно неинтуитивно, но вы не можете неявно преобразовать char ** в const char **. См. С++ FAQ Lite для подробного объяснения причин. Это технически говорит о С++, но аргументы одинаково справедливы для C. В C/С++ вам разрешено неявно преобразовывать из "указателя в тип" в "указатель на const type" на самом высоком уровне: преобразование, которое вы можете выполнить, от char ** до char * const * или эквивалентно от "указателя на (указатель на char)" на "указатель на (const указатель на char)".

Так как я предполагаю, что синтаксический анализ непостоянной строки гораздо более вероятен, чем анализ постоянной строки, я бы сказал, что const -корректность для маловероятного случая предпочтительнее сделать обычный случай ошибкой компилятора.

Ответ 2

Да, и другие функции имеют одинаковую проблему с "конт-отмыванием" (например, strchr, strstr, все эти партии).

Именно по этой причине С++ добавляет перегрузки (21.4: 4): подпись функции strchr(const char*, int) заменяется двумя объявлениями:

const char* strchr(const char* s, int c);
      char* strchr(      char* s, int c);

Но, конечно, в C вы не можете иметь как const-правильные версии с тем же именем, поэтому получаете компрометацию const.

В С++ не упоминаются аналогичные перегрузки для strtol и strtod, и в действительности мой компилятор (GCC) их не имеет. Я не знаю, почему нет: тот факт, что вы не можете неявно отбрасывать char** в const char** (вместе с отсутствием перегрузки), объясняет это для C, но я не совсем понимаю, что было бы неправильно с Перегрузка С++:

long strtol(const char*, const char**, int);

Ответ 3

"const char *" для первого аргумента означает, что strtol() не будет изменять строку.

Что вы делаете с возвращаемым указателем - это ваш бизнес.

Да, это можно рассматривать как нарушение безопасности типа; С++, вероятно, будет делать что-то по-другому (хотя, насколько я могу судить, ISO/IEC 14882: 1998 определяет <cstdlib> с той же сигнатурой, что и в C).

Ответ 4

У меня есть компилятор, который предоставляет при компиляции в режиме С++:

extern "C" {
long int strtol(const char *nptr, const char **endptr, int base);
long int strtol(char *nptr, char **endptr, int base);
}

Очевидно, что оба они разрешают один и тот же символ времени привязки.

EDIT: в соответствии со стандартом С++ этот заголовок не должен компилироваться. Я предполагаю, что компилятор просто не проверял это. Определения действительно отображались как это в файлах заголовков системы.