Why does cout fail to output strings sometimes, but printf has no problem?

161 views Asked by At

Problem: when printing items in a hash table, non top-level items do not print correctly. I add items line-by-line from a text file. First, number of lines are determined, and then, the hash table is constructed, with the size found in the first loop of the file.

hash constructor

hash::hash(unsigned int tSize)
{
    this->tableSize = tSize;
    hashTable.resize(tableSize);
    for (int i = 0; i < tableSize; i++)
    {
        hashTable[i] = new item;
        hashTable[i]->firstLetters = "__";
        hashTable[i]->str = "____";
        hashTable[i]->next = NULL;
    }
}

adding items, setting values, and printing them

void hash::add(std::string key)
{
    int index = hafn(key);
    if (hashTable[index]->str == "____")
    {
        hashTable[index]->str = key;
        setFirstLetters(index,key[0],key[1]);
    }
    else
    {
        item *iter = hashTable[index];
        item *n_ptr = new item;
        n_ptr->str = key;
        setFirstLetters(n_ptr, key[0], key[1]);
        n_ptr->next = NULL;
        while (iter->next != NULL)
        {
            iter = iter->next;
        }
        iter->next = n_ptr;
    }
}

void hash::setFirstLetters(item *n_ptr, char a, char b)
{
    n_ptr->firstLetters[0] = a;
    n_ptr->firstLetters[1] = b;
}

void hash::setFirstLetters(int index, char a, char b)
{
    hashTable[index]->firstLetters[0] = a;
    hashTable[index]->firstLetters[1] = b;
}


void hash::print()
{
    int num;
    for (int i = 0; i < tableSize; i++)
    {
        num = numInIndex(i);
        printer(num, i);
        if (num > 1)
        {
            int c = 0;
            for (int j = num - 1; j > 0; j--)
            {
                printer(c, num, i);
                c++;
            }
        }
    }
}

void hash::printer(int num, int i)
{
    item *iter = hashTable[i];
    cout << "-------------------------------" << endl;
    cout << "index = " << i << endl;
    cout << iter->str << endl;
    cout << iter->firstLetters << endl;
    cout << "# of items = " << num << endl;
    cout << "-------------------------------" << endl;
    cout << endl;
}

void hash::printer(int numIn, int num, int i)
{
    item *iter = hashTable[i];
    for (int j = 0; j < numIn; j++)
    {
        iter = iter->next;
    }
    cout << "-------------------------------" << endl;
    cout << "index = " << i << endl;
    cout << iter->str << endl;
    cout << std::flush;
    cout << iter->firstLetters << endl; //this does not work, though the pointer is pointing to the correct values
    //printf("%s\n", iter->firstLetters.c_str()); //this works, even without flushing
    cout << "# of items = " << num << endl;
    cout << "-------------------------------" << endl;
    cout << endl;
}


    struct item
    {
        std::string str;
        std::string firstLetters;
        item *next;
    }; 

The problem is that firstLetters does not print correctly. firstLetters is set correctly. However, in third- and later level items (ie, using the same hash index), firstLetters does not print at all.

In order to be more clear, here is an output example:

-------------------------------
index = 15
will
wi
# of items = 3
-------------------------------

-------------------------------
index = 15
will
wi
# of items = 3
-------------------------------

-------------------------------
index = 15
good

# of items = 3
-------------------------------

Notice, under the heading "adding items, setting values, and printing them," and in the method hash::add(std::string key), I use setFirstLetters() to access the elements inside std::string firstLetters without first initializing them. This causes any change of these values to be lost, essentially. When accessing iter->firstLetters when it's time to print the values, no actual data can be accessed. To avoid this undefined behavior, I changed the definition of hash::add(std::string key) to set the value of firstLetters before attempting to change them.

New definition of hash::add()

void hash::add(std::string key)
{
    int index = hafn(key);
    if (hashTable[index]->str == "____")
    {
        hashTable[index]->str = key;
        setFirstLetters(index,key[0],key[1]);
    }
    else
    {
        item *iter = hashTable[index];
        item *n_ptr = new item;
        n_ptr->firstLetters = "__";        //here
        n_ptr->str = key;
        setFirstLetters(n_ptr, key[0], key[1]);
        n_ptr->next = NULL;
        while (iter->next != NULL)
        {
            iter = iter->next;
        }
        iter->next = n_ptr;
    }
}

Adding this line fixes the output.
This is the new output:

-------------------------------
index = 15
will
wi
# of items = 3
-------------------------------

-------------------------------
index = 15
will
wi
# of items = 3
-------------------------------

-------------------------------
index = 15
good
go
# of items = 3
-------------------------------

This type of behavior counts as "undefined behavior." There are several moving parts here and the only cause of this behavior is due to lack of initialization. In cases where std::cout fails, but printf() does not, make sure any members/variables are initialized before attempting to access them. In specific cases using std::string, the [] operator can only function properly after initializing normally, using the = operator or other member functions of std::string.

0

There are 0 answers