Недавно я нашел ошибку в моем коде, которая заняла у меня несколько часов для отладки.
проблема была в функции, определенной как:
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};
и все работало нормально (на обеих машинах).
Я не могу понять, почему первый работает нормально на машине, а другой не работает.
Можете ли вы помочь мне понять это, чтобы я мог избежать тех или иных проблем в будущем?
За unsigned
значения, сложение / вычитание хорошо определены как арифметика по модулю, так 0U-1
будет работать на что-то вроде std::numeric_limits<unsigned>::max()
,
При преобразовании из неподписанного в подписанное, если тип назначения достаточно велик, чтобы вместить все значения без знака, он просто выполняет прямое копирование данных в тип назначения. Если тип назначения недостаточно велик, чтобы вместить все беззнаковые значения, я считаю, что он определен реализацией (постараюсь найти стандартную ссылку).
Так когда long
является 64-битным (предположительно на вашей 64-битной машине) неподписанным и копируется прямо.
когда long
32-битный на 32-битной машине, опять же, он, скорее всего, просто интерпретирует битовую комбинацию как знаковое значение, которое в данном случае равно -1.
РЕДАКТИРОВАТЬ: Самый простой способ избежать этих проблем, чтобы избежать смешивания подписанных и неподписанных типов. Что значит вычесть единицу из значения, концепция которого не допускает отрицательных чисел? Я собираюсь доказать, что параметр функции должен быть значением со знаком в вашем примере.
Тем не менее, G ++ (по крайней мере, версия 4.5) предоставляет удобный -Wsign-conversion
который обнаруживает эту проблему в вашем конкретном коде.
У вас также могут быть специальные отливки, улавливающие все отливки через перелив:
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.