интервал по сравнению с size_t на 64 битах

Портирование кода от 32 битов до 64 битов. Много мест с

int len = strlen(pstr);

Они все генерируют предупреждения теперь, потому что strlen () возвращает size_t, который составляет 64 бита, и интервал - все еще 32 бита. Таким образом, я заменял их

size_t len = strlen(pstr);

Но я просто понял, что это не безопасно, поскольку size_t не подписан, и его можно рассматривать, как подписано кодом (я на самом деле столкнулся с одним случаем, где он вызвал проблему, спасибо, модульные тесты!).

Вслепую кастинг strlen возвращается к (международным) грязным чувствам. Или возможно это не было должно?
Таким образом, вопрос: существует ли изящное решение для этого? У меня, вероятно, есть тысяча строк кода как этот в кодовой базе; я не могу вручную проверить каждый из них, и тестовое покрытие в настоящее время где-нибудь между 0.01 и 0,001%.

16
задан Michael Myers 25 March 2010 в 21:30
поделиться

5 ответов

В качестве компромисса можно использовать ssize_t (если есть). Если нет, то можно использовать long long, int_fast64_t, intmax_t, или иметь заголовок переноса платформы, который позволяет указать подходящий тип для платформы. ssize_t находится в POSIX, а не в стандартном C или 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.

5
ответ дан 30 November 2019 в 21:53
поделиться

Установка максимального уровня предупреждений компилятора должна дать вам хороший отчет обо всех неправильных преобразованиях знаков. В gcc подойдет '-Wall -Wextra'.

Вы также можете использовать статический анализатор кода, например cppcheck, чтобы проверить, все ли в порядке.

5
ответ дан 30 November 2019 в 21:53
поделиться

Вы можете использовать ssize_t (подписанный вариант size_t ).

4
ответ дан 30 November 2019 в 21:53
поделиться

В большинстве случаев можно безопасно обрабатывать подписанный site_t. Беззнаковый size_t будет рассматриваться как отрицательный, только если он (или промежуточные результаты в выражениях) больше 2 ^ 31 (для 32-битных) или 2 ^ 63 для 64-битных.

ОБНОВЛЕНИЕ: Извините, size_t будет небезопасным в конструкциях вроде while ((size_t) t> = 0) . Итак, правильный ответ - использовать ssize_t .

1
ответ дан 30 November 2019 в 21:53
поделиться

Некоторое время назад я опубликовал короткую заметку о подобных проблемах в своем блоге, и короткий ответ таков:

Всегда используйте правильные целочисленные типы C++

Длинный ответ: При программировании на C++ хорошей идеей является использование правильных целочисленных типов, соответствующих конкретному контексту. Немного строгости всегда окупается. Нередко можно наблюдать тенденцию игнорировать интегральные типы, определенные как специфические для стандартных контейнеров, а именно size_type. Он доступен для ряда стандартных контейнеров, таких как std::string или std::vector. Такое невежество может легко отомстить.

Ниже приведен простой пример неправильного использования типа для перехвата результата функции std::string::find. Я уверен, что многие подумают, что здесь нет ничего плохого в использовании unsigned int. Но на самом деле это просто ошибка. Я использую 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. Проблему можно было бы избежать, если бы тот, кто писал эту программу, позаботился об использовании правильного типа. Не говоря уже о том, что программа сразу стала бы переносимой.

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

Рекомендую блестящую статью 64-битная разработка, написанную Андреем Карповым, где можно найти много дополнительной информации на эту тему.

7
ответ дан 30 November 2019 в 21:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: