This is a creation in
Article, where the information may have evolved or changed.
P.S. 这里不讨论COW(copy-on-write)和SSO(short-string-optimization)
STL string (gcc 4.9.3)
Through the source can be found, std::string inheritance and basic_string template, while basic_string contains only _alloc_hider _m_dataplus a non-static variable, and _alloc_hider contains only a pointer, So sizeof (string) might surprise you.
class basic_string{ //统计信息结构 struct _Rep_base { size_type _M_length; //长度 size_type _M_capacity; //能力 _Atomic_word _M_refcount; //引用计数 }; struct _Alloc_hider : _Alloc { _CharT* _M_p; // The actual data. //真实数据,就是个char* };private: .... // Data Members (private): // 可以看到string仅包含这一个数据字段 mutable _Alloc_hider _M_dataplus; ....};
About basic_string There is a trickery place: basic_string statistics are placed outside of data, obtained through an array [-1], the advantage is not to occupy basic_string memory, but also in O (1) time to obtain the structure has requested memory, Memory and reference count (for cow) have been used.
_Rep* _M_rep() const _GLIBCXX_NOEXCEPT { return &((reinterpret_cast<_Rep*> (_M_data()))[-1]); }
Get Real data
_CharT*_M_data() const _GLIBCXX_NOEXCEPT{ return _M_dataplus._M_p; }
Graphic
+---------> +-----------+ | | _m_length| | | +-----------+ | |_m_capacity| | +-----------+ | |_m_refcount| | +-----------| | | | | _m_rep ()-+-------------+ | | _rep_base +----+ +-------------+basic_string--| _m_dataplus +----+ +-------------+ | | | +---------------+----------> |1234567890.....| +---------------+
Write a simple program verification under
// 禁止编译优化 输出调试信息// g++ -O0 -gdwarf-3 -o test test.cpp (gdb) p p$4 = (std::string *) 0x7fffffffe3d0(gdb) p (*((const std::string* )0x7fffffffe3d0)->_M_rep())._M_length$5 = 9(gdb) p (*((const std::string* )0x7fffffffe3d0)->_M_rep())._M_capacity$6 = 9(gdb) p (*((const std::string* )0x7fffffffe3d0)->_M_rep())._M_refcount$7 = 0
Memory allocations are raised when _m_p needs to be updated and _m_capacity is insufficient to hold all the data, while other operations, such as length (), iterator, operator[, etc., are combined _m_data () with _m_rep () A series of logical operations, here is not one by one repeat.
Go string
Take a look at the definition of Go string and the source code that *byte converted to string.
位于 src/runtime/string.go
String is the set of all strings of 8-bit bytes, conventionally but not//necessarily representing utf-8-encoded text. A string May is empty, but//not nil. Values of String type is Immutable.type string string//the visible string consists of two parts: a pointer to the real address of the character, the length of the string, type stringstruct struct { STR unsafe. Pointer Len int}//Strong turn pointer func stringstructof (SP *string) *stringstruct {return (*stringstruct) (unsafe. Pointer (SP))}//by byte pointer stringfunc gostring (p *byte) string {l: = Findnull (p) if L = = 0 {RE Turn ""} s, B: = Rawstring (L) memmove (unsafe. Pointer (&b[0]), unsafe. Pointer (P), UIntPtr (L)) return s}//allocates memory, initializes func rawstring (size int) (s string, b []byte) {p: = MALLOCGC (UI Ntptr (size), nil, false) Stringstructof (&s). str = P Stringstructof (&s). Len = size * (*slice) (U Nsafe. Pointer (&b)) = slice{p, size, size} for {ms: = maxstring if uintptr (size) <= Ms || Atomic. Casuintptr ((*uintptr) (unsafe. Pointer (&maxstring)), MS, UIntPtr (size)) {return}}}
Let's look at a simple example to verify the following:
func main() { s := "1234567890" sLen := len(s) fmt.Println(sLen) s2 := s[3:] sLen2 := len(s2) fmt.Println(sLen2)}
(gdb) p s$1 = { str = 0x4a5857 "12345678906103515625GOMAXPROCScasgstatuscomplex128float32nanfloat64nangcscandonegoroutine gp.stkbar=invalidptrowner diedplainErrorruntime: gschedtracesemacquireterminatedtracefree(tracegc()\nunknown pc"..., len = 10}(gdb) p s.str$2 = ( uint8 *) 0x4a5857 "12345678906103515625GOMAXPROCScasgstatuscomplex128float32nanfloat64nangcscandonegoroutine gp.stkbar=invalidptrowner diedplainErrorruntime: gschedtracesemacquireterminatedtracefree(tracegc()\nunknown pc"...//这里可见 s2.str - s.str == 3(gdb) p s2.str$3 = ( uint8 *) 0x4a585a "45678906103515625GOMAXPROCScasgstatuscomplex128float32nanfloat64nangcscandonegoroutine gp.stkbar=invalidptrowner diedplainErrorruntime: gschedtracesemacquireterminatedtracefree(tracegc()\nunknown pc (t"...(gdb) p &s$4 = (struct string *) 0xc420057ea8// 而s2与s的地址缺相差16 正好是stringStruct的大小(64位机器)(gdb) p &s2$5 = (struct string *) 0xc420057e98(gdb) p s2.len$6 = 7
The memory layout looks like this
+-----------------------+ |12345678906103515.... | ++--+-------------------+ ^ ^ | |s: | | +---+ | | |str+----------------------+ | +---+ | |len| 10 | +---+ | |s2: | +---+ | |str+-------------------------+ +---+ |len| 7 +---+
The elements in Go string are non-modifiable, and the append (+) operation also copies the contents of the two parameters into the new request memory, so there is a performance bottleneck for successive calls to the + operator, and a new object is generated by string and []byte] ( If necessary, it can be replaced by an unsafe pointer conversion). Of course the go provides a way to manipulate string methods set strings, such as join, and so on to some extent can optimize performance.
Summary comparison
| string |
STL |
Go |
| Can write itself |
Is |
Whether |
| Length complexity |
O (1) |
O (1) |
| Allocating memory |
Is |
Whether |
Reference documents
[1]. STL Source code Analysis--Houtie
[2]. Redis design and implementation--Huangjianhong
[3]. Go language Reading notes--rain marks