首页 > 解决方案 > 在 C++ 中以安全的方式将 char* 转换为 uint8_t*

问题描述

我有一个(基)类,它有一个应该返回指针的虚函数。有两个类派生自这个类。

class A{
//...
    virtual uint8_t* getPointer(){

    }
}

class B: public A{
//...
    uint8_t* getPointer() override {
        return static_cast<uint8_t*>(myUnsignedChar);
    }
private:
    unsigned char* myUnsignedChar;
}


class C: public A{
//...
    uint8_t* getPointer() override {
        //return??
    }
private:
    char* myChar;
}

B 类有一个unsigned char*,所以我可以简单地static_cast把它改成uint8_t*. 但是,C 类有一个char*,我不能简单地static_cast把它改为uint8_t*.

我有一些问题:

既然 char 不能保证是 8 位,那为什么编译器不抱怨static_cast<uint8_t*>(myUnsignedChar)呢?如果char在某些架构中恰好是 16 位,那么如何转换为 8 位整数?

我注意到这return reinterpret_cast<uint8_t*>(frame.get()->data());会起作用。我知道这是允许的,因为我只是告诉 C++ 读取指向(可能是 8 位数据)的指针,这只是另一件事。也就是说,如果 char 是 8 位,那么我要做的就是读取相同的 8 位,但将它们想象为正数。所以我猜它会将 -127 读作 0 或类似的东西(取决于我猜负数在体系结构中的表示方式)。

那么,我该如何解决这个问题呢?看起来只有 8 位是安全的unsigned char*,并且 reinterpret_cast 只有在8 位并且它指向的数据仅由正值组成时才是安全的。uint8_t*charchar

我应该怎么办?

标签: c++

解决方案


对于此转换,您可以使用 single reinterpret_cast,因为它是不兼容类型之间的转换,两者之间没有单向隐式转换,并且不涉及限定符的丢失。

return reinterpret_cast<uint8_t *>(myChar);

可以使用 C 风格的强制转换符号:

return (uint8_t *) myChar;

为了防止有人在myChar不考虑转换后果的情况下意外更改类型,我们可以改为:

return reinterpret_cast<uint8_t *>(static_cast<char *>(myChar));

现在如果myChar变为,静态转换将失败,如果变为const char *,它也会失败。换句话说,我们首先将值设置为我们已经期望的类型,然后设置为我们需要的类型。then 可以使用从这段代码本身可以明显看出的一对精确类型:很明显,它的输入是 a ,输出是。myCharint *static_castreinterpret_castreinterpret_castchar *uint8_t *

如果我们经常需要这样的演员表,我们可以像这样使用模板内联函数使它们更符合人体工程学:

// convert from F to T, without stripping qualifiers like const

template <typename T, typename F> inline T to_from_cast(const F &val)
{
  return reinterpret_cast<T>(static_cast<F>(val));
}

现在只是:

return to_from_cast<uint8_t, char *>(myChar);

C++ 强制转换符号模拟了使用显式参数实例化的模板函数的调用;因此,我们可以使用这些函数编写自己的演员表。但是,当类型被违反时,编译器诊断不会那么好。

to_from_cast请注意,如果不指定至少一个模板参数,则不能使用;T无法推断,因为T没有出现在函数签名中,只有F. 这里有一个缺点,to_from_cast<uint_8_t *>(myChar)只有一个模板参数是一个有效的表达式。 F被推导出来myChar,哎呀!(但请参阅此问题以了解如何抑制模板参数的推导)。不幸的是,具有更好诊断和所需所有类型参数的替代方法是预处理:

#define to_from_cast(T, F, V) (reinterpret_cast<T>(static_cast<F>(V)))

既然 char 不保证是 8 位,那么为什么编译器不抱怨 static_cast(myUnsignedChar)?

如果您unsigned char是 8 位,那么它与 ; 的类型相同uint8_tuint8_t只是. typedef_unsigned char

在具有 9 位字节的系统上,您可能不会有uint8_t; 精确宽度类型的可用性是实现定义的。

C 和 C++ 中的对象以字节为单位。根据定义,字符类型charunsigned char大小为 1。没有非零大小的对象的大小小于 1。

如果您必须编写可移植到此类系统的代码,则您的代码不能假定uint8_t存在。


推荐阅读