-->

便携和安全的方式来增加字节偏移任何指针(Portable and safe way to add b

2019-08-21 11:51发布

我在与C ++的工作还相当新,还没有掌握所有的复杂性和语言的微妙之处。

什么是添加一个任意字节偏移到任何类型的C ++ 11的指针最轻便的,正确的和安全的方式?

SomeType* ptr;
int offset = 12345 /* bytes */;
ptr = ptr + offset;             // <--

我发现对堆栈溢出和谷歌许多答案,但他们都提出了不同的东西。 我遇到了一些变种:

  1. 投射到char *

     ptr = (SomeType*)(((char*)ptr) + offset); 
  2. 投射到unsigned int

     ptr = (SomeType*)((unsigned int)ptr) + offset); 
  3. 投射到size_t

     ptr = (SomeType*)((size_t)ptr) + offset); 
  4. “的大小size_tptrdiff_t始终与指针的大小相一致。由于这一点,这些类型应该被用作大型阵列索引,指针和指针运算的存储”。 - 关于为size_t和ptrdiff_t的 CodeProject上

     ptr = (SomeType*)((size_t)ptr + (ptrdiff_t)offset); 
  5. 还是像以前的,但intptr_t ,而不是size_t ,这是签署的,而不是无符号:

     ptr = (SomeType*)((intptr_t)ptr + (ptrdiff_t)offset); 
  6. 只有转换为intptr_t ,因为offset已经是一个符号整数,并intptr_tsize_t

     ptr = (SomeType*)((intptr_t)ptr) + offset); 

而在所有这些情况下,是可以安全使用的旧的C风格的强制转型,或者是更安全还是更容易移植到使用static_castreinterpret_cast这个?

我应该承担的指针值本身是无符号数?

Answer 1:

我会用这样的:

unsigned char* bytePtr = reinterpret_cast<unsigned char*>(ptr);
bytePtr += offset;


Answer 2:

使用reinterpret_cast (或C样式转换)是指绕过类型系统是不可移植,而不是安全。 无论它是正确的,取决于你的架构。 如果你(必须)去做,你影射你知道你做什么 ,你基本上对自己从那时起。 这么多的警告。

如果添加了一些n的指针或键入T ,你通过移动这个指针n类型的元素 T 。 你所寻找的是一个类型,其中1元是指1个字节。

sizeof部分5.3.3.1:

sizeof运算符产生在其操作数的对象表示的字节数 。 [...] sizeof(char)sizeof(signed char)sizeof(unsigned char)1。 应用于任何其它类型的基本(3.9.1)的sizeof的结果是实现定义的。

注意,这是毫无声明sizeof(int)等等。

字节的定义(第1.7.1。):

在C ++存储器模型的基本存储单元是字节。 字节是至少足够大,以包含基本执行字符集(2.3)和Unicode UTF-8编码形式的八位代码单元的任何部件,并且由位的连续序列,其数量是实现定义。 [...]对C ++程序中可用的存储器包括连续字节的一个或多个序列。 每个字节都有一个唯一的地址。

因此,如果sizeof返回的字节的数目和sizeof(char)为1,比char具有一个字节的大小,以C ++。 因此, char逻辑上是与C字节++但不一定是事实上的标准的8位字节。 添加n到一个char*将返回一个指针,该指针是n字节(在C ++存储器模型而言)的距离。 因此,如果你要玩操控对象的指针逐字节的危险的游戏,你应该将其转换为一个char变量。 如果你的类型也有像预选赛const ,您应该将它们传输到您的“字节型”了。

    template <typename Dst, typename Src>
    struct adopt_const {
        using type = typename std::conditional< std::is_const<Src>::value,
            typename std::add_const<Dst>::type, Dst>::type;
    };

    template <typename Dst, typename Src>
    struct adopt_volatile {
        using type = typename std::conditional< std::is_volatile<Src>::value,
            typename std::add_volatile<Dst>::type, Dst>::type;
    };

    template <typename Dst, typename Src>
    struct adopt_cv {
        using type = typename adopt_const<
            typename adopt_volatile<Dst, Src>::type, Src>::type;
    };

    template <typename T>
    T*  add_offset(T* p, std::ptrdiff_t delta) noexcept {
        using byte_type = typename adopt_cv<unsigned char, T>::type;
        return reinterpret_cast<T*>(reinterpret_cast<byte_type*>(p) + delta);
    }



Answer 3:

请注意, NULL是特殊的。 添加在其上的偏移是危险的。
reinterpret_cast不能删除constvolatile预选赛。 更简便的方式是C样式转换。
reinterpret_cast与像@ user2218982的回答特点,似乎更安全。

template <typename T>
inline void addOffset( std::ptrdiff_t offset, T *&ptr ) { 
    if ( !ptr )
        return;
    ptr = (T*)( (unsigned char*)ptr + offset );
} 


Answer 4:

如果你有:

myType *ptr;

你也是:

ptr+=3;

编译器肯定会通过增加您的变量:

3*sizeof(myType)

而且它做到这一点,据我所知的标准方法。

如果你想遍历假设类型的myType这是做它的方式的元素的数组。

好吧,如果你想投做,使用

myNewType *newPtr=reinterpret_cast < myNewType * > ( ptr )

还是坚持普通的旧C和做:

myNewType *newPtr=(myNewType *) ptr;

然后递增



文章来源: Portable and safe way to add byte offset to any pointer