Int vs size_t на 64-битной
Портирование кода с 32 до 64 бит. Множество мест с
int len = strlen(pstr);
Все они генерируют предупреждения сейчас, потому что strlen() возвращает size_t, который равен 64 бит, а int все равно 32 бит. Поэтому я заменил их
size_t len = strlen(pstr);
Но я просто понял, что это небезопасно, поскольку size_t не имеет знака и его можно рассматривать как подписанный кодом (я фактически столкнулся с одним случаем, когда он вызвал проблему, спасибо, модульные тесты!).
Слепо отбрасывание strlen return to (int) чувствует себя грязным. Или, может быть, не стоит?
Поэтому вопрос: есть ли для этого элегантное решение? У меня, вероятно, есть тысяча строк кода, подобных этому в кодовой базе; Я не могу вручную проверять каждую из них, а охват тестирования в настоящее время составляет от 0,01 до 0,001%.
Ответы
Ответ 1
В качестве компромисса вы можете использовать ssize_t
(если доступно). Подделайте его, если нет, используя long long
, int_fast64_t
, intmax_t
или используйте заголовок для переноса платформы, который позволяет указать подходящий тип для платформы. ssize_t
находится в POSIX не стандартном C или С++, но если вы когда-либо попадаете на платформу, у которой нет подписанного типа того же размера, что и size_t
, то я сочувствую.
Приведение к int
почти безопасно (предполагается, что 32 бит int на вашей 64-битной платформе, что кажется разумным), потому что строка вряд ли будет больше 2 ^ 31 байта. Бросок на более крупный подписанный тип еще более безопасен. Клиенты, которые могут позволить себе 2 ^ 63 байта памяти, известны в торговле как "хорошая проблема", -)
Конечно, вы можете проверить это:
size_t ulen = strlen(pstr);
if (ulen > SSIZE_MAX) abort(); // preferably trace, log, return error, etc.
ssize_t len = (ssize_t) ulen;
Конечно, есть накладные расходы, но если у вас 1000 экземпляров, они не могут быть критичными по производительности. Для тех, которые (если они есть), вы можете выполнить работу, чтобы выяснить, действительно ли имеет значение len
. Если это не так, переключитесь на size_t
. Если это так, перепишите или просто рискуйте никогда не встретить объект, который абсурдно огромен. Первоначальный код почти наверняка все-таки ошибся на 32-битной платформе, если len
был отрицательным в результате strlen
, возвращающего значение больше, чем INT_MAX
.
Ответ 2
Некоторое время назад я опубликовал короткую заметку об этих проблемах в своем блоге, и короткий ответ:
Всегда используйте правильные целые типы С++
Длинный ответ:
При программировании на С++ рекомендуется использовать правильные целые типы, относящиеся к конкретному контексту. Немного строгости всегда окупается. Нередко видеть тенденцию игнорировать интегральные типы, определенные как конкретные для стандартных контейнеров, а именно size_type. Его доступно для количества стандартных контейнеров, таких как std::string или std::vector. Такое невежество может легко отомстить.
Ниже приведен простой пример неверно используемого типа для получения результата функции std::string:: find. Я уверен, что многие ожидали, что здесь нет ничего неправильного. Но на самом деле это всего лишь ошибка. Я запускаю Linux в 64-битной архитектуре, и когда я компилирую эту программу как есть, она работает так, как ожидалось. Однако, когда я заменяю строку в строке 1 с помощью abc, она по-прежнему работает, но не так, как ожидалось: -)
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s = "a:b:c"; // "abc" [1]
char delim = ':';
unsigned int pos = s.find(delim);
if(string::npos != pos)
{
cout << delim << " found in " << s << endl;
}
}
Исправить очень просто. Просто замените unsigned int std::string:: size_type. Проблему можно было бы избежать, если кто-то, кто написал эту программу, позаботился о правильном типе. Не говоря уже о том, что программа будет переноситься сразу.
Я видел такие проблемы довольно много раз, особенно в коде, написанном бывшими программистами на C, которые не любят носить морду строгости, которую система С++ использует и требует. Приведенный выше пример является тривиальным, но я считаю, что он хорошо описывает корень проблемы.
Я рекомендую блестящую статью 64-разрядную разработку, написанную Андреем Карповым, где вы можете найти намного больше по этому вопросу.
Ответ 3
Установка предупреждений компилятора на максимальный уровень должна дать вам хороший отчет о каждом неправильном преобразовании знака. В gcc, '-Wall -Wextra' должен делать.
Вы также можете использовать статический анализатор кода, например, cppcheck, чтобы убедиться, что все правильно.
Ответ 4
Вы можете использовать ssize_t
(подписанный вариант size_t
).
Ответ 5
В большинстве случаев вы можете безопасно обрабатывать сайт_t. Беззнаковый размер_t будет считаться отрицательным только тогда, когда он (или промежуточные результаты в выражениях) больше 2 ^ 31 (для 32-разрядных) или 2 ^ 63 для 64 бит.
UPDATE:
К сожалению, size_t будет небезопасным в таких конструкциях, как while ( (size_t)t >=0 )
. Правильный ответ - использовать ssize_t
.
Ответ 6
Если ваш компилятор поддерживает С++ 0x:
auto len = strlen(pstr);