gliwidilt
gliwidilt

Reputation: 31

C++ exception std::length_error sometimes?

I am trying to write a simple Hangman game in C++. The game works mostly but sometimes when I debug it I get the exception std::length_error. Usually after the user inputs the choice. I've written the relevant code here due to Stack Overflow's saying that most of my post was mostly code. But I'll provide the rest if it's needed. The main code, variable and function declarations:

#include "stdafx.h"
#include <iostream>
#include <string>
#include <ctime>
#include <stdlib.h>

using namespace std;

void game(int choice);
void check(char letter, string word);
bool check_win(string word);

char guessed[30];
int guessCount = 0, tries = 0;

int main()
{
    int choice, sentinel;

    cout << "Welcome to Phobez's Hangman game!" << endl;
    cout << "=================================" << endl;
    cout << "Guess a word which is represented by a row of dashes, representing each letter of the word." << endl;
    cout << "Pick a Topic" << endl;
    cout << "1. Animals\n2. Colours\n3. Days of the Week\n4. Months\n5. Body Parts\n6. Numbers" << endl;
    cout << "Choice: ";
    cin >> choice;

    game(choice);

    system("pause");

    return 0;
}

Then the game() function which takes care of setting up and running most of the game:

void game(int choice) {
    string word;
    const int MAX_TRIES = 6;
    char letter;

    string animal[] = { "cat", "dog", "horse", "elephant", "duck", "turtle", "bird", "crab", "spider", "bear", "pig", "penguin", "mouse", "rabbit", "lion", "monkey", "bull"};
    string colour[] = { "black", "white", "red", "yellow", "purple", "green", "blue" };
    string day[] = { "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" };
    string month[] = { "january", "february", "may", "march", "april", "june", "july", "august", "september", "october", "november", "december" };
    string bodPart[] = { "head", "shoulder", "knee", "forehead", "eyebrow", "ear", "eye", "nose", "chest", "mouth", "arm", "stomach", "hand", "leg", "foot", "toe" };
    string nums[] = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "zero" };

    srand(time(0));

    switch (choice) {
        case 1:
            word = animal[(rand() % 17) - 1];
            break;
        case 2:
            word = colour[(rand() % 7) - 1];
            break;
        case 3:
            word = day[(rand() % 7) - 1];
            break;
        case 4:
            word = month[(rand() % 12) - 1];
            break;
        case 5:
            word = bodPart[(rand() % 16) - 1];
            break;
        case 6:
            word = nums[(rand() % 10) - 1];
            break;
        default:
            word = animal[(rand() % 18) - 1];
    }

    do {
        cout << "Word: ";

        for (int i = 0; i < strlen(word.c_str()); i++) {
            bool isPrinted = 0;

            for (int j = 0; j < strlen(word.c_str()); j++) {
                if (word[i] == guessed[j]) {
                    cout << word[i];
                    isPrinted = 1;
                    break;
                }
            }

            if (isPrinted) {
                continue;
            }
            else {
                cout << "_";
            }
        }

        cout << endl;
        cout << "Misses: " << tries << endl;
        cout << "Guess: ";
        cin >> letter;

        check(letter, word);

        if (check_win(word)) {
            cout << word << endl;
            cout << "You won!" << endl;
            break;
        }
        else {
            continue;
        }

    } while (tries < MAX_TRIES);

    if (tries >= MAX_TRIES) {
        cout << "You lost!" << endl;
    }
}

I've tried searching Google and Stack Overflow for help on this question but I couldn't find one that could explain to me what's wrong with my code.

The error only pops up sometimes. Most of the time it works. I thought it was an error in my switch case (the random integer generator limits), but I tried changing them up a few times but there was no difference. Can anyone help please? That's the only error that I'm facing. Thanks in advance!

Upvotes: 0

Views: 6360

Answers (2)

user7860670
user7860670

Reputation: 37513

At if (word[i] == guessed[j]) j index might get out of bounds because it is capped by j < strlen(word.c_str()); instead of guessed length. Also it would be worth to change guessed type into ::std::string. So loop condition need to be changed to something like

for
(
    ::std::string::size_type guessed_symbol_index = 0;
    guessed.length() != guessed_symbol_index;
    ++guessed_symbol_index
) 
...
if (word[i] == guessed[guessed_symbol_index]) {

All the switch cases are filled with lines that may cause out of bounds access, such as animal[(rand() % 17) - 1]; if rand returns 17. Also you may accidentally set wrong array size (using magic numbers is bad). You should redefine those arrays as

::std::array<::std::string, 7> day = { "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" };

and then extract length properly:

day[static_cast<::std::size_t>(rand()) % day.size()];

Upvotes: 0

Christophe
Christophe

Reputation: 73376

With the debugger it goes faster. But here the deductive way:

  1. std::length_error is thrown when there are attempts to exceed implementation defined length limits for some object such as max_size for string or vector. (Note that this is not an out of range issue, but an issue with the size that is required to store the object).

  2. You don't use vector and the only string you have is word. The only place where word could exceed the storage limit is when you make your assignments to it. But the limit is such large that this cannot be triggered by the small words in your arrays.

  3. Consequently, this means that your array access doesn't access the elements you think it accesses. So there must be an issue with the subscripts.

  4. And here we land on rand() which returns a value between 0 and RAND_MAX. With the modulo, you ensure that the upper limit is ok. But what if rand() returns 0 ? You then access the element -1, which is undefined behavior and the root cause of your unexpected crash.

How to solve it ?

Change your subscript expressions. For example in animal[], you have 17 elements, indexed from 0 to 16. So if you change your subscript to rand() % 17 you will get a number between 0 and 16 which is guaranteed to be within the expected bounds.

You could also use vectors instead of arrays, which could avoid the manual counting of elements in it:

 <string> animal{ "cat", "dog", "horse", "elephant", "duck", "turtle", "bird", "crab", "spider", "bear", "pig", "penguin", "mouse", "rabbit", "lion", "monkey", "bull"};
 ...
 word = animal[ rand()%animal.size() ]; // asuming that the vector is not empty :-)

P.S: strlen(word.c_str()) can be replaced with word.size()

Upvotes: 3

Related Questions