【问题标题】:Nested structures allocation / deallocation causing crash嵌套结构分配/释放导致崩溃
【发布时间】:2018-07-17 16:37:29
【问题描述】:

经过长时间的休息后,我回到了 C,我对自己说“学习一些深度学习怎么样?”。 所以,我有这些结构:

  • 神经元,包含输入数组、权重数组和输入数量,
  • NeuralLayer,包含神经元阵列和神经元数量,
  • NeuralNetwork,包含一个 NeuralLayer 列表。

之前网络是一个层数组,但由于层数可能未知,我现在使用一个列表。

我希望网络按如下方式使用:创建、逐层添加、输出、删除。

// A neuron is an array of inputs, an array of weights, and the size of its arrays
typedef struct Neuron {
    double * inputs;    
    double * weights;
    size_t inputsCount;
} Neuron;




// Creates a Neuron and returns it
Neuron * Neuron_create (size_t inputsCount) { 
    Neuron * neuron;
    size_t weightsIndex;

    neuron = Structure_create (sizeof (Neuron));

    neuron->inputsCount = inputsCount;
    neuron->inputs = (double *) Structure_create (neuron->inputsCount * sizeof (double));
    neuron->weights = (double *) Structure_create (neuron->inputsCount * sizeof (double));

    for (weightsIndex = 0; weightsIndex < inputsCount; weightsIndex++) 
        neuron->weights [weightsIndex] = rand () / (RAND_MAX / 1.0); // On creation, random weights are used

    return neuron;
}


// Deletes the neuron and sets its pointer to NULL
void Neuron_delete (Neuron ** target) {
    if ((NULL == target) || (NULL == * target))
        return;

    printf ("    Deleting neuron: %p\n", * target);

    Structure_delete (& (* target)->inputs); 
    Structure_delete (& (* target)->weights); 
    Structure_delete (target);

    printf ("    Neuron deleted\n");
}

// A layer is an array of Neurons and its size
typedef struct NeuralLayer {

    size_t neuronsCount;
    Neuron ** neurons;
} NeuralLayer;




// Creates a Layer and returns it
NeuralLayer * NeuralLayer_create (size_t neuronsCount, size_t inputsCount) {

    NeuralLayer * layer;
    size_t neuronsIndex;

    layer = Structure_create (sizeof (NeuralLayer));
    layer->neuronsCount = neuronsCount;
    layer->neurons = Structure_create (layer->neuronsCount * sizeof (NeuralLayer *));

    for (neuronsIndex = 0; neuronsIndex < neuronsCount; neuronsIndex++)
        layer->neurons [neuronsIndex] = Neuron_create (inputsCount); 

    return layer;
}


// Deletes the layer and sets its pointer to NULL
void NeuralLayer_delete (NeuralLayer ** target) {

    if ((NULL == target) || (NULL == * target))
        return;

    printf ("  Deleting layer: %p\n", * target);

    for (size_t neuronIndex = 0; neuronIndex < (* target)->neuronsCount; neuronIndex++) {
        Neuron * buffer = (* target)->neurons [neuronIndex];
        Neuron_delete (& buffer);
    }      

    Structure_delete ((* target)->neurons); 
    Structure_delete (target);

    printf ("  Layer deleted\n");
}

// The network is simply a List of layers
typedef struct NeuralNetwork {
    List * layers;
} NeuralNetwork;




// Allocs the empty network and returns it
NeuralNetwork * NeuralNetwork_create (void) {
    NeuralNetwork * network;

    network = Structure_create (sizeof (NeuralNetwork));
    network->layers = List_create ();

    return network;
}


// Deletes the network and sets its pointer to NULL
void NeuralNetwork_delete (NeuralNetwork ** target) {
    if ((NULL == target) || (NULL == * target))
        return;

    printf ("Deleting network: %p\n", * target);

    // For each layer, delete layer
    for (size_t layerIndex = 0; layerIndex < List_size ((* target)->layers); layerIndex++) {
        NeuralLayer * buffer = List_get ((* target)->layers, layerIndex); // I'm using opaque structures
        NeuralLayer_delete (& buffer);
    }

    List_delete (& (* target)->layers);
    Structure_delete (target);

    printf ("Network deleted\n");
}


// We only need to know how many neurons the new layer will contain, they will have as many inputs as the number of neurons in the previous layer
void NeuralNetwork_addLayer (NeuralNetwork * dest, size_t neuronsCount) {
    if (NULL == dest->layers)
        List_add (& dest->layers, NeuralLayer_create (neuronsCount, 1));

    else {
        NeuralLayer * previousLayer = List_get (dest->layers, List_size (dest->layers) - 1);
        size_t inputsCount = NeuralLayer_getNeuronsCount (previousLayer);
        NeuralLayer * newLayer = NeuralLayer_create (neuronsCount, inputsCount);
        List_add (& dest->layers, newLayer);
    }
}

还有我的主要:

int main (int argc, char * argv []) {
    NeuralNetwork * network = NeuralNetwork_create (); 
    NeuralNetwork_addLayer (network, 4); // First layer are inputs, one for each pixel
    NeuralNetwork_addLayer (network, 3); // Hidden layer of 15 neurons
    NeuralNetwork_addLayer (network, 2); // Output layer, 

    NeuralNetwork_delete (& network);
    return EXIT_SUCCESS;
}

释放过程中程序崩溃,NeuralNetwork_delete() 调用 NeuralLayer_delete() 而调用 Neuron_delete(),分配/释放有问题。

哦,我忘记了基本记忆功能:

// Allocates memory, checks it and returns it
// Have to change the name, i don't use it only to alloc structures
void * Structure_create (size_t size) {
    void * structure;

    structure = malloc (size);
    if (NULL == structure) {
        printf ("Memory allocation failed, aborting.\n");
        system ("PAUSE");
        exit (EXIT_FAILURE);
    }

    return structure;
}



// Free the memory and sets the pointer to NULL
void Structure_delete (void ** target) {
    if ((NULL == target) || (NULL == * target))
        return;

    free (* target);
    * target = NULL;
}

我做错了什么?

编辑:添加 List_create / 删除

typedef struct List {
    void * content;
    List * next;
    List * previous;
} List;




// Create a single item and inits it
static List * _List_createItem (void * content);



// Returns NULL to hide implementation
List * List_create (void) {
    return (List* )NULL;
}


void List_delete (List ** target) {
    if (NULL == target) {
        printf ("Tried to delete NULL list\n");
        return;
    }

    else if (NULL == * target)
        return;

    List_delete (& (* target)->next);
    Structure_delete (target); 
}


void * List_get (const List * const list, size_t index) {
    if (NULL == list) {
        printf ("Tried to get from List with index out of range\n");
        return NULL;
    }

    else if (0 == index) 
        return list->content;

    return List_get (list, index - 1);
}


// If the list was empty, it becomes a new list, so we need pointer to pointer
void List_add (List ** dest, void * content) {
    if (NULL == dest) {
        printf ("Tried to add item on NULL pointer\n");
        return;
    }

    else if ((NULL != * dest) && (NULL == (* dest)->next)) {
        (* dest)->next = _List_createItem (content);
        (* dest)->next->previous = * dest;
    }

    else if (NULL == * dest) 
        * dest = _List_createItem (content);

    else
        List_add (& (* dest)->next, content);
}


static List * _List_createItem (void * content) {    
    List * item;

    item = (List *)Structure_create (sizeof (List));
    item->content = content;
    item->next = NULL;
    item->previous = NULL;

    return item;
}

【问题讨论】:

  • 恐怕您可能不得不花一些精力将其变成minimal reproducible example,从而将更多的权重放在“M”上。
  • Structure_delete (target); 是可疑的。你想要Structure_delete (&amp;target);吗?
  • 另请注意,未提供List_create/delete
  • 好吧,我怕代码太大太复杂,不考虑minimal reproducible example
  • 通过 valgrind 运行您的代码。如果你的内存管理不善,它会告诉你在哪里。

标签: c pointers segmentation-fault structure allocation


【解决方案1】:

您的 List_get 函数似乎没有正确遍历列表:

return List_get (list, index - 1);

应该是这样的:

return List_get (list->next, index - 1);

另外,在NeuralLayer_delete函数中:

Structure_delete ((* target)->neurons);

应该是:

Structure_delete (&(* target)->neurons);

附带说明:您已经发现这种“非常手动”的内存管理非常脆弱且容易出错。考虑在未来过渡到 C++。它具有RAII patternsmart pointers,用于更强大的资源管理。它还提供dynamic array (vector)linked list 容器,因此您不必自己发明它们(并且犯初学者错误:))。它们还可能提供更好的性能。

【讨论】:

  • 我犯了一个初学者的错误,这就是我的意思,我从另一个项目中导入了 List。它已修复,但仍有崩溃。
  • 我在删除函数中添加了 printf 并减少了每层的神经元数量。删除第一层的神经元时,不打印“Layer deleted”。
  • @Azhatoth,我想我发现了另一个问题。请查看更新后的答案。
  • @Azhatoth,是的,但是你的字段已经是一个双指针,因为它是一个指针数组,所以你必须在释放数组时获取这个指针的地址。
  • 我试过C++,我不喜欢它,我不喜欢OO,我不知道为什么,我学过Java,一些C++,python,C对我来说永远是王道。比起逃避困难,我更愿意去面对,学习。
猜你喜欢
  • 2011-08-07
  • 2022-10-14
  • 2012-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-02
  • 1970-01-01
相关资源
最近更新 更多