Thread: Help with structs and malloc

  1. #1
    Registered User
    Join Date
    Feb 2016
    Posts
    10

    Help with structs and malloc

    Hey everyone.

    So my error code on line 43: format '%c" expects type 'char *' but argument 3 has type 'char * (*)[20]'

    TL;DR, basically I have a file. it's contents look like this (format and all):

    3
    Hello 5 100
    Roses 3 98
    Again 4 64

    readAlbum reads in the list of songs. printAlbum prints each song in the array to the screen.

    Main reads in the input file as a command line argument, and calls readAlbum and printAlbum.


    This should be printed to the screen:


    Hello 5 100
    Roses 3 98
    Again 4 64

    Code:
    #include<stdio.h>
    #include<stdlib.h>
    #define MAXNAMESIZE 20
    
    
    typedef struct song {
        char* name[MAXNAMESIZE];
        int rating;
        int numTimesPlayed;
    } Song;
    
    
    Song* readAlbum(const char* filename, int* lenPtr);
    void printAlbum(Song* album, int len);
    
    
    int main(int argc, char* argv[])
    {
        int lenPtr;
        
        readAlbum(argv[1], &lenPtr);
    
    
    //    printAlbum(&album, lenPtr);
    
    
        return 0;
    }
    
    
    Song* readAlbum(const char* filename, int* lenPtr)
    {
        FILE* fp; //Declare file pointer
    
    
        if((fp = fopen(filename, "r"))==NULL) //Open file, return null if unable to open
        {
            printf("The file could not be opened. \n");
            return NULL;
        }
        
        fscanf(fp, "%d", lenPtr);//scan for the first line in the file
    
    
        Song* album=malloc(sizeof(Song)**lenPtr);
    
    
        int i = 0;
    
    
        for(i = 0; i < *lenPtr; i++)
        {
            fscanf(fp, "%c %d %d", &album[i].name, &album[i].rating, &album[i].numTimesPlayed);
        }    
        
        fclose(fp);
        
        return album;
    }
    
    
    void printAlbum(Song* album, int len)
    {
    }
    Thanks so much! I've been stuck on this personal project for days.

  2. #2
    Registered User naaissus's Avatar
    Join Date
    Jan 2016
    Location
    Balkan
    Posts
    23
    Code:
    typedef struct song {
        char* name[MAXNAMESIZE];
        int rating;
        int numTimesPlayed;
    } Song;
    Remove * from the second line, you don't wanna create an array of pointers to characters, you want to create array of characters.
    This is a string, you can't read it with a %c specifier, you're gonna need to use %s:
    Code:
    fscanf(fp, "%s %d %d", album[i].name, &album[i].rating, &album[i].numTimesPlayed);

  3. #3
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Quote Originally Posted by Joshua Green View Post
    Actually char * name is the same as char name[] in the implementation. You just have to allocate memory for char* name, which is where I got [MAXNAMESIZE]
    You're just making things more complex than they need to be.

    char *names[5]
    declare names as array 5 of pointer to char
    char names[5]
    declare names as array 5 of char

    You can confirm the difference for yourself here: cdecl.org. But the point is that one thing is a string, and the other thing is an array of char pointers.
    Last edited by whiteflags; 02-02-2016 at 10:17 PM. Reason: linked

  4. #4
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Joshua Green
    I'm working with strict implementation instructions. A 'backbone' was given to me for this code. I can't deviate from the code that was given.
    That's fine, but then you must be aware that this means that the name member is an array of pointers to char, hence the expectation is that it is an array of strings, which does not quite make sense for a member named "name" rather than "names" or "name_list". Hence, in your shoes I would clarify the requirements.

    By the way, the original poster's username is sahtopi, but your username is Joshua Green. Is there a reason for that?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  5. #5
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Quote Originally Posted by Joshua Green View Post
    I'm working with strict implementation instructions. A 'backbone' was given to me for this code. I can't deviate from the code that was given.
    Fine. You still need to know that each name[i] is a different string, and you need to allocate the ones you are going to use. And you are not doing that currently. This is all the reason why I think this particular implementation decision is an error.

  6. #6
    Registered User
    Join Date
    Feb 2016
    Posts
    10
    Quote Originally Posted by whiteflags View Post
    Fine. You still need to know that each name[i] is a different string, and you need to allocate the ones you are going to use. And you are not doing that currently. This is all the reason why I think this particular implementation decision is an error.
    I understand, thank you very much for the info. That definitely helps me understand what's going on here

  7. #7
    Registered User
    Join Date
    Feb 2016
    Posts
    10
    Quote Originally Posted by laserlight View Post
    That's fine, but then you must be aware that this means that the name member is an array of pointers to char, hence the expectation is that it is an array of strings, which does not quite make sense for a member named "name" rather than "names" or "name_list". Hence, in your shoes I would clarify the requirements.

    By the way, the original poster's username is sahtopi, but your username is Joshua Green. Is there a reason for that?
    Perhaps I'm not understanding, but the idea is the struct acts as a container that stores a song's (1)Name (2) rating (3) number of times played. The input file provides 3 different songs, each with their own rating and number of plays. Would:

    char name[MAXNAMESIZE] be preferred to char* name[MAXNAMESIZE]?

    edit: My reasoning would be that each song only has 1 name. For example, I would want to read the name "Hello" into "*name", the number "5" into "rating", and lastly the number 100 into "numTimesPlayed"


    Once again apologies if I'm misunderstanding
    Last edited by sahtopi; 02-02-2016 at 11:00 PM. Reason: clarity

  8. #8
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by sahtopi
    Perhaps I'm not understanding, but the idea is the struct acts as a container that stores a song's (1)Name (2) rating (3) number of times played. The input file provides 3 different songs, each with their own rating and number of plays. Would:

    char name[MAXNAMESIZE] be preferred to char* name[MAXNAMESIZE]?
    Not that it would be preferred: it is a matter of asking what do you want to store in each struct object. So, you have listed three things: a name would be a string, hence you would either have an array member:
    Code:
    char name[MAXNAMESIZE];
    or a pointer member:
    Code:
    char *name;
    but if you have a pointer member then you need to allocate memory for the string contents itself.

    But let's say, hypothetically, that you not only want to store the song's name, but also aliases to the song's name, e.g., some songs are popularly known by alternative names because fans like some of the lyrics so much that they use the lyrics as the song's name, even though officially it isn't its name. Then, maybe you really would store a list of names, perhaps with the first name as the official name, and other names as the aliases, in which case a member like this is fine:
    Code:
    char *names[MAX_NUM_NAMES];
    Last edited by laserlight; 02-02-2016 at 11:06 PM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  9. #9
    Registered User
    Join Date
    Feb 2016
    Posts
    10
    Quote Originally Posted by laserlight View Post
    but if you have a pointer member then you need to allocate memory for the string contents itself.

    But let's say, hypothetically, that you not only want to store the song's name, but also aliases to the song's name, e.g., some songs are popularly known by alternative names because fans like some of the lyrics so much that they use the lyrics as the song's name, even though officially it isn't its name. Then, maybe you really would store a list of names, perhaps with the first name as the official name, and other names as the aliases, in which case a member like this is fine:
    Code:
    char *names[MAX_NUM_NAMES];
    Exactly, so with my current implementation, isn't it correct? I defined MAXNAMESIZE as 20 in the program header, so when I stated char* name[MAXNAMESIZE] I have a pointer member with memory allocated for the string contents. Is this incorrect?

    Here is my code as it stands currently. No compile errors, I'm just stuck.

    Code:
    #include<stdio.h>
    #include<stdlib.h>
    #define MAXNAMESIZE 20
    
    
    typedef struct song {
        char* name[MAXNAMESIZE];
        int rating;
        int numTimesPlayed;
    } Song;
    
    
    Song* readAlbum(const char* filename, int* lenPtr);
    void printAlbum(Song* album, int len);
    
    
    int main(int argc, char* argv[])
    {
        int lenPtr;
        
        char *len = readAlbum(argv[1], &lenPtr);
    
    
        printAlbum(*len, 3);
    
    
        return 0;
    }
    
    
    Song* readAlbum(const char* filename, int* lenPtr)
    {
        FILE* fp; //Declare file pointer
    
    
        if((fp = fopen(filename, "r"))==NULL) //Open file, return null if unable to open
        {
            printf("The file could not be opened. \n");
            return NULL;
        }
        
        fscanf(fp, "%d", lenPtr);//scan for the first line in the file
    
    
        Song* album=malloc(sizeof(Song)**lenPtr);
    
    
        int i = 0;
    
    
        for(i = 0; i < *lenPtr; i++)
        {
            fscanf(fp, "%s %d %d", *album[i].name, &album[i].rating, &album[i].numTimesPlayed);
        }    
        
        fclose(fp);
        
        return album;
    }
    
    
    void printAlbum(Song* album, int len)
    {
        int i =0;
        for(i = 0; i < len; i++)
        {
            printf("%s %d %d\n", *album[i].name, album[i].rating, album[i].numTimesPlayed);
        }
    }

  10. #10
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by sahtopi
    Exactly, so with my current implementation, isn't it correct? I defined MAXNAMESIZE as 20 in the program header, so when I stated char* name[MAXNAMESIZE] I have a pointer member with memory allocated for the string contents. Is this incorrect?
    It is incorrect. You wrote "My reasoning would be that each song only has 1 name."
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  11. #11
    Registered User
    Join Date
    Feb 2016
    Posts
    10
    Quote Originally Posted by laserlight View Post
    It is incorrect. You wrote "My reasoning would be that each song only has 1 name."
    I understand that most people have their own (incorrect) names for songs. But the reality of the situation is that professionally produced music (i.e something you would purchase or listen to on the radio) has one song name, or multiple names, if so designated. If you were put in a contest to listen to a song and name it, you would be incorrect if you made up your own song title, or used some of the words in the song. Assume the same for this program. In the implementation of my program, I'm not accepting input of any song names other than the input file. Does that make sense? I need a program that reads in song names from an input file, not song names from the mind of a person who is inputting whatever they want as a song name.

    edit: approach this situation as if the only 3 songs that exist in the world are "Hello", "Roses", and "Again".
    Last edited by sahtopi; 02-02-2016 at 11:31 PM. Reason: clarity

  12. #12
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    If I were you, I would do it this way:
    Code:
    #define NAME_MAX_SIZE 20
    
    typedef struct song {
        char name[NAME_MAX_SIZE];
        int rating;
        int numTimesPlayed;
    } Song;
    
    typedef struct album {
        Song *songs;
        int numSongs;
    } Album;
    
    Albumm *readAlbum(const char *filename);
    void printAlbum(const Album *album);
    Observe:
    • Instead of squishing the macro name as MAXNAMESIZE, I renamed it to NAME_MAX_SIZE. Having NAME come first can be useful when you have many such macro names.
    • For the Song struct, I made name to be an array of NAME_MAX_SIZE char. No pointer, no additional allocation step required. However, you are indeed limited to a name of at most NAME_MAX_SIZE-1 length.
    • Instead of passing around an array of Song objects as a pointer along with a length, I combined both into an Album struct.

    Note that you should free what you malloc when done.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  13. #13
    Registered User
    Join Date
    Feb 2016
    Posts
    10
    Quote Originally Posted by laserlight View Post
    If I were you, I would do it this way:
    Code:
    #define NAME_MAX_SIZE 20
    
    typedef struct song {
        char name[NAME_MAX_SIZE];
        int rating;
        int numTimesPlayed;
    } Song;
    
    typedef struct album {
        Song *songs;
        int numSongs;
    } Album;
    
    Albumm *readAlbum(const char *filename);
    void printAlbum(const Album *album);
    Observe:
    • Instead of squishing the macro name as MAXNAMESIZE, I renamed it to NAME_MAX_SIZE. Having NAME come first can be useful when you have many such macro names.
    • For the Song struct, I made name to be an array of NAME_MAX_SIZE char. No pointer, no additional allocation step required. However, you are indeed limited to a name of at most NAME_MAX_SIZE-1 length.
    • Instead of passing around an array of Song objects as a pointer along with a length, I combined both into an Album struct.

    Note that you should free what you malloc when done.
    Thank you. I would like to leave the function headers and prototypes as is in my implementation.

  14. #14
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by sahtopi
    I would like to leave the function headers and prototypes as is in my implementation.
    That is your choice, of course. So back to this:
    Quote Originally Posted by sahtopi
    Exactly, so with my current implementation, isn't it correct?
    I wrote that this makes sense if you have multiple names per song:
    Code:
    char* name[MAXNAMESIZE];
    You confirmed that each song only has one name. Therefore, I am telling you that what you are trying to do does not make sense.

    Quote Originally Posted by sahtopi
    I defined MAXNAMESIZE as 20 in the program header, so when I stated char* name[MAXNAMESIZE] I have a pointer member with memory allocated for the string contents. Is this incorrect?
    No, you do not have memory allocated for the string contents. Let's take a look:
    Code:
    Song* album=malloc(sizeof(Song)**lenPtr);
    
    
    int i = 0;
    
    
    for(i = 0; i < *lenPtr; i++)
    {
        fscanf(fp, "%s %d %d", *album[i].name, &album[i].rating, &album[i].numTimesPlayed);
    }
    In the first line, you allocated memory for *lenPtr number of Song objects. Great. Then, you immediately begin the loop to read songs from the file. Fine. But in the loop body, you read a string into *album[i].name. album[i].name is an array of pointers. Therefore, *album[i].name is a pointer. This is fine, but the pointer does not point to anything!

    If you had written:
    Code:
    char name[MAXNAMESIZE];
    Then this would work:
    Code:
    fscanf(fp, "%s %d %d", album[i].name, &album[i].rating, &album[i].numTimesPlayed);
    Though it would be vulnerable to buffer overflow.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  15. #15
    Registered User
    Join Date
    Feb 2016
    Posts
    10
    Quote Originally Posted by laserlight View Post
    That is your choice, of course. So back to this:

    I wrote that this makes sense if you have multiple names per song:
    Code:
    char* name[MAXNAMESIZE];
    You confirmed that each song only has one name. Therefore, I am telling you that what you are trying to do does not make sense.


    No, you do not have memory allocated for the string contents. Let's take a look:
    Code:
    Song* album=malloc(sizeof(Song)**lenPtr);
    
    
    int i = 0;
    
    
    for(i = 0; i < *lenPtr; i++)
    {
        fscanf(fp, "%s %d %d", *album[i].name, &album[i].rating, &album[i].numTimesPlayed);
    }
    In the first line, you allocated memory for *lenPtr number of Song objects. Great. Then, you immediately begin the loop to read songs from the file. Fine. But in the loop body, you read a string into *album[i].name. album[i].name is an array of pointers. Therefore, *album[i].name is a pointer. This is fine, but the pointer does not point to anything!

    If you had written:
    Code:
    char name[MAXNAMESIZE];
    Then this would work:
    Code:
    fscanf(fp, "%s %d %d", album[i].name, &album[i].rating, &album[i].numTimesPlayed);
    Though it would be vulnerable to buffer overflow.
    Thank you very much. That's exactly what I needed to see

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Malloc, Pointers, and Structs
    By TheSquid in forum C Programming
    Replies: 8
    Last Post: 09-15-2009, 11:07 AM
  2. help with structs and malloc!
    By coni in forum C Programming
    Replies: 20
    Last Post: 09-14-2009, 05:38 PM
  3. Malloc/and structs plz help
    By dezz101 in forum C Programming
    Replies: 1
    Last Post: 09-11-2008, 07:44 PM
  4. Malloc with structs.
    By jrdoran in forum C Programming
    Replies: 4
    Last Post: 12-11-2006, 11:26 PM
  5. malloc for structs and arrays
    By rkooij in forum C Programming
    Replies: 15
    Last Post: 05-04-2006, 07:38 AM