2009-06-14 8 views
13

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

ответ

13

Я бы предположил, что, поскольку альтернатива была хуже. Предположим, что прототип был изменен, чтобы добавить 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++ вам разрешено неявно конвертировать из «указатель в типа« to »указатель на constтип« на самом высоком уровне : преобразование, которое вы можете выполнить, составляет от char ** до char * const *, или, что то же самое, от "указатель к (указатель на char)" на "указатель на (const указатель на char)".

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

+4

Но C++ не мешает вам перегружать функцию: у вас может быть 'long int strtol (char * nptr, char ** endptr, int base);' * и * 'long int strtol (const char * nptr, const char ** endptr, int base); ': это исправляет вашу ошибку компиляции. Действительно, стандарт делает это для других таких функций, таких как 'strchr' и' strstr', – Thanatos

+0

Вы можете обратиться к веб-сайту C FAQ [В чем разница между 'const char * p',' char const * p' и 'char * const p'?] (http://c-faq.com/ansi/constptrconst.html) и, более конкретно, [Почему я не могу передать' char ** 'функции, которая ожидает' const char ** '?] (http://c-faq.com/ansi/constmismatch.html) вместо часто задаваемых вопросов по C++, хотя я не полностью убежден, что объяснения легко понять. –

1

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

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

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

+0

C++ определяет strtol (и все остальное в cstdlib) с той же подписью, что и C, но не все в cstring и cwchar. –

6

Да, и другие функции имеют одинаковую проблему с «конт-отмыванием» (например, 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 вы не можете иметь как константные-корректные версии с тем же именем, так что вы получите const-неправильный компромисс.

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

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

stlport обеспечивает эту перегрузку (и некоторые другие). –

1

у меня есть компилятор, который обеспечивает, при компиляции в режиме 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++ этот заголовок не должен компилироваться. Я предполагаю, что компилятор просто не проверял это. Определения действительно отображались как это в файлах заголовков системы.