Thread: Managing a dynamic array of structures

  1. #1
    Registered User
    Join Date
    Oct 2012
    Posts
    42

    Managing a dynamic array of structures

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>
    #include <string.h>
    
    
    char *vector(char friends[], int *length) {
        char *temp;
       temp = calloc(*length+10, sizeof(char));    // Make new dynamic array.
       memcpy(temp, friends, *length);                // Copy data to new array.
       *length+=10;                                        // Increment & record new length.
       free(friends);                                        // Free old array.
       return temp;                                        // Return new array.
    }
    
    int main(void) {
    
        struct list {
            char IDP[3];
            char first[42];
            char last[42];
            char phone[42];
        } *friends;
    
        char friendArray;
        int  i, j, k, l, count;
        int base = 10;
        char IDP[3], first[42], last[42], phone[42];
        FILE *data;
        
    //Determine size of data files
        data = fopen("./hw4.data", "r");
        count = 0;
        while (1) {
            fscanf(data, "%s %s %s %s", IDP, first, last, phone);
            if (feof(data)) break;
            count++;
            
        }
        
    //Create dynamic array of structures
        friends = (struct list *)calloc(base, sizeof(struct list));
    
    // Populate dynamic array, deleting and printing where necessary
       rewind(data);
       j = 0;
       printf("%d\n", count);
       for (i = 0; i < count; i++) {
    
           if (friends[i].IDP == "I") { //Add line to dynamic array
               fscanf(data, "%s %s %s %s", friends[i].IDP, friends[i].first, friends[i].last, friends[i].phone);
           }
           else if (friends[i].IDP == "D") { //Delete line from dynamic array
               //Find and delete friend
           }
           else if (friends[i].IDP == "P") { //Print entire array
               for (k = 0; k < count; k++) {
                   printf("%s %s %s %s\n", friends[i].IDP, friends[i].first, friends[i].last, friends[i].phone);
                } 
           }
            /*j++;
          if (j == 10) {
             j = 0; 
             friends = vector(friends, &base);        // Adjust vector length
          }*/
       }
        fclose(data);
    
        free(friends);
        return 0;
    }
    Data File:
    Code:
    I Steve Marsh 701-222-3333
    I Dave Marsh 218-444-6666
    I Radell Marsh 701-235-8133
    I Brenda Marsh 701-277-5050
    I Kia Marsh 701-275-1234
    I Triston Marsh 701-275-0987
    P
    The issue I'm having is it doesn't seem to be printing the structure array when it reads in the last line. My program compiles and runs; it just doesn't print the array when it gets to the "P" in the data file. Is the comparison
    Code:
    friends[i].IDP == "I"
    correct or am I doing something wrong?

    Also, I'm having an issue importing the friends array into the array re-size function (vector, hence why that section is commented out) but I want to solve this problem first.

  2. #2
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    string comparisons are not done using the == operator (the == operator does another type of comparison when supplied with strings). Look up the strcmp() function, which is declared in the standard header <string.h>.

    With the "friends" array you have several problems

    1) calloc() does not copy data from a file to the array it allocates. You need to write code to do that. You have not.

    2) Presumably you intend "base" to keep track of the number of elements in "friends". If so, you need to ensure it does. At present, base is initialised to a value of 10, and friends is a pointer to a single struct list. 10 is not equal to 1.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  3. #3
    Registered User
    Join Date
    Oct 2012
    Posts
    42
    I looked up strcmp, and I tried
    Code:
     if (strcmp(friends[i].IDP, "I") == 0)
    but it still isn't going through the for loop.

    I rewrote my code to be less confusing and to focus on the current problem.
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>
    #include <string.h>
    
    int main(void) {
    int   i, j;
    int   count;
    char  text1[42], text2[42], text3[42], text4[42];
    char  compare;
    
    struct list {
      char IDP [42];
      char first[42];
      char last[42];
      char phone[42];
    } *friends;
    
    FILE *data;
    
        // Determine size of data file.
        data = fopen("./hw4.data", "r");
        count = 0;
        while (1) {
            fscanf(data, "%s %s %s %s", text1, text2, text3, text4);
            if (feof(data)) break;
            count++;
        }
    
        // Create dynamic array of structures.
        friends = (struct list *)calloc(count, sizeof(struct list));
    
        // Populate dynamic array.
        rewind(data);
        for (i = 0; i < count; i++) {
            if (strcmp(friends[i].IDP, "I") == 0) { //add entry to array
                printf("I found!"); //for testing
                fscanf(data, "%s %s %s %s", friends[i].IDP, friends[i].first, friends[i].last, friends[i].phone);
            }
            else if (strcmp(friends[i].IDP, "P") == 0) { //print entire array
                printf("P found!"); //for testing
                for (j = 0; j < i; j++) {
                      printf("%s %s %s\n", friends[j].first, friends[j].last, friends[j].phone);
        }
    
            }
            else if (strcmp(friends[i].IDP, "D") == 0) { //delete entry from array
                printf("D found!"); //for testing
            }
        }
        fclose(data);
    
        // Free dynamic arrays.
        free(friends);
        return 0;
    }
    Last edited by Paul Omans; 06-08-2013 at 07:15 PM.

  4. #4
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    You haven't addressed the other problems I referred to with your "friends" array.

    First time through the loop if the first time your code even touches friends[0].IDP (other than the calloc() call, which sets all bytes in the struct to zero). The loop will only do something if friends[0].IDP compares equal to "I", "P" or "D".

    Your code is reading text1, text2, etc in a loop. There is no magic that makes data read to text1 magically appear in friends[0].IDP.

    The fact that you're only allocating one structure, and trying to manipulate ten gives undefined behaviour. How often do you expect to be able to get ten apples out of a bag containing one?
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  5. #5
    Registered User
    Join Date
    Oct 2012
    Posts
    42
    I get it, I have to put the item in the array before I can compare anything to it. Sorry for overlooking that, it seems really simple when I look at it that way.

    So,
    Code:
        // Populate dynamic array.
        rewind(data);
        for (i = 0; i < count; i++) {
            fscanf(data, "%s %s %s %s", friends[i].IDP, friends[i].first, friends[i].last, friends[i].phone);
            if (strcmp(friends[i].IDP, "I") == 0) {
                printf("Friend Added!\n");
            }
            else if (strcmp(friends[i].IDP, "P") == 0) {
                printf("Printing List!\n");
                for (j = 0; j < i; j++) {
                    printf("%s %s %s\n", friends[j].first, friends[j].last, friends[j].phone);
                } 
            }
            else if (strcmp(friends[i].IDP, "D") == 0) {
                printf("Deleting Friend!\n");
            }
        }
    Seems to work. This way though when I delete a friend I'll just have to delete both the entry that was just put in and the matching entry.

  6. #6
    Registered User
    Join Date
    Oct 2012
    Posts
    42
    Alright, I've made some more progress, but I'm stuck again.
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>
    #include <string.h>
    
    int main(void) {
    
    struct list{
        char IDP [10];
        char first[42];
        char last[42];
        char phone[42];
    } *friends, *temp;
    
    int   i, j;
    int   k = 0;
    int   length;
    int   count;
    int   base = 10;
    char  text1[10], text2[42], text3[42], text4[42];
    char  compare;
    
    FILE *data;
    
        // Determine size of data file.
        data = fopen("./hw4.data", "r");
        count = 0;
        while (1) {
            fscanf(data, "%s %s %s %s", text1, text2, text3, text4);
            if (feof(data)) break;
            count++;
        }
    
        // Create dynamic array of structures.
        friends = (struct list *)calloc(base, sizeof(struct list));
    
        // Populate dynamic array, resizing when needed.
        rewind(data);
        for (i = 0; i < count; i++) {
            fscanf(data, "%s %s %s %s", friends[i].IDP, friends[i].first, friends[i].last, friends[i].phone);
            if (strcmp(friends[i].IDP, "I") == 0) {
                printf("Friend Added!\n");
                k++;
                // Resize the array every 3 additions.
                   if (k = 3) {
                    printf("Resizing list...\n");
                    temp = (struct list *)calloc(base+3, sizeof(struct list));            // Make temp dynamic array.
                    memcpy(temp, friends, base);                                        // Copy friends data to temp array.
                    free(friends);                                                        // Free friends array.
                    friends = (struct list *)calloc(base+3, sizeof(struct list));        // Remake friends dynamic array.
                    memcpy(friends, temp, base);                                        // Copy resized temp array back into friends array.
                    free(temp);                                                            // Free temp array.
                    k = 0;                                                                // Reset addition counter.
                    base+=3;                                                            // Increment & record new length.                        
                   }
            }
            else if (strcmp(friends[i].IDP, "P") == 0) {
                printf("Printing List!\n");
                // Prints list thus far.
                for (j = 0; j < i; j++) {
                    printf("%s %s %s\n", friends[j].first, friends[j].last, friends[j].phone);
                } 
            }
            else if (strcmp(friends[i].IDP, "D") == 0) {
                printf("Deleting Friend!\n");
                // Find and delete friend.
            }
    
        }
        fclose(data);
        
        // Free dynamic array.
        free(friends);
        return 0;
    }
    For some reason, it's going through the if statement in line 45 for every single iteration of the line 39 for loop. Also, I can't seem to figure out the logic. I'm basically supposed to do what realloc does but without realloc (using memcpy and multiple callocs instead). The logic in lines 45-55 looks right to me, but it must be wrong because it prints out a blank list of friends, so the array's contents must be getting lost in the mix somewhere.

  7. #7
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Well there is one mistake that I can see and one optimization that you can do.

    The mistake is that you are calling memcpy with the wrong size argument. You're thinking that you want to manipulate base objects, but memcpy works in bytes. So you have to do a multiplication to convert the number of base objects into the number of bytes.

    The optimization that you can do is basically not to throw away the temp memory. After copying to temp, free your pointer, then the temp memory can be assigned to your pointer as the new memory. There is little point in allocating again.
    Last edited by whiteflags; 06-09-2013 at 12:23 AM.

  8. #8
    Registered User
    Join Date
    May 2012
    Posts
    1,066
    Code:
                   if (k = 3) {
    What does the = operator do?

    Bye, Andreas

  9. #9
    Registered User
    Join Date
    Oct 2012
    Posts
    42
    Quote Originally Posted by AndiPersti View Post
    Code:
                   if (k = 3) {
    What does the = operator do?

    Bye, Andreas
    Ah, sorry, that's supposed to be ==. I guess I overlooked that.

    whiteflags, I think I get the error, I just changed the memcpy statements to
    Code:
    memcpy(temp, friends, sizeof(temp));
    But I still don't get the optimization you stated. Are you saying not to free(temp), but after I memcpy friends over to temp, free(friends), and then *temp = *friends?

  10. #10
    Registered User
    Join Date
    May 2012
    Posts
    1,066
    Quote Originally Posted by Paul Omans View Post
    I just changed the memcpy statements to
    Code:
    memcpy(temp, friends, sizeof(temp));
    Well, "temp" is a pointer thus sizeof(temp) will probably be 4 or 8 (depending on your system), i.e. not enough. You need to calculate the total amount of bytes you want to copy.
    How many bytes is one struct object? How many objects do you want to copy?

    Bye, Andreas

  11. #11
    Registered User
    Join Date
    Oct 2012
    Posts
    42
    Ah, I get it. What I needed to do is get the sizeof(struct list) and multiply that by length to get the new desired size. So:
    Code:
    memcpy(temp, friends, sizeof(struct list)*length);
    seems to work.

    Here is the updated code with the working expansion function:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>
    #include <string.h>
    
    int main(void) {
    
    struct list{
        char IDP [10];
        char first[42];
        char last[42];
        char phone[42];
    } *friends, *temp;
    
    int   i, j;
    int   k = 0;
    int   count;
    int   base = 10;
    int   length = 10;
    char  text1[10], text2[42], text3[42], text4[42];
    char  compare;
    
    FILE *data;
    
        // Determine size of data file.
        data = fopen("./hw4.data", "r");
        count = 0;
        while (1) {
            fscanf(data, "%s %s %s %s", text1, text2, text3, text4);
            count++;
            if (feof(data)) break;
        }
    
        // Create dynamic array of structures.
        friends = (struct list *)calloc(base, sizeof(struct list));
    
        // Populate dynamic array, resizing when needed.
        rewind(data);
        for (i = 0; i < count; i++) {
            fscanf(data, "%s %s %s %s", friends[i].IDP, friends[i].first, friends[i].last, friends[i].phone);
            if (strcmp(friends[i].IDP, "I") == 0) {
                printf("Friend Added!\n");
                k++;
                // Resize the array every 3 additions.
                   if (k == 3) {
                    printf("Resizing list...\n");
                    temp = (struct list *)calloc(length+3, sizeof(struct list));        // Make temp dynamic array.
                    memcpy(temp, friends, sizeof(struct list)*length);                    // Copy friends data to temp array.
                    free(friends);                                                        // Free friends array.
                    friends = (struct list *)calloc(length+3, sizeof(struct list));        // Remake friends dynamic array.
                     memcpy(friends, temp, sizeof(struct list)*length);                     // Copy resized temp array back into friends array.
                    k = 0;                                                                // Reset addition counter.
                     length+=3;                                                             // Increment & record new length.                         
                   }
            }
            else if (strcmp(friends[i].IDP, "P") == 0) {
                printf("Printing List!\n");
                // Prints list thus far.
                for (j = 0; j < i; j++) {
                    printf("%s %s %s\n", friends[j].first, friends[j].last, friends[j].phone);
                } 
            }
            else if (strcmp(friends[i].IDP, "D") == 0) {
                printf("Deleting Friend!\n");
                // Find and delete friend.
            }
    
        }
        fclose(data);
        
        // Free dynamic array.
        free(friends);
        return 0;
    }
    Here is the updated data file I'm using, for the below question.

    Code:
    I Ron Marsh 701-222-3333
    I Dave Marsh 218-444-6666
    I Radell Marsh 701-235-8133
    I Brenda Marsh 701-277-5050
    I Kyle Marsh 701-224-2452
    I Kia Marsh 701-275-1234
    I Steve Marsh 701-275-0987
    I Paul Marsh 701-276-0987
    I Sheila Marsh 701-277-0987
    I Rick Marsh 701-278-0987
    I Gregory Marsh 701-279-0987
    I Keith Marsh 701-275-0987
    I Shirley Marsh 701-275-0987
    I Bobby Marsh 701-275-0987
    P
    I Junior Marsh 701-275-0987
    I Peter Marsh 701-275-0987
    I Hanson Marsh 701-275-0987
    I Frank Marsh 701-275-0987
    I Seth Marsh 701-275-0987
    I David Marsh 701-275-0987
    The problem with my code now is that the code exits the line 39 for loop after executing the line 56 if else statement. For example, this is what my output is:
    Code:
    C:\cygwin\home\Paul>a.exe
    Friend Added!
    Friend Added!
    Friend Added!
    Resizing list...
    Friend Added!
    Friend Added!
    Friend Added!
    Resizing list...
    Friend Added!
    Friend Added!
    Friend Added!
    Resizing list...
    Friend Added!
    Friend Added!
    Friend Added!
    Resizing list...
    Friend Added!
    Friend Added!
    Printing List!
    Ron Marsh 701-222-3333
    Dave Marsh 218-444-6666
    Radell Marsh 701-235-8133
    Brenda Marsh 701-277-5050
    Kyle Marsh 701-224-2452
    Kia Marsh 701-275-1234
    Steve Marsh 701-275-0987
    Paul Marsh 701-276-0987
    Sheila Marsh 701-277-0987
    Rick Marsh 701-278-0987
    Gregory Marsh 701-279-0987
    Keith Marsh 701-275-0987
    Shirley Marsh 701-275-0987
    Bobby Marsh 701-275-0987
    With the input file I provide, it should print the list and then continue to add to it, but it's quitting as soon as it hits the first P.
    Last edited by Paul Omans; 06-09-2013 at 01:53 PM. Reason: Moved count++ up a line

  12. #12
    Registered User
    Join Date
    Aug 2005
    Location
    Austria
    Posts
    1,990
    Code:
    fscanf(data, "%s %s %s %s", friends[i].IDP, friends[i].first, friends[i].last, friends[i].phone);
    When you read a line starting with "P" then the fields shift and the next id will be read into first (because no data follows on that line).
    you have to use fgets to read a whole line and parse using sscanf

    BTW counting the lines has the same problem

    Kurt
    Last edited by ZuK; 06-09-2013 at 02:10 PM.

  13. #13
    Registered User
    Join Date
    Oct 2012
    Posts
    42
    Alright, I changed the for loop to a while loop that reads the file line by line:
    Code:
    while (fgets(test, sizeof(test), data) != NULL) {
            sscanf(test, "%s", text1);
            if (strcmp(text1, "I") == 0) {
                printf("Friend Added!\n");
                fscanf(data, "%s %s %s %s", friends[i].IDP, friends[i].first, friends[i].last, friends[i].phone);
                k++;
                i++;
                // Resize the array every 3 additions.
                   if (k == 3) {
                    printf("Resizing list...\n");
                    temp = (struct list *)calloc(length+3, sizeof(struct list));        // Make temp dynamic array.
                    memcpy(temp, friends, sizeof(struct list)*length);                    // Copy friends data to temp array.
                    free(friends);                                                        // Free friends array.
                    friends = (struct list *)calloc(length+3, sizeof(struct list));        // Remake friends dynamic array.
                    memcpy(friends, temp, sizeof(struct list)*length);                    // Copy resized temp array back into friends array.
                    k = 0;                                                                // Reset addition counter.
                    length+=3;                                                            // Increment & record new length.                        
                   }
            }
            else if (strcmp(text1, "P") == 0) {
                printf("Printing List!\n");
                // Prints list thus far.
                for (j = 0; j < i; j++) {
                    printf("%s %s %s\n", friends[j].first, friends[j].last, friends[j].phone);
                }
            }
            else if (strcmp(text1, "D") == 0) {
                printf("Deleting Friend!\n");
                // Find and delete friend.
            }
    
        }
    But I don't think sscanf is working properly, as it doesn't seem to make a distinction between lines with a P and lines with an I.

  14. #14
    Registered User
    Join Date
    Aug 2005
    Location
    Austria
    Posts
    1,990
    You must not mix fgets() and fscanf() calls they both read from the same file.
    Kurt

  15. #15
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    You also didn't do the optimization I talked about! Here's an example with my tinkering. It doesn't use structures, but the idea is applicable nonetheless.
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    size_t JckResize(int **mem, size_t oldSize, size_t growthFactor)
    {
        size_t newSize = oldSize + growthFactor;
        void *temp = malloc(sizeof(**mem) * newSize);
        if (!temp) {
            printf("Memory allocation failure\n");
            return oldSize;
        }
        memcpy(temp, *mem, oldSize * sizeof(**mem));
        free(*mem);
        *mem = temp;
        return newSize;
    }
    
    int main()
    {
        size_t arrayLen = 10;
        int *array = malloc(sizeof(*array) * arrayLen);
        if (!array) {
            printf("Couldn\'t allocate initial memory\n");
            return 0;
        }
    
        for (size_t i = 0; i < 30 /* big enuf to get the point */; i++) {
            if (i == arrayLen) {
                size_t result = JckResize(&array, arrayLen, 10);
                if (result > arrayLen) {
                    arrayLen = result;
                }
                else {
                    free(array);
                    return 0;
                }
            }
    
            array[i] = i+1;
            // The above could be changed to store input from a file, etc.
        }
    
        for (size_t i = 0; i < arrayLen; i += 3) {
            printf("%2d  %2d  %2d\n", array[i], array[i+1], array[i+2]);
        }
    
        free(array);
        array = 0;
    }
    See how easy that was?
    Last edited by whiteflags; 06-09-2013 at 03:34 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Dynamic Array Of Structures - Often Changing - HOW?
    By roadhogbob in forum C Programming
    Replies: 5
    Last Post: 01-04-2012, 04:17 PM
  2. Replies: 10
    Last Post: 12-03-2011, 02:26 PM
  3. Replies: 2
    Last Post: 07-11-2008, 07:39 AM
  4. dynamic array of structures
    By cjam in forum C++ Programming
    Replies: 2
    Last Post: 03-12-2004, 03:18 PM
  5. Managing a heap using an array.
    By rahuls in forum C Programming
    Replies: 3
    Last Post: 03-20-2003, 02:49 PM