它与std :: string的区别如何?
"字符串"实际上只是一个char
s 数组; 以null结尾的字符串是一个空字符'\0'
标记字符串结尾的字符串(不一定是数组的结尾).代码中的所有字符串(由双引号分隔""
)由编译器自动以空值终止.
例如,"hi"
就像是一样{'h', 'i', '\0'}
.
以null结尾的字符串是连续的字符序列,最后一个字符的二进制位模式全为零.我不确定你的意思是"通常的字符串",但如果你的意思是std::string
,那么a std::string
不需要(直到C++ 11)是连续的,并且不需要有终结符.此外,一个std::string
字符串数据总是由std::string
包含它的对象分配和管理; 对于以null结尾的字符串,没有这样的容器,您通常使用裸指针来引用和管理这些字符串.
所有这些都应该包含在任何体面的C++教科书中 - 我建议掌握Accelerated C++,这是最好的之一.
表示字符串有两种主要方式:
1)结尾处带有ASCII null(nul)字符0的字符序列.您可以通过搜索终结器来判断它有多长.这称为以空字符结尾的字符串,或者有时以空字符结尾.
2)一系列字符,加上一个单独的字段(整数长度或指向字符串末尾的指针),告诉你它有多长.
我不确定"通常的字符串",但经常发生的是,在谈论特定语言时,"字符串"一词用于表示该语言的标准表示.所以在Java中,java.lang.String是一个类型2字符串,所以这就是"字符串"的含义.在C中,"string"可能表示类型1字符串.为了准确起见,标准非常冗长,但人们总是想省略什么是"显而易见的".
不幸的是,在C++中,这两种类型都是标准的.std :: string是类型2字符串[*],但是从C继承的标准库函数对类型1字符串进行操作.
[*]实际上,std :: string通常实现为字符数组,具有单独的长度字段和 nul终止符.这样c_str()
就可以在不需要复制或重新分配字符串数据的情况下实现该功能.我不记得在没有存储长度字段的情况下实现std :: string是否合法:问题是标准需要什么样的复杂性保证.对于容器,一般size()
建议为O(1),但实际上并不需要.所以,即使它是合法的,只使用nul-terminators的std :: string实现也会令人惊讶.
'\0'
是一个ASCII字符,代码为0,空终止符,空字符,NUL.在C语言中,它用作表示字符串结尾的保留字符.许多标准函数,如strcpy,strlen,strcmp等都依赖于此.否则,如果没有NUL,则必须使用另一种信号字符串结束信号的方式:
这允许字符串是任何长度,只有一个字节的开销; 存储计数的替代方法要求字符串长度限制为255或开销超过一个字节.
来自维基百科
C++ std::string
遵循这个其他约定,其数据由一个名为的结构表示_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::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 (&_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); };
可以通过以下方式获得实际数据:
_Rep* _M_rep() const { return &((reinterpret_cast<_Rep*> (_M_data()))[-1]); }
此代码段来自basic_string.h
我机器上的文件usr/include/c++/4.4/bits/basic_string.h
如您所见,差异很大.