C++ 将字节偏移量添加到任何指针的可移植且安全的方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15934111/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 19:55:08  来源:igfitidea点击:

Portable and safe way to add byte offset to any pointer

c++pointersc++11pointer-arithmetic

提问by Daniel A.A. Pelsmaeker

I'm quite new at working with C++ and haven't grasped all the intricacies and subtleties of the language.

我刚开始使用 C++,还没有掌握该语言的所有复杂性和微妙之处。

What is the most portable, correct and safeway to add an arbitrary byte offset to a pointer of any type in C++11?

在 C++11 中将任意字节偏移量添加到任何类型的指针的最便携、正确和安全的方法是什么?

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


I found many answers on Stack Overflow and Google, but they all propose different things. Some variants I have encountered:

我在 Stack Overflow 和 Google 上找到了很多答案,但他们都提出了不同的建议。我遇到的一些变体:

  1. Cast to char *:

    ptr = (SomeType*)(((char*)ptr) + offset);
    
  2. Cast to unsigned int:

    ptr = (SomeType*)((unsigned int)ptr) + offset);
    
  3. Cast to size_t:

    ptr = (SomeType*)((size_t)ptr) + offset);
    
  4. "The size of size_tand ptrdiff_talways coincide with the pointer's size. Because of this, it is these types that should be used as indexes for large arrays, for storage of pointers and pointer arithmetic." - About size_t and ptrdiff_ton CodeProject

    ptr = (SomeType*)((size_t)ptr + (ptrdiff_t)offset);
    
  5. Or like the previous, but with intptr_tinstead of size_t, which is signed instead of unsigned:

    ptr = (SomeType*)((intptr_t)ptr + (ptrdiff_t)offset);
    
  6. Only cast to intptr_t, since offsetis already a signed integer and intptr_tis not size_t:

    ptr = (SomeType*)((intptr_t)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始终一致用指针的大小。由于这个原因,它是这些类型应该被用作大型阵列索引,指针和指针运算的存储”。-关于CodeProject 上的size_t 和 ptrdiff_t

    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_t不是size_t

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

And in all these cases, is it safe to use old C-style casts, or is it safer or more portable to use static_castor reinterpret_castfor this?

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

Should I assume the pointer value itself is unsigned or signed?

我应该假设指针值本身是无符号的还是有符号的?

采纳答案by freddy.smith

I would use something like:

我会使用类似的东西:

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

回答by freddy.smith

Using reinterpret_cast(or C-style cast) means circumventing the type system and is not portable and not safe. Whether it is correct, depends on your architecture. If you (must) do it, you insinuate that you know what you doand you are basically on your own from then on. So much for the warning.

使用reinterpret_cast(或 C 风格的强制转换)意味着绕过类型系统并且不可移植且不安全。它是否正确,取决于您的架构。如果您(必须)这样做,则表明您知道自己在做什么,并且从那时起您基本上就靠自己了。警告就这么多。

If you add a number nto a pointer or type T, you move this pointer by nelementsof type T. What you are looking for is a type where 1 element means 1 byte.

如果将数字添加n到指针或类型T,则会按类型n元素移动此指针T。您正在寻找的是一种类型,其中 1 个元素表示 1 个字节。

From the sizeofsection 5.3.3.1.:

sizeof第 5.3.3.1. 节:

The sizeof operator yields the number of bytesin the object representation of its operand. [...] sizeof(char), sizeof(signed char)and sizeof(unsigned char)are 1. The result of sizeof applied to any other fundamental type (3.9.1) is implementation-defined.

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

Note, that there is no statement about sizeof(int), etc.

请注意,没有关于sizeof(int)等的声明。

Definition of byte(section 1.7.1.):

字节的定义(第 1.7.1 节):

The fundamental storage unit in the C++ memory model is the byte. A byte is at least large enough to contain any member of the basic execution character set (2.3) and the eight-bit code units of the Unicode UTF-8 encoding form and is composed of a contiguous sequence of bits, the number of which is implementation-defined. [...] The memory available to a C++ program consists of one or more sequences of contiguous bytes. Every byte has a unique address.

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

So, if sizeofreturns the number of bytes and sizeof(char)is 1, than charhas the size of one byte to C++. Therefore, charis logicallya byte to C++ but not necessarily the de facto standard 8-bit byte. Adding nto a char*will return a pointer that is nbytes (in terms of the C++ memory model) away. Thus, if you want to play the dangerous game of manipulating an object's pointer bytewise, you should cast it to one of the charvariants. If your type also has qualifiers like const, you should transfer them to your "byte type" too.

因此,如果sizeof返回字节数sizeof(char)为 1,则比charC++ 具有 1 个字节的大小。因此,char在逻辑上一个字节到C ++但不一定是事实上的标准的8位字节。添加n到 achar*将返回一个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);
    }

Example

例子

回答by where23

Please note that, NULLis special. Adding an offset on it is dangerous.
reinterpret_castcan't remove constor volatilequalifiers. More portable way is C-style cast.
reinterpret_castwith traits like @user2218982's answer, seems more safer.

请注意,NULL是特殊的。在其上添加偏移量是危险的。
reinterpret_cast不能删除constvolatile限定符。更便携的方式是C-style cast。
reinterpret_cast像@user2218982's answer这样的特征,似乎更安全。

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

回答by Mppl

if you have:

如果你有:

myType *ptr;

and you do:

你也是:

ptr+=3;

The compiler will most certainly increment your variable by:

编译器肯定会通过以下方式增加您的变量:

3*sizeof(myType)

And it's the standard way to do it as far as I know.

据我所知,这是标准的做法。

If you want to iterate over let's say an array of elements of type myType that's the way to do it.

如果你想迭代,让我们说一个 myType 类型的元素数组,这是这样做的方法。

Ok, if you wanna cast do that using

好的,如果你想投射,请使用

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

Or stick to plain old C and do:

或者坚持使用普通的旧 C 并执行以下操作:

myNewType *newPtr=(myNewType *) ptr;

And then increment

然后递增