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
Portable and safe way to add byte offset to any pointer
提问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 上找到了很多答案,但他们都提出了不同的建议。我遇到的一些变体:
ptr = (SomeType*)(((char*)ptr) + offset);
Cast to
unsigned int
:ptr = (SomeType*)((unsigned int)ptr) + offset);
ptr = (SomeType*)((size_t)ptr) + offset);
"The size of
size_t
andptrdiff_t
always 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 CodeProjectptr = (SomeType*)((size_t)ptr + (ptrdiff_t)offset);
Or like the previous, but with
intptr_t
instead ofsize_t
, which is signed instead of unsigned:ptr = (SomeType*)((intptr_t)ptr + (ptrdiff_t)offset);
Only cast to
intptr_t
, sinceoffset
is already a signed integer andintptr_t
is notsize_t
:ptr = (SomeType*)((intptr_t)ptr) + offset);
ptr = (SomeType*)(((char*)ptr) + offset);
投射到
unsigned int
:ptr = (SomeType*)((unsigned int)ptr) + offset);
ptr = (SomeType*)((size_t)ptr) + offset);
“的大小
size_t
和ptrdiff_t
始终一致用指针的大小。由于这个原因,它是这些类型应该被用作大型阵列索引,指针和指针运算的存储”。-关于CodeProject 上的size_t 和 ptrdiff_tptr = (SomeType*)((size_t)ptr + (ptrdiff_t)offset);
或者像以前一样,但使用
intptr_t
而不是size_t
,它是有符号的而不是无符号的:ptr = (SomeType*)((intptr_t)ptr + (ptrdiff_t)offset);
仅强制转换为
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_cast
or reinterpret_cast
for this?
而在所有这些情况下,是可以安全使用的旧的C风格的类型转换,或者是更安全还是更容易移植到使用static_cast
或reinterpret_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 n
to a pointer or type T
, you move this pointer by n
elementsof type T
. What you are looking for is a type where 1 element means 1 byte.
如果将数字添加n
到指针或类型T
,则会按类型n
元素移动此指针T
。您正在寻找的是一种类型,其中 1 个元素表示 1 个字节。
From the sizeof
section 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)
andsizeof(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 sizeof
returns the number of bytes and sizeof(char)
is 1, than char
has the size of one byte to C++. Therefore, char
is logicallya byte to C++ but not necessarily the de facto standard 8-bit byte.
Adding n
to a char*
will return a pointer that is n
bytes (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 char
variants.
If your type also has qualifiers like const
, you should transfer them to your "byte type" too.
因此,如果sizeof
返回字节数sizeof(char)
为 1,则比char
C++ 具有 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);
}
回答by where23
Please note that, NULL
is special. Adding an offset on it is dangerous.reinterpret_cast
can't remove const
or volatile
qualifiers. More portable way is C-style cast.reinterpret_cast
with traits like @user2218982's answer, seems more safer.
请注意,NULL
是特殊的。在其上添加偏移量是危险的。reinterpret_cast
不能删除const
或volatile
限定符。更便携的方式是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
然后递增