1. Limited time only! Sign up for a free 30min personal tutor trial with Chegg Tutors
    Dismiss Notice
Dismiss Notice
Join Physics Forums Today!
The friendliest, high quality science and math community on the planet! Everyone who loves science is here!

C++: Dynamic memory allocation using a struct member

  1. Sep 25, 2012 #1

    Dembadon

    User Avatar
    Gold Member

    1. The problem statement, all variables and given/known data

    I am trying to allocate memory on the heap (free-store) for an array of characters using one of my struct members, which is a pointer. However, I am running into scoping issues no matter how I try to go about doing it.

    2. Relevant equations

    <data type> *<pointer name>;
    <pointer name> = new <data type>[ <size of array> ];

    I am using the gcc compiler in unix.

    3. The attempt at a solution

    Here is my struct:
    Code (Text):

    struct card {
        char *pSuit;
        int rank;
        char color;
    };
     
    Here is a snippet of my initialization section within main:
    Code (Text):

    pSuit = new char[ 11 ];
     
    Shouldn't this allocate space on the heap for an array of 12 characters? The compiler keeps barking at me, saying: "‘pSuit’ was not declared in this scope". When I try to allocate memory within my struct in hopes to resolve the scoping problem, it tells me the action is forbidden.
     
    Last edited: Sep 25, 2012
  2. jcsd
  3. Sep 25, 2012 #2

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    The left hand side allocates an array of 11 chars, not 12. If you want to use this to store a C-style string, that's enough space to hold a string with at most 10 (not 12) characters.

    The right hand side is looking to assign that to a local variable named pSuit. You don't have such a variable, hence the error. You don't want to assign it to the struct itself; that doesn't make sense. What you should be doing is assigning this allocated memory to an instance of the struct. If you have seven cards, you will have to make seven of these allocations.

    You will also have to remember to free that memory once you are done using it. Forgetting to release allocated memory is called a memory leak. The operating system will clean up after you, so failing to release resources might not be a problem your current program, but it will kill you eventually.

    Always clean up after yourself. That's what destructors are for.

    What you should have instead of a simple struct where memory is allocated from the outside is a class with a constructor that allocates memory upon construction, releases memory upon destruction. The C++ mantra is "resource allocation is initialization", which really means "resource allocation should be done at initialization time only, and don't forget to clean up the mess when you are done."

    In your case you don't have to do any of this resource allocation/deallocation rigamarole. Simply change the type of your data member pSuit from char* to std::string. Prefer C++ strings over C-style strings if at all possible. You don't do the allocation or deallocation; the string object does. Example: Suppose make this change and you have a card card1; variable and you want to set it to the ten of clubs. You can do this via card1.rank = 10; card1.suit = "clubs";
     
  4. Sep 25, 2012 #3

    jbunniii

    User Avatar
    Science Advisor
    Homework Helper
    Gold Member

    pSuit is not a standalone variable. It's an element of a structure. In order to use it, you must have an instance of the structure. For example:

    Code (Text):

    int main ()
    {
        card c;
        c.pSuit = new char[11];
        ...
        delete[] c.pSuit;
    }
     
    However, you shouldn't do it this way unless there's a good reason. Generally, you should put new/delete into the constructor/destructor for the struct or class. That way, you cannot accidentally forget to allocate or deallocate the memory pointed to by pSuit.

    Code (Text):

    struct card {
            card() { pSuit = new char[11]; }
            ~card() { delete[] pSuit; }
        char *pSuit;
        int rank;
        char color;
    };
     
    Moreover, if the array you're constructing always has the same size, consider whether it needs to be dynamically allocated in the first place. Would the following not suffice:

    Code (Text):

    struct card {
        char pSuit[11];
        int rank;
        char color;
    };
     
     
    Last edited: Sep 25, 2012
  5. Sep 25, 2012 #4

    Dembadon

    User Avatar
    Gold Member

    Indeed. Not sure why I said 12, since the address at 11 will hold the null terminator.

    Last week's assignment required the suit member to be a C-style string; this week is must be a char* member. I'm familiar with reading-in values to an instance of the struct when the member data type is a character array, but the pointer requirement is throwing me off.

    The professor mentioned this last lecture, during which we were introduced to classes. We aren't allowed to use classes for this week's assignment, but next week's assignment will require us to change our program to use a class instead of a struct.

    Since I can't change my struct to a class yet, my understanding of cleaning-up is to delete the memory allocation and set the corresponding pointer to NULL.

    -------------------------------------------------------------------------------------------------------

    I greatly appreciate the responses, D H and jbunniii.

    After reading through your replies, I realized I could've been more specific with respect to the requirements for this week's assignment. I apologize for wasting your time by not including them in my original post. :redface:

    Requirements:
    1. For this project you will rewrite Project 3 using dynamic memory for all arrays.
    2. Square brackets should only be used when allocating and deallocating arrays.
    3. The suit array in the card struct must be represented as a char* data member.
    4. Memory for the suit array will be allocated in the program.
    5. Pointers must be used to manipulate all arrays.

    There are other requirements, but they aren't relevant to memory allocation, so I've omitted them from the list above.

    I understand points 1 and 2 to mean that all memory allocation for arrays must occur on the heap rather than the stack.

    From Point 3, I infer that I must carve out a chunk of memory on the heap for my suit data member. If this must be done within the program, won't I run into a scoping issue when trying to use pSuit to allocate the memory? I don't understand how I can use my pointer in the struct to allocate memory within main, if said pointer isn't available outside of my struct!

    Point 4 seems to imply that I will need to use my suit pointer and the new command to allocate memory for an array that will hold a string which represents the suit of a given card.

    Point 5 basically means I will be passing pointers to functions instead of arrays, and will be using pointer arithmetic to "walk" along my arrays. This requirement seems redundant to me, though, since it follows directly from point 2.
     
  6. Sep 25, 2012 #5

    jbunniii

    User Avatar
    Science Advisor
    Homework Helper
    Gold Member

    Thanks for clarifying the requirements. I'm not sure why you think the requirement to use a struct instead of a class would change anything with regard to how you allocate and de-allocate the memory. The only difference between a struct and a class is that a struct's elements are public by default, whereas a class's elements are private by default. A struct can have a constructor and a destructor, just like a class.
     
  7. Sep 25, 2012 #6

    jbunniii

    User Avatar
    Science Advisor
    Homework Helper
    Gold Member

    Please review the first block of code in my earlier post. You first need to create an instance of the struct. Then you can use the pSuit member variable belonging to that instance:

    Code (Text):

    int main ()
    {
        card c; // this creates an instance of the card struct
        c.pSuit = new char[11]; // allocate memory and point c.pSuit to the memory
        ...
        delete[] c.pSuit; // deallocate the memory before exiting
    }
     
    However, as suggested previously, it's much better to do the allocation and de-allocation in the constructor and destructor for the struct:

    Code (Text):

    struct card {
            card() { pSuit = new char[11]; }
            ~card() { delete[] pSuit; }
        char *pSuit;
        int rank;
        char color;
    };

    int main()
    {
        card c; // Create an instance of the struct. This also allocates memory and points c.pSuit to that memory.
        ....
        // No need to deallocate the memory before exiting. The destructor will automatically do that when c goes out of scope
    }
     
     
  8. Sep 25, 2012 #7

    Dembadon

    User Avatar
    Gold Member

    I understand now. Thank you, jbunniii!
     
  9. Sep 25, 2012 #8
    While irrelevant to the original question, I should warn against using this code. One should use either

    Code (Text):

            card() pSuit(0) { pSuit = new char[11]; }
     
    or

    Code (Text):

            card() pSuit(new char[11]) {}
     
    The problem with the original code is that operator new may throw an exception, in which case the original code will call the destructor, which will then call operator delete[] on an uninitialized pointer. The first alternative makes sure the pointer is initialized with a safe value, and the second alternative ensures that the destructor will not be called if operator new throws.
     
Know someone interested in this topic? Share this thread via Reddit, Google+, Twitter, or Facebook