cpass-by-referencedynamic-memory-allocationdouble-pointer

C, double pointers and array of structs


trying to relearn C after 30+ years...specifically double pointers. I currently use Java, Node and AWS services now for the last 25+ years. I need to allocate memory using calloc() as an array of structs, not an array of pointers to structs. The issue, as I see it, is how do I dereference the double pointer to the calloc() memory array outside of the loadLibrary() in listTracks()???

Platform : Windows 10, cygwin, gcc

Here is the code :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <time.h>
#include <stdbool.h>
#include <string.h>
#define FALSE 0
#define TRUE 1

#define MAX_TITLE_LENGTH 100
#define MAX_ARTIST_LENGTH 100

typedef struct {
    char title[MAX_TITLE_LENGTH];
    char artist[MAX_ARTIST_LENGTH];
    int year;
} Track;

int displayMenu();
int loadLibrary(const char* filename, Track** library);
void listTracks(Track* library, int count);
void copyStruct(Track **library, Track trackTemp[], int numTracks);

int main() {

        //The pointer for what will be an array of tracks
        Track* trackLibrary;
        char file[] = "tracks_test.txt";
        char *ptrFile = file;
        int numTracks = 0;

        numTracks = loadLibrary(ptrFile, &trackLibrary);
        listTracks(trackLibrary, numTracks);

    return 0;

}

int loadLibrary(const char* filename, Track** library) {
    static const int numTracks = 3;
    Track trackTemp[numTracks];
    Track *ptrMem = calloc(numTracks, sizeof(Track));

    // MOVE : Move the pointer to the memory...
    *library = ptrMem;

    // SIMULATE : Loading from text file...
    trackTemp[0] = (Track) { "Dancing Queen", "ABBA", 1976 };
    trackTemp[1] = (Track) { "Waterfalls", "TLC", 1995 };
    trackTemp[2] = (Track) { "Blinding Lights", "The Weeknd", 2020 };

    // ??? : Not sure about this...does this create an array of pointers instead
    // of an array of structs, need an array of structs.
    // library[0] = &trackTemp[1];
    // library[1] = &trackTemp[1];
    // library[2] = &trackTemp[2];

    copyStruct(library, trackTemp, numTracks);

    int o = 0;
    for (o = 0; o < numTracks; o++)
    {
        printf("Track      Song: [%s]  Artist: [%s]  Year: [%i] \n", trackTemp[o].title, trackTemp[o].artist, trackTemp[o].year);
        printf("Library    Song: [%s]  Artist: [%s]  Year: [%i] \n", library[o]->title, library[o]->artist, library[o]->year);
    }

    free(*library);
    return numTracks;
}

void copyStruct(Track** library, Track trackTemp[], int numTracks) {
    char *ptrSource;
    char *ptrTarget;
    int j = 0;
    for (j = 0; j < numTracks; j++) {
        ptrTarget = &library[j]->title[0];
        ptrSource = &trackTemp[j].title[0];
        strcpy(ptrTarget, ptrSource);
        ptrTarget = &library[j]->artist[0];
        ptrSource = &trackTemp[j].artist[0];
        strcpy(ptrTarget, ptrSource);
        library[j]->year = trackTemp[j].year;
    }
}

void listTracks(Track* library, int numTracks) {
    int i = 0;
    printf("listTracks Song-------------------------------------------------------------------------------- \n");
    for (i = 0; i < numTracks; i++) {
        printf("listTracks Song: [%s]  Artist: [%s]  Year: [%i] \n", library[i].title, library[i].artist, library[i].year);
    }
}

and here is my output :

$ ./a.exe
Track      Song: [Dancing Queen]  Artist: [ABBA]  Year: [1976]
Library    Song: [Dancing Queen]  Artist: [ABBA]  Year: [1976]
Track      Song: [Waterfalls]  Artist: [TLC]  Year: [1995]
Library    Song: [Waterfalls]  Artist: [TLC]  Year: [1995]
Track      Song: [Blinding Lights]  Artist: [The Weeknd]  Year: [2020]
Library    Song: [Blinding Lights]  Artist: [The Weeknd]  Year: [2020]
listTracks Song--------------------------------------------------------------------------------
listTracks Song: [`"¢]  Artist: [ABBA]  Year: [1976]
listTracks Song: []  Artist: []  Year: [0]
listTracks Song: []  Artist: []  Year: [0]

I can see the first track, but for some reason, not the other 2. What or how do I dereference the double pointer correctly?? The output from with loadLibrary() looks correct...tia, adym


Solution

  • This code snippet except the for loop where data members are outputted:

    // SIMULATE : Loading from text file...
    trackTemp[0] = (Track) { "Dancing Queen", "ABBA", 1976 };
    trackTemp[1] = (Track) { "Waterfalls", "TLC", 1995 };
    trackTemp[2] = (Track) { "Blinding Lights", "The Weeknd", 2020 };
    // ??? : Not sure about this...does this create an array of pointers instead
    // of an array of structs, need an array of structs.
    // library[0] = &trackTemp[1];
    // library[1] = &trackTemp[1];
    // library[2] = &trackTemp[2];
    
    copyStruct(library, trackTemp, numTracks);
    
    int o = 0;
    for (o = 0; o < numTracks; o++)
    {
        printf("Track      Song: [%s]  Artist: [%s]  Year: [%i] \n", trackTemp[o].title, trackTemp[o].artist, trackTemp[o].year);
        printf("Library    Song: [%s]  Artist: [%s]  Year: [%i] \n", library[o]->title, library[o]->artist, library[o]->year);
    }
    
    free(*library);
    return numTracks;
    

    does not make sense.

    You allocated an array of structures:

    Track *ptrMem = calloc(numTracks, sizeof(Track));
    

    and the address of the allocated array is assigned to the original pointer trackLibrary that was passed to the function indirectly through the pointer library:

    // MOVE : Move the pointer to the memory...
    *library = ptrMem;
    

    So the variable length array trackTemp:

    Track trackTemp[numTracks];
    

    is redundant. You could just write:

    ptrMem[0] = (Track) { "Dancing Queen", "ABBA", 1976 };
    ptrMem[1] = (Track) { "Waterfalls", "TLC", 1995 };
    ptrMem[2] = (Track) { "Blinding Lights", "The Weeknd", 2020 };
    

    These commented statements:

    // library[0] = &trackTemp[1];
    // library[1] = &trackTemp[1];
    // library[2] = &trackTemp[2];
    

    are trying to access memory outside the allocated memory that will result in undefined behavior.

    Using the function copyStruct:

    copyStruct(library, trackTemp, numTracks);
    

    is just redundant because the array was already initialized as shown above.

    And at last you freed the allocated memory:

    free(*library);
    

    So the original pointer trackLibrary after exiting the function will have an invalid value that points to already deallocated memory. Remove this statement.

    You will need to free the allocated memory before the end of the program when the allocated array will not be required any more.