Thread: Dynamic array receives random values

  1. #1
    Registered User
    Join Date
    Jan 2016
    Posts
    4

    Question Dynamic array receives random values

    Hi guys,

    I'm totally lost and in need of help with a programming problem no one I talked to could solve so far. I'm not a particularly experienced programmer, it's just a hobby of mine but I think this is something above the usual beginner errors.

    I work on a little text-based command line game, just for fun and exercise. In this game there is a class Player that contains an instance of Inventory. The Inventory class itself dynamically creates and governs an array of invSpaces with a fixed size, each space containing a pointer to a specific Item and its quantity. I shortened the code so that it still produces the same problem. If executed it scans input using getline() and then prints the value of each inventory space:

    Code:
    #include <iostream>
    #include <string>
    using namespace std;
    
    class Item {
        public:
            string name;
            string description;
    
            Item() {}
            Item(const string new_name, const string new_description) {
                name = new_name;
                description = new_description;
            }
    };
    
    Item item01("sword", "A sharp and deadly weapon.");
    Item item02("shield", "This will protect you. To a certain extent.");
    Item item03("stick", "What is this for exactly?");
    Item item04("bottle of water", "A bottle full of refreshing spring water.");
    
    class invSpace {                    
        public:
            Item* item;
            int quantity;
    
            invSpace() {
                item = NULL;
                quantity = 0;
            }
    };
    
    class Inventory {
        private:
            invSpace* spaces = NULL;
            size_t size;
    
        public:
            int free_space() {
                int free = 0;
                for (int i = 0; i < size; i++) {
                    if (spaces[i].item == NULL) {
                        free++;
                        cout << i << " = free" << endl;
                    }
                    else {
                        cout << spaces[i].item << " / " << spaces[i].quantity << endl;
                    }
                }
                return free;
            }
    
            Inventory() {}
            Inventory(size_t new_size) {    
                size = new_size;
                spaces = new invSpace[size];
                /*for (int i = 0; i < size; i++) {    //makes no difference, just for testing    
    
                    spaces[i] = invSpace();
    
                }*/                
            }
    
            ~Inventory() {
                delete[] spaces;
            }
    };
    
    class Player {
        public:
            string name;
            Inventory inventory;
    
            Player(const string new_name) {
                inventory = Inventory(40);
                name = new_name;
            }
    };
    
    Player player("Me");
    
    int main() {
    
    string input;
    //Inventory inventory(40);    //no error if declared outside ouf player class
    
    while (1) {
            cout << "\n>> ";
            getline(cin, input);
            if (input == "x") {
                break;        
            }
            else {
                player.inventory.free_space();
            }
        }
    }
    Now here's the problem: About a third of invSpaces in the inventory gets filled with random values (adresses and integers) when getline() is called in the while loop in main(), although there is absolutely no connection between them two. The inventory is clean as it should be when the programm commences but gets random values when getline() is called. More than that, the values slightly change with each new call of getline(). This also occurs with cin, but not with <stdio.h> functions like scanf().

    There are two other stange things: This error doesn't happen when the inventory is declared outside the Player class; it remains clean and empty. Second, as you can see, I declared some dummy instances of Item above, that aren't used in the rest of the code. They are just there. When I don't declare any items, only the first invSpace receives random values. The number of random values seems to increase the more items there are.

    So there are a number of parameters that effect each other to produce this error although, as far as I'm concerned, they aren't connected in the code. I have absolutely no clue how the random adresses appear in the first place and why they increase with the number of declared items.

    Now, I know I could use a vector for the same purpose instead, but nonetheless I would like to understand the working behind all this.

    I would be overjoyed if someone could help me!

    Greetings,
    Benniczek

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Your problem might lie with this line:
    Code:
    spaces = new invSpace[size];
    This is not guaranteed to zero initialise the elements, so you won't necessarily have 40 null pointers. You should have written the version with value initialisation, which then results in zero initialisation:
    Code:
    spaces = new invSpace[size]();
    But one of the first things I would do is to replace the manual memory management done in Inventory with a container. std::vector looks suitable here:
    Code:
    class Inventory {
    public:
        Inventory() {}
    
        explicit Inventory(size_t new_size) : spaces(new_size) {}
    
        std::size_t free_space() const {
            std::size_t free_count = 0;
            for (std::size_t i = 0; i < spaces.size(); ++i) {
                if (!spaces[i].item) {
                    ++free_count;
                    cout << i << " = free" << endl;
                }
                else {
                    cout << spaces[i].item << " / " << spaces[i].quantity << endl;
                }
            }
            return free_count;
        }
    private:
        std::vector<invSpace*> spaces;
    };
    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

  3. #3
    Registered User
    Join Date
    Jan 2016
    Posts
    4
    Thanks for your advise!

    Concerning the zero initialisation: Yes, I tried this before, makes no difference and as I said: Before the first call of getline() the array is, in fact, initialised to zero.
    Concerning the use of vector: This actually works, thanks for that! Still, I would like to know WHY all this happens, why getline() spams my array with random values. Someone once suggested to me that this could be due to memory corruption, which, I guess, would be the fault of my computer. Did you compile my code on your PC and does it show the same issues?

  4. #4
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Benniczek
    Concerning the zero initialisation: Yes, I tried this before, makes no difference and as I said: Before the first call of getline() the array is, in fact, initialised to zero.
    I tried your code with a main function like this:
    Code:
    int main() {
        player.inventory.free_space();
    }
    I was presented with:
    Code:
    0 = free
    1 = free
    2 = free
    3 = free
    4 = free
    5 = free
    6 = free
    7 = free
    8 = free
    9 = free
    10 = free
    11 = free
    12 = free
    13 = free
    14 = free
    15 = free
    16 = free
    17 = free
    18 = free
    19 = free
    20 = free
    21 = free
    22 = free
    23 = free
    24 = free
    25 = free
    26 = free
    27 = free
    28 = free
    29 = free
    30 = free
    31 = free
    32 = free
    33 = free
    34 = free
    35 = free
    36 = free
    37 = free
    38 = free
    39 = free
    *** Error in `./test': double free or corruption (top): 0x0000000001b34220 ***
    Aborted (core dumped)
    Notice the error message at the end. Basically, you have a problem in that your manual memory management was incomplete: besides implementing the constructor and destructor, you should have implemented the copy constructor and copy assignment operator. By failing to do so, most probably this line in the constructor of the Player class:
    Code:
    inventory = Inventory(40);
    resulting in memory being allocated for the spaces of the temporary Inventory object, then copied over, and then deallocated. Then when you destroy the Player object, an attempt was made to deallocate the already deallocated memory.

    By substituting with std::vector, I avoided this entire problem.
    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
    Registered User
    Join Date
    Jan 2016
    Posts
    4
    Thank you for pointing that out Still, that's not the reason for the random values in the array that appear only when getline() is called. Has somebody an idea why this happens?

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Benniczek
    Still, that's not the reason for the random values in the array that appear only when getline() is called. Has somebody an idea why this happens?
    No, it is the reason: you are looking at the effects of undefined behaviour. If the dynamic array has already been destroyed, then accessing its contents results in undefined behaviour. So, the memory corresponding to the "random values in the array" could have been overwritten because they are supposed to no longer be in use, and by attempting to access the non-existent dynamic array, you take a peek into that overwritten memory.

    Compile and run this modified version of your code to see that it has nothing to do with getline per se:
    Code:
    #include <iostream>
    #include <string>
    using namespace std;
    
    class Item {
        public:
            string name;
            string description;
    
            Item() {}
            Item(const string new_name, const string new_description) {
                name = new_name;
                description = new_description;
            }
    };
    
    Item item01("sword", "A sharp and deadly weapon.");
    Item item02("shield", "This will protect you. To a certain extent.");
    Item item03("stick", "What is this for exactly?");
    Item item04("bottle of water", "A bottle full of refreshing spring water.");
    
    class invSpace {
        public:
            Item* item;
            int quantity;
    
            invSpace() {
                item = NULL;
                quantity = 0;
            }
    };
    
    class Inventory {
        private:
            invSpace* spaces = NULL;
            size_t size;
    
        public:
            int free_space() {
                int free = 0;
                for (int i = 0; i < size; i++) {
                    if (spaces[i].item == NULL) {
                        free++;
                        cout << i << " = free" << endl;
                    }
                    else {
                        cout << spaces[i].item << " / " << spaces[i].quantity << endl;
                    }
                }
                return free;
            }
    
            Inventory() {}
            Inventory(size_t new_size) {
                size = new_size;
                spaces = new invSpace[size];
                /*for (int i = 0; i < size; i++) {    //makes no difference, just for testing
    
                    spaces[i] = invSpace();
    
                }*/
            }
    
            ~Inventory() {
                delete[] spaces;
            }
    };
    
    class Player {
        public:
            string name;
            Inventory inventory;
    
            Player(const string new_name) {
                inventory = Inventory(40);
                name = new_name;
            }
    };
    
    Player player("Me");
    
    int main() {
    
        string input;
    
        for (int i = 0; i < 20; ++i) {
            input = std::string('a', i);
        }
    
        player.inventory.free_space();
    }
    You will probably get similiar "random values". If you don't, no big deal: that's also an effect of undefined behaviour.

    Now compile and run this fixed version of your code:
    Code:
    #include <iostream>
    #include <string>
    #include <algorithm>
    using namespace std;
    
    class Item {
        public:
            string name;
            string description;
    
            Item() {}
            Item(const string new_name, const string new_description) {
                name = new_name;
                description = new_description;
            }
    };
    
    Item item01("sword", "A sharp and deadly weapon.");
    Item item02("shield", "This will protect you. To a certain extent.");
    Item item03("stick", "What is this for exactly?");
    Item item04("bottle of water", "A bottle full of refreshing spring water.");
    
    class invSpace {
        public:
            Item* item;
            int quantity;
    
            invSpace() {
                item = NULL;
                quantity = 0;
            }
    };
    
    class Inventory {
        private:
            invSpace* spaces = NULL;
            size_t size;
    
        public:
            int free_space() {
                int free = 0;
                for (int i = 0; i < size; i++) {
                    if (spaces[i].item == NULL) {
                        free++;
                        cout << i << " = free" << endl;
                    }
                    else {
                        cout << spaces[i].item << " / " << spaces[i].quantity << endl;
                    }
                }
                return free;
            }
    
            Inventory() {}
            Inventory(size_t new_size) {
                size = new_size;
                spaces = new invSpace[size]();
            }
    
            Inventory(const Inventory& other) : size(other.size) {
                spaces = new invSpace[size];
                std::copy(spaces, other.spaces, size);
            }
    
            Inventory& operator=(const Inventory& other) {
                Inventory temp(other);
                swap(temp);
                return *this;
            }
    
            ~Inventory() {
                delete[] spaces;
            }
    
            void swap(Inventory& other) {
                using std::swap;
                swap(spaces, other.spaces);
                swap(size, other.size);
            }
    };
    
    class Player {
        public:
            string name;
            Inventory inventory;
    
            Player(const string new_name) {
                inventory = Inventory(40);
                name = new_name;
            }
    };
    
    Player player("Me");
    
    int main() {
    
        string input;
        //Inventory inventory(40);    //no error if declared outside ouf player class
    
        while (1) {
            cout << "\n>> ";
            getline(cin, input);
            if (input == "x") {
                break;
            }
            else {
                player.inventory.free_space();
            }
        }
    }
    Actually, although it allowed you to observe this "random values" problem, note that this constructor implementation is rather inefficient:
    Code:
    Player(const string new_name) {
        inventory = Inventory(40);
        name = new_name;
    }
    My demonstrated fix involved implementing the copy assignment operator for Inventory, but in reality you don't need to copy any Inventory objects here. You could use move assignment, but a more directly efficient solution is to use the Player constructor's initialiser list:
    Code:
    Player(const string new_name) : name(new_name), inventory(40) {}
    Of course, doing this also means that there is no temporary Inventory object whose dynamic array is destroyed, hence even if you do not implement the copy constructor and copy assignment operator, the "random values" will no longer happen, although as long as you have such incomplete RAII implementation, a disaster is waiting to happen.
    Last edited by laserlight; 01-30-2016 at 09:13 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

  7. #7
    Registered User
    Join Date
    Jan 2016
    Posts
    4
    Ah, now I understand! Thank you very much for your thorough explanation. Guess I'll stick to vectors until I have a better understanding of memory mechanics.

  8. #8
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    There is no need to go back to manual memory management. It is discouraged because it's more complicated, more error prone and more code to write and maintain. Stick with vectors. They're there for a reason.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. why can't a static array copy values from a dynamic array
    By c++noob145 in forum C++ Programming
    Replies: 2
    Last Post: 03-13-2013, 10:25 AM
  2. passing random values into array
    By $ingh in forum C Programming
    Replies: 8
    Last Post: 06-22-2010, 03:32 PM
  3. Storing values into a dynamic array
    By porsche911nfs in forum C++ Programming
    Replies: 5
    Last Post: 04-24-2009, 09:08 AM
  4. Replies: 17
    Last Post: 06-06-2008, 04:47 AM
  5. Checking maximum values for dynamic array...
    By AssistMe in forum C Programming
    Replies: 1
    Last Post: 03-21-2005, 12:39 AM

Tags for this Thread