Подтвердить что ты не робот

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%.

4b9b3361

Ответ 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);