在 C++11 中,a 的字符std::string
必须连续存储,如 § 21.4.1/5 指出的:
basic_string 对象中的类字符对象应连续存储。也就是说,对于任何 basic_string 对象 s,恒等式 &*(s.begin() + n) == &*s.begin() + n 对于 n 的所有值都成立,使得 0
但是,第 21.4.7.1 节列出了检索指向底层存储的指针的两个函数(重点是我的):
const charT* c_str() const noexcept;
const charT* data() const noexcept;
1 返回:一个指针 p,对于 [0,size()] 中的每个 i,p + i == &operator[](i)。
2 复杂性:恒定时间。
3 要求:程序不得更改存储在字符数组中的任何值。
对于第 3 点,我能想到的一种可能性是,该指针可能会因对象的以下使用而变得无效(第 21.4.1/6 节):
- 作为任何标准库函数的参数,将对非常量 basic_string 的引用作为
争论。
- 调用非常量成员函数,operator[]、at、front、back、begin、rbegin、end 和 除外
撕裂。
即便如此,迭代器也可能会失效,但我们仍然可以修改它们,直到它们失效为止。我们仍然可以使用该指针,直到它也无法从缓冲区读取。
为什么我们不能直接写入这个缓冲区?是因为它会使类处于不一致的状态,例如,end()
不会更新新结局吗?如果是这样,为什么允许直接写入类似的缓冲区std::vector
?
其用例包括能够传递std::string
到 C 接口来检索字符串而不是传入vector<char>
相反,并使用迭代器初始化字符串:
std::string text;
text.resize(GetTextLength());
GetText(text.data());
为什么我们不能直接写入这个缓冲区?
我将陈述显而易见的一点:因为它是const
。并抛弃一个const
值然后修改该数据是......粗鲁的。
现在,这是为什么const
?这可以追溯到写时复制被认为是一个好主意的时代,所以std::basic_string
必须允许实现来支持它。获取指向字符串的不可变指针(例如,用于传递给 C-API)将非常有用without产生副本的开销。所以c_str
需要返回一个const
指针。
至于为什么是这样still const
?嗯……这涉及到标准中的一个奇怪的事情:空终止符。
这是合法的代码:
std::string stupid;
const char *pointless = stupid.c_str();
pointless
必须是以 NUL 结尾的字符串。具体来说,它必须是指向 NUL 字符的指针。那么NUL字符从何而来呢?有几种方法std::string
实现以使其发挥作用:
- 使用小字符串优化,这是一种常见的技术。在这个方案中,每个
std::string
实现有一个内部缓冲区,可用于单个 NUL 字符。
- 返回一个指向静态存储器,包含 NUL 字符。因此,每
std::string
执行将返回same如果是空字符串则为指针。
不应强迫每个人都实施 SSO。因此,标准委员会需要一种方法来保留#2。其中一部分是给你一个const
字符串来自c_str()
。既然这段记忆很可能real const
,不是假的“请不要修改此内存const
,”给你一个指向它的可变指针是一个坏主意。
当然,你仍然可以通过这样做来获得这样的指针&str[0]
,但是标准很明确修改 NULL 终止符是一个坏主意 https://stackoverflow.com/questions/12740403/legal-to-overwrite-stdstrings-null-terminator.
现在,话虽如此,它是完全有效修改&str[0]
指针,以及其中的字符数组。只要你保持在半开范围内 [0,str.size()
)。你只是不能通过返回的指针来做到这一点data
or c_str
。是的,尽管事实上标准requires str.c_str() == &str[0]
是真实的。
这对你来说是标准的。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)