3
votes

I'm coding my version of the String class, but Valgrind whines about my implementation of the << operator for my string. The error is at the wrong line, if I print char by char it works great.

Where am I wrong?

Valgrind error:

==2769== Conditional jump or move depends on uninitialised value(s)

==2769== at 0x4C2AC28: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)

==2769== by 0x4ECAD60: std::basic_ostream >& std::operator<< >(std::basic_ostream >&, char const*) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.17)

==2769== by 0x400BD5: operator<<(std::ostream&, String&) (string.cpp:22)

==2769== by 0x400AAC: main (main.cpp:12)

My << operator for string:

ostream & operator << (ostream & o, String & inS) {
    o << inS._pData << " "; // the wrong line
    return o;
}

My String class:

class String {
    public:
         unsigned _size;
         char *   _pData;
         String();
         String(const char* inCString);
};

Constructor (for char*):

String::String(const char* inCString) {
    _size = strlen(inCString);
    _pData = new char[_size + 1];
    strncpy(_pData, inCString, _size);
}

Main.cpp:

int main(int, char**) {
    String s1("hello");
    cout << s1;
    return 0;
}
3
Can I ask why you're going to the trouble of re-implementing string?Benj
Did you obey the Rule of Three?Mooing Duck
@MooingDuck : Yup, but not the whole code's here; only the relevant ones.bagage
@Benj : it's a classwork; first month of cpp for us, why wanted you to know ?bagage
@Cqnqrd Classwork is a totally legit reason, if you were going to do this in production code, I'd call you crazy ;-)Benj

3 Answers

10
votes

I don't recommend using raw strings like this.

However, the culprit is here:

strncpy(_pData, inCString, _size+1);

Or, alternatively, store the NUL-termination character manually:

_pData[_size] = 0;

By missing the NUL-termination character, the output operation will just keep on running past the end of the string. (The behaviour may happen to look okay, since the character may be nul by chance, depending on compiler, options etc.)

Hint:

  • consider using C++ style instead of C API's
  • if you must use C-style char*, at least use stdrup and free
  • if you insist on doing NUL-terminated strings, consider writing that the C++ way too:

    #include <iostream>
    #include <vector>
    
    class String
    {
    public:
        std::vector<char> _data;
        String();
        String(const char* inCString);
    };
    
    std::ostream & operator << (std::ostream & o, String const& inS)
    {
        o.write(inS._data.data(), inS._data.size());
        return o << ' ';
    }
    
    String::String(const char* inCString)
    {
        for (const char* it=inCString; it && *it; ++it)
            _data.push_back(*it);
    }
    
    int main(int, char**)
    {
        String s1("hello");
        std::cout << s1;
        return 0;
    }
    
1
votes

Because you fail to write the zero byte at the end. You need:

strncpy(_pData, inCString, _size + 1);
//                              ^^^^

You should always read the manual very carefully with the n-versions of C string functions, since they all have subtly different semantics.

-1
votes

Notice that you are not explicit initializing your data members, you are assigning a value to them:

...in order to initialize, you should change to:

String::String(const char* inCString) :
    _size(strlen(inCString)),
    _pData(new char[_size + 1])
{
    strncpy(_pData, inCString, _size);
}