'\0' 
is an ASCII character with code 0, null terminator, null character, **NUL**. In **C** language it serves as a reserved character used to signify the end of a string. Many standard functions such as strcpy, strlen, strcmp among others rely on this. Otherwise, if there was no **NUL**, another way to signal end of string must have been used:

> This allows the string to be any length with only the overhead of one
> byte; the alternative of storing a count requires either a string
> length limit of 255 or an overhead of more than one byte.
> 
> from [wikipedia](http://en.wikipedia.org/wiki/Null_character)

**C++** `std::string` follows this other convention and its data is represented by a structure called `_Rep`:

 // _Rep: string representation
 // Invariants:
 // 1. String really contains _M_length + 1 characters: due to 21.3.4
 // must be kept null-terminated.
 // 2. _M_capacity >= _M_length
 // Allocated memory is always (_M_capacity + 1) * sizeof(_CharT).
 // 3. _M_refcount has three states:
 // -1: leaked, one reference, no ref-copies allowed, non-const.
 // 0: one reference, non-const.
 // n>0: n + 1 references, operations require a lock, const.
 // 4. All fields==0 is an empty string, given the extra storage
 // beyond-the-end for a null terminator; thus, the shared
 // empty string representation needs no constructor.
 
 struct _Rep_base
 {
 	size_type		_M_length;
 	size_type		_M_capacity;
 	_Atomic_word		_M_refcount;
 };

 struct _Rep : _Rep_base
 {
 	// Types:
 	typedef typename _Alloc::template rebind<char>::other _Raw_bytes_alloc;
 
 	// (Public) Data members:
 
 	// The maximum number of individual char_type elements of an
 	// individual string is determined by _S_max_size. This is the
 	// value that will be returned by max_size(). (Whereas npos
 	// is the maximum number of bytes the allocator can allocate.)
 	// If one was to divvy up the theoretical largest size string,
 	// with a terminating character and m _CharT elements, it'd
 	// look like this:
 	// npos = sizeof(_Rep) + (m * sizeof(_CharT)) + sizeof(_CharT)
 	// Solving for m:
 	// m = ((npos - sizeof(_Rep))/sizeof(CharT)) - 1
 	// In addition, this implementation quarters this amount.
 	static const size_type	_S_max_size;
 	static const _CharT	_S_terminal;
 
 	// The following storage is init'd to 0 by the linker, resulting
 // (carefully) in an empty string with one reference.
 static size_type _S_empty_rep_storage[];
 
 static _Rep&
 _S_empty_rep()
 { 
 	 // NB: Mild hack to avoid strict-aliasing warnings. Note that
 	 // _S_empty_rep_storage is never modified and the punning should
 	 // be reasonably safe in this case.
 	 void* __p = reinterpret_cast<void*>(&_S_empty_rep_storage);
 	 return *reinterpret_cast<_Rep*>(__p);
 	}
 
 bool
 	_M_is_leaked() const
 { return this->_M_refcount < 0; }
 
 bool
 	_M_is_shared() const
 { return this->_M_refcount > 0; }
 
 void
 	_M_set_leaked()
 { this->_M_refcount = -1; }
 
 void
 	_M_set_sharable()
 { this->_M_refcount = 0; }
 
 	void
 	_M_set_length_and_sharable(size_type __n)
 	{
 #ifndef _GLIBCXX_FULLY_DYNAMIC_STRING
 	 if (__builtin_expect(this != &_S_empty_rep(), false))
 #endif
 	 {
 	 this->_M_set_sharable(); // One reference.
 	 this->_M_length = __n;
 	 traits_type::assign(this->_M_refdata()[__n], _S_terminal);
 	 // grrr. (per 21.3.4)
 	 // You cannot leave those LWG people alone for a second.
 	 }
 	}
 
 	_CharT*
 	_M_refdata() throw()
 	{ return reinterpret_cast<_CharT*>(this + 1); }
 
 	_CharT*
 	_M_grab(const _Alloc& __alloc1, const _Alloc& __alloc2)
 	{
 	 return (!_M_is_leaked() && __alloc1 == __alloc2)
 	 ? _M_refcopy() : _M_clone(__alloc1);
 	}
 
 	// Create & Destroy
 	static _Rep*
 	_S_create(size_type, size_type, const _Alloc&);
 
 	void
 	_M_dispose(const _Alloc& __a)
 	{
 #ifndef _GLIBCXX_FULLY_DYNAMIC_STRING
 	 if (__builtin_expect(this != &_S_empty_rep(), false))
 #endif
 	 if (__gnu_cxx::__exchange_and_add_dispatch(&this->_M_refcount,
 						 -1) <= 0)
 	 _M_destroy(__a);
 	} // XXX MT
 
 	void
 	_M_destroy(const _Alloc&) throw();
 
 	_CharT*
 	_M_refcopy() throw()
 	{
 #ifndef _GLIBCXX_FULLY_DYNAMIC_STRING
 	 if (__builtin_expect(this != &_S_empty_rep(), false))
 #endif
 __gnu_cxx::__atomic_add_dispatch(&this->_M_refcount, 1);
 	 return _M_refdata();
 	} // XXX MT
 
 	_CharT*
 	_M_clone(const _Alloc&, size_type __res = 0);
 };


----------


the actual data might be obtained with:

 _Rep* _M_rep() const
 { return &((reinterpret_cast<_Rep*> (_M_data()))[-1]); }

this code snippet comes from file `basic_string.h` which on my machine is located in `usr/include/c++/4.4/bits/basic_string.h`

So as you can see, the difference is significant.