如果我在 std::string
变量上使用 std::reverse
函数,我可以安全地假设空字符 '\0'
将被放置在字符串的开头吗?
不,它不会包含一个NUL字节(除非你自己添加)。前向迭代器也不会包含NUL字节,它只会迭代字符串本身中的字符。
然而,NUL字节保证在.c_str()或.data()
的结尾之后。
#include <string>
#include <iostream>
#include <algorithm>
int main() {
std::string s = "Hello";
std::cout << "Forwards:\n";
for (auto i = std::begin(s), e = std::end(s); i != e; ++i) {
std::cout << *i << ' ' << static_cast<int>(*i) << '\n';
}
std::cout << "Backwards:\n";
std::reverse(std::begin(s), std::end(s));
for (auto i = std::begin(s), e = std::end(s); i != e; ++i) {
std::cout << *i << ' ' << static_cast<int>(*i) << '\n';
}
return 0;
}
正向: H 72 e 101 l 108 l 108 o 111 反向: o 111 l 108 l 108 e 101 H 72
c_str()
是 O(1) 的原因。 - Barry.c_str()
或.data()
中的字符,包括NUL字节。你只是不能使用标准的std::string
迭代器。 - Dietrich Epp\0
的方法是...一开始就附加\0
。否则,它就不会是O(1)... - Barrys.data()
或s.c_str()
或s[s.length()]
或任何其他const函数必须是安全的,这使得实现在这些函数中执行任何写入操作非常困难(如果它们确实进行任何此类写入,则必须添加足够的同步以避免数据竞争)。 - Jonathan Wakelystd::string
的规则已经发生了变化。在 C++11 之前,你不能保证有一个类似 C 的结束零字节。而自从 C++11 之后,你是有保障的,实际上 c_str()
和 data()
是相同的。 - user4992621std::string
实际上在end()
迭代器下有数据( 标准虽然使解引用 end()
仍然是非法的,但实际上无法避免它)。.size()=N
的std::string
有N+1
个条目,最后一个条目是'\0'
。前面的N
个条目也可能包含'\0'
,但最后一个条目会自动放置。
begin()
返回指向第一个条目的迭代器,而end()
返回指向最后一个条目后一个位置的迭代器(有效地,指向终止的'\0'
,但根据标准是不允许检查的(这意味着调试迭代器可以捕获该错误,并告诉你你犯了错误))。.data()
和.c_str()
返回指向原始缓冲区的指针。使用.c_str()
读取'\0'
"超出结尾"是合法的。有趣的是,使用.data()
读取'\0'
结尾之外的内容是不合法的,因为只有可以迭代到的元素才允许被读取。这在标准下是合法的,但不太可能,在第一次调用.c_str()
之前'\0'
处会有一个未初始化的字符(甚至是一个页面错误)。两者都不允许在C++11中分配。(我不对C++03或更早版本的C++做任何保证)。rbegin()
和rend()
也返回字符串中的元素,不包括终止的'\0'
,并且reverse(begin(), end())
再次操作字符串中的元素,不包括终止的'\0'
。'\0'
,则这些不会终止字符串。如果将.c_str()
传递给const char*
API,则它将假定字符串已结束,但由std::string
管理的实际缓冲区将超出您注入的'\0'
。 end()
仍然将继续引用该字符串的"最后一个元素的后一个位置",而不是您注入的'\0'
。std::string
是什么? 您运行了 std::string x = "abcd\0";
吗?还是运行了 std::string x = "abcd";
?或者是这样运行的 char const buff = "abcd"; std::string x(std::begin(buff), std::end(buff));
?这三者都可能被某些人称为 std::string
'abcd\0',但其中两个与第三个不同。如果您想知道代码做什么,请实际编写该代码并询问它做了什么。您还可以添加代码的解释以及您正在做事情的原因,但要询问伪代码的作用通常需要包含在伪代码中的细节。 - Yakk - Adam Nevraumontstd::string x = "abcd";
。 - user5090588std::reverse(x.begin(), x.end());
后,保证 x=="dcba"
为真。 - Yakk - Adam Nevraumontstd::reverse
之后变为dcba\0? - user5090588data()[i] == operator[](i)
对于每个i在[0, size())上成立。 (直到C++11)" 和 "data() + i == &operator[](i)
对于每个i在[0, size())上成立。 (自C++11起)"。这解释了cppreference文档中的模糊用语。有趣的是,data()
强制[size()]
返回[size()-1]
之后的元素,即使operator[]
没有这样做! - Yakk - Adam Nevraumonts.c_str()
或s.data()
或s[s.length()]
,则保证存在。 - Jonathan Wakely
std::string
在其内部表示中不一定必须包含\0
,反转它也不会改变这一点。 - Maksim Solovjov*std::end(s)
在许多实现中可能有效,但标准并不保证其有效性,在其他实现上实际上会导致错误。这是因为大多数std::string
的实现会在末尾保留一个空字符以简化.data()
和.c_str()
的实现。我知道确实会导致错误,在某些 MSVC 实现的调试版本中解引用std::end(s)
。 - Dietrich Eppcout
结果时,可以检查\0
并省略千位。 - user5090588