std::vector<uint8_t> vector1 = { 1, 2, 3, 4 };
我想将上面的向量转换为 uint32_t 版本。我尝试做:
std::vector<uint32_t> vector2(vector1.begin(), vector2.end());
但是此代码在 32 位版本中返回相同的数组,因此内容仍然是 { 1, 2, 3, 4 }。
我期望得到的是一个包含单个值(在本例中)0x01020304 的数组。
有什么方法可以最好地在不使用for循环的情况下实现这一点?
谢谢你。
编辑:
我的解决方案,不知道这是否是一个好的做法,但从我对向量的了解来看,它应该是完全有效的:
std::vector<uint32_t> vector2((uint32_t*)vector1.data(), (uint32_t*)(vector1.data() + vector1.size()*sizeof(uint8_t)));
您可以通过使用 range-v3 库或 C++20 std::ranges 来使用范围。
std::vector<uint8_t> vector1 = { 1, 2, 3, 4, 5, 6, 7, 8 };
std::vector<uint32_t> vec2 =
vector1
| view::chunk(4)
| views::transform(
[](auto&& range){
return accumulate(
range,
static_cast<uint32_t>(0),
[](auto acc, auto i)
{
return acc << 8 | i;
}
);
})
| to<std::vector>();
vec2
将包含 {0x1020304, 0x5060708}。
在 godbolt 上查看:https://godbolt.org/z/6z6ehfKbq
为了简单起见,我建议使用循环来执行此操作
#include <cstdint>
#include <exception>
#include <vector>
std::uint32_t
combine(std::uint8_t a,
std::uint8_t b,
std::uint8_t c,
std::uint8_t d)
{
return (std::uint32_t(a) << 24) |
(std::uint32_t(b) << 16) |
(std::uint32_t(c) << 8) |
std::uint32_t(d);
}
std::vector<std::uint32_t>
combine_vector(const std::vector<std::uint8_t>& items)
{
if (items.size() % 4 != 0)
throw std::exception();
std::vector<std::uint32_t> ret;
ret.reserve(items.size() / 4);
for (std::size_t i = 0; i < items.size(); i += 4) {
ret.push_back(
combine(items[i + 0],
items[i + 1],
items[i + 2],
items[i + 3]));
}
return ret;
}
我怀疑您需要实现自定义迭代器类型才能在不使用原始循环的情况下执行此操作。
我做了这样一个迭代器
#include <iterator>
class combine_iterator
{
public:
using value_type = std::uint32_t;
using reference = const value_type&;
using pointer = const value_type*;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
combine_iterator(const std::uint8_t* data, std::size_t count) :
m_data(data)
{
if (count % 4 != 0)
throw std::exception();
if (count > 0)
++*this;
}
reference
operator*() const
{
return m_cur_val;
}
pointer
operator->() const
{
return &m_cur_val;
}
friend combine_iterator&
operator++(combine_iterator& rhs)
{
std::uint8_t a = *(rhs.m_data++);
std::uint8_t b = *(rhs.m_data++);
std::uint8_t c = *(rhs.m_data++);
std::uint8_t d = *(rhs.m_data++);
rhs.m_cur_val = combine(a, b, c, d);
return rhs;
}
friend combine_iterator
operator++(combine_iterator& lhs, int)
{
auto cp = lhs;
++lhs;
return cp;
}
friend bool
operator!=(const combine_iterator& lhs, const combine_iterator& rhs)
{
return (lhs.m_data != rhs.m_data);
}
private:
const std::uint8_t* m_data;
std::uint32_t m_cur_val;
};
int main()
{
std::vector<std::uint8_t> data = { 1, 2, 3, 4, 5, 6, 7, 8 };
std::vector<std::uint32_t> res(
combine_iterator(data.data(), data.size()),
combine_iterator(data.data() + data.size(), 0));
}
迭代器至少包含一个错误。我将这些错误作为教育课程保留下来,为什么使用循环通常比实现自定义迭代器更容易获得正确性。理想情况下,只有当我们创建需要它的容器时才应创建自定义迭代器,否则创建自定义迭代器的心理开销是合理的。
使用
std::vector<uint32_t> vector2((uint32_t*)vector1.data(), (uint32_t*)(vector1.data() + vector1.size()*sizeof(uint8_t)));
会产生相反顺序的向量。例如{4,3,2,1}.
更糟糕的是,如果您尝试迭代不是
mod 4
的字节向量,那么您可能会遇到段错误/异常。
如果您想转换不是
mod 4
的字节向量,那么您必须做出决定:用零值字节填充或截断回最接近的字节向量长度,即 mod 4
。毕竟,当 32 位字是结果的最小可能单位时,就不存在字节这样的东西了。
这是一个填充/截断然后转换的函数:
void bytes_to_words(std::vector<uint8_t> &bytes, std::vector<uint32_t> &words, bool zero_pad = true)
{
int word_width = sizeof(uint32_t);
int byte_mod = bytes.size() % word_width;
if(zero_pad)
{
// pad the word with zero-value bytes
if(byte_mod) bytes.insert(bytes.end(), word_width - byte_mod, 0x00);
}
else
{
// truncate the word vector down to the nearest whole word
bytes = std::vector<uint8_t> {bytes.begin(), bytes.end() - byte_mod};
}
for (auto it = bytes.begin(); it != bytes.end(); it += word_width)
{
words.push_back(
*(it) << 24
| *(it+1) << 16
| *(it+2) << 8
| *(it+3)
);
}
}
输入:
std::vector<uint8_t> bytes1{ 0x11, 0x22, 0x33, 0x44, 0x55};
填充后的结果:
0x11223344 0x55000000
截断结果:
0x11223344