﻿# Проверка кроссплатформенной библиотеки элементов интерфейса GTK\+ с помощью PVS\-Studio

Для упрощения процесса разработки графического интерфейса приложения используются библиотеки элементов интерфейса\. Одной из таких библиотек является GTK\+\. И именно этот проект я выбрал для своей первой статьи, посвященной анализатору PVS\-Studio\. Итак, я использовал PVS\-Studio для выявления дефектов в коде GTK\+\. Анализатор выдал довольно большое количество предупреждений, сигнализирующих об ошибках или подозрительных местах в коде библиотеки\. Некоторые предупреждения достаточно серьезны\. Все ошибки в статье не поместятся поэтому расскажу о некоторых из них, самых характерных\.

![0383_GTK_ru/image1.png](https://import.viva64.com/docx/blog/0383_GTK_ru/image1.png)

## Введение

[GTK\+](https://www.gtk.org/) \(сокращение от GIMP ToolKit\) — кроссплатформенная библиотека элементов интерфейса, имеет простой в использовании API, наряду с [Qt](https://www.qt.io/) является одной из двух наиболее популярных на сегодняшний день библиотек для X Window System\.

Будучи изначально частью графического редактора GIMP, она развилась в отдельный проект и приобрела заметную популярность\. GTK\+ — свободное ПО, распространяемое на условиях GNU LGPL, позволяющей создавать как свободное, так и проприетарное программное обеспечение с использованием библиотеки\. GTK\+ является официальной библиотекой для создания графического интерфейса проекта GNU\. 

Мы проверили код библиотеки с помощью статического анализатора [PVS\-Studio](https://pvs-studio.ru/ru/pvs-studio/) и проанализировали полученные предупреждения\. Код был проверен анализатором версии 6\.02\.

## Избыточный код

Для начала рассмотрим предупреждения, связанные с построением логических выражений\. Зачастую тут нет ошибок, просто присутствуют избыточные проверки, которые затрудняют понимание условия\. Такие выражения можно значительно упростить\.

[V728](https://pvs-studio.ru/ru/docs/warnings/v728/) An excessive check can be simplified\. The '\|\|' operator is surrounded by opposite expressions '\!mount' and 'mount'\.  gtkplacesview\.c 708

```cpp
static void
add_volume (....)
{
  ....
  GMount *mount;
  ....
  if (!mount ||
      (mount && !g_mount_is_shadowed (mount)))
  ....
}
```

В данном примере мы видим избыточную проверку указателя 'mount'\. Код можно модифицировать следующим образом: 

```cpp
  if (!mount || !g_mount_is_shadowed (mount)))
```

Еще один подобный случай:

[V728](https://pvs-studio.ru/ru/docs/warnings/v728/) An excessive check can be simplified\. The '\|\|' operator is surrounded by opposite expressions 'ret' and '\!ret'\.  gtktreeview\.c 13682

```cpp
void
gtk_tree_view_get_cell_area (....)
{
  ....
  gboolean ret = ...;
  ....
      /* Get vertical coords */
      if ((!ret && tree == NULL) || ret)
  ....
}
```

Опять избыточная проверка, на этот раз булевой переменной 'ret'\. Модифицируем код:

```cpp
if (ret || tree == NULL)
```

[V590](https://pvs-studio.ru/ru/docs/warnings/v590/) Consider inspecting the 'str\[0\] \=\= '\\0' \|\| str\[0\] \!\= 'U'' expression\. The expression is excessive or contains a misprint\. gtkcomposetable\.c 62

```cpp
static gboolean
is_codepoint (const gchar *str)
{
  int i;

  /* 'U' is not code point but 'U00C0' is code point */
  if (str[0] == '\0' || str[0] != 'U' || str[1] == '\0')
    return FALSE;

  for (i = 1; str[i] != '\0'; i++)
    {
      if (!g_ascii_isxdigit (str[i]))
        return FALSE;
    }

  return TRUE;
}
```



Проверка str\[0\] \=\= '\\0' избыточна, так как это частный случай выражения str\[0\] \!\= 'U'\. Можно упростить код, удалив излишнюю проверку: 

```cpp
if (str[0] != 'U' || str[1] == '\0')
    return FALSE;
```

Это были предупреждения, не приводящие к ошибкам, такой код будет исправно работать, но будут совершаться ненужные проверки\.

![0383_GTK_ru/image2.png](https://import.viva64.com/docx/blog/0383_GTK_ru/image2.png)

## Повторное использование кода

Работа программной индустрии во многом построена на повторном использовании уже написанного кода\. Действительно зачем изобретать велосипед\. Очень частой причиной ошибок становится копирование кода, который необходимо модифицировать\. Зачастую программист пропускает кусок кода, который надо было исправить, что приводит к ошибкам в программе\. Одной из сильных сторон статического анализатора PVS\-Studio является как раз выявление подобных мест\.

Рассмотрим несколько случаев использования copy\-paste:



[V523](https://pvs-studio.ru/ru/docs/warnings/v523/) The 'then' statement is equivalent to the 'else' statement\. gtkprogressbar\.c 1232



```cpp
static void
gtk_progress_bar_act_mode_enter (GtkProgressBar *pbar)
{
  ....
  /* calculate start pos */
  if (orientation == GTK_ORIENTATION_HORIZONTAL)
    {
      if (!inverted)
        {
          priv->activity_pos = 0.0;
          priv->activity_dir = 0;
        }
      else
        {
          priv->activity_pos = 1.0;
          priv->activity_dir = 1;
        }
    }
  else
    {
      if (!inverted)
        {
          priv->activity_pos = 0.0;
          priv->activity_dir = 0;
        }
      else
        {
          priv->activity_pos = 1.0;
          priv->activity_dir = 1;
        }
    }
  ....
}
```



Видим одинаковый код в if\-блоке 'if \(orientation \=\= GTK\_ORIENTATION\_HORIZONTAL\)' и в else\-блоке\. Возможно функциональность еще не реализована, а может быть тут и ошибка\.



[V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions '\(box\-\>corner\[GTK\_CSS\_TOP\_RIGHT\]\.horizontal\)' to the left and to the right of the '\>' operator\. gtkcssshadowvalue\.c 685

[V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions '\(box\-\>corner\[GTK\_CSS\_TOP\_LEFT\]\.horizontal\)' to the left and to the right of the '\>' operator\. gtkcssshadowvalue\.c 696



```cpp
static void
draw_shadow_corner (....
                    GtkRoundedBox       *box,
                                         ....)
{
  ....
  overlapped = FALSE;
  if (corner == GTK_CSS_TOP_LEFT || 
      corner == GTK_CSS_BOTTOM_LEFT)
    {
      ....
      max_other = MAX(box->corner[GTK_CSS_TOP_RIGHT].horizontal,
                      box->corner[GTK_CSS_TOP_RIGHT].horizontal);
      ....
    }
  else
    {
      ....
      max_other = MAX(box->corner[GTK_CSS_TOP_LEFT].horizontal
                      box->corner[GTK_CSS_TOP_LEFT].horizontal);
      ....
    }
  ....
}
```



В макрос MAX подаются одинаковые переменные\. Возможно забыли вставить правильные значения констант вместо 'GTK\_CSS\_TOP\_RIGHT' и 'GTK\_CSS\_TOP\_LEFT', а может сравнение должно было быть совсем с другой переменной\. 



[V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions 'G\_PARAM\_EXPLICIT\_NOTIFY' to the left and to the right of the '\|' operator\. gtkcalendar\.c 400



```cpp
static void
gtk_calendar_class_init (GtkCalendarClass *class)
{
  ....
  g_object_class_install_property (gobject_class,
    PROP_YEAR,
    g_param_spec_int ("year",
      P_("Year"),
      P_("The selected year"),
      0, G_MAXINT >> 9, 0,
      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY|
                          G_PARAM_EXPLICIT_NOTIFY));
  ....
}
```



В этом примере или скопировали лишний раз константу 'G\_PARAM\_EXPLICIT\_NOTIFY' или забыли заменить ее на другую\.



Еще один подобный случай, на этот раз с константой 'G\_PARAM\_DEPRECATED':



[V501](https://pvs-studio.ru/ru/docs/warnings/v501/) There are identical sub\-expressions 'G\_PARAM\_DEPRECATED' to the left and to the right of the '\|' operator\. gtkmenubar\.c 275



```cpp
static void
gtk_menu_bar_class_init (GtkMenuBarClass *class)
{
  ....
  gtk_widget_class_install_style_property (widget_class,
    g_param_spec_int ("internal-padding",
      P_("Internal padding"),
      P_("Amount of border space between ...."),
      0, G_MAXINT, 0,
      GTK_PARAM_READABLE |
      G_PARAM_DEPRECATED|G_PARAM_DEPRECATED));
  ....
}
```



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



В следующем примере очень длинный список инициализации, неудивительно совершить какую\-нибудь ошибку в таком случае:



[V519](https://pvs-studio.ru/ru/docs/warnings/v519/) The 'impl\_class\-\>set\_functions' variable is assigned values twice successively\. Perhaps this is a mistake\. Check lines: 5760, 5761\. gdkwindow\-x11\.c 5761



```cpp
static void
gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass)
{
  ....
  GdkWindowImplClass *impl_class = GDK_WINDOW_IMPL_CLASS (klass);
  ....
  impl_class->set_decorations = gdk_x11_window_set_decorations;
  impl_class->get_decorations = gdk_x11_window_get_decorations;
  impl_class->set_functions = gdk_x11_window_set_functions;
  impl_class->set_functions = gdk_x11_window_set_functions;
  ....
}
```



Сначала я подумал, что здесь по аналогии с вышестоящей инициализацией должна быть пара 'get\-set': 'set\_functions' и 'get\_functions'\. Но оказалось, что в структуре 'GdkWindowImplClass' нет поля 'get\_functions'\. Здесь возможно случайно размножили инициализацию, а может быть хотели заменить чем\-то другим и забыли\. В любом случае следует убедиться, что проинициализировано все что нужно, и удалить при необходимости лишнюю операцию\.



Следующее предупреждение аналогично предыдущему:



[V519](https://pvs-studio.ru/ru/docs/warnings/v519/) The 'impl\_class\-\>set\_functions' variable is assigned values twice successively\. Perhaps this is a mistake\. Check lines: 1613, 1614\. gdkwindow\-broadway\.c 1614



```cpp
static void
gdk_window_impl_broadway_class_init 
  (GdkWindowImplBroadwayClass *klass)
{
  ....
  GdkWindowImplClass *impl_class = GDK_WINDOW_IMPL_CLASS (klass);
  ....
  impl_class->set_functions = 
    gdk_broadway_window_set_functions;
  impl_class->set_functions = 
    gdk_broadway_window_set_functions;
  ....
}
```



Опять двойное присвоение 'impl\_class\-\>set\_functions', возможно оно перекочевало из предыдущего примера\.



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



[V524](https://pvs-studio.ru/ru/docs/warnings/v524/) It is odd that the body of 'gtk\_mirror\_bin\_get\_preferred\_height' function is fully equivalent to the body of 'gtk\_mirror\_bin\_get\_preferred\_width' function\. offscreen\_window2\.c 340



```cpp
static void
gtk_mirror_bin_get_preferred_width (GtkWidget *widget,
                                    gint      *minimum,
                                    gint      *natural)
{
  GtkRequisition requisition;
  gtk_mirror_bin_size_request (widget, &requisition);
  *minimum = *natural = requisition.width;
}

static void
gtk_mirror_bin_get_preferred_height (GtkWidget *widget,
                                     gint      *minimum,
                                     gint      *natural)
{
  GtkRequisition requisition;

  gtk_mirror_bin_size_request (widget, &requisition);

  *minimum = *natural = requisition.width;
}
```



В функции gtk\_mirror\_bin\_get\_preferred\_height скорее всего надо использовать 'requisition\. height' вместо 'requisition\.width', Правильный код должен вероятно выглядеть следующим образом:

```cpp
 *minimum = *natural = requisition.height;
```

А может это не ошибка, а так и задумано, но выглядит странно\.



Нашелся еще один случай, где на мой взгляд есть путаница с шириной и высотой:



[V524](https://pvs-studio.ru/ru/docs/warnings/v524/) It is odd that the body of 'gtk\_hsv\_get\_preferred\_height' function is fully equivalent to the body of 'gtk\_hsv\_get\_preferred\_width' function\. gtkhsv\.c 310



```cpp
static void
gtk_hsv_get_preferred_width (GtkWidget *widget,
                             gint      *minimum,
                             gint      *natural)
{
  GtkHSV *hsv = GTK_HSV (widget);
  GtkHSVPrivate *priv = hsv->priv;
  gint focus_width;
  gint focus_pad;

  gtk_widget_style_get (widget,
                        "focus-line-width", &focus_width,
                        "focus-padding", &focus_pad,
                        NULL);

  *minimum = priv->size + 2 * (focus_width + focus_pad);
  *natural = priv->size + 2 * (focus_width + focus_pad);
}

static void
gtk_hsv_get_preferred_height (GtkWidget *widget,
                              gint      *minimum,
                              gint      *natural)
{
  GtkHSV *hsv = GTK_HSV (widget);
  GtkHSVPrivate *priv = hsv->priv;
  gint focus_width;
  gint focus_pad;

  gtk_widget_style_get (widget,
                        "focus-line-width", &focus_width,
                        "focus-padding", &focus_pad,
                        NULL);

  *minimum = priv->size + 2 * (focus_width + focus_pad);
  *natural = priv->size + 2 * (focus_width + focus_pad);
}
```



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



В следующем примере не совсем понятно какой аргумент должен использоваться вместо скопированного 'component\_name', но однозначно подозрительно сравнивать строку саму с собой:



[V549](https://pvs-studio.ru/ru/docs/warnings/v549/) The first argument of 'strcmp' function is equal to the second argument\. gtkrc\.c 1400



```cpp
GtkStyle *
gtk_rc_get_style_by_paths (....)
{
  ....
  pos = gtk_widget_path_append_type (path, component_type);
  if (component_name != NULL && 
      strcmp (component_name, component_name) != 0)    // <=
    gtk_widget_path_iter_set_name (path, pos, component_name);
  ....
}
```

Продолжаем просматривать предупреждения, связанные с копированием кода:



[V570](https://pvs-studio.ru/ru/docs/warnings/v570/) The 'tmp\_info' variable is assigned to itself\. gtkimcontextxim\.c 442



```cpp
static GtkXIMInfo *
get_im (....)
{
  ....
  GtkXIMInfo *info;
  ....
  info = NULL;
  tmp_list = open_ims;
  while (tmp_list)
    {
      ....
      else
        {
          tmp_info = tmp_info;           // <=
          break;
        }
      ....
    }
  if (info == NULL)
    {
      ....
    }
  ....
}
```



Посмотрев на код кажется логичным, что присвоить значение хотели переменной 'info', только тогда код, находящийся за 'while', имеет смысл\. Попробуем исправить:

```cpp
info = tmp_info;
```

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

## Работа с указателями

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



[V528](https://pvs-studio.ru/ru/docs/warnings/v528/) It is odd that pointer to 'char' type is compared with the '\\0' value\. Probably meant: \*data\-\>groups\[0\] \!\= '\\0'\. gtkrecentmanager\.c 979



```cpp
struct _GtkRecentData
{
  ....
  gchar **groups;
  ....
};

gboolean
gtk_recent_manager_add_full (GtkRecentManager    *manager,
                             const gchar         *uri,
                             const GtkRecentData *data)
{
  ....
  if (data->groups && data->groups[0] != '\0')
      ....
  ....
}
```

По адресу 'data\-\>groups\[0\]' лежит 'gchar\*', то есть тоже указатель, который сравнивать с '\\0' некорректно\. В текущем примере по факту происходит сравнение указателя 'data\-\>groups\[0\]' с нулевым указателем\. Если надо было убедиться, что указатель ненулевой, то правильный код должен быть следующим:

```cpp

if (data->groups && data->groups[0] != NULL)
```

Если же автор кода хотел проверить символ лежащий по адресу 'data\-\>groups\[0\]' на равенство терминальному нулю, тогда надо было разыменовать указатель:

```cpp
if (data->groups && *data->groups[0] != '\0')
```



Вот еще один такой пример, опять некорректное сравнение:

[V528](https://pvs-studio.ru/ru/docs/warnings/v528/) It is odd that pointer to 'char' type is compared with the '\\0' value\. Probably meant: \*priv\-\>icon\_list\[0\] \=\= '\\0'\. gtkscalebutton\.c 987



```cpp
struct _GtkScaleButtonPrivate
{
  ....
  gchar **icon_list;
  ....
};

struct _GtkScaleButton
{
  ....
  GtkScaleButtonPrivate *priv;
};

static void
gtk_scale_button_update_icon (GtkScaleButton *button)
{
  GtkScaleButtonPrivate *priv = button->priv;
  ....
  if (!priv->icon_list || priv->icon_list[0] == '\0')
  ....
}
```

При использовании указателей в C/C\+\+ следует соблюдать осторожность\. Если нет уверенности, что по указателю есть данные следует обязательно проверять его на нулевое значение\. 

Обращению по нулевому указателю приведет к неопределенному поведению программы или ее аварийному завершению\. Следующие предупреждения сообщают о вероятности такого обращения\. 



[V595](https://pvs-studio.ru/ru/docs/warnings/v595/) The 'completion' pointer was utilized before it was verified against nullptr\. Check lines: 2231, 2239\. gtkentrycompletion\.c 2231



```cpp
static gboolean
gtk_entry_completion_key_press (...., gpointer user_data)
{
  ....
  GtkEntryCompletion *completion = 
    GTK_ENTRY_COMPLETION (user_data);

  if (!completion->priv->popup_completion)
    return FALSE;

  ....
  if (completion && completion->priv->completion_timeout) // <=
    {
      ....
    }
  ....
}
```

В теле функции программист проверяет указатель 'completion' на нулевое значение, а потом использует его: 



```cpp
if (completion && completion->priv->completion_timeout)
```

Исходя из этой проверки, программист допускает вероятность того, что указатель окажется нулевым\. Но выше происходит обращение к этому указателю без его проверки на нулевое значение:



```cpp
if (!completion->priv->popup_completion)
    return FALSE;
```

Если указатель в этом месте окажется нулевым, то мы получаем неопределенное поведение\. Надо или проверить указатель 'completion' еще выше или та проверка, которая происходит ниже, бессмыслена\.



Таких примеров в коде библиотеки больше десятка, поэтому рассмотрим только еще один случай:



[V595](https://pvs-studio.ru/ru/docs/warnings/v595/) The 'dispatch\-\>backend' pointer was utilized before it was verified against nullptr\. Check lines: 1570, 1580\. gtkprintbackendcups\.c 1570



```cpp
static void 
cups_dispatch_watch_finalize (GSource *source)
{
  ....
  if (dispatch->backend->username != NULL)
    username = dispatch->backend->username;
  else
    username = cupsUser ();
  ....
  if (dispatch->backend)
    dispatch->backend->authentication_lock = FALSE;
  ....
}
```

Проверка указателя 'dispatch\-\>backend' происходит уже после того, когда мы к нему обращались, это тоже потенциально проблемное место\.

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

V595 The 'impl\-\>toplevel' pointer was utilized before it was verified against nullptr\. Check lines: 514, 524\. gdkwindow\-x11\.c 514

V595 The 'pointer\_info' pointer was utilized before it was verified against nullptr\. Check lines: 9610, 9638\. gdkwindow\.c 9610

V595 The 'elt' pointer was utilized before it was verified against nullptr\. Check lines: 2218, 2225\. gtktreemodelfilter\.c 2218

V595 The 'tmp\_list' pointer was utilized before it was verified against nullptr\. Check lines: 5817, 5831\. gtktreeview\.c 5817

V595 The 'dispatch\-\>data\_poll' pointer was utilized before it was verified against nullptr\. Check lines: 1470, 1474\. gtkprintbackendcups\.c 1470

## Другие ошибки

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

В данном примере автор забыл расставить 'break' в конце 'case'\-ов:



[V519](https://pvs-studio.ru/ru/docs/warnings/v519/) The 'type' variable is assigned values twice successively\. Perhaps this is a mistake\. Check lines: 187, 189\. testselection\.c 189



```cpp
void
selection_get (....
               guint      info,
               ....)
{
  ....
  switch (info)
    {
    case COMPOUND_TEXT:
    case TEXT:
      type = seltypes[COMPOUND_TEXT];
    case STRING:
      type = seltypes[STRING];
    }
  ....
}
```

Независимо от того, какое из трех значений примет 'info', мы получим присвоение 'type \= seltypes\[STRING\];'\. Чтобы избежать этого, надо добавить оператор 'break':

```cpp

switch (info)
    {
    case COMPOUND_TEXT:
    case TEXT:
      type = seltypes[COMPOUND_TEXT];
      break;
    case STRING:
      type = seltypes[STRING];
      break;
    }
```

Следующее место очень подозрительно, одна и та же переменная \('i'\) используется как счетчик для основного цикла и вложенного цикла:



[V535](https://pvs-studio.ru/ru/docs/warnings/v535/) The variable 'i' is being used for this loop and for the outer loop\. Check lines: 895, 936\. gtkstyleproperties\.c 936



```cpp
void
gtk_style_properties_merge (....)
{
  ....
  guint i;
  ....
  for (i = 0; i < prop_to_merge->values->len; i++)
    {
     ....
      else if (_gtk_is_css_typed_value_of_type (data->value, 
                G_TYPE_PTR_ARRAY) && value->value != NULL)
        {
          ....
          for (i = 0; i < array_to_merge->len; i++)
            g_ptr_array_add (array, 
              g_ptr_array_index (array_to_merge, i));
        }
    ....
    }
  ....
}
```

Даже не знаю, чему будет равен 'i' после выполнения вложенного цикла, и как будет исполняться основной цикл после этого\. Чтобы исключить неопределенность в этом месте, возможно лучше было бы использовать свой счетчик для вложенного цикла, например:



```cpp
guint j;
for (j = 0; j < array_to_merge->len; j++)
  g_ptr_array_add (array, 
  g_ptr_array_index (array_to_merge, j));
```

Еще парочка потенциально опасных использований счетчика в цикле:



[V557](https://pvs-studio.ru/ru/docs/warnings/v557/) Array overrun is possible\. The value of 'i \+ 1' index could reach 21\. gtkcssselector\.c 1219

[V557](https://pvs-studio.ru/ru/docs/warnings/v557/) Array overrun is possible\. The value of 'i \+ 1' index could reach 21\. gtkcssselector\.c 1224



```cpp
#define G_N_ELEMENTS(arr)   (sizeof (arr) / sizeof ((arr)[0]))

static GtkCssSelector *
parse_selector_pseudo_class (....)
{
  static const struct {
    ....
  } pseudo_classes[] = {
    { "first-child",   0, 0,  POSITION_FORWARD,  0, 1 },
    ....
    { "drop(active)",  0, GTK_STATE_FLAG_DROP_ACTIVE, }
  };
  guint i;
  ....
  for (i = 0; i < G_N_ELEMENTS (pseudo_classes); i++)
    {
      ....
      {
        if (pseudo_classes[i + 1].state_flag == 
            pseudo_classes[i].state_flag)
          _gtk_css_parser_error_full (parser,
          GTK_CSS_PROVIDER_ERROR_DEPRECATED,
          "The :%s pseudo-class is deprecated. Use :%s instead.",
          pseudo_classes[i].name,
          pseudo_classes[i + 1].name);
        ....
      }
       ....
    }
  ....
}
```

Мы видим цикл по количеству элементов в массиве 'pseudo\_classes', я надеюсь, что этот цикл никогда не дойдет до последнего элемента, в противном случае конструкция 'pseudo\_classes\[i\+1\]' приведет нас к выходу за границы массива\.



Следующее потенциально ошибочное место в коде похоже на опечатку:



[V559](https://pvs-studio.ru/ru/docs/warnings/v559/) Suspicious assignment inside the condition expression of 'if' operator\. gdkselection\-x11\.c 741



```cpp
gboolean
gdk_x11_display_utf8_to_compound_text (....)
{
  ....
  GError *error = NULL;
  ....
  if (!(error->domain = G_CONVERT_ERROR &&
        error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
  ....
}
```



Довольно часто случается, что вместо оператора сравнения '\=\=' пишут присвоение '\='\. Код будет скомпилирован, часть компиляторов возможно выдаст предупреждение в такой ситуации\. Вы же не блокируете ненужные предупреждения, и не допустите такой ошибки? Скорее всего код должен выглядеть так:



```cpp
if (!(error->domain == G_CONVERT_ERROR &&
        error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
```

Теперь анализатор сообщает нам об использовании в условном операторе if выражения, значение которого всегда постоянно:



[V560](https://pvs-studio.ru/ru/docs/warnings/v560/) A part of conditional expression is always false: \!auto\_mnemonics\. gtklabel\.c 2693



```cpp
static void
gtk_label_set_markup_internal (....)
{
  ....

  gboolean enable_mnemonics = TRUE;
  gboolean auto_mnemonics = TRUE;

  g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
                "gtk-enable-mnemonics", &enable_mnemonics,
                NULL);

  if (!(enable_mnemonics && priv->mnemonics_visible &&
        (!auto_mnemonics ||
         (gtk_widget_is_sensitive (GTK_WIDGET (label)) &&
          (!priv->mnemonic_widget ||
           gtk_widget_is_sensitive (priv->mnemonic_widget))))))
  ....
}
```

В принципе нельзя сказать сразу, что это ошибка\. Но если внимательно посмотреть, то можно увидеть, что рядом с переменной 'auto\_mnemonics' создается переменная 'enable\_mnemonics', а затем она инициализируется значением из настроек\. Возможно здесь забыли взять таким же образом значение переменной 'auto\_mnemonics'\. Если же нет, тогда возможно стоит убрать проверку условия '\!auto\_mnemonics'\.

И еще одно предупреждение с переменной 'auto\_mnemonics':



[V560](https://pvs-studio.ru/ru/docs/warnings/v560/) A part of conditional expression is always false: \!auto\_mnemonics\. gtklabel\.c 2923



```cpp
static void
gtk_label_set_pattern_internal (....)
{
  ....
  gboolean enable_mnemonics = TRUE;
  gboolean auto_mnemonics = TRUE;

  ....
  g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
                "gtk-enable-mnemonics", &enable_mnemonics,
                NULL);

  if (enable_mnemonics && priv->mnemonics_visible && pattern &&
      (!auto_mnemonics ||
       (gtk_widget_is_sensitive (GTK_WIDGET (label)) &&
        (!priv->mnemonic_widget ||
         gtk_widget_is_sensitive (priv->mnemonic_widget)))))
  ....
}
```

А вот предупреждение об опасном сравнении беззнаковой переменной типа 'guint' cо знаковой константой:



[V605](https://pvs-studio.ru/ru/docs/warnings/v605/) Consider verifying the expression\. An unsigned value is compared to the number \-3\. gtktextview\.c 9162

[V605](https://pvs-studio.ru/ru/docs/warnings/v605/) Consider verifying the expression\. An unsigned value is compared to the number \-1\. gtktextview\.c 9163



```cpp
struct GtkTargetPair {
  GdkAtom   target;
  guint     flags;
  guint     info;
};

typedef enum
{
  GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS = - 1,
  GTK_TEXT_BUFFER_TARGET_INFO_RICH_TEXT       = - 2,
  GTK_TEXT_BUFFER_TARGET_INFO_TEXT            = - 3
} GtkTextBufferTargetInfo;

static void
gtk_text_view_target_list_notify (....)
{
  ....
  if (pair->info >= GTK_TEXT_BUFFER_TARGET_INFO_TEXT &&
      pair->info <= GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS)
  ....
}
```



Есть соответствующие предупреждения компилятора о подобных сравнениях, но зачастую они незаслуженно отключаются\. В данном случае произойдет неявное приведение знакового типа к беззнаковому\. Хорошо, если программист учел это в условии, но даже если это и так, то все равно такой код не очень хорош\. Как минимум желательно использовать явное приведение, чтобы было видно, что программист понимает, что происходит в этом месте\. Если 'pair\-\>info' может принимать значения только из перечисления  'GtkTextBufferTargetInfo' тогда почему не сделать переменную info такого же типа\. Если же в ней могут оказаться и другие значения, тогда может быть небезопасным использовать такой подход\.



Последнее рассмотренное предупреждение сообщает о пересечении диапазонов в условии 'if\.\.\.elseif':



[V695](https://pvs-studio.ru/ru/docs/warnings/v695/) Range intersections are possible within conditional expressions\. Example: if \(A < 5\) \{ \.\.\.\. \} else if \(A < 2\) \{ \.\.\.\. \}\. Check lines: 580, 587\. broadway\-server\.c 587



```cpp
static void
parse_input (BroadwayInput *input)
{
  ....
  while (input->buffer->len > 2)
    {
      ....
      if (payload_len > 125)
        {
          ....
        }
      else if (payload_len > 126)
        {
          ....
        }
      ....
    }
}
```



Проанализировав код, мы видим что при любом значении 'payload\_len', которое больше 125, будет исполняться ветка 'if \(payload\_len \> 125\)', а ветка 'else if \(payload\_len \> 126\)' является частным случаем исходной проверки\. Поэтому код, находящийся в условии 'elseif' никогда не будет исполняться\. Разработчику необходимо просмотреть этот код и скорректировать его\.

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

После анализа кода библиотеки GTK\+, мы видим, что в ней есть, как и простые опечатки, так и более интересные ошибки, которые требуют исправления\. Для устранения подобных ошибок на раннем этапе хорошо помогают статические анализаторы, которые экономят время разработчикам\. Время, потраченное на отладку и поиск некоторых ошибок, можно будет использовать для разработки функциональности\. Напоминаю, что вы можете опробовать работу статического анализатора PVS\-Studio бесплатно\. Скачать дистрибутив программы можно [здесь](https://pvs-studio.ru/ru/pvs-studio/)\.