【问题标题】:allocate memory for a vector and change size without calling default constructor为向量分配内存并更改大小而不调用默认构造函数
【发布时间】:2021-04-23 17:56:49
【问题描述】:

我正在使用一个 openssl EVP_PKEY_sign,其中签名缓冲区大小是已知的,可以使用向量进行分配。

size_t SignatureLength;
std::vector<unsigned char> Signature;
EVP_PKEY_sign(EvpPkeyCtx, NULL, &SignatureLength, MessageDigest.data(), MessageDigest.size());
Signature.resize(SignatureLength);
EVP_PKEY_sign(EvpPkeyCtx, Signature.data(), &SignatureLength, MessageDigest.data(), MessageDigest.size());
Signature.resize(SignatureLength);

我正在使用Signature.data() 来获取指向向量使用的缓冲区的原始指针。 第一次调用EVP_PKEY_sign 给出了输出签名缓冲区的最大长度。 在调用 resize 时,缓冲区会被 0 填满,这会导致额外的开销,使其成为 O(n) 操作,而只需要 O(1) 分配。

另一种方法是调用reserve,但它失败了,因为它只分配内存并再次调用EVP_PKEY_signMessageDigest.size()仍然为零并将其调整为实际签名长度(来自第二次调用)然后将覆盖缓冲区默认值。

这样做的有效方法是什么?

【问题讨论】:

  • 这里的根本问题是两种不同语言之间的互操作,C 和 C++。它相对容易——例如,C++ 和 Python 更难,或者 C 和 Java。但即便如此,由于 C 和 C++ 有不同的方式来管理内存,因此效率会有所下降。

标签: c++ vector stl openssl c++17


【解决方案1】:

签名往往很短。将少量值设置为 0 是一种非常快速的操作,因此对于例如单个分配,尤其是对于充其量具有线性复杂度的签名计算而言,这不太可能是显着的成本。

使用custom allocator 可以避免初始化。


您可以通过使用构造函数在创建向量时分配内存来简化:

EVP_PKEY_sign(EvpPkeyCtx, nullptr, &SignatureLength, ...
std::vector<unsigned char> Signature(SignatureLength);
EVP_PKEY_sign(EvpPkeyCtx, Signature.data(), ...

但这不会影响初始化。

【讨论】:

  • DefaultInsertable 是值初始化,不是吗?并且非类非数组类型的值初始化将其设置为零。此外,如果不是这样,C++11 的变化将破坏数百万个完全正常的 C++98 程序。
  • @MSalters 嗯,看起来是这样。我已经修改了答案。
【解决方案2】:

我同意值初始化现在很重要的另一个答案。如果你仍然想绕过它,你可以改用std::unique_ptr&lt;unsigned char[]&gt;

size_t SignatureLength;

EVP_PKEY_sign(EvpPkeyCtx, NULL, &SignatureLength, MessageDigest.data(), MessageDigest.size());
std::unique_ptr<unsigned char[]> Signature{new char[SignatureLength]};
EVP_PKEY_sign(EvpPkeyCtx, *Signature, &SignatureLength, MessageDigest.data(), MessageDigest.size());

我认为很多时候,std::unique_ptr&lt;T[]&gt; 可以很好地与 C-API 配合使用,而 C-API 将在 C 中与一些手动管理的数组或堆栈缓冲区一起使用。上面的代码有两个小警告:

  1. 你有一个原始的new 在那里。这有点难看,但没问题,因为从原始指针构造 std::unique_ptr 不能抛出,而且你没有用 new 调用任何可能抛出的构造函数,所以它永远不会泄漏。如果您使用make_unique,它将初始化数组,与向量相同。
  2. unique_ptr 不知道它自己的大小,因此您必须自己记住这一点或围绕它编写一个包装器。这也意味着您无法将其下方的内存调整为实际长度。但请注意,调整向量的大小也只是减小了向量的大小(基本上写入大小成员变量)并且不会释放任何容量(第二个需要新的分配和整个数据的副本,这可以是相当昂贵)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-04
    • 2016-03-25
    • 2018-12-21
    • 1970-01-01
    • 1970-01-01
    • 2015-03-29
    • 2022-10-23
    • 2021-08-31
    相关资源
    最近更新 更多