0

I'm working on some code that initialises a couple of blocks of memory with random values. I will use these blocks of memory as matrices for the weights and biases in a neural net later on but that is not the point of this question.

I have two pieces off code (Code A & Code B) which does essentially the same thing, namely initialising a block of memory with random values. Both pieces of code compile just fine, however when I execute Code B I get a segmentation fault 11 error and the code stops executing. Code B works with a structure of variable member length and I believe this creates the problem. However i don't know how to fix this.

My question: Does someone know what the problem causes and how to fix it? I would really love to have Code B working since this is convenient in the code later on. I have pasted both pieces of code below. The system where I have this code tested is macOS.

Any help would be greatly appreciated!

Code A:

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

#define DEBUG

/*
  Function prototypes
*/
void init_rand(float *p, int size);


int main(int argc, char const *argv[])
{

  int neurons [] = {10,10,10}; // use this array to define the neuralnet
  int layers = sizeof(neurons)/sizeof(int); // this calculates the amount of layers in the neuralnet (i.e. the amount of members in the array 'neurons')

  // array of pointers for the memory given by malloc
  float *pwl[layers];
  float *pbl[layers];
  float *pzl[layers];
  float *pal[layers];

  for (int i = 1; i < layers; i++) // create memory for the matrices and assign the start adress of each matrix to the pointers
  {
    pwl[i] = (float *)malloc(neurons[i]*neurons[(i-1)]*sizeof(float));
    pbl[i] = (float *)malloc(neurons[i]*sizeof(float));
    pzl[i] = (float *)malloc(neurons[i]*sizeof(float));
    pal[i] = (float *)malloc(neurons[i]*sizeof(float));
  }

  for (int i = 1; i < layers; i++) // initialize the weights and the biases with random values
  {
    init_rand(pwl[i], neurons[i]*neurons[i-1]);
    init_rand(pbl[i], neurons[i]);
  }

  return 0;
}

/*
  Random generator with a gaussian distribution. Mean = 0, Variance = 1
*/
double gaussrand()
{
    static double V1, V2, S;
    static int phase = 0;
    double X;

  if(phase == 0)
  {
        do
    {
            double U1 = (double)rand() / RAND_MAX;
            double U2 = (double)rand() / RAND_MAX;

            V1 = 2 * U1 - 1;
            V2 = 2 * U2 - 1;
            S = V1 * V1 + V2 * V2;
            } while(S >= 1 || S == 0);

        X = V1 * sqrt(-2 * log(S) / S);
    } else
        X = V2 * sqrt(-2 * log(S) / S);

    phase = 1 - phase;

    return X;
}

/*
  This function initializes a float array with random gaussian distributed numbers
*/
void init_rand(float *p, int size)
{
  for (int i = 0; i < size; i++, p++)
  {
    *p = (float)gaussrand();

#ifdef DEBUG
    printf("%d:*p = %f\n", i, *p); //
#endif
  }
}

Code B:

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

#define DEBUG

struct neuralnet
{
  int struct_size;
  float *pwl[0];
  float *pbl[0];
  float *pzl[0];
  float *pal[0];
};

/*
  Function prototypes
*/
void init_rand(float *p, int size);
struct neuralnet* create_neuralnet(struct neuralnet* pnet, int layers);


int main(int argc, char const *argv[])
{

  int neurons [] = {10,10,10}; // use this array to define the neuralnet
  int layers = sizeof(neurons)/sizeof(int); // this calculates the amount of layers in the neuralnet

  struct neuralnet *pnet; // create a struct neuralnet pointer

  pnet = create_neuralnet(pnet, layers); // this function will create a neuralnet structure with variable size members

  for (int i = 1; i < layers; i++) // create memory for the matrices and assign the start adress of each matrix to the pointers
  {
    pnet->pwl[i] = (float *)malloc(neurons[i]*neurons[(i-1)]*sizeof(float));
    pnet->pbl[i] = (float *)malloc(neurons[i]*sizeof(float));
    pnet->pzl[i] = (float *)malloc(neurons[i]*sizeof(float));
    pnet->pal[i] = (float *)malloc(neurons[i]*sizeof(float));
  }

  for (int i = 1; i < layers; i++) // initialize the weights and the biases with random values
  {
    init_rand(pnet->pwl[i], neurons[i]*neurons[i-1]);
    init_rand(pnet->pbl[i], neurons[i]);
  }

  return 0;
}

/*
  Random generator with a gaussian distribution. Mean = 0, Variance = 1
*/
double gaussrand()
{
    static double V1, V2, S;
    static int phase = 0;
    double X;

    if(phase == 0)
  {
        do
    {
            double U1 = (double)rand() / RAND_MAX;
            double U2 = (double)rand() / RAND_MAX;

            V1 = 2 * U1 - 1;
            V2 = 2 * U2 - 1;
            S = V1 * V1 + V2 * V2;
            } while(S >= 1 || S == 0);

        X = V1 * sqrt(-2 * log(S) / S);
    } else
        X = V2 * sqrt(-2 * log(S) / S);

    phase = 1 - phase;

    return X;
}

/*
  This function initializes a float array with random gaussian distributed numbers
*/
void init_rand(float *p, int size)
{
  for (int i = 0; i < size; i++, p++)
  {
    *p = (float)gaussrand();

#ifdef DEBUG
    printf("%d:*p = %f\n", i, *p); //
#endif
  }
}

/*
    This function creates the structure with members of variable length
*/
struct neuralnet* create_neuralnet(struct neuralnet *pnet, int layers)
{
  pnet = (struct neuralnet *)malloc(sizeof(*pnet) + sizeof(float *) * layers * 4);
  return pnet;
}
Mauricio Paulusma
  • 43
  • 1
  • 1
  • 10
  • [Do I cast the result of malloc?](https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc) **No.** – Deduplicator Aug 29 '18 at 21:31
  • Are you sure `neuralnet` should contain 4 arrays of length **zero** containing pointers to `float`? One could be part of the struct hack (which was replaced by flexible array members in 1999, when dynamically-sized automatic arrays which you also used were introduced), but four... – Deduplicator Aug 29 '18 at 21:33
  • 1
    Even then, a structure can have at-most *one* flexible array member, and it must be at the *end* of the structure. This is one of those times when one-out-of-two isn't good enough. From what I see, I don' understand why you're not just using simple pointers and writing serialize mechanics. – WhozCraig Aug 29 '18 at 21:40
  • @Deduplicator Aah thanks for the cast tip, didn't knew it was unnecessary and even a risk for potential errors. With regards to the struct hack, I really do mean to have 4 arrays. But as you and WhozCraig point out you can only use one variable member array in a structure, so more is absolutely not possible? In that case four would not be possible and I have to stick with Code A (just arrays with variable length). – Mauricio Paulusma Aug 29 '18 at 21:47
  • How do you expect multiple flexible members to work? How is it supposed to know where `pwl` ends and `pbl` begins if you haven't declared a length? – Barmar Aug 29 '18 at 21:49
  • The reason the flexible array member has to be at the end is because it depends on the fact that C doesn't prevent you from accessing outside the bounds of the struct, so you can allocate more memory with `malloc()` than is needed for the struct, and use the extra space for the last array. – Barmar Aug 29 '18 at 21:51
  • Note `pwl[0]` and `pbl[0]` are never assigned. – chux - Reinstate Monica Aug 29 '18 at 21:52
  • `float *pwl[0];` is not a valid member declaration. What compiler/options are you using? – chux - Reinstate Monica Aug 29 '18 at 21:53
  • @Barmar, I'm not sure actually. I thought maybe the OS fixes something in the memory at run time and keeps track of everything. But I actually have no idea. I'm still a student, so I'm not an expert as you may have noticed ;). However it works with just the arrays of variable length (without the struct) so why would it not be possible with a struct in it? – Mauricio Paulusma Aug 29 '18 at 21:54
  • @chux, gcc at macOS – Mauricio Paulusma Aug 29 '18 at 21:55
  • It only does what you tell it to do. If you tell it the size of the array is 0, that's how much memory it uses for it. – Barmar Aug 29 '18 at 21:55
  • With members `float *pwl[0]; float *pbl[0];`, I'd expect that `pwl` and `pbl` overlay each other. Do not use an array size of 0`. – chux - Reinstate Monica Aug 29 '18 at 21:57
  • @Barmar Aah I get it now. So the struct hack works because the last member just uses the extra memory allocated with malloc. Now that I'm using multiple pointer arrays with size 0 as my "last member" they probably overlap each other and will thus use the same extra memory from malloc. – Mauricio Paulusma Aug 29 '18 at 22:13
  • If you really want to allocate those arrays along with the struct, there are ways to do it but accessing them will become more complicated (flexible array member might work for one, but not four of them). It's probably easier just to use `float **` and allocate the arrays separately (inside `create_neuralnet()`). – Dmitri Aug 29 '18 at 22:13
  • Arrays of size 0 are always illegal in C - an array does have to have at least one member. It was how you did the struct hack *before* in *some compilers*. Now you're supposed to use a flexible array member, i.e. empty brackets `[]`. – Antti Haapala -- Слава Україні Aug 29 '18 at 23:17

0 Answers0