当数据存储为uint8_t的向量时,如何传递'char* data'?

How to pass on 'char* data' when the data is stored as vector of uint8_t?

我有一个 class 定义如下:

class sco
{
private:

public:
    vector<uint8_t> data;

    sco(vector<uint8_t> data);
    ~sco();

};

构造函数所在位置:

sco::sco(vector<uint8_t> data) {       
    this->data = data;
}

然后我有一个声明如下的函数:

void send(unsigned& id, char* data, char len);

我的问题是我需要将一个sco成员的数据传递给它,但是类型和指针的不同让我很困惑。如果我有一个成员 newSco,里面有一些数据,将这个发送函数称为 send(someId, (char*)&(newSco.data.begin()), newSco.data.size() ); 是否合理?请注意,函数 send 是针对微控制器的,它接受 char 类型,所以我不能改变它,我也不能改变 uint8_t 因为这是来自串行通信的类型。我已经浪费了 3 天多的时间试图将类型转换为相互的东西,只是为了将其反转回来,因为它破坏了一切。我放弃了,我将不再尝试操纵类型,因为我只是没有那种时间,只是需要它工作,即使这是不好的做法。我以为 uint8_t 和 char 的大小相同,所以没关系。

是的,在这种情况下将指针转换为 char* 是合理的。通常,由于 strict aliasing rules,您可能不会使用不同类型的指针引用相同的内存位置。但是,'char' 是一种特殊情况,因此允许转换。

您不应该将 uint32_t* 转换为 int32_t,例如,即使它在某些情况下可能有效。

编辑:如以下评论所述:从 uint8_t* 转换为 char* 可能没问题,从迭代器转换则不然。使用 .data() 而不是 .begin()。

这应该有效:

send(someId, static_cast<char*>(&newSco.data[0]), static_cast<char>(newSco.data.size()));

附加信息:

How to convert vector to array

What is the difference between static_cast<> and C style casting?

send(someId, (char*)&(newSco.data.begin()), newSco.data.size() )

你几乎已经掌握了它,但还不够。

原因如下:

  • begin() 给你一个迭代器。您正在获取该迭代器的地址,因此您处于间接级别。使用 C-style 转换掩盖了 type-related 编译错误。

    我们可以写 (char*)&*(newSco.data.begin()) 来取消引用迭代器,然后取第一个结果元素的地址。

  • 但是如果容器是空的,这就很破了。你不能取消引用一个不存在的东西。

所以现在我们尝试:

send(someId, (char*)&newSco.data[0], newSco.data.size() )

不幸的是,如果容器为空,不安全,因为.data[0]也有效地取消引用了一个可能不存在的元素。有人认为隐含的 &* "cancels it out",但这是有争议的,我从不相信。

如果你曾经迁移到 C++11 或更高版本,你可以使用完全安全的:

send(someId, (char*)newSco.data.data(), newSco.data.size() )

否则,坚持使用 &newSco.data[0] ,但当 newSco.data.size() 为零 时跳过整个 send 调用。怎么强调都不为过。

转换为 char* 本身是安全的;您可以以这种方式自由地将 uint8_ts 解释为 chars;有一个特殊的规则。这个模式我自己用过几次。

但是,如上所述,C-style 演员表并不理想。喜欢一个很好的重新解释。我还会添加一些 const 以备不时之需(如果您的 MC API 不允许,请跳过此步骤):

if (!newSco.data.empty())
{
   send(
      someId,
      reinterpret_cast<const char*>(&newSco.data[0]),
      newSco.data.size()
   );
}

那里。华丽的。

最后一点,由于 API 的最后一个参数是 char,我会考虑为容器大小设置一个上限。您可以 运行 循环中的函数,一次发送 CHAR_MAXnewSco.data.size() 字节(以较小者为准)。否则,如果您期望容器中有超过 CHAR_MAX 个元素,您将会遇到严重的溢出!

尝试使用reinterpret_cast。 示例:

#include <iostream>
#include <unistd.h>
#include <vector>

int main(int argc, char const *argv[])
{
    std::vector<uint8_t> v{65,66,67,68,69,70};
    char* ptr = reinterpret_cast<char*>(v.data());

    for (auto i{0}; i < v.size(); i++) {
        std::cout << *ptr++ << std::endl;
    }
    return 0;
}

您的情况:

void send(someId, reinterpret_cast<char*>(sco.data.data()), sco.data.size());

如果您至少可以使用 C++11,则 std::vector class 提供 data() 其中 returns 原始数组。
如果您在 C++11 之前,恐怕您必须遍历向量并手动构建 char*.

但是,你不能 static_cast 一个 unsigned char* 到一个 char*。这是不允许的。
但你很幸运,char*是一个例外,没有违反严格的别名规则。所以你可以使用 reinterpret_cast 代替。

所以您可以通过以下方式解决您的问题:

C++11 之前:

std::vector<uint8_t> a; // For the example
a.push_back('a');
a.push_back('b');
a.push_back('c');
a.push_back('d');

char b[a.size()];
for(unsigned int i = 0; i < a.size(); ++i)
{
    b[i] = static_cast<char>(a[i]);
}

C++11之后:

std::vector<uint8_t> a {'a', 'b', 'c', 'd'}; //uint8_t aka unsigned char
char * b = reinterpret_cast<char*>(a.data()); // b is a char* so the reinterpret_cast is safe

希望对你有所帮助