Неверный указатель при удалении элементов GLib.Tree в Vala - PullRequest
0 голосов
/ 06 мая 2018

Этот код вызывает следующую ошибку

free(): invalid pointer

Онлайн пример

using GLib;
using GLib.Random;

// Global variable
Tree<string, Tree<int, string> > mainTree;

public static int main (string[] args) {
    // Initiate random seed
    Random.set_seed ((uint32) get_monotonic_time());
    // mainTree initialization
    mainTree = new Tree<string, Tree<int, string> >.full (mainTreeCompareDataFunction, free, free);
    // Random sized for loop
    for (int i = 0; i < int_range (1000, 10001); i++) {
        // If a condition is met (i is even)
        if (i % 2 == 0) {
            // Create a Tree to nest onto mainTree
            Tree<int, string> treeToNest = new Tree<int, string>.full (treeToNestCompareDataFunction, free, free);
            // Insert random content into treeToNest
            treeToNest.insert (int_range (0, 101), randomString ());
            // Insert the tree onto mainTree
            mainTree.insert (randomString (), treeToNest);
        }
    }

    // Empty the tree
    mainTree.@foreach ((mainTreeKey, mainTreeValue) => {
        mainTree.remove (mainTreeKey); // This line causes a free(): invalid pointer error
        return false;
    });

    return 0;
}

public string randomString () {
    string charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    string stringToReturn = "";
    // Create a random 8 character string
    for (int i = 0; i < 8; i++) {
        stringToReturn += charset[int_range (0, charset.length)].to_string ();
    }
    return stringToReturn;
}

public int treeToNestCompareDataFunction (int a, int b) {
    if (a < b) return -1;
    if (a > b) return 1;
    return 0; 
}

public int mainTreeCompareDataFunction (string a, string b) {
    return strcmp (a, b);
}

Я подозреваю, что это потому, что внутри дерева есть вложенный GLib.Tree, и free() нельзя использовать на этих объектах. Если я использую null вместо функции уничтожения для значений mainTree, не произойдет сбоя, но будет утечка памяти, если я буду использовать переменную mainTree.

Есть ли способ очистить дерево и освободить память?

Ответы [ 2 ]

0 голосов
/ 06 мая 2018

Вы можете написать лямбду, которая удалит поддерево, используя передачу прав собственности.

    mainTree = new Tree<string, Tree<int, string> >.full (mainTreeCompareDataFunction, free,
        (data) => {
          var tree = (Tree<string, Tree<int, string>>) data;
          // Ownership is transfered to this local var which calls unref when it goes out of scope
          var tree2 = (owned) tree;
        });

Вот полный код:

using GLib;
using GLib.Random;

// Global variable
Tree<string, Tree<int, string> > mainTree;

public static int main (string[] args) {
    // Initiate random seed
    Random.set_seed ((uint32) get_monotonic_time());
    // mainTree initialization
    mainTree = new Tree<string, Tree<int, string> >.full (mainTreeCompareDataFunction, free,
        (data) => {
          var tree = (Tree<string, Tree<int, string>>) data;
          // Ownership is transfered to this local var which calls unref when it goes out of scope
          var tree2 = (owned) tree;
        });
    // Random sized for loop
    for (int i = 0; i < int_range (1000, 10001); i++) {
        // If a condition is met (i is even)
        if (i % 2 == 0) {
            // Create a Tree to nest onto mainTree
            Tree<int, string> treeToNest = new Tree<int, string>.full (treeToNestCompareDataFunction, null, free);
            // Insert random content into treeToNest
            treeToNest.insert (int_range (0, 101), randomString ());
            // Insert the tree onto mainTree
            mainTree.insert (randomString (), treeToNest);
        }
    }

    // Empty the tree
    mainTree.@foreach ((mainTreeKey, mainTreeValue) => {
        mainTree.remove (mainTreeKey);
        return false;
    });

    return 0;
}

public string randomString () {
    string charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    string stringToReturn = "";
    // Create a random 8 character string
    for (int i = 0; i < 8; i++) {
        stringToReturn += charset[int_range (0, charset.length)].to_string ();
    }
    return stringToReturn;
}

public int treeToNestCompareDataFunction (int a, int b) {
    if (a < b) return -1;
    if (a > b) return 1;
    return 0; 
}

public int mainTreeCompareDataFunction (string a, string b) {
    return strcmp (a, b);
}

Я бы не стал использовать класс GLib.Tree в Vala из-за сложности правильного управления памятью. Вам следует рассмотреть возможность использования Gee.TreeMap.

0 голосов
/ 06 мая 2018

Нашли решение, которое заключается в ручном управлении памятью. Сначала сделайте вложенное дерево указателем . Во-вторых, передайте null аргументу для указания функции для освобождения вложенного дерева. В-третьих, передайте null аргументу для указания функции для освобождения ключей int вложенного дерева. Наконец, используйте ключевое слово delete для уменьшения ссылки на вложенное дерево при очистке основного дерева.

Поскольку ссылка на вложенное дерево в этом коде упадет до 0 из-за ручного вызова delete, который здесь конкретно вызывает g_tree_unref в сгенерированном Vala C-коде, все ключи и значения вложенного дерева уничтожается и освобождается вся память, выделенная деревом. Таким образом, больше не будет ошибок недействительных указателей и утечек памяти.

using GLib;
using GLib.Random;

// Global variable
Tree<string, Tree<int, string>* > mainTree;

public static int main (string[] args) {
    // Initiate random seed
    Random.set_seed ((uint32) get_monotonic_time());
    // mainTree initialization
    mainTree = new Tree<string, Tree<int, string> >.full (mainTreeCompareDataFunction, free, null);
    // Random sized for loop
    for (int i = 0; i < int_range (1000, 10001); i++) {
        // If a condition is met (i is even)
        if (i % 2 == 0) {
            // Create a Tree to nest onto mainTree
            Tree<int, string>* treeToNest = new Tree<int, string>.full (treeToNestCompareDataFunction, null, free);
            // Insert random content into treeToNest
            treeToNest->insert (int_range (0, 101), randomString ());
            // Insert the tree onto mainTree
            mainTree.insert (randomString (), treeToNest);
        }
    }

    // Empty the tree
    mainTree.@foreach ((mainTreeKey, mainTreeValue) => {
        delete mainTree.lookup (mainTreeKey);
        mainTree.remove (mainTreeKey);
        return false;
    });

    return 0;
}

public string randomString () {
    string charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    string stringToReturn = "";
    // Create a random 8 character string
    for (int i = 0; i < 8; i++) {
        stringToReturn += charset[int_range (0, charset.length)].to_string ();
    }
    return stringToReturn;
}

public int treeToNestCompareDataFunction (int a, int b) {
    if (a < b) return -1;
    if (a > b) return 1;
    return 0; 
}

public int mainTreeCompareDataFunction (string a, string b) {
    return strcmp (a, b);
}

Источники:

...