大端法,小端法,位域[1]

 

大端法和小端法(1)

                                                                                                                              Monday, September 24, 2007

 

 

如果文件中存储的一段数据为 ----------- 10001010 10100100 -------------(以大端法表示的数据)

 

 

如何拼接成Javaint类型,如何拼接成C++int类型。

 

现在假设内存上的

(736424) 10001010                     //低地址736424

(736425) 10100100                     //高地址736425

 

现在知道这两块内存存放的是用大端法表示的无符号整数!(大端法――低地址存放高位, X86平台存放文件小端法,

 

分别用C++Java求解上面的整数?

 

//a   (java)

class  Test

{

       public static void main(String[] args)

       {

              //(736424)      10001010

              //(736425)      10100100                     高位在前的无符号整型

              byte[] buf = new byte[2];

              buf [0] = (byte)0x8A;//低地址存放高位

              buf [1] = (byte)0xA4;//高地址存放低位

              int result = (( buf[0] < 0 ? 256 + buf[0] : buf[0] ) << 8 ) | (buf[1] < 0 ? 256 + buf[1] : buf[1]);

              System.out.println(result);

              System.out.println(Integer.toBinaryString(result));

       }

}

//snail:因为byte是有符号数,如果最高位是1,在进行移位操作时前会自动提到int(也可能是移好位再提升的,没有验证),在符号位前的所有24位会填充1,这样就会导致问题,要避免这个问题,可以像上面程序buf[0] < 0 ? 256 + buf[0] : buf[0]这样处理。只要执行下面三行,就知道是如何填充的了。

byte data = (byte)0x8F;

System.out.println(data);

System.out.println((int)data);

上面buf[0] < 0 ? 256 + buf[0] : buf[0]的意思将buf展开成整型的时候可以忽略符号位的影响。

下面 C++就没有这个麻烦,只要把映射2个地址的时候用unsigned char映射就行了,就不会认为最高位为符号位,在进行类型提升的时候也不会自动填充符号位1,见例b.

 

 

//b    (C++)

#include <iostream>

 

using namespace std;

 

int main(int argc,int argv[]){

       //10001010 10100100  高位在前的无符号整型

       unsigned char buf[2];

       buf[0] = (char)0x8A;

       buf[1] =  (char)0xA4;

       int result = buf[0] << 8 | buf[1];

       cout << result << endl;

       return 0;

}

 

 

 

上面这个问题的讨论不涉及到位顺序,只涉及到字节顺序,实际上大端法和小端法表示的文件在位顺序是不是相反的呢?

(我原本一直以为是相反,而且从程序的种种迹象也表明是相反,但其实没有相反)

 

 

 

C++基本知识:

C++的一个char类型几乎都包含8个二进制位,因此它可以保存256种不同的值。

 

char不同的是,普通的int总是有符号的.说明默认的char不一定总是有符号的

 

一个char是有符号的还是没符号的?

不幸的是,关于普通char如何选择的问题是由实现决定的。

 

char c = 255;   //2551”, 十六进制的0xFF

int i = c;

cout << c << endl;   // c 等于 -1;

windows平台 x86环境,g++ v3.8编译器编译下 默认的char类型是有符号的!

 

 

 

下面引用其他网上的两篇文章,对理解字节序的问题可能有帮助。

也谈字节序问题- [技术前沿]

Tag: 技术前沿

 

引用自 http://bigwhite.blogbus.com/logs/2005/09/

一次Sun SPARCIntel X86的平台移植让我们的程序遭遇了字节序问题,既然遇到了也就不妨深入的学习一下。

一、字节序定义
字节序,顾名思义字节的顺序,再多说两句就是大于一个字节类型的数据在内存中的存放顺序(一个字节的数据当然就无需谈顺序的问题了)

其实大部分人在实际的开发中都很少会直接和字节序打交道。唯有在跨平台以及网络程序中字节序才是一个应该被考虑的问题。

在所有的介绍字节序的文章中都会提到字节序分为两类:Big-EndianLittle-Endian。引用标准的Big-EndianLittle-Endian的定义如下:
a) Little-Endian
就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
b) Big-Endian
就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
c)
网络字节序:TCP/IP各层协议将字节序定义为Big-Endian,因此TCP/IP协议中使用的字节序通常称之为网络字节序。

其实我在第一次看到这个定义时就很糊涂,看了几个例子后也很是朦胧。什么高/低地址端?又什么高低位?翻阅了一些资料后略有心得。

二、高/低地址与高低字节
首先我们要知道我们C程序映像中内存的空间布局情况:在《C专家编程》中或者《Unix环境高级编程》中有关于内存空间布局情况的说明,大致如下图:
-----------------------
最高内存地址 0xffffffff
 | 
栈底
 .
 .             

 .
  
栈顶
-----------------------
 |
 |
/|/

NULL (空洞

/|/
 |
 |
-----------------------
               

-----------------------
未初始化的数据
----------------(
统称数据段)
初始化的数据
-----------------------
正文段(代码段)
-----------------------
最低内存地址 0x00000000

以上图为例如果我们在栈上分配一个unsigned char buf[4],那么这个数组变量在栈上是如何布局的呢[1]?看下图:
栈底 (高地址)
----------
buf[3]
buf[2]
buf[1]
buf[0]
----------
栈顶 (低地址)

现在我们弄清了高低地址,接着我来弄清高/低字节,如果我们有一个32位无符号整型0x12345678(呵呵,恰好是把上面的那4个字节buf看成一个整型),那么高位是什么,低位又是什么呢?其实很简单。在十进制中我们都说靠左边的是高位,靠右边的是低位,在其他进制也是如此。就拿0x12345678来说,从高位到低位的字节依次是0x120x340x560x78

高低地址和高低字节都弄清了。我们再来回顾一下Big-EndianLittle-Endian的定义,并用图示说明两种字节序:
unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value
Big-Endian:
低地址存放高位,如下图:
栈底 (高地址)
---------------
buf[3] (0x78) --
低位
buf[2] (0x56)
buf[1] (0x34)
buf[0] (0x12) --
高位
---------------
栈顶 (低地址)

Little-Endian: 低地址存放低位,如下图:
栈底 (高地址)
---------------
buf[3] (0x12) --
高位
buf[2] (0x34)
buf[1] (0x56)
buf[0] (0x78) --
低位
---------------
栈顶 (低地址)

在现有的平台上IntelX86采用的是Little-Endian,而像SunSPARC采用的就是Big-Endian

三、例子
测试平台: Sun SPARC Solaris 9Intel X86 Solaris 9
我们的例子是这样的:在使用不同字节序的平台上使用相同的程序读取同一个二进制文件的内容。
生成二进制文件的程序如下:
/* gen_binary.c */
int main() {
        FILE    *fp = NULL;
        int     value = 0x12345678;
        int     rv = 0;

        fp = fopen("temp.dat", "wb");
        if (fp == NULL) {
                printf("fopen error/n");
                return -1;
        }

        rv = fwrite(&value, sizeof(value), 1, fp);
        if (rv != 1) {
                printf("fwrite error/n");
                return -1;
        }

        fclose(fp);
        return 0;
}

读取二进制文件的程序如下:
int main() {
        int             value   = 0;
        FILE         *fp     = NULL;
        int             rv      = 0;
        unsigned        char buf[4];

        fp = fopen("temp.dat", "rb");
        if (fp == NULL) {
                printf("fopen error/n");
                return -1;
        }

        rv = fread(buf, sizeof(unsigned char), 4, fp);
        if (rv != 4) {
                printf("fread error/n");
                return -1;
        }

        memcpy(&value, buf, 4); // or value = *((int*)buf);
        printf("the value is %x/n", value);

        fclose(fp);
        return 0;
}

测试过程:
(1)
SPARC平台下生成temp.dat文件
SPARC平台下读取temp.dat文件的结果:
the value is 12345678

X86平台下读取temp.dat文件的结果:
the value is 78563412

(1) X86平台下生成temp.dat文件
SPARC平台下读取temp.dat文件的结果:
the value is 78563412

X86平台下读取temp.dat文件的结果:
the value is 12345678

[1]
buf[4]
在栈的布局我也是通过例子程序得到的:
int main() {
        unsigned char buf[4];

        printf("the buf[0] addr is %x/n", buf);
        printf("the buf[1] addr is %x/n", &buf[1]);

        return 0;
}
output:
SPARC
平台:
the buf[0] addr is ffbff788
the buf[1] addr is ffbff789
X86
平台:
the buf[0] addr is 8047ae4
the buf[1] addr is 8047ae5

两个平台都是buf[x]所在地址高于buf[y] (x > y)

 

 

网络字节顺序、大端法、小端法    

引用自 http://blog.csdn.net/dodorunning/archive/2006/03/23/636605.aspx

在国内的4种短信协议的协议头部分,都定义了4个字节长度的message length字段,字段的数据类型为无符号整形(也就是说,这个字段的范围是02^16-1);而在java语言中,没有无符号整形这种数据类型(如果用int类型来表示,由于javaint型是有符号数,则会发送溢出),我设想将message length存入long类型中,将数字的大小控制在02^16-1范围之内,当超过此范围归零重新开始。

在网络传输时,将long类型先转化为byte数组,步骤如下:

long l;

byte[] b;

b[0]=(byte)(l>>>24);

b[1]]=(byte)(l>>>16);

b[2]]=(byte)(l>>>8);

b[3]]=(byte)(l);

此时,b[]中就按照网络字节顺序(大端法,即l的高位数据存放在byte[]的低位地址,因为地址是
从低向高发展的)存放着4bytes的数据
使用OutputStreampublic void write(byte[] b,int off,int len)方法来向Socket写字节流
,写byte[0]byte[3]的字节。

 

java.io
Class OutputStream

write

public abstract void write(int b)                    throws IOException

Writes the specified byte to this output stream. The general contract for write is that one byte is written to the output stream. The byte to be written is the eight low-order bits of the argument b. The 24 high-order bits of b are ignored.

Subclasses of OutputStream must provide an implementation for this method.

 

Parameters:

b - the byte.

Throws:

IOException - if an I/O error occurs. In particular, an IOException may be thrown if the output stream has been closed.

write

public void write(byte[] b,                  int off,                  int len)           throws IOException

Writes len bytes from the specified byte array starting at offset off to this output stream. The general contract for write(b, off, len) is that some of the bytes in the array b are written to the output stream in order; element b[off] is the first byte written and b[off+len-1] is the last byte written by this operation.

The write method of OutputStream calls the write method of one argument on each of the bytes to be written out. Subclasses are encouraged to override this method and provide a more efficient implementation.

If b is null, a NullPointerException is thrown.

If off is negative, or len is negative, or off+len is greater than the length of the array b, then an IndexOutOfBoundsException is thrown.

 

Parameters:

b - the data.

off - the start offset in the data.

len - the number of bytes to write.

Throws:

IOException - if an I/O error occurs. In particular, an IOException is thrown if the output stream is closed.

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值