0

I'm trying to create an array of a structure in an external function "add", and print it's fields, but when I get back to the main function "arr" it is still NULL. I'm confused because I've been creating arrays in external functions many times and it worked.. probably this time the dynamic memory allocation is messing the things up. Can I please get an advice on this matter? Thanks!

typedef struct {
    char* id;
    char gender;
    char *name;
}Member;

void add(Member arr[], int size);
void print(Member arr[], int *size);

int main()
{
    char temp[100];
    int size=0;
    Member *arr = NULL;
    Member *data = (Member*)malloc(sizeof(Member));
    //scan fields
    gets(temp);
    data->id = (char*)malloc((strlen(temp) + 1) * sizeof(char));
    strcpy(data->id, temp);
    gets(temp);
    data->gender = temp;
    gets(temp);
    data->name = (char*)malloc((strlen(temp) + 1) * sizeof(char));
    strcpy(data->name, temp);

    add(data, &arr, &size);
    print(arr, &size);
    return 0;
}

void add(Member *data, Member arr[], int *size) 
{
    arr = (Member*)realloc(arr, (*size + 1) * sizeof(Member));
    arr[*size] = *data;
}

void print(Member arr[], int *size)
{
    for (int i = 0;i < *size;i++)
    {
        puts(arr->id);
        puts(arr->gender);
        puts(arr->name);
    }
}
  • 1
    Hint: you're not actually passing an array, but only a pointer to the address of its 0th element, and like any other pass-by-value, the function gets its own local copy of that pointer. – underscore_d Dec 31 '18 at 18:10
  • 2
    Never ***ever*** use the `gets` function. It's [dangerous](https://stackoverflow.com/questions/1694036/why-is-the-gets-function-so-dangerous-that-it-should-not-be-used), and has therefore been removed from the C specification. Use e.g. [`fgets`](https://en.cppreference.com/w/c/io/fgets) instead (but be aware of the differences). – Some programmer dude Dec 31 '18 at 18:11
  • 1
    There are also many other problems in your code, like for example the assignment `data->gender = temp`, which should generate a compiler warning. – Some programmer dude Dec 31 '18 at 18:12
  • 2
    There is a mismatch between the caller and the callee. The caller correctly passes the address of the pointer, but the callee takes the pointer (and should take its address so it can modify the pointer). – Weather Vane Dec 31 '18 at 18:13

1 Answers1

0

Imagine code like this:

#include <stdio.h>

void f(int i){
  i++;
}

int main(){
  int i = 3;
  f(3);
  printf("%d\n", i);
}

We all know that f() incremented its local copy of i, not the variable that was passed into f() to initially set that value. With that having been said, let's take another look at your add():

void add(Member *data, Member arr[], int *size) 
{
    arr = (Member*)realloc(arr, (*size + 1) * sizeof(Member));
    arr[*size] = *data;
}

When arr is passed into the function, it contains a memory address of the current arr, which starts as NULL. But just like when we change the local value of i in f() above, setting arr to a new value within add() only changes the local value; it does not change main()'s arr.

We also know that if we pass a function an address of data we want it to change, the function can then change the data at that address and the data at that address will reflect the change in the calling function:

#include <stdio.h>

void f(int * i){
  *i = *i + 1;
}

int main(){
  int i = 3;
  f(&i);
  printf("%d\n", i);
}

The same logic applies ( though it gets more confusing) when you want to change a pointer's value; send that pointer's address! Let's start with a very simple case:

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

void f(int** i){
  *i = (int*)malloc(sizeof(int));
  **i = 99;
}

int main(){
  int *i = NULL;
  f(&i);
  printf("%d\n", *i);
}

Here we create a pointer to an int in main, and initialize it to NULL. Then we send the address of that pointer (that is, the address we stored the NULL) to f(), which (like in your program) allocates some memory and puts the address of the newly allocated pointer _at the address of main's i. Now, the data stored at &i has changed, and dereferencing i from main() will dereference the newly allocated address.

In your code, just as in mine, you'll have to change the way you're passing arr to add() as well as how you interact with it - an exercise you'll get the most out of thinking through yourself. But in short, something like this should get you started:

  1. pass add arr's address, not the address it stores.
  2. Store new address of reallocated memory back to the same address, that is, &arr
  3. make sure to update add() to dereference the pointer to a pointer twice to set the member at the address stored at the address &arr.
erik258
  • 14,701
  • 2
  • 25
  • 31
  • Thank you Dan for pointing out the issue. Can you please explain how to fix this? should I send a pointer to the array to change it's value globally? – Boris Erlich Dec 31 '18 at 18:21
  • in short yes. I'm adding some detail for you. – erik258 Dec 31 '18 at 18:31
  • `int i = f(3);` is a bit nonsensical in your first example -- given you declare `void f(int * i)`... (the compiler won't let you do that) – David C. Rankin Dec 31 '18 at 18:56
  • yeah, that first example didn't actually get run through a compiler. Fixed. – erik258 Dec 31 '18 at 19:01
  • 1
    I knew what you meant, but poor Boris was probably a bit confused... Sometimes it helps to step back and start with "What's a pointer?" (e.g., *A pointer is simply a normal variable that holds the address of something else as its value. In other words, a pointer points to the address where something else can be found.*) and then going forward with why you need to pass the address of the original pointer to insure the function doesn't simply receive a copy-of-the-pointer (with a very different address of its own -- pass-by-value) – David C. Rankin Dec 31 '18 at 19:05
  • Thank you DavidC.Rankin and Dan Farrell, I appreciate your help. – Boris Erlich Jan 01 '19 at 19:45