2014-09-19 92 views
3

我注意到std::end在涉及字符串或字符数组时总是会引用空终止符。我认为std::end应该是指最后一个有效元素后面的数组的末尾。 '\0'不被视为有效元素?它是数组的一部分。这里有一些测试,所有返回true:应该std ::结束为字符串指向空过终止符?

#include <iostream> 

int main() 
{ 
    std::string s("hello!"); 
    auto s_end = *(s.data() + s.size() + 1); 
    std::cout << std::boolalpha << (*std::end(s) == s_end) << "\n" 
       << (s_end == '\0') << "\n"; 
    char buf[6 + 1]; 
    std::copy(s.begin(), s.end(), &buf[0]); 
    auto buf_end = *(buf + s.size() + 1); 
    std::cout << (*std::end(buf) == buf_end) << "\n" 
       << (buf_end == '\0') << "\n"; 

    char test[3] = {'h', '\0', 'e'}; 
    std::cout << (*std::end(test) == '\0'); 
    return 0; 
} 
+1

'end'迭代器不可解引用。通过取消引用,您的程序会显示未定义的行为。 – 2014-09-19 02:48:25

+3

null终止符不是被认为存储在'std :: string'中的一部分。例如,它不包含在'size()'中,而不是由'back()'返回。 – 2014-09-19 02:49:36

回答

4

对于字符数组,std::end确实指向过去的数组中的最后一个字符。对于

char test[3] = {'h', '\0', 'e'}; 

指针std::end(test)相同test + 3。解除引用与评估test[3]相同。这是未定义的行为。在你的具体情况下,它发生了,它产生了'\0'。但总的来说,它可能会产生不同的价值,或者崩溃,或者完全不同。 std::end(test)确实不是指向'\0'字符在索引1在阵列test

请注意,std::end对于所有阵列的行为是一致的。也就是说,如果我们有一个数组T a[N],那么std::end(a)返回a + N,无论Tchar还是a的内容是什么。它不会给你字符串的结尾;它会给你数组的结束。同样,返回值始终为a + N。没有例外!

对于std::string,有一个终止空字符,但它不被视为字符串的一部分。 (不像其他的字符,你不允许修改它,对未定义行为疼痛。)如果您有

std::string s("hello"); 

然后s[5]将有空字符的值,但正如我所说,这不是考虑部分字符串:s被认为有五个字符,而不是六个字符。最好认为std::string根本不是空终止的。最后一个字符是s[4],它的值为'o',而std::end(s)是刚过std::begin(s) + 4的迭代器,即std::begin(s) + 5

这比看起来更微妙一些,因为标准在技术上并不保证std::end(s)是可解引用的,所以你不一定会说它指向终止null。在实践中,它确实指向终止空值,但解除引用仍然是未定义的行为。