﻿# Intel IPP Samples for Windows \- работа над ошибками

Это моя очередная заметка о том, как PVS\-Studio делает программы более надёжными\. То есть где, и какие  ошибки он обнаруживает\. На этот раз под молоток попали примеры, демонстрирующие работу с библиотекой IPP 7\.0 \(Intel Performance Primitives Library\)\.

В состав Intel Parallel Studio 2011 входит библиотека Performance Primitives Library\. Эта библиотека включает в себя большое количество примитивов, позволяющих создавать эффективные видео и аудио кодеки, программы обработки сигналов, механизмы рендеринга изображений, архиваторы и многое\-многое другое\. Естественно, с такой библиотекой работать не просто\. Поэтому компания Intel подготовила большое количество демонстрационных программ, построенных на основе этой библиотеки\. Вы можете познакомиться с описанием примеров и скачать их по следующим ссылкам: [Code Samples for the Intel Integrated Performance Primitives \(Intel IPP\) Library](https://software.intel.com/content/www/us/en/develop/home.html)

Все примеры разбиты на четыре группы: 

* IPP Samples for Windows
* IPP UIC Demo for Windows
* IPP DMIP Samples for Windows
* IPP Cryptography Samples for Windows

В каждом наборе находится большое количество проектов, так что для начала я взял для проверки только первый набор IPP Samples for Windows\. Проверку я осуществил, используя [PVS\-Studio](https://pvs-studio.ru/ru/pvs-studio/) версии 4\.10\.

Этим постом я хочу показать, что статический анализ полезен вне зависимости от профессионализма программистов и уровня разрабатываемого решения\. Идея "надо брать профессионалов и писать сразу без ошибок" не работает\. Даже высококвалифицированные разработчики не застрахованы от ошибок и опечаток в процессе кодирования\. Ошибки в примерах для IPP очень хорошо это демонстрируют\.

Подчеркну, что IPP Samples for Windows является высококачественным проектом\. Однако, в силу своего размера в 1\.6 миллионов строк кода, он неизбежно содержит в себе разнообразнейшие ошибки\. Рассмотрим некоторые из них\.

## Неудачная замена индексов массива

Этот пример мог бы замечательно пополнить мою предыдущую статью "[Последствия использования технологии Copy\-Paste при программировании на Си\+\+ и как с этим быть](https://pvs-studio.ru/ru/blog/posts/cpp/a0068/)":

```cpp
struct AVS_MB_INFO
{
  ...
  Ipp8u refIdx[AVS_DIRECTIONS][4];
  ...
};

void AVSCompressor::GetRefIndiciesBSlice(void){
  ...
  if (m_pMbInfo->predType[0] & predType)
  {
    m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][0];
    iRefNum += 1;
  }
  if (m_pMbInfo->predType[1] & predType)
  {
    m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][1];
    iRefNum += 1;
  }
  if (m_pMbInfo->predType[2] & predType)
  {
    m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][2];
    iRefNum += 1;
  }
  if (m_pMbInfo->predType[3] & predType)
  {
    m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][30];
    iRefNum += 1;
  }
  ...
}
```

Диагностическое сообщение PVS\-Studio: V557 Array overrun is possible\. The '30' index is pointing beyond array bound\. avs\_enc umc\_avs\_enc\_compressor\_enc\_b\.cpp 495

Программист несколько раз скопировал фрагмент кода и изменил значение индексов массивов\. Но в самом конце его рука дрогнула\. Он вписал число 3, но не удалил число 0\. В результате получился индекс 30 и при исполнении кода произойдет доступ далеко за границы массива\.

## Одинаковые ветви кода

Раз мы начали с копирования кода, то вот ещё один пример на эту тему:

```cpp
AACStatus aacencGetFrame(...)
{
  ...
  if (maxEn[0] > maxEn[1]) {
    ics[1].num_window_groups = ics[0].num_window_groups;
    for (g = 0; g < ics[0].num_window_groups; g++) {
      ics[1].len_window_group[g] = ics[0].len_window_group[g];
    }
  } else {
    ics[1].num_window_groups = ics[0].num_window_groups;
    for (g = 0; g < ics[0].num_window_groups; g++) {
      ics[1].len_window_group[g] = ics[0].len_window_group[g];
    }
  }
  ...
}
```

Диагностическое сообщение PVS\-Studio: V523 The 'then' statement is equivalent to the 'else' statement\. aac\_enc aac\_enc\_api\_fp\.c 1379

Но в этот раз, наоборот, забыли отредактировать скопированный код\. Обе ветки условного оператора "if" выполняют одни и те же действия\.

## Путаница с приоритетом операции декремента "\-\-" и разыменованием указателя "\*"

```cpp
static void
sbrencConflictResolution (..., Ipp32s *nLeftBord)
{
  ...
  *nLeftBord = nBordNext - 1;
  ...
  if (*lenBordNext > 1) {
    ...
    *nLeftBord--;
  }
  ...
}
```

Диагностическое сообщение PVS\-Studio: V532 Consider inspecting  the statement of '\*pointer\-\-' pattern\. Probably meant: '\(\*pointer\)\-\-'\. aac\_enc sbr\_enc\_frame\_gen\.c 428

Указатель "nLeftBord" использует для возврата значения из функции "sbrencConflictResolution"\.  Сначала по указанному адресу записывается значение "nBordNext \- 1"\. При определенных условиях это значение должно уменьшаться на единицу\. Для уменьшения значения программист использовал следующий код:

```cpp
*nLeftBord--;
```

Ошибка в том, что вместо значения уменьшается сам указатель\. Корректный код должен выглядеть так:

```cpp
(*nLeftBord)--;
```

## Ещё более запутанная ситуация с операцией инкремента "\+\+" и разыменованием указателя "\*"

Следующий код мне совершенно не понятен\. Я не знаю, как его исправить, чтобы он приобрел смысл\. Возможно, здесь чего\-то не хватает\.

```cpp
static IppStatus mp2_HuffmanTableInitAlloc(Ipp32s *tbl, ...)
{
  ...
  for (i = 0; i < num_tbl; i++) {
    *tbl++;
  }
  ...
}
```

Диагностическое сообщение PVS\-Studio: V532 Consider inspecting  the statement of '\*pointer\+\+' pattern\. Probably meant: '\(\*pointer\)\+\+'\. mpeg2\_dec umc\_mpeg2\_dec\.cpp 59

Сейчас, приведенный в примере цикл, эквивалентен следующему коду:

```cpp
tbl += num_tbl;
```

Анализатор PVS\-Studio предположил, что ,возможно, здесь забыты скобки и следовало написать "\(\*tbl\)\+\+;"\. Но и этот код не имеет смысла\. Тогда цикл будет эквивалентен:

```cpp
*tbl += num_tbl;
```

В общем, очень странный какой\-то цикл\. Ошибка есть, но исправить ее, видимо, может только автор кода\.

## Потеря признака, что произошла ошибка

В коде имеется функция "GetTrackByPidOrCreateNew", которая возвращает значение "\-1" , если возникает ошибка\.

```cpp
typedef signed int     Ipp32s;
typedef unsigned int   Ipp32u;

Ipp32s StreamParser::GetTrackByPidOrCreateNew(
  Ipp32s iPid, bool *pIsNew)
{
  ...
  else if (!pIsNew || m_uiTracks >= MAX_TRACK)
    return -1;
  ...
}
```

С функцией "GetTrackByPidOrCreateNew" всё нормально\. Но имеется ошибка при её использовании:

```cpp
Status StreamParser::GetNextData(MediaData *pData, Ipp32u *pTrack)
{
  ...
  *pTrack = GetTrackByPidOrCreateNew(m_pPacket->iPid, NULL);

  if (*pTrack >= 0 && TRACK_LPCM == m_pInfo[*pTrack]->m_Type)
    ippsSwapBytes_16u_I((Ipp16u *)pData->GetDataPointer(),
                        m_pPacket->uiSize / 2);
  ...
}
```

Диагностическое сообщение PVS\-Studio: V547 Expression '\* pTrack \>\= 0' is always true\. Unsigned type value is always \>\= 0\. demuxer umc\_stream\_parser\.cpp 179

Значение, которое возвращает функция "GetTrackByPidOrCreateNew" сохраняется в беззнаковом формате \(unsigned int\)\. Это значит, что "\-1" превратится в "4294967295", а условие "\*pTrack \>\= 0" всегда истинно\.

В итоге, если функция "GetTrackByPidOrCreateNew " вернет значение "\-1", то произойдет Access Violation при выполнении кода "m\_pInfo\[\*pTrack\]\-\>m\_Type"\. 

## Copy\-Paste и забытый \+1

```cpp
void H264SegmentDecoder::ResetDeblockingVariablesMBAFF()
{
  ...
  if (GetMBFieldDecodingFlag(m_gmbinfo->mbs[m_CurMBAddr 
                                            - mb_width * 2]))
    m_deblockingParams.nNeighbour[HORIZONTAL_DEBLOCKING] =
      m_CurMBAddr - mb_width * 2;
  else
    m_deblockingParams.nNeighbour[HORIZONTAL_DEBLOCKING] =
      m_CurMBAddr - mb_width * 2;
  ...
}
```

Диагностическое сообщение PVS\-Studio: V523 The 'then' statement is equivalent to the 'else' statement\. h264\_dec umc\_h264\_segment\_decoder\_deblocking\_mbaff\.cpp 340

Если посмотреть код рядом, то становится понятно, что в скопированной строчке забыли прибавить единицу\. Корректный код должен выглядеть так:

```cpp
if (GetMBFieldDecodingFlag(m_gmbinfo->mbs[m_CurMBAddr 
                                          - mb_width * 2]))
  m_deblockingParams.nNeighbour[HORIZONTAL_DEBLOCKING] =
    m_CurMBAddr - mb_width * 2;
else
  m_deblockingParams.nNeighbour[HORIZONTAL_DEBLOCKING] =
    m_CurMBAddr - mb_width * 2 + 1;
```

Неподалеку в функции "H264CoreEncoder\_ResetDeblockingVariablesMBAFF" есть ещё в точности такая же ошибка с забытым "\+ 1"\.

Диагностическое сообщение PVS\-Studio: V523 The 'then' statement is equivalent to the 'else' statement\. h264\_enc umc\_h264\_deblocking\_mbaff\_tmpl\.cpp\.h 366

## Remove, который ничего не удаляет

```cpp
void H264ThreadGroup::RemoveThread(H264Thread * thread)
{
    AutomaticUMCMutex guard(m_mGuard);
    std::remove(m_threads.begin(), m_threads.end(), thread);
}
```

Диагностическое сообщение PVS\-Studio: V530 The return value of function 'remove' is required to be utilized\. h264\_dec umc\_h264\_thread\.cpp 226

Интересное сочетание\. С одной стороны, всё солидно\. Используется mutex, чтобы корректно удалять элемент в многопоточном приложении\. А с другой стороны, банально забыли, что функция std::remove не удаляет элементы из массива, а только меняет их порядок\. На самом деле должно быть написано так:

```cpp
m_threads .erase(
  std::remove(m_threads.begin(), m_threads.end(), thread),
  m_threads.end());
```

## Сравнение полей структур с самими собой

Смотрю на ошибки и обратил внимание, что реализация стандарта сжатия видео H264 какая\-то глючноватая\. К этому проекту относится достаточно большое количество найденных ошибок\. Вот, например, кто\-то спешил при программировании и использовал сразу два неверных имени переменных\.

```cpp
bool H264_AU_Stream::IsPictureSame(H264SliceHeaderParse & p_newHeader)
{
  if ((p_newHeader.frame_num != m_lastSlice.frame_num) ||
      (p_newHeader.pic_parameter_set_id !=
       p_newHeader.pic_parameter_set_id) ||
      (p_newHeader.field_pic_flag != p_newHeader.field_pic_flag) ||
      (p_newHeader.bottom_field_flag != m_lastSlice.bottom_field_flag)
      ){
      return false;
  }
  ...
}
```

Диагностические сообщения PVS\-Studio:

V501 There are identical sub\-expressions 'p\_newHeader\.pic\_parameter\_set\_id' to the left and to the right of the '\!\=' operator\. h264\_spl umc\_h264\_au\_stream\.cpp 478

V501 There are identical sub\-expressions 'p\_newHeader\.field\_pic\_flag' to the left and to the right of the '\!\=' operator\. h264\_spl umc\_h264\_au\_stream\.cpp 479



Функция сравнения не работает, так как некоторые члены структуры сравниваются сами с собою\. Вот две исправленные строчки:

```cpp
(p_newHeader.pic_parameter_set_id != m_lastSlice.pic_parameter_set_id)
(p_newHeader.field_pic_flag != m_lastSlice.field_pic_flag)
```

## Некорректное копирование данных

Бывают ошибки использования не тех объектов, не только при сравнении, но и при копировании состояний объектов:

```cpp
Ipp32s ippVideoEncoderMPEG4::Init(mp4_Param *par)
{
  ...
  VOL.sprite_width = par->sprite_width;
  VOL.sprite_height = par->sprite_height;
  VOL.sprite_left_coordinate = par->sprite_left_coordinate;
  VOL.sprite_top_coordinate = par->sprite_left_coordinate;
  ...
}
```

Диагностическое сообщение PVS\-Studio: V537 Consider reviewing the correctness of 'sprite\_left\_coordinate' item's usage\. mpeg4\_enc mp4\_enc\_misc\.cpp 387

В "VOL\.sprite\_top\_coordinate" помещается неверное значение\. Корректное присваивание должно выглядеть так:

```cpp
VOL.sprite_top_coordinate = par->sprite_top_coordinate;
```

## Два цикла по одной переменной

```cpp
JERRCODE CJPEGDecoder::DecodeScanBaselineNI(void)
{
  ...
  for(c = 0; c < m_scan_ncomps; c++)
  {
    block = m_block_buffer + (DCTSIZE2*m_nblock*(j+(i*m_numxMCU)));

    // skip any relevant components
    for(c = 0; c < m_ccomp[m_curr_comp_no].m_comp_no; c++)
    {
      block += (DCTSIZE2*m_ccomp[c].m_nblocks);
    }
  ...
}
```

Диагностическое сообщение PVS\-Studio: V535 The variable 'c' is being used for this loop and for the outer loop\. jpegcodec jpegdec\.cpp 4652

Для двух циклов, вложенных друг в друга, используется одна переменная 'c'\. Результат работы такой функции декодирования может быть весьма странным и неожиданным\.

## Двойное присваивание для большей надежности

```cpp
H264EncoderFrameType*
H264ENC_MAKE_NAME(H264EncoderFrameList_findOldestToEncode)(...)
{
  ...
  MaxBrefPOC = 
    H264ENC_MAKE_NAME(H264EncoderFrame_PicOrderCnt)(pCurr, 0, 3);
  MaxBrefPOC = 
    H264ENC_MAKE_NAME(H264EncoderFrame_PicOrderCnt)(pCurr, 0, 3);
  ...
}
```

Диагностическое сообщение PVS\-Studio: V519 The 'MaxBrefPOC' object is assigned values twice successively\. Perhaps this is a mistake\. h264\_enc umc\_h264\_enc\_cpb\_tmpl\.cpp\.h 784

Когда я увидел этот код, то мне вспомнился старый программистский анекдот:

_\- А почему у тебя в коде подряд два одинаковых GOTO стоят?_

_\- А вдруг первый не сработает\!_

Данная ошибка, пожалуй, не критична, но все\-таки это ошибка\.

## Код, который настораживает

```cpp
AACStatus sbrencResampler_v2_32f(Ipp32f* pSrc, Ipp32f* pDst)
{
  ...
  k = nCoef-1;
  k = nCoef;
  ...
}
```

Диагностическое сообщение PVS\-Studio: V519 The 'k' object is assigned values twice successively\. Perhaps this is a mistake\. aac\_enc sbr\_enc\_resampler\_fp\.c 90

Это двойное присваивание настораживает гораздо больше, чем в предыдущем примере\. Такое ощущение, что программист был не уверен в себе\. Или решил, в начале, попробовать "nCoef\-1", а потом "nCoef"\. Ещё это называют "программированием методом эксперимента"\. И в любом случае, это именно то место в коде, увидев которое следует задержаться и предаться размышлениям\.

## Минимальное значение, которое не совсем минимально

```cpp
void MeBase::MakeVlcTableDecision()
{
  ...
  Ipp32s BestMV= IPP_MIN(IPP_MIN(m_cur.MvRate[0],m_cur.MvRate[1]),
                         IPP_MIN(m_cur.MvRate[2],m_cur.MvRate[3]));
  Ipp32s BestAC= IPP_MIN(IPP_MIN(m_cur.AcRate[0],m_cur.AcRate[1]),
                         IPP_MIN(m_cur.AcRate[2],m_cur.AcRate[2]));
  ...
}
```

Диагностическое сообщение PVS\-Studio: V501 There are identical sub\-expressions to the left and to the right of the '<' operator: \(m\_cur\.AcRate \[2\]\) < \(m\_cur\.AcRate \[2\]\) me umc\_me\.cpp 898

Вновь опечатка в индексе массива\. Последний индекс должен быть 3, а не 2\. Корректный вариант кода:

```cpp
Ipp32s BestAC= IPP_MIN(IPP_MIN(m_cur.AcRate[0],m_cur.AcRate[1]),
                       IPP_MIN(m_cur.AcRate[2],m_cur.AcRate[3]));
```

Подобные ошибки неприятны тем, что код "почти работает"\. Ошибка проявит себя только в том случае, если минимальный элемент хранится в "m\_cur\.AcRate\[3\]"\. Такие ошибки любят проявлять себя не при тестировании, а у пользователя на их наборах входных данных\.

## Максимальное значение, которое не совсем максимально

С максимальными значениями тоже не всегда ладится:

```cpp
Ipp32s ippVideoEncoderMPEG4::Init(mp4_Param *par)
{
  ...
  i = IPP_MAX(mBVOPsearchHorBack, mBVOPsearchHorBack);
  ...
}
```

Диагностическое сообщение PVS\-Studio: V501 There are identical sub\-expressions '\(mBVOPsearchHorBack\)' to the left and to the right of the '\>' operator\. mpeg4\_enc mp4\_enc\_misc\.cpp 547

Два раза используется переменная mBVOPsearchHorBack\. На самом деле планировалось использовать mBVOPsearchHorBack и mBVOPsearchVerBack:

```cpp
i = IPP_MAX(mBVOPsearchHorBack, mBVOPsearchVerBack);
```





## Попадаем пальцем в небо

```cpp
typedef struct
{
  ...
  VM_ALIGN16_DECL(Ipp32f)
    nb_short[2][3][__ALIGNED(MAX_PPT_SHORT)];
  ...
} mpaPsychoacousticBlock;

static void mp3encPsy_short_window(...)
{
  ...
  if (win_counter == 0) {
    nb_s = pBlock->nb_short[0][3];
  }
  ...
}
```

Диагностическое сообщение PVS\-Studio: V557 Array overrun is possible\. The '3' index is pointing beyond array bound\. mp3\_enc mp3enc\_psychoacoustic\_fp\.c 726

Здесь видимо простая опечатка\. Случайно  использовали индекс '3' вместо '2'\. Последствия, думаю, понятны\.

## Ошибка, приводящая к замедлению скорости работы

```cpp
void lNormalizeVector_32f_P3IM(Ipp32f *vec[3], Ipp32s* mask, 
                               Ipp32s len) {
  Ipp32s  i;
  Ipp32f  norm;

  for(i=0; i<len; i++) {
    if(mask<0) continue;
    norm = 1.0f/sqrt(vec[0][i]*vec[0][i]+
           vec[1][i]*vec[1][i]+
           vec[2][i]*vec[2][i]);
           vec[0][i] *= norm; vec[1][i] *= norm; vec[2][i] *= norm;
  }
}
```

Диагностическое сообщение PVS\-Studio: V503 This is a nonsensical comparison: pointer < 0\. ipprsample ippr\_sample\.cpp 501

Это красивый пример кода, который из\-за ошибки работает медленнее, чем мог бы\. Алгоритм должен нормализовать только те элементы, которые отмечены в массиве масок\. Но приведенный код делает нормализацию всех элементов\. Ошибка находится в условии "if\(mask<0\)"\. Здесь забыли использовать индекс "i"\. Указатель "mask" будет практически всегда больше или равен нулю, а ,значит, мы обработаем все элементы\.

Корректная проверка:

```cpp
if(mask[i]<0) continue;
```

## Результат вычитания всегда равен 0

```cpp
int ec_fb_GetSubbandNum(void *stat)
{
    _fbECState *state=(_fbECState *)stat;
    return (state->freq-state->freq);
}
```

Диагностическое сообщение PVS\-Studio: V501 There are identical sub\-expressions to the left and to the right of the '\-' operator: state\-\>freq \- state\-\>freq speech ec\_fb\.c 250

Здесь из\-за опечатки функция всегда будет возвращать значение 0\. Что\-то не то мы здесь вычитаем\. Что нужно вычитать, я не знаю\.

## Некорректная обработка нехватки буфера

```cpp
typedef unsigned int    Ipp32u;

UMC::Status Init(..., Ipp32u memSize, ...)
{
  ...
  memSize -= UMC::align_value<Ipp32u>(m_nFrames*sizeof(Frame));
  if(memSize < 0)
      return UMC::UMC_ERR_NOT_ENOUGH_BUFFER;
  ...
}
```

Диагностическое сообщение PVS\-Studio: V547 Expression 'memSize < 0' is always false\. Unsigned type value is never < 0\. vc1\_enc umc\_vc1\_enc\_planes\.h 200

Неверно, обрабатывается ситуация, когда размер буфера для работы недостаточен\. Вместо возврата кода ошибки программа продолжит работу и скорее всего, аварийно завершится\. Дело в том, что переменная "memSize" имеет тип "unsigned int"\. Следовательно, условие "memSize < 0" всегда ложно и мы продолжаем работу с буфером недостаточного размера\.

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

## Некорректная проверка и как следствие выход за границу массива

```cpp
Ipp32u m_iCurrMBIndex;
VC1EncoderMBInfo* VC1EncoderMBs::GetPevMBInfo(Ipp32s x, Ipp32s y)
{
  Ipp32s row = (y>0)? m_iPrevRowIndex:m_iCurrRowIndex;
  return ((m_iCurrMBIndex - x <0 || row <0)? 0 :
    &m_MBInfo[row][m_iCurrMBIndex - x]);
}
```

Диагностическое сообщение PVS\-Studio: V547 Expression 'm\_iCurrMBIndex \- x < 0' is always false\. Unsigned type value is never < 0\. vc1\_enc umc\_vc1\_enc\_mb\.cpp 188

Переменная "m\_iCurrMBIndex" имеет тип "unsigned"\. Из\-за этого выражение "m\_iCurrMBIndex \- x" также имеет тип "unsigned"\. Следовательно, условие "m\_iCurrMBIndex \- x < 0" всегда ложно\. Рассмотрим последствия\.

Пусть переменная "m\_iCurrMBIndex" равна 5, а переменная "x" равна 10\.

Выражение "m\_iCurrMBIndex \- x" равно 5u \- 10i \= 0xFFFFFFFBu\.

Условие "m\_iCurrMBIndex \- x < 0" имеет значение false\.

Выполняется выражение "m\_MBInfo\[row\]\[0xFFFFFFFBu\]" и происходит выход за границу массива\.

## Ошибка использования тернарного оператора '?:'

Тернарный оператор достаточно опасен, так как легко допустить ошибку\. Тем не менее, программисты любят написать покороче и использовать интересную конструкцию языка\. Язык Си\+\+ наказывает за это\.

```cpp
vm_file* vm_file_fopen(...)
{
  ...
  mds[3] = FILE_ATTRIBUTE_NORMAL |
           (islog == 0) ? 0 : FILE_FLAG_NO_BUFFERING;
  ...
}
```

Диагностическое сообщение PVS\-Studio: V502 Perhaps the '?:' operator works in a different way than it was expected\. The '?:' operator has a lower priority than the '\|' operator\. vm vm\_file\_win\.c 393

Код должен составлять комбинацию из флагов FILE\_ATTRIBUTE\_NORMAL и FILE\_FLAG\_NO\_BUFFERING\. Но на самом деле элементу "mds\[3\]" всегда присваивается значение 0\.

Программист забыл, что приоритет оператора "\|" выше, чем приоритет оператора "?:"\. Получается, что в коде написано следующее выражение \(обратите внимание на скобки\): 

```cpp
(FILE_ATTRIBUTE_NORMAL | (islog == 0)) ?
  0 : FILE_FLAG_NO_BUFFERING;
```

Условие "FILE\_ATTRIBUTE\_NORMAL \| \(islog \=\= 0\)" всегда истинно и мы присваиваем элементу "mds\[3\]" значение 0\.

Корректное выражение должно выглядеть так \(вновь обратите внимание на скобки\):

```cpp
FILE_ATTRIBUTE_NORMAL |
  ((islog == 0) ? 0 : FILE_FLAG_NO_BUFFERING);
```

## Странная работа с массивом

```cpp
AACStatus alsdecGetFrame(...)
{
  ...
  for (i = 0; i < num; i++) {
    ...
    *tmpPtr = (Ipp32s)((tmp << 24) + ((tmp & 0xff00) << 8) +
                      ((tmp >> 8) & 0xff00) + (tmp >> 24));
    *tmpPtr = *srcPrt;
    ...
  }
  ...
}
```

Диагностическое сообщение PVS\-Studio: V519 The '\* tmpPtr' object is assigned values twice successively\. Perhaps this is a mistake\. aac\_dec als\_dec\_api\.c 928

Предлагаю читателю самому изучить код и сделать выводы\. Я опишу этот код только одним словом \- "оригинально"\.

## Паранормальные присваивания

```cpp
static
IPLStatus ownRemap8u_Pixel(...) {
  ...
  saveXMask    = xMap->maskROI;
  saveXMask    = NULL;
  saveYMask    = yMap->maskROI;
  saveYMask    = NULL;  
  ...
}
```

Диагностические сообщения PVS\-Studio:

V519 The 'saveXMask' object is assigned values twice successively\. Perhaps this is a mistake\. ipl iplremap\.c 36

V519 The 'saveYMask' object is assigned values twice successively\. Perhaps this is a mistake\. ipl iplremap\.c 38

Причина появления такого странного кода мне непонятна\. Причем подобный блок повторяется в разных функциях 8 раз\!

Встречаются и другие подозрительные присваивания одной переменной:

```cpp
Ipp32s ippVideoEncoderMPEG4::Init(mp4_Param *par)
{
  ...
  mNumOfFrames = par->NumOfFrames;
  mNumOfFrames = -1;
  ...
}
```

Диагностическое сообщение PVS\-Studio: V519 The 'mNumOfFrames' object is assigned values twice successively\. Perhaps this is a mistake\. mpeg4\_enc mp4\_enc\_misc\.cpp 276

## Заключение

В этой статье перечислена только часть ошибок, обнаруженных мною в IPP Samples for Windows\. Некоторые ошибки я не привел, так как они являются братьями\-близнецами тех примеров, которые я рассмотрел в статье, и читать про них будет не интересно\. Я не стал приводить здесь несущественные ошибки\. Примером может служить assert\(\), у которого из\-за опечатки условие всегда истинно\. Многие участков кода я пропустил, так как просто не знаю, ошибка это или просто некрасиво написано\. Однако я думаю, что описанных дефектов достаточно, чтобы показать сложность написания больших проектов даже профессиональными разработчиками\.

Ещё раз сформулирую мысль, озвученную в начале статьи\. Даже хороший программист не застрахован от опечаток, забывчивости, желания сделать Copy\-Paste и ошибок в логике\. Я думаю, в будущем ссылка на эту статью станет хорошим ответом людям, которые уверены что произнесение фразы "надо правильно писать код" защитит их от любых ошибок\. 

Удачи всем в ваших C/C\+\+/C\+\+0x проектах\. И желаю вам находить побольше ошибок, используя так любимую мной методологию статического анализа\.