大端小端再总结

来源:互联网 发布:知乎日报启动画面 编辑:程序博客网 时间:2024/06/08 14:20

大端格式:

在这种格式中,字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中  

小端格式:

与大端存储格式相反,在小端存储格式中,低地址中存放的是字数据的低字节,高地址存放的是字数据的高字节

 

 请写一个C函数,若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1

解答:

int checkCPU( )

{

   {

          union w

          {  

                 int  a;

                 char b;

          } c;

          c.a = 1;

           return(c.b ==1);

   }

}

剖析:

嵌入式系统开发者应该对Little-endianBig-endian模式非常了解。例如,16bit宽的数0x1234Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:

内存地址

0x4000

0x4001

存放内容

0x34

0x12

而在Big-endian模式CPU内存中的存放方式则为:

内存地址

0x4000

0x4001

存放内容

0x12

0x34

 

32bit宽的数0x12345678Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:

内存地址

0x4000

0x4001

0x4002

0x4003

存放内容

0x78

0x56

0x34

0x12

而在Big-endian模式CPU内存中的存放方式则为:

内存地址

0x4000

0x4001

0x4002

0x4003

存放内容

0x12

0x34

0x56

0x78

联合体union的存放顺序是所有成员都从低地址开始存放。

=============== 呵呵 还是附上 另一段代码吧,摘自一个开源项目 ====

int  big_endian (void)

     {
       union{
          long l;
          char c[sizeof(long)];
       }u;
 
       u.l = 1;
       return  (u.c[sizeof(long) - 1] == 1);
     }
  

 

有时候,用C语言写程序时需要知道是大端模式还是小端模式。 所谓的大端模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;所谓的小端模式,是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中。为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如果将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。例如一个16bit的short型x,在内存中的地址为0x0010,x的值为0x1122,那么0x11为高字节,0x22为低字节。对于大端模式,就将0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,刚好相反。我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。
 
下面这段代码可以用来测试一下你的编译器是大端模式还是小端模式:
 
short int x;

char x0,x1;

x=0x1122;

x0=((char*)&x)[0];  //低地址单元
x1=((char*)&x)[1];  //高地址单元

若x0=0x11,则是大端; 若x0=0x22,则是小端。
 

(判断大端小端模式) 试题1:请写一个C函数,若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1

解答:

int checkCPU( )

{

    {

           union w

           {  

                  int a;

                  char b;

           } c;

           c.a = 1;

           return(c.b ==1);

    }

}

剖析:

嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。采用Little-endian模式的CPU对操作数的存放方式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。例如,16bit宽的数0x1234在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:

内存地址
0x4000
0x4001

存放内容
0x34
0x12

而在Big-endian模式CPU内存中的存放方式则为:

内存地址
0x4000
0x4001

存放内容
0x12
0x34

32bit宽的数0x12345678在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:

内存地址
0x4000
0x4001
0x4002
0x4003

存放内容
0x78
0x56
0x34
0x12

而在Big-endian模式CPU内存中的存放方式则为:

内存地址
0x4000
0x4001
0x4002
0x4003

存放内容
0x12
0x34
0x56
0x78

联合体union的存放顺序是所有成员都从低地址开始存放,面试者的解答利用该特性,轻松地获得了CPU对内存采用Little-endian还是Big-endian模式读写。如果谁能当场给出这个解答,那简直就是一个天才的程序员。

一、如何判断CPU是大端还是小端?

明白大端和小端的区别,实现起来就非常简单:

#include <stdio.h>

int main()
{
 union ut{
   short s;
   char c[2];
  }u;

 if(sizeof(short) == 2)
 {
  u.s = 0x0102;
  if(u.c[0] == 1 && u.c[1] == 2)
  {
   printf("big enidan\n");
  }else if(u.c[0] == 2 && u.c[1] == 1)
  {
   printf("little endian.\n");
  }
  return 0;
 }
}

二、大端与小端相互转换

 #include <stdio.h>


typdedef unsigned int u_32;

typedef unsigned short u_16;


#define  BSWAP_16(x) \
     (u16) ( ((((u16)(x)) & 0x00ff) << 8 ) | \
                 ((((u16)(x)) & 0xff00) >> 8 ) \
          )

#define BSWAP_32(x) \
     (u32) ( (( ((u32)(x)) & 0xff000000 ) >> 24) | \
                    (( ((u32)(x)) & 0x00ff0000 ) >> 8 ) | \
     (( ((u32)(x)) & 0x0000ff00 ) << 8 ) | \
     (( ((u32)(x)) & 0x000000ff ) << 24) \
              )

u16 bswap16(u16 x)
{
    return (x & 0x00ff) << 8 |
     (x & 0xff00) >> 8
    ;
}

u32 bswap32(u32 x)
{
    return     ( x & 0xff000000 ) >>24 |
        ( x & 0x00ff0000 ) >>8 |
        ( x & 0x0000ff00 ) <<8 |
        ( x & 0x000000ff ) << 24
    ;
}
   

int main(void)
{


    //关键是要能对错误进行处理,给一个0x123490 照样能得出 0x9034的值,而且, 占内存要小的

    printf("macro conversion:%#x\n",BSWAP_16(0x123490 ));//要能正确转换

    printf("macro conversion:%#x\n", BSWAP_32(0x1234567890)); //要能正确转换

   
    printf("-----------------\n");
   
    printf("function conversion:%#x\n",bswap16(0x123490));
    printf("function conversion:%#x\n", bswap32(0x1234567890));
   
   
    return 0;
}

下面是一篇从网上摘录的关于大端小端的帖子,讲得比较经典的

Big-endian和little-endian(转载)

Big-endian和little-endian是描述排列存储在计算机内存里的字节序列的术语。

Big-endian是一种大值的一端(序列中更典型值)存在前面(在最小的存储地址)的顺序。Little-endian是一种小值的一端(序列中较不典型的值)存储在前的顺序。比如,在Big-endian的电脑中,需要两个字节把十六位数4F52当作4F52存在存储器中(如果4F存在存储地址1000中,比如说,52将存在1001中)。在little-endian系统里,将被存为524F(52存在存储地址1000中,比如说,4F将存在1001中)。

IBM的370主机,多数基于RISC计算机,和Motorola的微处理器使用big-endian方法。TCP/IP也使用big-endian方法(因此big-endian方法也叫做网络编码)。对于人来说我们的语言都是从左到右的习惯方式。这看上去似乎被认为是自然的存储字符和数字方式-你同样也希望以同样的方式出现在你面前。我们总的许多人因此也会认为big-endian是流行的存储方式,正如我们平时所读到的。另一个方面说,Intel处理器(CPUs)和DEC Alphas和至少一些在他们的平台的其他程序都是little-endian的。

有个关于little-endian的争议是如果你增加数字的值,你可能在左边增加数字(高位非指数函数需要更多的数字)。因此,经常需要增加两位数字并移动存储器里所有Big-endian顺序的数字,把所有数向右移。使用little-endian的存储器中不重要的字节可以存在它原来的位置,新的数可以存在它的右边的高位地址里。这就意味着有些计算机的运作可以变得简单和快速。

语言编译器比如说Java或FORTRAN必须明确他们开发的目标代码使用的是什么存储方式。有必要的话可以使用转换器可以用来转换存储顺序。

注意在使用big-endian和little-endian字节顺序中,一个位里面都是使用big-endian。也就是说,在存储的字节里一个位串代表的给定数没有big-endian和little-endian之分。比如说,有个十六位数4F放在一个给定的字节地质前面或者后面,在这个字节的这一位的里的顺序是:

 01001111

从这一位的顺序来看有可能是big-endian也可能是little-endian,但是CPUs和程序几乎都是设计成big-endian位序的。尽管如此在数据的交换中它有可能其中的一种顺序。 

Eric Raymond评述说Internet域名地址和e-mail地址是little-endian的。比如我们的网址用big-endian来表述是com.whatis.www。

Big-endian和little-endian来自于Jonathan Swift的Gulliver's Travels ,Big-endian是那个大鸡蛋是打破大头的那个政治派别,而反对派Lilliputian King要求他的国民打鸡蛋的时候用小的那一头。

目前的存储器,多以byte为访问的最小单元,当一个逻辑上的整理必须分割为物理上的若干单元时就存在了先放谁后放谁的问题,于是endian的问题应运而生了。对于不同的存储方法,就有Big-endian和Little-endian两个描述。(这两个术语来自于 Jonathan Swift 的《《格利佛游记》其中交战的两个派别无法就应该从哪一端--小端还是大端--打开一个半熟的鸡蛋达成一致。在那个时代,Swift是在讽刺英国和法国之间的持续冲突,Danny Cohen,一位网络协议的早期开创者,第一次使用这两个术语来指代字节顺序,后来这个术语被广泛接纳了。)

存在“如果说"跟word或者说字长根本就没关系",假设有一数据文件里面有N多数顺序排布,如果想以Little-Endian format 读入内存某区域,那么应该怎么读?怎么排?”这样的问题是由于对于endian的实质理解的偏差,endian指的是当物理上的最小单元比逻辑上的最小单元小时,逻辑到物理的单元排布关系。这里的“有一数据文件里面有N多数顺序排布”,这个“有一数据”显然不是逻辑上的最小单元,而其中的“N多数”的一个才是逻辑最小单元,于是可应用楼主表格中的原则排列,而“N多数”之间的顺序则是由这“N多数”的宿主决定的,比如是你写的程序,这个顺序由你决定。

刚才谈到了,endian指的是当物理上的最小单元比逻辑上的最小单元小时,逻辑到物理的单元排布关系。咱们接触到的物理单元最小都是byte,在通信领域中,这里往往是bit,不过原理也是类似的。

实践可以给你更多的经验,比如在一个嵌入式系统的通信协议中,从底层射频驱动到上层的协议栈全部需要实现,那么很可能遇到多个endian的问题,底层的bit序、协议层的byte序、应用层的byte序,这些都是不同的概念。

Big-Endian 和 Little-Endian 字节排序
  字节排序                  含义
Big-Endian    一个Word中的高位的Byte放在内存中这个Word区域的低地址处。
Little-Endian  一个Word中的低位的Byte放在内存中这个Word区域的低地址处。
必须注意的是:表中一个Word的长度是16位,一个Byte的长度是8位。如果一个数超过一个Word的长度,必须先按Word分成若干部分,然后每一部分(即每个Word内部)按Big-Endian或者Little-Endian的不同操作来处理字节。

一个例子:

如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为
           big-endian   little-endian
0x0000      0x12        0xcd
0x0001      0x34        0xab
0x0002      0xab        0x34
0x0003      0xcd        0x12

除了moto的68K系列和dec的sparc是big endian外,常见的cpu都是little endian。ARM同时支持 big和little,实际应用中通常使用little endian。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/flickedball/archive/2009/04/21/4096991.aspx

2.

char str[] = “Hello” ;

char *p = str ;

int n = 10;

请计算

sizeof (str ) = 6 (2分)

sizeof ( p ) = 4 (2分)

sizeof ( n ) = 4 (2分)
void Func ( char str[100])

{

请计算

sizeof( str ) = 4 (2分)

}

void *p = malloc( 100 );

请计算

sizeof ( p ) = 4 (2分)

3、在C++程序中调用被 C编译器编译后的函数,为什么要加 extern “C”? (5分)

答:C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为: void foo(int x, int y);

该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。

C++提供了C连接交换指定符号extern“C”来解决名字匹配问题。

4.有关内存的思考题

void GetMemory(char *p)

{

p = (char *)malloc(100);

}

void Test(void)

{

char *str = NULL;

GetMemory(str);

strcpy(str, "hello world");

printf(str);

}

请问运行Test函数会有什么样的结果?

答:程序崩溃。

因为GetMemory并不能传递动态内存,

Test函数中的 str一直都是 NULL。

strcpy(str, "hello world");将使程序崩溃。
char *GetMemory(void)

{

char p[] = "hello world";

return p;

}

void Test(void)

{

char *str = NULL;

str = GetMemory();

printf(str);

}

请问运行Test函数会有什么样的结果?

答:可能是乱码。

因为GetMemory返回的是指向“栈内存”的指针,该指针的地址不是 NULL,但其原现的内容已经被清除,新内容不可知。

void GetMemory2(char **p, int num)

{

*p = (char *)malloc(num);

}

void Test(void)

{

char *str = NULL;

GetMemory(&str, 100);

strcpy(str, "hello");

printf(str);

}

请问运行Test函数会有什么样的结果?

答:

(1)能够输出hello

(2)内存泄漏

 

void Test(void)

{

char *str = (char *) malloc(100);

strcpy(str, “hello”);

free(str);

if(str != NULL)

{

strcpy(str, “world”);

printf(str);

}

}

请问运行Test函数会有什么样的结果?

答:篡改动态内存区的内容,后果难以预料,非常危险。

因为free(str);之后,str成为野指针,

if(str != NULL)语句不起作用

计算机都是以八位一个字节为存储单位,那么一个16位的整数,也就是C语言中的short,在内存中可能有两种存储顺序big-endianlitte-endian.考虑一个short整数0xAF32(0x32是低位,0xAF是高位),把它赋值给一个short变量,那么它在内存中的存储可能有如下两种情况:

大端字节(Big-endian):较高的有效字节存放在较低的存储器地址,较低的有效字节存放在较高的存储器地址。

——————————————--------

 |    0x2000         |          0x2001    |

------------------------------------

 |    0xAF             |          0x32       |

------------------------------------

小端字节(Big-endian):字数据的高字节存储在高地址中,而字数据的低字节则存放低地址中。

——————————————--------

 |    0x2000         |          0x2001    |

------------------------------------

 |    0x32             |          0xAF       |

------------------------------------


判断计算机的存储方式:


[cpp] view plaincopyprint?
  1. /********大端返回0;小端返回0*******/  
  2. int checkCPU()  
  3. {  
  4.     union w  
  5.     {  
  6.         int x ;  
  7.         char y ;  
  8.     }c ;  
  9.     c.x = 1;  
  10.     return (c.y==1))  
  11. }  

大小端转换:

[cpp] view plaincopyprint?
  1. #include<stdio.h>   
  2.   
  3. typedef unsigned int uint_32 ;  
  4. typedef unsigned short uint_16 ;  
  5.   
  6. #define BSWAP_16(x) \   
  7.     (uint_16)((((uint_16)(x) & 0x00ff) << 8) | \  
  8.               (((uint_16)(x) & 0xff00) >> 8) \  
  9.              )  
  10. #define BSWAP_32(x) \   
  11.     (uint_32)((((uint_32)(x) & 0xff000000) >> 24) | \  
  12.               (((uint_32)(x) & 0x00ff0000) >> 8) | \  
  13.               (((uint_32)(x) & 0x0000ff00) << 8) | \  
  14.               (((uint_32)(x) & 0x000000ff) << 24) \  
  15.              )  
  16.   
  17. uint_16 bswap_16(uint_16 x)  
  18. {  
  19.     return (((uint_16)(x) & 0x00ff) << 8) | \  
  20.            (((uint_16)(x) & 0xff00) >> 8) ;  
  21. }  
  22. uint_32 bswap_32(uint_32 x)  
  23. {  
  24.     return (((uint_32)(x) & 0xff000000) >> 24) | \  
  25.            (((uint_32)(x) & 0x00ff0000) >> 8) | \  
  26.            (((uint_32)(x) & 0x0000ff00) << 8) | \  
  27.            (((uint_32)(x) & 0x000000ff) << 24) ;  
  28. }  
  29. int main(int argc,char *argv[])  
  30. {  
  31.     printf("------------带参宏-------------\n");  
  32.     printf("%#x\n",BSWAP_32(0x12345678));  
  33.     printf("%#x\n",BSWAP_16(0x1234)) ;  
  34.     printf("------------函数调用-----------\n");  
  35.     printf("%#x\n",bswap_32(0x12345678));  
  36.     printf("%#x\n",bswap_16(0x1234)) ;  
  37.       
  38.     return 0 ;  
  39. }  

linux编程函数:

[cpp] view plaincopyprint?
  1. #include <stdio.h>   
  2. struct ST{  
  3.     short val1;  
  4.     short val2;  
  5. };  
  6. union U{  
  7.     int val;  
  8.     struct ST st;  
  9. };  
  10.    
  11. int main(void)  
  12. {  
  13.     int a = 0;  
  14.     union U u1, u2;  
  15.    
  16.     a = 0x12345678;  
  17.     u1.val = a;  
  18.     printf("u1.val is 0x%x\n", u1.val);  
  19.     printf("val1 is 0x%x\n", u1.st.val1);  
  20.     printf("val2 is 0x%x\n", u1.st.val2);  
  21.     printf("after first convert is: 0x%x\n", htonl(u1.val));  
  22.     u2.st.val2 = htons(u1.st.val1);  
  23.     u2.st.val1 = htons(u1.st.val2);  
  24.     printf("after second convert is: 0x%x\n", u2.val);  
  25.     return 0;  
  26. }  
原创粉丝点击