Почему я не могу использовать страх?
Я переношу некоторый код в Windows, а компилятор Microsoft (Visual С++ 8) сообщает мне, что strerror()
является небезопасным.
Отбросив фактор досады во всех безопасных строковых материалах от Microsoft, я действительно вижу, что некоторые из устаревших функций опасны. Но я не могу понять, что может быть неправильным с strerror()
. Он принимает код (int
) и возвращает соответствующую строку или пустую строку, если этот код неизвестен.
Где опасность?
Есть ли хорошая альтернатива в C?
Есть ли хорошая альтернатива в С++?
[править]
Имея некоторые хорошие ответы и теперь понимая, что некоторые реализации могут быть настолько сумасшедшими, чтобы на самом деле писать общий общий буфер - небезопасно для повторного размещения в одном потоке, неважно между потоками! - мой вопрос перестает быть "Почему я не могу использовать его, и каковы альтернативы?" на "Есть ли приличные, сжатые альтернативы в C и/или С++?"
Заранее спасибо
Ответы
Ответ 1
strerror
устарел, поскольку он не является потокобезопасным. strerror
работает во внутреннем статическом буфере, который может быть перезаписан другими параллельными потоками. Вы должны использовать безопасный вариант под названием strerror_s
.
Для безопасного варианта требуется, чтобы размер буфера передавался функции, чтобы подтвердить, что буфер достаточно велик, прежде чем записывать на него, что позволяет избежать переполнения буфера, которые могут привести к выполнению вредоносного кода.
Ответ 2
strerror
сам по себе небезопасен. В прежние дни до нарезки это просто не проблема. В потоках два или более потока могут вызвать strerror
, оставляя возвращаемый буфер в состоянии undefined. Для однопоточных программ не должно повредить использование strerror
, если они не играют какие-то странные игры в libc, как общая память для всех приложений в DLL.
Чтобы устранить это, появился новый интерфейс с той же функциональностью:
int strerror_r(int errnum, char *buf, size_t buflen);
Обратите внимание, что вызывающий объект предоставляет пространство буфера и размер буфера. Это решает проблему. Даже для однопоточных приложений вы также можете использовать его. Это не повредит немного, и вы можете привыкнуть к этому более безопасному способу.
ПРИМЕЧАНИЕ: вышеупомянутый прототип представляет собой спецификацию XSI. Он может различаться для каждой платформы или с параметрами компилятора или символами #define
. GNU, например, делает эту версию или ее собственную версию доступной в зависимости от #define
Ответ 3
Имея некоторые хорошие ответы и теперь понимая, что некоторые реализации могут быть настолько сумасшедшими, чтобы на самом деле писать общий общий буфер - небезопасно для повторного размещения в одном потоке, неважно между потоками! - мой вопрос перестает быть "Почему я не могу использовать его, и каковы альтернативы?" на "Есть ли приличные, сжатые альтернативы в C и/или С++?"
Posix указывает strerror_r()
, а в Windows вы можете использовать strerror_s()
, который немного отличается, но имеет ту же цель. Я делаю это:
#define BAS_PERROR(msg, err_code)\
bas_perror(msg, err_code, __FILE__, __LINE__)
void bas_perror (const char* msg, int err_code, const char* filename,
unsigned long line_number);
void
bas_perror (const char* usr_msg, int err_code, const char* filename,
unsigned long line_number)
{
char sys_msg[64];
#ifdef _WIN32
if ( strerror_s(sys_msg, sizeof sys_msg, err_code) != 0 )
{
strncpy(sys_msg, "Unknown error", taille);
sys_msg[sizeof sys_msg - 1] = '\0';
}
#else
if ( strerror_r(err_code, sys_msg, sizeof sys_msg) != 0 )
{
strncpy(sys_msg, "Unknown error", sizeof sys_msg);
sys_msg[sizeof sys_msg - 1] = '\0';
}
#endif
fprintf(stderr, "%s: %s (debug information: file %s, at line %lu)\n",
usr_msg, sys_msg, filename, line_number);
}
Я написал эту функцию, потому что функции потоков Posix не изменяют errno
, вместо этого они возвращают код ошибки. Таким образом, эта функция в основном такая же, как perror()
, за исключением того, что она позволяет вам предоставить код ошибки, отличный от errno
, а также отображает некоторую отладочную информацию. Вы можете адаптировать его к вашим потребностям.
Ответ 4
Вы не можете полагаться на строку, возвращаемую strerror()
, потому что она может измениться при следующем вызове функции. Тогда ранее возвращаемые значения могут устареть. Особенно в многопоточных средах, вы не можете гарантировать, что строка действительна при ее доступе.
Представьте себе следующее:
Thread #1:
char * error = strerror(1);
Thread #2
char * error = strerror(2);
printf(error);
В зависимости от реализации strerror()
, этот код выводит код ошибки для кода ошибки 2, а не код ошибки 1.
Ответ 5
Для краткой обертки вы можете использовать STLSoft stlsoft::error_desc
, как в:
std::string errstr = stlsoft::error_desc(errno);
Если посмотреть на код, он будет реализован в терминах strerror()
, что означает, что он будет безопасным для повторного размещения в потоке (т.е. если он используется несколько раз в рамках данного оператора), но он не адресует многопоточность.
Кажется, они работают довольно быстрыми циклами выпуска для дефектов, поэтому вы можете попробовать запросить мод?
Ответ 6
Хотя я не знаю причины Microsoft, я отмечаю, что strerror возвращает неконстантный char *, что означает, что существует риск того, что какой-то Merry Prankster вызвал страх перед вами и изменил сообщение.