Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

size of c array of struct at runtime

I need to make an array of structs. It's not until I run the program that I'll know how many structs I'll need to store in the array. The plan was to pass a pointer to a struct to an function that'll read data into it but I'm making som stupid mistake. Here's some code to illustrate what I'm trying to do:

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

typedef struct {
    int myVar;
} myStruct;

myStruct *myBigList = NULL; 

int defineMyList(myStruct **myArray) {  
    int i = 0, size = rand() % 10;
    *myArray = malloc(size * sizeof *myArray);

    for (i = 0; i < size; i++) {
        myStruct *aStruct = malloc(sizeof(myStruct));
        aStruct->myVar = i + 1;
        myArray[i] = aStruct;
    }
    return size;
}

int main() {
    int size = 0, i = 0;
    srand(time(NULL));

    size = defineMyList(&myBigList);

    for (i = 0; i < size; i++)
        printf("myBigList[%i].myVar: %i\n", i, myBigList[i].myVar);

    return EXIT_SUCCESS;
}

I adapted this code from another question that had a problem similar to mine.

My problem is that when I try to print out the array in main after I put data into it I just get this:

myBigList[0].myVar: 1
myBigList[1].myVar: 0
myBigList[2].myVar: 0
myBigList[3].myVar: 0
myBigList[4].myVar: 0

When I was expecting this:

myBigList[0].myVar: 1
myBigList[1].myVar: 2
myBigList[2].myVar: 3
myBigList[3].myVar: 4
myBigList[4].myVar: 5

I suspect I've misunderstood something with indexing and pointers. When I run the program with valgrind it reports "Invalid read of size 4 at 0x40074D: main" and "Address 0x51fc0d4 is 0 bytes after a block of size 4 alloc'd at 0x4C2AB80: malloc".

like image 910
Erik Göök Avatar asked Mar 12 '23 20:03

Erik Göök


2 Answers

You're confusing yourself with indirection. Change to:

int defineMyList(myStruct **myArray)
{   
  int i = 0, size = rand() % 10;
  *myArray = malloc(size * sizeof **myArray);  // Note two **

  for(i = 0; i < size; i++) {
    (*myArray)[i].myVar = i + 1;
  }

  return size;
}

or even better, avoid the confusing indirection entirely:

int defineMyList(myStruct **myArray)
{   
  int i = 0, size = rand() % 10;

  myStruct * new_array = malloc(size * sizeof *new_array);
  if ( !new_array ) {
      perror("couldn't allocate memory for new array");
      exit(EXIT_FAILURE);
  }

  for ( i = 0; i < size; ++i ) {
      new_array[i].myVar = i + 1;
  }   

  *myArray = new_array;
  return size;
}
like image 173
Crowman Avatar answered Mar 21 '23 04:03

Crowman


There is a little bit wrong with the code. In your code, myBigList is a pointer to an array of structures. The memory for the structures is already in the array (it stores structures instead of pointers to structures), so you don't need to allocate memory for each structure member.

Your code should have not compiled as you try to assign a pointer to a structure to an array member (which is a structure, not a pointer to a structure), but it succeeds due to another mistake. Remember that myArray is a pointer to the array, not the array itself, so instead of

myArray[i] = aStruct;

you should have written

(*myArray)[i] = aStruct;

and the compilation would have failed as it should. To fix your code, replace the entire loop with this:

  for(i = 0; i < size; i++) {
    (*myArray)[i].myVar = i + 1;
  }

and read more on pointers to understand why this problem occured.

As Paul Griffiths wrote, you also need to change the malloc() call to use the correct size. Remember, you want to allocate size array members. The size of an array member is the size of (*myArray)[0] or equally **myArray; *myArray is the size of a pointer to a structure which isn't enough.

like image 43
fuz Avatar answered Mar 21 '23 04:03

fuz