Thread: memory allocation for flexible array member of struct

  1. #1
    Registered User
    Join Date
    Sep 2005
    Posts
    20

    memory allocation for flexible array member of struct

    I'm working on an assignment and can't seem to figure out how to properly allocate space for a flexible array member of a struct type in a second structure. The application is as follows:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define NUMCOURSES 5
    #define LINESIZE 50
    
    typedef struct studentCourse {
        char courseName[20];
        int courseCredit;
        int grade;
    } course;
    
    typedef struct studentRecord {
        char firstName[20];
        char lastName[20];
        int ID;
        course courseInfo[NUMCOURSES];
    } student;
    
    int menu()
    {
        int choice;
    
        printf("\n**********************************\n");
        printf("  1) Select input file\n");
        printf("  2) Sort records by last name\n");
        printf("  3) Search records by last name\n");
        printf("  4) Search records by GPR\n");
        printf("  5) Quit\n");
        printf("**********************************\n\n");
    
        scanf("%d", &choice);
        return choice;
    }
    
    student parseStudent(char * line)
    {
        student aStudent;
    
        sscanf(line, "%19s %19s %d %s %d %d %s %d %d %s %d %d %s %d %d %s %d %d",
               aStudent.firstName, aStudent.lastName, &aStudent.ID,
               aStudent.courseInfo[0].courseName, &aStudent.courseInfo[0].courseCredit, &aStudent.courseInfo[0].grade,
               aStudent.courseInfo[1].courseName, &aStudent.courseInfo[1].courseCredit, &aStudent.courseInfo[1].grade,
               aStudent.courseInfo[2].courseName, &aStudent.courseInfo[2].courseCredit, &aStudent.courseInfo[2].grade,
               aStudent.courseInfo[3].courseName, &aStudent.courseInfo[3].courseCredit, &aStudent.courseInfo[3].grade,
               aStudent.courseInfo[4].courseName, &aStudent.courseInfo[4].courseCredit, &aStudent.courseInfo[4].grade);
    
        return aStudent;
    }
    
    void getStudentArray(student * arr)
    {
        char filename[25];
        FILE * fp;
        int numRecords;
        int i;
        char line[LINESIZE];
    
    
        student * ps1;
    
    
        printf("Enter the input file name: ");
        scanf("%s", filename);
    
        if((fp = fopen(filename, "r")) == NULL)
        {
            printf("File input error.");
            exit(1);
        }
    
        fgets(line, sizeof(int), fp);
        numRecords = atoi(line);
        arr = (student *) malloc((sizeof(student) * numRecords) + (numRecords * NUMCOURSES * sizeof(course)));
        for( i = 0; i < numRecords; i++)
        {
            arr[i] = parseStudent(fgets(line, 2 * sizeof(student), fp));
        }
    
    }
    
    int main(void)
    {
        student * studentArray;
        int choice;
    
        do
        {
            choice = menu();
            switch(choice)
            {
                case 1:
                    printf("Data input.\n");
                    getStudentArray(studentArray);
                    break;
                case 2:
                    printf("Sort by last name.\n");
                    break;
                case 3:
                    printf("Search by last name.\n");
                    break;
                case 4:
                    printf("Search by GPR.\n");
                    break;
                case 5:
                    printf("Goodbye.\n");
                    exit(0);
            }
        } while(1);
    
        return 0;
    }
    An example of the input file input.data is

    Code:
    5
    Jeffrey Carouth 371625342 MATH152 4 3 PHYS218 4 4 ENGR111 2 4 HIST105 3 3 CPSC206 4 4
    Bob Dillan 873193526 MATH151 4 4 PHYS218 4 4 ENGR111 2 3 KINE199 1 4 POLS206 3 4
    Matthew Bennett 943721745 MATH150 4 2 ENGR111 2 2 PHYS218 4 1 HIST105 3 3 LAND111 3 1
    Jasen Petersen 624392761 MATH111 3 4 BUSI112 4 3 MGMT209 4 4 HIST109 3 3 MKTG209 4 4
    Jordan Brown 926873321 ENGL432 3 4 ENGR482 3 4 CPSC469 3 3 MKTG422 4 4 HIST322 3 4
    After it loops through the first element of the student array, I get a segmentation fault I assume because I didn't properly allocate space. I tried to separate the allocation into separate steps (i.e. allocate memory for the student "array" and then for each element of that "array" allocate space for the courseInfor array. That didn't work. Any help is appreciated.

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    Ok, there's a bunch of stuff which needs to be fixed.

    > student * ps1;
    Prune away unused stuff, it just clutters the code and makes people wonder what it's there for,

    > scanf("%s", filename);
    Do not mix scanf with other input methods such as fgets(). Their treatment of newlines in particular is radically different, and not what you want. Generally speaking, stick to fgets() for all reading of input, then use sscanf() or whatever else seems appropriate to convert that input into the form you want. For inputting filenames, this usually means stripping off the '\n' at the end (see the FAQ).

    > fgets(line, sizeof(int), fp);
    Always fgets(line, sizeof(line), fp);
    sizeof(int) is say 4, which would allow you to type in 123, but not 1234
    This is probably not what you want.

    > arr = (student *) malloc((sizeof(student) * numRecords) + (numRecords * NUMCOURSES * sizeof(course)));
    There's no need to cast malloc in C - at best it does nothing useful, at worst it hides errors.
    Also, what are you trying to achieve with this?
    Without a lot of extra effort, the number of courses is fixed, and all you have is the number of students.
    This is easily achieved with
    Code:
    /* allocate what would be student arr[numRecords]; */
    arr = malloc ( numRecords * sizeof *arr );
    > arr[i] = parseStudent(fgets(line, 2 * sizeof(student), fp));
    Again, pass to fgets the size of the line, not some random expression.
    arr[i] = parseStudent(fgets(line, sizeof(line), fp));

    parseStudent() should really check for a NULL pointer being passed to it, if you're doing that.

    > getStudentArray(studentArray);
    This does NOT update your local pointer variable with what you malloc'ed in the function.
    You need to do something like
    studentArray = getStudentArray();

    Where your function looks something like this
    Code:
    student *getStudentArray ( void ) {
        student * arr;
        // some stuff
        arr = malloc ( numRecords * sizeof *arr );
        // some more stuff
        return arr;
    }
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Nonconformist Narf's Avatar
    Join Date
    Aug 2005
    Posts
    174
    Pre-C99 code would do it like this:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    struct test {
      int a[1];
    };
    
    int main() {
      size_t struct_size = sizeof(struct test);
      size_t array_size = 5 * sizeof(int);
      struct test *p = malloc(struct_size + array_size);
    
      if (p) {
        int i;
    
        for (i = 0; i < 5; i++) p->a[i] = i + i;
        for (i = 0; i < 5; i++) printf("%d\n", p->a[i]);
      }
    
      return 0;
    }
    But since that's technically illegal, C99 made up its own variation using an incomplete array type as the last member for people who just had to have it:
    Code:
    struct test {
      int a[];
    };
    But since you already have an array of NUMCOURSES, there's no point in using a flexible array.
    Just because I don't care doesn't mean I don't understand.

  4. #4
    Registered User
    Join Date
    Sep 2005
    Posts
    20
    Quote Originally Posted by Salem
    Prune away unused stuff
    Sorry forgot that one line, it was part of a debug attempt.
    Quote Originally Posted by Salem
    > arr = (student *) malloc((sizeof(student) * numRecords) + (numRecords * NUMCOURSES * sizeof(course)));

    Also, what are you trying to achieve with this?
    Without a lot of extra effort, the number of courses is fixed, and all you have is the number of students.
    I realize that in the code I posted I had changed the struct declaration of student to incorporate an array of courses of fixed size. That was part of the same debug effort from before. The correct structure is
    Code:
    typedef struct studentRecord {
        char firstName[20];
        char lastName[20];
        int ID;
        course courseInfo[];
    } student;
    which is what is throwing me for a loop
    Quote Originally Posted by Salem
    This does NOT update your local pointer variable with what you malloc'ed in the function.
    You need to do something like
    studentArray = getStudentArray();

    Where your function looks something like this
    Code:
    student *getStudentArray ( void ) {
        student * arr;
        // some stuff
        arr = malloc ( numRecords * sizeof *arr );
        // some more stuff
        return arr;
    }
    That's kind of what I figured. But the problem I ran into was how do I allocate space inside the getStudentArray() for the courseInfo array in the current element of the student array?

    Thanks for your help. I will implement your suggestions.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Link List math
    By t014y in forum C Programming
    Replies: 17
    Last Post: 02-20-2009, 06:55 PM
  2. Screwy Linker Error - VC2005
    By Tonto in forum C++ Programming
    Replies: 5
    Last Post: 06-19-2007, 02:39 PM
  3. Replies: 28
    Last Post: 07-16-2006, 11:35 PM
  4. Request for comments
    By Prelude in forum A Brief History of Cprogramming.com
    Replies: 15
    Last Post: 01-02-2004, 10:33 AM
  5. Hi, could someone help me with arrays?
    By goodn in forum C Programming
    Replies: 20
    Last Post: 10-18-2001, 09:48 AM