C, double pointers and array of structs

112 Views Asked by At

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

3

There are 3 best solutions below

0
On BEST ANSWER

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.

0
On

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()?

You don't, for two reasons:

  1. You presently free() that memory before returning from loadLibrary(), so after the return there is no allocated memory to access. Since the purpose of the function seems to be entirely to allocate that object and fill it with data, that free() is probably just erroneous.

  2. loadLibrary() receives the address of main()'s trackLibrary variable so that it can modify the ((single-)pointer) value of that variable. Supposing that you do not free that memory prematurely, the data are then available via the single pointer or any copy of it. Although you could take its address again and use that double pointer to access the single pointer, that's pretty convoluted.

main()'s call to listTracks() appears to be right, and listTracks() appears to be doing the right thing with its library parameter.

7
On

The local trackTemp array serves no purpose. You have dynamically allocated memory for the Tracks and have the ptrMem pointer to that memory, so use it. Removing that makes copyStruct redundant, which is good since it accesses memory out of bounds. You should also hold off with free(*library); until after you are done with it, which is in main.

Example:

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

#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 loadLibrary(const char* filename, Track** library) {
    static const int numTracks = 3;
    Track* ptrMem = calloc(numTracks, sizeof(Track));

    // Copy the pointer to the return parameter:
    *library = ptrMem;

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

    // I'd remove this printing from the loadLibrary function. It doesn't
    // belong here:
    for(int o = 0; o < numTracks; o++) {
        printf("Track      Song: [%s]  Artist: [%s]  Year: [%i] \n",
               ptrMem[o].title, ptrMem[o].artist, ptrMem[o].year);
    }

    return numTracks;
}

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

int main() {
    Track* trackLibrary;
    char file[] = "tracks_test.txt";

    int numTracks = loadLibrary(file, &trackLibrary);
    listTracks(trackLibrary, numTracks); // you use the allocated memory here

    free(trackLibrary); // <- so free it here
}