Почему определенные неявные преобразования типов безопасны на компьютере, а не на другом? Как я могу предотвратить эти кроссплатформенные проблемы?

Недавно я нашел ошибку в моем коде, которая заняла у меня несколько часов для отладки.

проблема была в функции, определенной как:

unsigned int foo(unsigned int i){
long int v[]={i-1,i,i+1} ;
.
.
.
return x ; // evaluated by the function but not essential how for this problem.
}

Определение v не вызывало каких-либо проблем на моей машине разработки (32-битная версия ubuntu 12.04, компилятор g ++), где беззнаковое int неявно преобразовывалось в long int, и поэтому отрицательные значения были правильно обработаны.

Однако на другом компьютере (64-разрядная версия Ubuntu 12.04, компилятор g ++) эта операция была небезопасной. Когда i = 0, v [0] был установлен не в -1, а в какое-то странное большое значение (как это часто бывает
при попытке сделать неподписанный int отрицательным).

Я мог бы решить проблему приведения значения i к длинному int

long int v[]={(long int) i - 1, (long int) i, (long int) i + 1};

и все работало нормально (на обеих машинах).

Я не могу понять, почему первый работает нормально на машине, а другой не работает.

Можете ли вы помочь мне понять это, чтобы я мог избежать тех или иных проблем в будущем?

5

Решение

За unsigned значения, сложение / вычитание хорошо определены как арифметика по модулю, так 0U-1 будет работать на что-то вроде std::numeric_limits<unsigned>::max(),

При преобразовании из неподписанного в подписанное, если тип назначения достаточно велик, чтобы вместить все значения без знака, он просто выполняет прямое копирование данных в тип назначения. Если тип назначения недостаточно велик, чтобы вместить все беззнаковые значения, я считаю, что он определен реализацией (постараюсь найти стандартную ссылку).

Так когда long является 64-битным (предположительно на вашей 64-битной машине) неподписанным и копируется прямо.

когда long 32-битный на 32-битной машине, опять же, он, скорее всего, просто интерпретирует битовую комбинацию как знаковое значение, которое в данном случае равно -1.

РЕДАКТИРОВАТЬ: Самый простой способ избежать этих проблем, чтобы избежать смешивания подписанных и неподписанных типов. Что значит вычесть единицу из значения, концепция которого не допускает отрицательных чисел? Я собираюсь доказать, что параметр функции должен быть значением со знаком в вашем примере.

Тем не менее, G ++ (по крайней мере, версия 4.5) предоставляет удобный -Wsign-conversion который обнаруживает эту проблему в вашем конкретном коде.

6

Другие решения

У вас также могут быть специальные отливки, улавливающие все отливки через перелив:

template<typename O, typename I>
O architecture_cast(I x) {
/* make sure I is an unsigned type. It  */
static_assert(std::is_unsigned<I>::value, "Input value to architecture_cast has to be unsigned");

assert(x <= static_cast<typename std::make_unsigned<O>::type>( std::numeric_limits<O>::max() ));

return static_cast<O>(x);
}

Используя это, вы поймете в отладке все броски с больших чисел, чем результирующий тип может вместить. Это включает в себя ваш случай беззнакового int равным 0 и вычитаемым -1, что приводит к наибольшему unsigned int.

0

По вопросам рекламы [email protected]