Search code examples
arrayscpointersstructdynamic-memory-allocation

How to return pointer to array of struct in C


I created a struct of student inside the function using malloc and populated it with data. I want to return the address of the array of struct back to main and print it line by line, but with my implementation, it is not printing. I debugged my code and indeed, it is able to return the address of my array to main. I don't know why it's not printing though. Any thoughts?

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

typedef struct student Student;

struct student
{
    char name[100];
    int age;
    char sex;
};

Student **getstudents(int n)
{
    Student **t = malloc(sizeof *t * n); // memory for the array of pointers
    for (int i = 0; i < n; i++) // memory for each individual pointer
    {
        t[i] = malloc(sizeof **t);
    }
    /* Data is inputted by user with form <student> <sex> <age>, then get mapped to the struct *t */

    return t; /* Pointer to an array of pointers */
}


int main()
{
    Student **students;
    int n;
    scanf("%d\n", &n);
    students = getstudents(n);
    for (int i = 0; i < n; i++)
    {
        printf("Name: %s, Sex: %s, Age: %d\n", students[i]->name, students[i]->sex, students[i]->age);
    }

    for (int i = 0; i < n; i++)
    {
        free(students[i]);
    }
    free(students);
    return 0;
}


I am only allowed to modify the code in `Student **getstudents(int n)`. 

Solution

  • In the line:

    Student *t = (char*)malloc(sizeof(Student)*n); 
    

    You are allocating memory for one pointer, if you want to return a pointer to pointer, you need to allocate memory accordingly:

    Student **t = malloc(sizeof *t * n); // memory for the array of pointers
    
    for(int i = 0; i < n; i++){ // memory for each individual pointer
        t[i] = malloc(sizeof **t);
    }
    

    To later free the pointers you also need to free each individual pointer previously allocated:

    for(int i = 0; i < n; i++){ // memory for each individual pointer
        free(students[i]);
    }
    free(students);
    

    Note that the specifier for a single character is %c, the printf will need to be corrected:

    printf("Name: %s, Sex: %c, Age: %d\n", students[i]->name, students[i]->sex, students[i]->age);
    //                     ^^
    

    Another thing I would change is in strncpy instead of null terminating the string later I would let the function do it:

    // one more byte and strncpy terminates the string for you
    strncpy(t[i]->name, data[0], strlen(data[0]) + 1); 
    //      ^^^^
    // already corrected for the new pointer
    

    Having corrected the issues here is a possible alternative you can use to parse all the elements in the struct with sscanf from entry in one go, if you want to:

    Student **getstudents(int n)
    {
        Student **t = malloc(sizeof *t * n); // memory for the array of pointers
    
        if (t == NULL)
        {
            perror("malloc");
            exit(EXIT_FAILURE);
        }
    
        for (int i = 0; i < n; i++) // memory for each individual pointer
        {
            t[i] = malloc(sizeof **t);
            if (t[i] == NULL)
            {
                perror("malloc");
                exit(EXIT_FAILURE);
            }
        }
    
        for (int i = 0; i < n; i++)
        {
            char entry[100];
            if (fgets(entry, sizeof entry, stdin))
            {
                if (sscanf(entry, "%25s %c %d", t[i]->name, &t[i]->sex, &t[i]->age) != 3)
                {
                    // deal with bad input
                }
            }
        }
        return t;
    }