我在与C ++的工作还相当新,还没有掌握所有的复杂性和语言的微妙之处。
什么是添加一个任意字节偏移到任何类型的C ++ 11的指针最轻便的,正确的和安全的方式?
SomeType* ptr;
int offset = 12345 /* bytes */;
ptr = ptr + offset; // <--
我发现对堆栈溢出和谷歌许多答案,但他们都提出了不同的东西。 我遇到了一些变种:
投射到char *
:
ptr = (SomeType*)(((char*)ptr) + offset);
投射到unsigned int
:
ptr = (SomeType*)((unsigned int)ptr) + offset);
投射到size_t
:
ptr = (SomeType*)((size_t)ptr) + offset);
“的大小size_t
和ptrdiff_t
始终与指针的大小相一致。由于这一点,这些类型应该被用作大型阵列索引,指针和指针运算的存储”。 - 关于为size_t和ptrdiff_t的 CodeProject上
ptr = (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);
而在所有这些情况下,是可以安全使用的旧的C风格的强制转型,或者是更安全还是更容易移植到使用static_cast
或reinterpret_cast
这个?
我应该承担的指针值本身是无符号数?
我会用这样的:
unsigned char* bytePtr = reinterpret_cast<unsigned char*>(ptr);
bytePtr += offset;
使用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);
}
例
请注意, NULL
是特殊的。 添加在其上的偏移是危险的。
reinterpret_cast
不能删除const
或volatile
预选赛。 更简便的方式是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 );
}
如果你有:
myType *ptr;
你也是:
ptr+=3;
编译器肯定会通过增加您的变量:
3*sizeof(myType)
而且它做到这一点,据我所知的标准方法。
如果你想遍历假设类型的myType这是做它的方式的元素的数组。
好吧,如果你想投做,使用
myNewType *newPtr=reinterpret_cast < myNewType * > ( ptr )
还是坚持普通的旧C和做:
myNewType *newPtr=(myNewType *) ptr;
然后递增