Нужно ли мне преобразовать результат strtol в int?

Следующий код не выдает предупреждение с g++ 4.1.1 и -Wall.

int octalStrToInt(const std::string& s)
{    
    return strtol(s.c_str(), 0, 8);
}

Я ожидал предупреждения, потому что strtol возвращает long int, но моя функция возвращает только int. Могут ли другие компиляторы выдать здесь предупреждение? Должен ли я в этом случае привести возвращаемое значение к типу int?


person Michael Kristofik    schedule 11.03.2010    source источник
comment
Другие компиляторы могут, да. Например, у MSVC есть предупреждение C4244, когда вы сужаете целочисленный тип. Однако я не знаю, применимо ли это к long -> int, поскольку они имеют одинаковый размер (по крайней мере, для версий Windows этого тысячелетия). Странно, однако, видеть вызов strtol без по крайней мере 3 строк кода проверки ошибок, поэтому лично я бы добавил проверку диапазона, как предлагает Let_Me_Be, или определил функцию в стиле приведения для проверки диапазона.   -  person Steve Jessop    schedule 11.03.2010
comment
Я исключил все лишнее для простоты. Меня интересует только, нужен ли/рекомендуется ли актерский состав.   -  person Michael Kristofik    schedule 11.03.2010
comment
Ага, с достаточно недавними GCC и -Wconversion, компиляцией, например. linux/x64, я ожидаю предупреждения. Является ли 4.1.1 достаточно новой, я не знаю. -Wconversion не используется в -Wall или -Wextra, потому что вы получаете много ложных срабатываний.   -  person Steve Jessop    schedule 11.03.2010


Ответы (5)


Вам может понадобиться флаг -Wconversion, чтобы включить эти предупреждения. Однако он не будет предупреждать о long -> int, поскольку они имеют тот же размер, что и GCC (значение не изменится из-за преобразования). Но это было бы, если вы конвертируете, например, длинный -> короткий

Я полагаю, что просто делать приведение не рекомендуется, так как это просто скроет возможность ошибок. Это было бы нормально после того, как вы проверили, что такое приведение не изменит значение, чтобы успокоить компилятор.

person UncleBens    schedule 11.03.2010

Лучший подход:

long x = strtol(...); assert(x <= INT_MAX); return (int)x;

Вам нужны limits.h и assert.h

person Šimon Tóth    schedule 11.03.2010
comment
Проверьте и на отрицательном конце. - person Steve Jessop; 11.03.2010

Если у вас нет доступа к boost::numeric_cast, вы можете написать простую имитацию:

template <typename T, typename S>
T range_check(const S &s) {
    assert(s <= std::numeric_limits<T>::max());
    assert(s >= std::numeric_limits<T>::min());
    return static_cast<T>(s); // explicit conversion, no warnings.
}

return range_check<int>(strtol(some_value,0,8));

На самом деле это немного обман, поскольку он не работает для типов назначения с плавающей запятой. min() для них не то же самое, что и для целочисленных типов, вам нужно проверить против +/- max(). Упражнение для читателя.

Используете ли вы assert или какую-либо другую обработку ошибок, зависит от того, что вы на самом деле хотите сделать с недопустимым вводом.

Также есть boost::lexical_cast (навскидку, я не знаю, как сделать так, чтобы это читалось восьмерично) и stringstream. Читайте тип, который вы хотите, а не тип, для которого есть библиотечная функция C.

person Steve Jessop    schedule 11.03.2010
comment
Использование assert для этого кажется несколько неправильным. IMO, assert предназначен для перехвата ошибок программиста (например, вызывающая сторона не должна передавать указатель NULL), но здесь ошибка зависит от данных времени выполнения, которые вы ему предоставляете. Это означает, что вызывающая сторона должна была сначала проверить, что содержимое строки подходит для этой функции, что делает все это немного бесполезным - теперь мне нужна другая функция, чтобы сначала проверить, будет ли преобразование успешным. - person UncleBens; 11.03.2010
comment
Конечно, но в примере кода я склонен использовать assert, если только нет причин использовать что-то другое. В качестве альтернативы я пишу if (bad_thing) panic();, предоставляя читателю реализовать panic(). В реальной жизни я согласен с вами, у этой функции должен быть способ указать на сбой, возможно, путем создания исключения. Поскольку это не так, и я не хочу вводить исключения, я использую assert, чтобы указать, что здесь произошла ошибка. Смирись с этим. - person Steve Jessop; 11.03.2010
comment
Хорошо, но я все еще думаю, что эта практика вводит в заблуждение. (Например, можете ли вы сказать, имеет ли Let_Me_Be подобное соглашение для примеров, или он серьезно рекомендует assert как лучший способ обработки неверных данных во время выполнения?) - person UncleBens; 11.03.2010
comment
Я не могу, но мне не нужно знать, потому что я не оцениваю его код ;-) Кристо говорит в комментариях, что проверка ошибок в реальном коде была удалена, чтобы сосредоточиться на преобразовании/ В ролях. Если мы собираемся беспокоиться о том, что люди будут слепо копировать код, то я бы сказал, что передача нулевого указателя в strtol является еще более серьезной проблемой, чем assert. Ни один из способов не подходит для работы с ненадежными входными данными, даже если предположить, что входные данные Кристо ненадежны. - person Steve Jessop; 11.03.2010

Вы не видите здесь никаких предупреждений, потому что типы данных "int" и "long int" на вашей платформе имеют одинаковый размер и диапазон. В зависимости от архитектуры они могли стать разными.

Чтобы защитить себя от странных ошибок, используйте проверку диапазона. Я предлагаю вам использовать std::numeric_limits::min/max (см. Ресурсы).

После проверки диапазона вы можете безопасно использовать static_cast или c-style cast.

С другой стороны, вы можете положиться на ту же функциональность, реализованную в классе std::stringstream. Преобразование с помощью std::stringstream по умолчанию будет безопасным для платформы и типа.

person user291562    schedule 11.03.2010
comment
Не было бы никаких предупреждений, если бы они были разными, без указания -Wconversion. Для long -> short на 32-битной версии x86 вы получаете предупреждение на 4.3.2, но не на 3.4.4, не уверен, когда появилось изменение. - person Steve Jessop; 11.03.2010
comment
Не уверен насчет -Wconversion, кажется, он будет генерировать слишком много информации, не относящейся к потенциально опасному усечению типов. После версии 4.3 был введен параметр -Wp64, этот флаг точно определяет усечение типа long int-›int. - person user291562; 11.03.2010
comment
Прикольно, не знал об этом. Я не имел в виду, что спрашивающий должен обязательно использовать -Wconversion, просто когда вы говорите, вы не видите здесь никакого предупреждения, потому что X, это не только из-за X, это также потому, что спрашивающий не включил это предупреждение... - person Steve Jessop; 11.03.2010

Большинство современных компиляторов будут предупреждать о преобразовании и возможном усечении в зависимости от настроенного вами уровня предупреждения. В MSVC это уровень предупреждения 4.

Лучшей практикой было бы вернуть long из вашей функции и позволить вызывающему коду решить, как обрабатывать преобразование. За исключением этого, по крайней мере убедитесь, что значение, которое вы возвращаете из strtol, будет соответствовать int, прежде чем вы вернетесь, как это было предложено Let_Me_Be.

person John Dibling    schedule 11.03.2010