1

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)`. 
6
  • 1
    I think that you are missing some key parts of your code. For instance, what's the type of "students" in main? That's quite important because you return Student ** from getstudents(), while it is in fact returning t, which is a Student *, so the problem most likely goes that way. Commented Mar 28, 2021 at 14:00
  • This code doesn't even come close to compiling. Missing semicolon, incorrect punctuation, many undefined variables (student, Data). Turn on compiler warnings and they will tell you other things that you are doing wrong. Commented Mar 28, 2021 at 14:03
  • @RaymondChen I did remove irrelevant parts and typed in manually. Let me fix them. My bad. Commented Mar 28, 2021 at 14:04
  • 1
    The declaration of getstudents says it is returning a Student ** but it is returning a Student *. Change it to Student *getstudents(int); Commented Mar 28, 2021 at 14:11
  • @JohnnyMopp I forgot to mention that I am only allowed to modify the code inside Student **getstudents(int n) and not their parameters. Does that mean I should change my first line in Student **getstudents to a **t instead of a *t? Commented Mar 28, 2021 at 14:22

2 Answers 2

2

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;
}
Sign up to request clarification or add additional context in comments.

9 Comments

@CEPB, it's a matter of great debate, I don't like the cast (as you can see in my correction), because it may obfuscate errors, but I don't delve in it too much because it's opinion based.
I actually have a fix for the strncpy problem, but your solution is cleaner and better. Anyway, I updated the code based on your suggestions, and It still prints nothing.
@lambduh, is that right? smething else must be wrong. let me check.
The problem was that I am using %s for sex which was initialized as char. Credits for Zilog80 for pointing that out. I was surprised I missed that tiny detail. Anyway, code works as expected. Thank you!
@Zilog80, that will do, I guess now one for powershell ;) (kidding)
|
1

anastaciu answer points many troubles, but there is others :

Another problem in your code is that you use '%s' for the sex as sex is a only char. You should use %c or else the printf function will try to parse a string and will get a SEGFAULT.

I urge you too to stricly check every memory allocation. Always.

The revised code from my pov :

#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);
void free_students(Student**, int);

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

Student **getstudents(int n){
    Student **t = (Student **)malloc(sizeof(Student *)*n);
    if (t==NULL) {
       perror("Memory: can't allocate.");
       return(NULL);
    }

    /* Input: <name> <sex> <age> */
    char entry[100];
    for(int i = 0; i < n; i++){
        t[i]=NULL;
        if (fgets(entry,100,stdin) != NULL) {
            int readBytes = strlen(entry);
            char newString[3][25];

            int k,j,ctr;
            j=0; ctr=0;
            for(k=0;k<=readBytes;k++)
            {
                if(entry[k]==' '||entry[k]=='\0'||entry[k]=='\n')
                {
                    newString[ctr][j]='\0';
                    ctr++;
                    j=0;
                }
                else
                {
                    newString[ctr][j]=entry[k];
                    j++;
                }
            }

            t[i] = (Student *)malloc(sizeof(Student));
            if (t[i] == NULL) {
               perror("Memory: can't allocate.");
               return(NULL);
            }
            strncpy(t[i]->name, newString[0], strlen(newString[0]));
            t[i]->name[strlen(newString[0])] = '\0';
            t[i]->sex = *newString[1];
            t[i]->age = atoi(newString[2]);
       }
    }

    return t;
}

void free_students(Student **students, int n){
    for(int i=0; i<n; i++){
        free(students[i]);
    }
    free(students);
}

3 Comments

Oh God I totally forgot about the %s for sex. After changing it, the code works.
@anastaciu As it seems to be a student work, i guess copying strlen bytes then expressly put \0 maybe more useful for student's understanding than simply copying the string plus the zero byte, no ?
I guess, as long as it's there, it's ok.

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.