Aayush
Aayush

Reputation: 124

C malloc giving pointer to already used memory

I have a struct in my code that gets its value changed even though I don't reference at all. The code is:

layer l = linear(2, 3);
neural_network network = create_network();
add_layer(&network, &l);
printf("Before: %llu\n", network.layers[0].weights.column_size);
matrix i = create_matrix(2, 1);
printf("After: %llu\n", network.layers[0].weights.column_size);

And the output to this code is:

Before: 2
After: 0

This doesn't make sense to me as create_matrix is defined as:

matrix create_matrix(uint64_t row_size, uint64_t column_size) {
    matrix mat;
    mat.row_size = row_size;
    mat.column_size = column_size;
    mat.array = malloc(sizeof(double) * mat.row_size * mat.column_size);
    return mat;
}

I guessed that malloc was messing up for some reason so I printed the addresses of each one and got this:

Address of network.layers[0].weights.column_size: 0x600000b4c010
Before: 2
Address of mat.array: 0x600000b4c010
After: 0

So C is somehow allocating heap memory that should already be used. I'm not really sure why this is going on as I never freed any of the memory. The relevant structs are defined as:

typedef struct layer {
    uint64_t neurons;
    matrix weights;
    matrix biases;
    matrix (*compute_activations)(struct layer *l, matrix *activations);
} layer;

typedef struct {
    uint16_t number_of_layers;
    layer *layers;
} neural_network;

typedef struct {
    uint64_t row_size;
    uint64_t column_size;
    double *array;
} matrix;

Minimum Reproductible Example:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <cblas.h>
#include <string.h>

typedef struct {
    uint64_t row_size;
    uint64_t column_size;
    double *array;
} matrix;

typedef struct layer{
    uint64_t neurons;
    matrix weights;
    matrix biases;
    matrix (*compute_activations)(struct layer *l, matrix *activations);
} layer;

typedef struct {
    uint16_t number_of_layers;
    layer *layers;
} neural_network;

matrix create_matrix(uint64_t row_size, uint64_t column_size) {
    matrix mat;
    mat.row_size = row_size;
    mat.column_size = column_size;
    mat.array = malloc(sizeof(double) * mat.row_size * mat.column_size);
    return mat;
}

matrix matrix_m_multiply(matrix *A, matrix *B, matrix *C, double alpha, double beta) {
    matrix C_copy = create_matrix(C->row_size, C->column_size);
    memcpy(C_copy.array, C->array, C->row_size * C->column_size * sizeof(double));
    cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
                A->row_size, B->column_size, A->column_size, alpha,
                A->array, A->column_size, B->array, B->column_size, beta,
                C_copy.array, C->column_size);
    return C_copy;
}

matrix matrix_v_multiply(matrix *A, matrix *B, matrix *C, double alpha, double beta) {
    matrix C_copy = create_matrix(C->row_size, C->column_size);
    memcpy(C_copy.array, C->array, C->row_size * C->column_size * sizeof(double));
    cblas_dgemv(CblasRowMajor, CblasNoTrans,
                A->row_size, A->column_size, alpha, 
                A->array, A->column_size, B->array, 1, beta,
                C_copy.array, 1);
    return C_copy;
}

neural_network create_network() {
    neural_network network = { .number_of_layers = 0 };
    network.layers = malloc(sizeof(layer) * network.number_of_layers);
    return network;
}

void add_layer(neural_network *network, layer *l) {
    network->layers = realloc(network->layers, ++network->number_of_layers);
    network->layers[network->number_of_layers - 1] = *l;
}

matrix forward_pass(neural_network *network, matrix *inputs) {
    matrix activations = *inputs;
    for (int i = 0; i < network->number_of_layers; i++) {
        activations = network->layers[i].compute_activations(&network->layers[i], &activations);
    }
    return activations;
}

matrix linear_function(layer *linear_layer, matrix *activations) {
    return matrix_v_multiply(&linear_layer->weights, activations, &linear_layer->biases, 1.0, 1.0);
}

layer linear(uint64_t in, uint64_t out) {
    layer linear_layer;
    linear_layer.neurons = out;
    linear_layer.weights = create_matrix(out, in); // Don't have to transpose matrix when doing vector product with inputs
    linear_layer.biases = create_matrix(out, 1);
    linear_layer.compute_activations = linear_function;
    return linear_layer;
}

int main(int argc, char *argv[]) {
    srand(0);
    layer l = linear(2, 3);
    neural_network network = create_network();
    add_layer(&network, &l);
    printf("Before: %llu\n", network.layers[0].weights.column_size);
    matrix i = create_matrix(2, 1);
    printf("After: %llu\n", network.layers[0].weights.column_size);
    matrix output = forward_pass(&network, &i);

    return 0;
}

Upvotes: 0

Views: 66

Answers (1)

chqrlie
chqrlie

Reputation: 144695

There are at least two problems in your code:

  • in function create_network(), you allocate 0 bytes with malloc(), which has implementation defined behavior. You might instead initialize the layers member to a null pointer:

    neural_network create_network(void) {
        neural_network network = { .number_of_layers = 0, .layers = NULL };
        return network;
    }
    
  • the size reallocated by add_layer() is incorrect: you forgot to multiply the new number of elements by the element size. This problem is the likely explanation for your observations. You should write:

    void add_layer(neural_network *network, layer *l) {
        network->layers = realloc(network->layers,
                                  sizeof(*network->layers) *
                                      (network->number_of_layers + 1));
        network->layers[network->number_of_layers++] = *l;
    }
    
  • you do not check for memory allocation failure anywhere in your code. I would recommend using malloc, calloc, strdup and realloc wrappers to test for unlikely yet possible allocation failure and exit with an informative message.

Upvotes: 4

Related Questions