0

I have a struct which holds 3 char pointers (arbitrary length strings) and I created a dynamic array with malloc since It can have an arbitrary amount of entries. This is my struct data type:

typedef struct student{
    char* name;
    char* phoneNumber;
    char* department;
}STUDENT;

I used a function to realloc to increase the size of the struct array whenever a new entry is needed and another function to print the whole array.

The problem that i encounter is that: I am able to print the new entry inside the addNumber function but it is not always the case when I tried printing outside the addNumber function. Sometimes it works, most of the time I got a segmentation fault: 11 and one time while adding a new entry I got a 'malloc: *** error for object 0x7fc426c058f0: pointer being realloc'd was not allocated'

Here is inside of my main:

int nEntry = 0;
STUDENT* directory = malloc(nEntry * sizeof *directory);
if(directory == NULL){
    puts("Unable to allocate memory");
    exit(-1);
}
while(1){
    int choice = 0;
    scanf("%d", &choice);
    while(getchar() != '\n');
    switch(choice){
        case 1:
            printDirectory(directory, nEntry);
            break;
        case 2:
            addNumber(directory, &nEntry);
            break;
        default:
            printf("Unknown option!\n");
            break;
    }
}
return 0;

Here is my addNumber function:

void addNumber(STUDENT* Array, int* nArray){
    *nArray += 1;
    int x = *nArray - 1;
    STUDENT* tempDirectory = realloc(Array, *nArray * sizeof *Array);
    if(tempDirectory == NULL){
        puts("Unable to allocate memory");
        exit(-1);
    }
    else{
        Array = tempDirectory;
        Array[x].name = (char*)malloc(sizeof(char*));
        Array[x].phoneNumber = (char*)malloc(sizeof(char*));
        Array[x].department = (char*)malloc(sizeof(char*));

        printf("Name: ");
        scanf("%[^\n]", Array[x].name);
        while(getchar() != '\n');

        printf("Number: ");
        scanf("%[^\n]", Array[x].phoneNumber);
        while(getchar() != '\n');

        printf("Department: ");
        scanf("%[^\n]", Array[x].department);
        while(getchar() != '\n');

        for(int i = 0; i < *nArray; i++){
        printf("%s\t%s\t(%s)\n", (Array + i)->name, (Array + i)->phoneNumber, (Array + i)->department);
        }
    }
}

and Here is my print function:

void printDirectory(STUDENT* Array, int nArray){
    int i;
    for(i = 0; i < nArray; i++){
        printf("%s\t%s\t(%s)\n", (Array + i)->name, (Array + i)->phoneNumber, (Array + i)->department);
    }
}

The print function works fine if I hardcode the entry into main, the problem seems to be that whats created in the addNumber function isn't properly passed back? But then for all the arguments i am passing by reference, I am confused why I'm getting an undefined behaviour.

3
  • 1
    Remember that in C all arguments are passed by value. This means the value in the call in copied into the functions argument variable. All modifications of that variable inside the function will be lost as its life-time ends when the function returns. Either return the new "array", or research emulate pass by reference in C. Commented Nov 10, 2020 at 15:57
  • @user3121023 I need the strings to be able fit in an arbitrary length, is there a way to do malloc like that? or do i not need to use malloc in that case? since my original struct data type was supposed to be like that. Commented Nov 10, 2020 at 16:16
  • @Someprogrammerdude thanks for the reminder, I'll try to keep that in mind Commented Nov 10, 2020 at 16:16

2 Answers 2

2

For starters this code snippet

int nEntry = 0;
STUDENT* directory = malloc(nEntry * sizeof *directory);
if(directory == NULL){
    puts("Unable to allocate memory");
    exit(-1);
}

does not make a sense. The behavior of a call of malloc with the argument equal to 0 is implementation defined. That is such a call can return either NULL or some valid pointer depending on the used system.

It will be enough to write

int nEntry = 0;
STUDENT* directory = NULL;

The function addNumber deals with a copy of the value of the passed argument. So changing the copy within the function does not influence on the original argument.

You have to pass the original argument by reference. That is through pointer to pointer.

void addNumber( STUDENT **Array, int *nArray );
                        ^^^^^^^

Also the similar parameter names like Array and nArray make function code unreadable.

The function could be declared the following way

int addNumber( STUDENT **directory, int *n )
{
    STUDENT *tempDirectory = realloc( *directory, ( *n + 1 ) * sizeof **directory );
    if ( !tempDirectory ) return 0;

    ++*n;
    *directory = tempDirectory;

    // and so on

    return 1;
}

and the caller could check the return value of the function whether it ends with success or failure and issue a corresponding message if required.

These memory allocations

    Array[x].name = (char*)malloc(sizeof(char*));
    Array[x].phoneNumber = (char*)malloc(sizeof(char*));
    Array[x].department = (char*)malloc(sizeof(char*));

also do not make a sense. You need to allocate character arrays where you are going to store inputted strings in calls like this

scanf("%[^\n]", Array[x].name);

You could declare an auxiliary character array like for example

char s[100];

and read strings in this arrays. For example

    printf("Name: ");
    fgets( s, sizeof( s ), stdin );
    s[ strcspn( s, "\n" ) ] = '\0'; 

Then you could allocate a memory for the data member name knowing the length of the read string like

( *Array )[x].name = malloc( strlen( s ) + 1 );

and copy the string

strcpy( ( *Array )[x].name, s );
Sign up to request clarification or add additional context in comments.

2 Comments

thankyou very much, now i know a lot of my problems, although about the strings, what if i need to enter a name longer than 100? wouldn't it be invalid since its larger than the char array size? I need to accommodate any length of input string. Thankyou beforehand.
I found out a way by creating another function to do the job, and pass it on to addNumber. Thanks, you explanation really helps!
2

As pointed out in comments, your mallocs are the wrong size, so there are other problems with your code, but to address your specific concern about realloc:

To store the return value of realloc so it can be accessed in your main function, you're going to need to pass the address of the pointer.

So in main:

addNumber(&directory, &nEntry); // Note the ampersand

And in addNumber:

void addNumber(STUDENT** Array, int* nArray){ // Note the pointer to a pointer
...
STUDENT* tempDirectory = realloc(*Array, *nArray * sizeof *Array); // Note Array is dereferenced
...
*Array = tempDirectory;
...

If you picture directory in main as a variable stored at location 0x1 that points to memory location 0x2, in your current code you are sending 0x2 to the function. The function can read the data there, but it has no reference to 0x1 to update the directory variable in main.

The general rule for passing by reference is: If you want to update the reference in a function so that the caller can read it, you need to pass it with one more asterix than the variable has in the caller. So if you have an int in main, you would want to update an int* in the function. If you have an int*** in main, you would want to update an int****.

4 Comments

i will try this soon, thankyou. Although I have a question: in my code what I have been doing is something like passing the struct array by value is it? The funny thing is if i used the addNumber function only once, I am able to print the array outside addNumber function. I thought when i wrote Array = tempDictionary; (without an extra pointer and dereference) I have re-wired the pointer to the location(since *directory itself is a pointer). Can you help explain the concept of whats going on? thankyou beforehand
The fact that it works once is just a coincidence. realloc doesn't always move the data (sometimes it will just resize the existing mapping and return the same pointer as before). There's no reason it couldn't fail the first time, or work the first 200 times.
I have now added a bit of an explanation to my answer. I hope that helps, but if you Google "passing by reference" you can get many examples and explanations.
thanks a lot explanation concept, ill hardwire that to my brain!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.