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

В качестве компромисса вы можете использовать ssize_t (если есть). Подделайте его, если нет, используя long long , int_fast64_t , intmax_t или используйте заголовок для переноса платформы, который позволяет указать подходящий тип для платформы. ssize_t находится в POSIX, не ssize_t стандартным 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 экземпляров, они не могут быть критичными по производительности. Для тех, которые (если они есть), вы можете выполнить работу, чтобы выяснить, действительно ли имеет значение подпись. Если это не так, переключитесь на size_t . Если это так, перепишите или просто рискуйте никогда не встретить объект, который абсурдно огромен. Первоначальный код почти наверняка проделал бы неправильную вещь в любом случае на 32-битной платформе, если бы len был отрицательным в результате того, что strlen вернул значение больше INT_MAX .

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

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

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

Ниже приведен простой пример неверно используемого типа, чтобы поймать результат функции std :: string :: find. Я совершенно уверен, что многие ожидали, что здесь нет ничего плохого в unsigned int. Но на самом деле это всего лишь ошибка. Я запускаю Linux в 64-битной архитектуре, и когда я компилирую эту программу как есть, она работает так, как ожидалось. Однако, когда я заменяю строку в строке 1 на abc, она по-прежнему работает, но не так, как ожидалось 🙂

 #include  #include  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, которые не любят носить дужку строгости, которую система C ++ использует и требует. Приведенный выше пример является тривиальным, но я считаю, что он хорошо описывает корень проблемы.

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

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

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

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

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

UPDATE: Извините, size_t будет небезопасным в таких конструкциях, как while ( (size_t)t >=0 ) . Правильный ответ – использовать ssize_t .

Если ваш компилятор поддерживает c ++ 0x:

 auto len = strlen(pstr);