2010-03-25 4 views
16

Портирующий код от 32 бит до 64 бит. Много мест сint vs size_t на 64-битной

int len = strlen(pstr); 

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

size_t len = strlen(pstr); 

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

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

+1

У вас есть пример, где эта длина трактуется как знаковое? – kroimon

+0

Пример, вероятно, что-то вроде строк: 'len--; if (len <0) {break} ' – Tim

ответ

5

В качестве компромисса вы можете использовать ssize_t (при его наличии). Подделайте его, если нет, используя long long, int_fast64_t, intmax_t или используйте заголовок для переноса платформы, который позволяет указать подходящий тип для платформы. ssize_t в POSIX не является стандартным C или C++, но если вы когда-либо попадаете на платформу, у которой нет подписанного типа того же размера, что и size_t, тогда я сочувствую.

Листинг int почти безопасен (предполагается, что 32-битная версия на вашей 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.

+0

Я согласен, что приведение в int почти безопасно, но я не понимаю, в чем смысл ssize_t: он также * nerly * safe. Это немного безопаснее, чем int, но все же - size_t может быть больше, чем ssize_t. –

+0

@MK, 'ssize_t' должен быть равен размеру' size_t' – osgx

+2

@MK: Я думаю, что общее намерение 'ssize_t' состоит в том, что на практике реализации POSIX не позволят отдельным объектам, размер которых больше половины размера доступное адресное пространство. Достаточно легко обеспечить, что это «malloc», хотя я не думаю, что это гарантировано. Полезно иметь тип подписанного размера для представления смещений, которые могут быть отрицательными. –

1

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

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

+1

Я имел в виду случай, когда я затем уменьшаю len до точки, где он становится отрицательным. Как в цикле while (len> 0) –

+0

loop 'while (len> 0)' должен останавливаться на 'len == 0'. Пожалуйста, покажите нам ваш пример, проблема в котором была обнаружена с помощью модульных тестов. – osgx

+2

Бла, извините, я имел в виду, если (len <0). У меня был цикл с этой обратной проверкой «если (len <0) пропустить что-то»; вместо «если (len> = 0) что-то делать»; –

5

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

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

+0

и -wall найдут все места, где size_t используется в подписанном контексте. Вы действительно должны использовать size_t – pm100

4

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

7

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

Always use proper C++ integer types

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

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

#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; 
    } 
} 

Fix очень просто. Просто замените unsigned int на std :: string :: size_type. Проблему можно было бы избежать, если кто-то, кто написал эту программу, позаботился о правильном типе. Не говоря уже о том, что программа будет переноситься сразу.

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

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

+2

Хотя я обычно согласен с «использованием правильных типов», 'std :: some_container :: size_type' сводится к' size_t' во всех приличных реализациях.Насколько я могу судить, по крайней мере 'std :: bitset :: size_type',' std :: array :: size_type', 'std :: initializer_list' и' std :: allocator :: size_type' являются typedefs для ' size_t'. Поэтому, если вы не используете сумасшедший распределитель или очень специальные параметры шаблона, достаточно 'size_t'. – rubenvb

1

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

auto len = strlen(pstr); 
Смежные вопросы