嵌套结构分配/释放导致崩溃

时间:2018-07-17 16:37:29

标签: c pointers segmentation-fault structure allocation

经过很长的休息后,我回到了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;
}

我做错了什么?

编辑:添加列表创建/删除

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;
}

1 个答案:

答案 0 :(得分: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容器,因此您不必自己发明它们(也不会犯初学者的错误:)。它们也可能会提供更好的性能。