среда, 4 января 2017 г.

Разбор багов компиляторов и программистов

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

1. Знаковые и беззнаковые типы.
  Этот код работает на Visual Studio, но в XCode все отрицательные значение при конвертации в size_t становятся нулями.
size_t i = static_cast<size_t>(double(-1.0))
double d = static_cast<double>((int)i);

  Тут сравниваятся разные типы, на это есть предупреждения компилятора, но у меня почему-то компилятор VS не вывел предупреждение, помогло только явное включение предупреждения. А проблема тут в том, что знаковое -1 конвертируется в беззнаковое 0xFFFFFFFF что больше 1.
signed int i = -1;
unsigned int u = 1;

if ( i > u )
    ...


2. Compile-time константы как поля классов.
  Пока не появилась поддержка inline для статичных полей класса приходится писать такие конструкции, причем ошибка возникает только в релизной конфигурации. Что интересно, до появления constexpr вариант с static const такого не требовал, хотя по стандарту надо всегда выносить объявление статичных полей из класса если они используются в run-time, например передаются в функции или даже в static_assert, а вот если это значение используется только в рассчетах в compile-time, то выносить объявление не требуется. Подробнее тут.
struct A {
    template <typename T>
    static constexpr int value = sizeof(T);
}; 

template <typename T>
constexpr int A::value;


3. Выравнивание.
  Ошибка почти банальная, в stl специально для этого даже добавили тип std::aligned_union, но в моем старом коде такая ошибка все же была. Вот простой пример как проявляется ошибка с выравниванием, типы B, C и D должны быть одинаковыми, но из-за разного выравнивания возникает ошибка. А обнаружилась ошибка благодаря проверке передается ли в placement new выравненый указатель.
// sizeof = 8, align = 4
struct A {
    float f;
    bool b;
};

// sizeof = 12, align = 4
struct B {
    A a;
    bool b;
};

// sizeof = 9, align = 1
struct C {
    char c[ sizeof(A) ];
    bool b;
};

// sizeof = 12, align = 4
struct D {
    std::aligned_union_t<0, A>  a;
    bool b;
};


4. Move-конструктор и порядок выполнения.
  В этом примере сначала вызывается move-конструктор для value, а потом считается хэш уже от невалидного value.
template <typename T>
void Fuu (T &&value)
{
    AddValue( Hash( value ), TmpObj( std::move(value) ) );
}


5. Возвращение ссылки на временный объект.
  Когда-то давно наткнулся на такую ошибку. В классе Optional был метод для получения значения, потом к нему добавили еще один метод, где возвращалось либо значение из Optional класса, либо дефолтовое, передаваемое как аргумент функции. Для оптимизации все передавалось через константную ссылку, вот тут и происходит ошибка если defaultValue - создается при вызове функции, то и прибивается после выхода из функции. Valgrind как раз указал на один такой случай...
template <typename T>
T const& Optional<T>::GetValue () const
{
    return value;
}

template <typename T>
T const& Optional<T>::GetValueOrDefault (const T& defaultValue) const
{
    return IsDefined() ? value : defaultValue;
}


6. Дефолтный дефолтный конструктор.
 Использование  = default   для дефолтных конструкторов может привести к неинициализированным значемниям (как минимум в VisualStudio), так же есть проблемы с шаблонным конструктором с вариадиками, есть множество примеров когда такой конструктор работает не так как ожидается, поэтому конструктор с вариадиками стоит использовать осторожно.
пример на ideone

Комментариев нет:

Отправить комментарий