Являются ли strtol, strtod небезопасными? - PullRequest
14 голосов
/ 15 июня 2009

Кажется, что 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 *, что приведет к небезопасному изменению типа.) Разве это не опасно?

Ответы [ 4 ]

13 голосов
/ 15 июня 2009

Я бы догадался, потому что альтернатива была хуже. Предположим, что прототип был изменен, чтобы добавить 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 **. См. C ++ FAQ Lite для подробного объяснения причин. Там технически говорится о C ++, но аргументы в равной степени действительны и для C. В C / C ++ вам разрешено неявно преобразовывать только указатель « тип » в «указатель const * 1015». * введите"на самом высоком уровне: вы можете выполнить преобразование из char ** в char * const * или, что эквивалентно, из" указателя на (указатель на char) "на" указателя на (const указатель на char)».

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

7 голосов
/ 15 июня 2009

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

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

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

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

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

long strtol(const char*, const char**, int);
1 голос
/ 15 июня 2009

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

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

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

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

1 голос
/ 15 июня 2009

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

То, что вы делаете с возвращенным указателем, это ваше дело.

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

...