【问题标题】:What does vec.data() return if vec.size() == 0?如果 vec.size() == 0,vec.data() 会返回什么?
【发布时间】:2016-03-15 09:20:04
【问题描述】:

cppreferencestd::vector::data 的注释:

返回指向作为元素存储的底层数组的指针。指针使得范围[data(); data() + size()) 始终是有效范围,即使容器为空。

这里的“有效范围”到底是什么意思?如果向量长度为​​零,data() 会返回什么?

具体来说,对于零长度向量:

  1. data() 可以是空指针吗?
  2. 可以安全地取消引用吗? (即使它指向垃圾。)
  3. 是否保证两个不同(零长度)向量之间存在差异?

我正在使用一个 C 库,它接受数组并且即使对于零长度数组也不允许空指针。但是,如果数组长度为零,它实际上并没有取消引用数组存储指针,它只是检查它是否为NULL。我想确保我可以安全地将data() 传递给这个 C 库,所以唯一相关的问题是上面的 (1)。 (2) 和 (3) 只是出于好奇,以防出现类似情况。


更新

根据没有转成答案的cmets,我们可以试试下面的方案:

#include <iostream>
#include <vector>

using namespace std;

int main() {
    vector<int> v;
    cout << v.data() << endl;

    v.push_back(1);
    cout << v.data() << endl;

    v.pop_back();
    cout << v.data() << endl;

    v.shrink_to_fit();
    cout << v.data() << endl;

    return 0;
}

用我的编译器输出:

0x0
0x7f896b403300
0x7f896b403300
0x0

这表明:

  • data() 确实可以是空指针,因此答案是 (1) 是 (2) 否 (3) 否

  • 但它并不总是零大小向量的空指针

是的,显然我应该在询问之前尝试一下。

【问题讨论】:

  • size() == 0 时,[data(); data() + size()) 中没有元素,所以可能没有有效的指针。 (我将其作为评论发布,因为我不确定)
  • 是、否和否.. 现在没有标准的链接,否则会回答的。
  • 我会说返回值是未定义的,因为标准没有明确定义(例如arraydata方法返回值被定义为未指定和begin() == end() == data()
  • @Revolver_Ocelot 如果未定义,则为缺陷。
  • @Szabolcs:实际上,“即使容器是空的”文本在您的引用中实现的所有效果,是确认 data() 必须返回 T* 类型的值,即使向量是空的。我猜他们觉得有必要加强这一点,以防万一阅读文本的实施者认为由于没有“底层存储”用于空向量,因此用户不应该在其上调用data()。因此,因为对于任何指针值 pp + 0 == p[p,p) 都是有效范围,通过说“即使为空”,他们基本上只是在说“它仍然是一个指针”。

标签: c++ vector


【解决方案1】:

“有效范围”由 [iterator.requirements.general]/7 (C++14) 定义:

“范围[i,j) 有效当且仅当j 可以从i 到达”。

幸运的是,C++ 定义将0 添加到空指针会产生空指针。那么,空指针 reachable 是否可以从空指针中获取?这是由同一部分的第 6 点定义的:

迭代器 j 从迭代器 i 被称为可达,当且仅当表达式 ++i 的应用程序的有限序列使得 i == j

零长度序列是有限序列,因此data() 可能返回空指针。

因此,您的问题的答案是:

  1. data() 可以是空指针吗?

是的

  1. 可以安全地取消引用吗? (即使它指向垃圾。)

  1. 是否保证两个不同(零长度)向量之间存在差异?

【讨论】:

  • 我很惊讶nullptr + 0 需要明确讨论。指针不是 always 只是非负整数(为了算术,而不是类型转换)?如果是这样,那么nullptr + 0 == nullptr 就很明显了。
  • @Szabolcs:不,指针不是偶数。从您不能添加两个指针的事实来看,这一点非常明显。至于非负数,因为不是数字,所以不能和数字比较,所以ptr &lt; 0也没有意义,既不是有符号也不是无符号。
  • @Szabolcs 这是 C 中未定义的行为
  • @Szabolcs:至于“指针不总是非负整数”不,在几乎每个系统上,成员指针通常是其他指针的两倍。
  • @Szabolcs,指针不是 C/C++ 中的数字。有一个子句不仅禁止取消引用,甚至禁止循环或生成无效指针值。这是有原因的。过去,曾提出过将指针存储在专用寄存器中的 CPU 架构。将值加载到此类寄存器中会导致其关联的页面描述符被预取,如果值无效,这可能会导致“过早”分段错误。
【解决方案2】:

评论太长,所以在这里发布。

我希望迭代器对于空序列为 nullptr,因此我对其进行了测试。

#include <iostream>
#include <vector>

void pr(std::vector<int>& v){
    std::cout << &*v.begin() << ", " << &*v.end() << "\n";
} 
// technically UB, but for this experiment I don't feel too bad about it.
// Thanks @Revolver    
int main(int argc, char** argv) {
    std::vector<int> v1;
    std::vector<int> v2;

    pr(v1);
    pr(v2);

    return 0;
}

这确实打印出来了

0, 0
0, 0 

现在对于空容器,有效范围的唯一合理操作是begin() == end()。不,垃圾不能被取消引用,所以 *v.begin() 不是问题。

【讨论】:

  • 此代码包含未定义的行为(取消引用无效的迭代器)。无论如何:coliru.stacked-crooked.com/a/7ebaf9f496bbfb21
  • 我现在感到很尴尬...当然我应该尝试打印data(),然后我会看到它是0,我的编译器会自动回答所有三个问题。它可以为空,因此不能保证差异,也不能取消引用。
  • @Szabolcs 另一个更接近原始答案的反驳:coliru.stacked-crooked.com/a/2ce8a1a2eeeed071 只有当向量 always 为空时,您才会得到空指针
  • @Revolver_Ocelot 感谢您的展示! shrink_to_fit() 用我的编译器再次使它成为一个空指针。将所有这些都写在答案中会很好。
  • 这并不能回答向量会做什么的问题。它回答了你的编译器在这个特定实例中所做的事情。
【解决方案3】:

来自标准:

23.3.6.4 [vector.data]

T* data() noexcept;

const T* data() const noexcept;

返回:[data(),data() + size()) 是有效范围的指针。为一个 非空向量,data() == &front()。

因此,对于空向量,它可以为 null,但不一定是可解引用的,也不一定是唯一的。

【讨论】:

  • 你引用的哪一部分支持data 被允许为空向量的null 的断言?
  • 如果这个答案明确回答了 OP 问题 1,2,3,那就太好了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-31
  • 2012-07-29
  • 2015-06-21
  • 2012-09-25
  • 1970-01-01
  • 2021-05-04
相关资源
最近更新 更多