当endianes确实很重要时,进行强制转换操作
when endianess does matter - cast operations
可能的重复:
Endianness何时成为一个因素?
阅读这篇关于endianes的教程,我会发现这个例子中endianes确实很重要。这是关于写一个充满1和0的字符*。然后可以将其转换为short,结果取决于endianes,little或big。下面是引用的例子。
无符号char endian[2]={1,0};短x;
x = *(short *) endian;
x的值是多少?让我们看看这个代码在做什么。您正在创建一个由两个字节组成的数组,然后将该数组强制转换为两个字节组成一个短。通过使用数组,您基本上强制一个特定的字节顺序,您将看到系统如何处理这两个字节。如果这是一个小端序系统,则0和1是向后解释,并将其视为0,1。由于高字节是0,这无关紧要,低字节是1,所以x等于1。关于另一方面,如果是big-endian系统,则高字节为1x的值为256。
我想知道:当你用给定数量的内存字节分配(这里是两个字节)实例化一个数组时,只要数组已经分配了与这个字节对应的字节数量,如何转换为任何类型(short,int…)?如果没有分配足够的内存来"包含此类型",下一个内存地址还会被读取吗?例如,如果我想将endian强制转换为long,会执行此操作,从endian的开头读取四个字节,还是会失败?
然后,关于字节序的一个问题是:这是处理器在内存中写入字节的习惯,其中最有意义的字节位于最低内存位置(big-endian)或最高内存位置(little-endian)。在这种情况下,已经分配了一个具有两个单字节元素的数组。为什么说1是最有意义的字节?
不要忘记编译器只会编写汇编代码。如果忽略编译器发出的所有警告,则可以检查编译器生成的程序集代码,并了解实际情况。
我采用了这个简单的程序:
#include <iostream>
int main()
{
unsigned endian[2] = { 0, 0 } ;
long * casted_endian = reinterpret_cast<long*>( endian );
std::cout << *casted_endian << std::endl;
}
并且我使用CCD_ 1提取该代码。让我们破译一下。
804879c: 55 push %ebp
804879d: 89 e5 mov %esp,%ebp
804879f: 83 e4 f0 and $0xfffffff0,%esp
80487a2: 83 ec 20 sub $0x20,%esp
这些行只是函数的序言,忽略它们。
unsigned endian[2] = { 0, 0 } ;
80487a5: c7 44 24 14 00 00 00 movl $0x0,0x14(%esp)
80487ac: 00
80487ad: c7 44 24 18 00 00 00 movl $0x0,0x18(%esp)
80487b4: 00
从这两行中,您可以看到(0x14)%esp是用0初始化的。因此,您知道数组endian
在堆栈上,位于寄存器%ESP(堆栈指针)+0x14中的地址。
long * casted_endian = reinterpret_cast<long*>( endian );
80487b5: 8d 44 24 14 lea 0x14(%esp),%eax
LEA只是一种算术运算。EAX现在包含%ESP+0x14,这是堆栈上数组的地址。
80487b9: 89 44 24 1c mov %eax,0x1c(%esp)
在地址ESP+0x1c(这是变量casted_endian
的位置),我们放置EAX,因此是endian的第一个字节的地址。
std::cout << *casted_endian << std::endl;
80487bd: 8b 44 24 1c mov 0x1c(%esp),%eax
80487c1: 8b 00 mov (%eax),%eax
80487c3: 89 44 24 04 mov %eax,0x4(%esp)
80487c7: c7 04 24 40 a0 04 08 movl $0x804a040,(%esp)
80487ce: e8 1d fe ff ff call 80485f0 <std::ostream::operator<<(long)@plt>
然后,我们准备对运营商<lt;与相关的论点没有任何更多的检查。就这样,程序不会再进行任何检查了。变量的类型与机器完全无关。
现在,当operator<<
读取*casted_endian
中不在数组中的部分时,可能会发生两件事。
它的地址在当前映射的内存页中,或者没有。在第一种情况下,operator<<
会毫无怨言地读取该地址的任何内容。这可能会在屏幕上写一些奇怪的东西。在第二种情况下,您的操作系统会抱怨程序试图读取他无法读取的内容,并引发中断。这就是著名的分割错误。
如果您试图强制转换到大于数组的大小,则会得到未定义的行为。它可能会尝试读取数组后面的内存内容,但结果不能保证,也不需要一致。
天啊。我在这里要说的是,为什么这适用于大多数架构,但我不能说其中有多少是标准的。
您正在做的是将数组endian
强制转换为short。现在,数组基本上是指针,数组的名称实际上包含第一个元素的地址。唯一真正的区别是数组包含更有用的元数据,并且数组上的某些操作不同(例如sizeof
)。然后使用该地址(endian
)并从中创建一个objdump
0指针。内存地址保持不变,只是您对指向的数据进行了不同的解释。然后,您将取消引用该指针以获取值,并将其分配给x
。
简短的旁注。这可能不适用于所有系统。在C中,int
仅定义为与体系结构的本机字大小一样宽(x86上为4字节,x86_64上为8字节)。short
则仅被定义为短于int(或者等于,如果内存服务正确的话)。因此,该代码将在8位体系结构上失败。要实现此操作,目标数据类型的大小(以字节为单位)必须等于或小于数组的大小。
同样,long
只是被定义为比int
长,通常在x86和x86_64上分别为8或16个字节。在这种情况下,此代码将在x86:上工作
unsigned char endian[8] = {1,2,3,4,5,6,7,8};
long x = *(long*)endian;
无论如何,处理器的字节序完全取决于处理器。x86是little-endian(并且基本上启动了LE设备的约定IIRC)。SPARC是big-endian(直到9,两者都可以)。ARM和MIPS也是可配置的,Microblaze取决于使用的总线(AXI或PLB)。无论如何,字节序不仅限于处理器,在与硬件或其他计算机通信时也是一个问题。
对于最后一个问题,最高有效字节被称为,因为所代表的值大于较小字节所能代表的最大值。在16位字的情况下,最低有效字节可以表示0-255,最高有效字节可以代表256-65535。
在任何情况下,除非您正在进行低级别系统编程(我的意思是,直接修改内存)或编写通信协议,否则您永远不需要担心endianness。
unsigned char endian[2] = {1, 0};
short x;
x = *(short *) endian;
此代码具有未定义的行为。结果可能是x
设置为12564000,或者程序可能崩溃,或者其他任何事情都可能合法发生。即使不考虑数组是否足够大以适应它所转换的类型,情况也是如此
以下是对代码的改写,使其合法化,并按照作者的意图行事。
unsigned char endian[sizeof(short)] = {1};
short x;
std::memcpy(&x, endian, sizeof(short));
如果您编写的代码试图从该数组中获取int
,那么它将访问合法数组边界之外的内容,并且您将再次遇到未定义的行为;任何事情都有可能发生。
在这种情况下,已经分配了一个具有两个一字节元素的数组。为什么说1是最有意义的字节?
(我猜你是想问为什么endian[1]
被认为包含最高有效字节。)
因为在这个例子中,系统是小端序,正如你所说,小端序的定义是内存位置中具有最高地址的最高有效字节。CCD_ 19具有比CCD_ 20更高的地址,因此CCD_。
- 逐位操作的隐式类型转换
- C++:如何将 std::vector 操作转换为 Eigen::VectorXf?
- 使用用户定义的操作隐式转换为类型
- 复制交换习惯用法-我们可以在这里使用动态强制转换操作吗
- 自动符号转换,用于对类等数学向量进行操作
- 位操作将最左侧的设置位转换为右侧交替位?
- 将 int 转换为双精度以执行 std::complex <double>的"*"操作
- CAN模板类型推理考虑原始操作类型转换
- 将 lua 脚本转换为等效于它的指令(操作码)
- C 库中是否有任何功能将字符串转换为算术操作
- 为什么C++标准指定有符号整数在具有混合符号的二进制操作中强制转换为无符号整数
- 是否可以将结构与条件语句完全相同的字符串转换为可以读取和操作的语句?
- 无法通过此操作,因为类型的错误转换
- 操作方法:将 boost::endian 缓冲区类型转换回本机格式
- 如何将字节操作转换为C#
- 将字符串数值操作转换为实际整数操作
- 如何将std::array的c++ 11操作转换为Boost+VS08
- 用简单的操作转换子类向量时得到错误
- 如何在c++中将div_t操作转换为int
- 如何使用位操作转换大小写