Как избежать утечек памяти в C GTK (используя libxml) - PullRequest
0 голосов
/ 25 января 2019

Мой код страдает огромными утечками памяти, как показывает Вальгринд.Я читаю XML-файлы и не знаю, как правильно скопировать строковые значения в мою структуру без потери памяти.

Я пытался не дублировать строки, удаляя g_strdup в on_button1_clicked функция, но в случае, если она просто не работает (без вывода).

C файл:

#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <glib-object.h>
#include <glib.h>
#include <glib/gprintf.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <string.h>


typedef struct {
    gchar *name;
    gint *age;
} item_struct;


typedef struct {
    GtkWidget *main_win;
    GtkWidget *button1;
    item_struct item[10];
    gint *item_index;
    gchar *local_path;
} app_widgets;


void on_button1_clicked (GtkButton *button, app_widgets *app_wid)
{
    g_printf("Button clicked !\n");

    int i;
    xmlDoc          *doc;
    xmlNodePtr      root, node, unode;
    gchar           *filename = g_strdup_printf("%s/test.xml",app_wid->local_path);

    doc = xmlReadFile((char*)filename, NULL, XML_PARSE_NOBLANKS);
    root = xmlDocGetRootElement(doc);

    if(root == NULL){
        g_printf("%s: Document is empty!\n",filename);
        return;
    }
    else {

        node = root->children; //item

        i=0;
        while(node != NULL) {

            unode = node->children;

            xmlChar *xml_name = xmlNodeGetContent(unode);
            app_wid->item[i].name = g_strdup((gchar*)xml_name);
            xmlFree(xml_name);

            unode = unode->next;

            xmlChar *xml_age = xmlNodeGetContent(unode);
            app_wid->item[i].age =  GINT_TO_POINTER((gint)g_ascii_strtoll(g_strdup((gchar*)xml_age),NULL,10));
            xmlFree(xml_age);

            i++;
            node = node->next;

        }
        app_wid->item_index = GINT_TO_POINTER((gint)i);

    }

    xmlFreeDoc(doc);
    xmlCleanupParser();
    g_free(filename);


    for(i=0;i<GPOINTER_TO_INT(app_wid->item_index);i++) {

         g_printf("%d name:%s age:%d\n",i,app_wid->item[i].name,GPOINTER_TO_INT(app_wid->item[i].age));

    }
}

void on_main_win_destroy(GtkWidget *object)
{
    gtk_main_quit();
}

int main(int argc, char *argv[])
{
    GtkBuilder *builder;
    app_widgets *widgets = g_slice_new(app_widgets);

    gtk_init(&argc, &argv);

    builder = gtk_builder_new();
    gtk_builder_add_from_file (builder, "test52.glade", NULL);

    widgets->main_win = GTK_WIDGET(gtk_builder_get_object(builder, "main_win"));
    widgets->button1 = GTK_WIDGET(gtk_builder_get_object(builder, "button1"));

    widgets->local_path = g_strdup("/home/bobby/tmptest52");

    gtk_builder_connect_signals(builder, widgets);
    gtk_widget_show(widgets->main_win);
    g_object_unref(builder);
    gtk_main();

    g_slice_free(app_widgets, widgets);
    return 0;
}

файл glade:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
  <requires lib="gtk+" version="3.12"/>
  <object class="GtkWindow" id="main_win">
    <property name="can_focus">False</property>
    <signal name="destroy" handler="on_main_win_destroy" swapped="no"/>
    <child>
      <object class="GtkFixed" id="fixed1">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <child>
          <object class="GtkButton" id="button1">
            <property name="label" translatable="yes">Test</property>
            <property name="width_request">100</property>
            <property name="height_request">80</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <signal name="clicked" handler="on_button1_clicked" swapped="no"/>
          </object>
          <packing>
            <property name="x">144</property>
            <property name="y">81</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

test.xmlfile:

<?xml version="1.0" encoding="UTF-8"?>
<document>
  <item>
    <name>Georges</name>
    <age>19</age>
  </item>
  <item>
    <name>Arthur</name>
    <age>53</age>
  </item>
  <item>
    <name>Louisa</name>
    <age>22</age>
  </item>
  <item>
    <name>Walter</name>
    <age>42</age>
  </item>
  <item>
    <name>Richard</name>
    <age>28</age>
  </item>
</document>

Я ожидал, что удаление g_strdup подойдет, но даже с приведением (gchar*) значения не передаются.

Обновление - отчет из valgrind (есть2 раза одна и та же ошибка, но я копирую только одну):

==5956== 645 bytes in 215 blocks are definitely lost in loss record 6,472 of 6,728
==5956==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5956==    by 0x5A0B7B8: g_malloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4800.2)
==5956==    by 0x5A2458E: g_strdup (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4800.2)
==5956==    by 0x4010E4: on_button1_clicked (main.c:68)
==5956==    by 0x57791D3: ??? (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2)
==5956==    by 0x57939A5: g_signal_emit_valist (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2)
==5956==    by 0x579408E: g_signal_emit (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2)
==5956==    by 0x4F5A6AC: ??? (in /usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9)
==5956==    by 0x4F5A714: ??? (in /usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1800.9)
==5956==    by 0x57791D3: ??? (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2)
==5956==    by 0x57939A5: g_signal_emit_valist (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2)
==5956==    by 0x579408E: g_signal_emit (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4800.2)

1 Ответ

0 голосов
/ 28 января 2019

Хорошо, поэтому я отвечаю на свой собственный вопрос.

Я не знал, что мне нужно остерегаться использовать g_strdup и все функции GTK-Glib, возвращающие вновь распределенные строки.Я думал, что смогу использовать их столько, сколько хотел, чтобы возвращаемая строка входила в предопределенный gchar*, потому что я думал, что GTK изящно освободит себя содержимое этой переменной.Но это не так.

Так что, прежде чем использовать одну из этих функций, мы должны g_free их, если только у нас не будет утечек памяти.

Я думаю, это поможет людям не тратить часыинтересно, что происходит (как я).Я не говорю, что это нигде не написано, но что можно возиться со строками GTK в течение сотен часов, не зная об этом.

Я до сих пор не знаю, почему прямая атрибуция app_wid->item[i].name = (gchar*)xml_name не являетсяза работой.Но это на самом деле не имеет значения, так как есть другой способ сделать это (g_free значение, затем g_strdup это).

...