计算机的大小端

计算机的大小端主要体现在两个点:

1,内存存储的大小端

2,网络字节序,确定的是大端。

 

内存之所以有大小端,原因为:超过1个字节的存储,比如short,int等,该如何组织存储单元?数据的高位放在内存的高地址还是低地址?都需要有明确的定义。

大端指的是数据的高位放在内存的低地址,小端则相反。

具体记忆的时候可以这样:我们肉眼看的或者写的就是大端。从左往右权重依次减小,内存地址依次增大。

 

内存存储大小端是由什么关键因素决定的?

分析下(纯属个人臆想分析)可能的决定点有:操作系统和CPU。

如果是操作系统,那么在程序经过编译之后应该会有显示的对于汇编指令的特殊调用来适配大小端操作,也就是说不应该是正常的调用汇编指令,所以只需要查看生成的汇编指令文件即可确定,gcc -S a.c,X86-64上面任写一个文件可以清楚的看到是直接使用汇编指令,并无特殊之处。目测应该不是操作系统来决定。

剩下的就是CPU本身了,CPU更能理解一点,汇编指令经过汇编之后生成的机器码,统一接口,硬件操作。

 

当然具体的也百度了下,网上一致说的也都是由CPU决定的,官方文档没有查看过。

 

 

接下来就是网络字节序的大小端。

why?为什么网络字节序需要大小端?

用于网络通信的套接字,端口和ip地址的存储都是大于一个字节的数据类型,同时这两个信息也是由用户来设置的,大小端的服务器都需要进行网络通信,网络模块代码也不可能说大端一份小端一份,那么这时候就需要做一个约定规定了,网络字节序使用大端,所以在写套接字代码的时候端口设置要用到htons htonl函数来显示的赋值大端模式下的值。

那接收端也是同样的,接收到之后肯定就是大端模式,根据大端模式来解析出端口或者ip地址。

这里还有一个问题:网络通信的tcp层用户数据需要显示的转换为大端吗?

回答是:可要可不要。

可要的原因是:服务器的客户端肯定有大端小端之分,且传输的数据必然有int等类型,那么必须需要一个统一的格式,要不然大端的服务器发送到小端的客户端上面,数据解析就肯定出错了,那么就需要采用协议栈标准的网络字节序了。

可不要的原因是:现在网络传输有各种各样的流库,比如json,protobuf等,把数据完全字符串话,也就是说避免了大小端的区分了,都是一个一个独立的char组成的字符串。

总结下就是:网络的字节序大端,主要就是协议栈内部使用。

另外一点,也是提供一个标准吧,毕竟网络是处在中间位置,两边可能不一致。

 

C语言判断大小端的代码:

理论上应该还可以通过CPU信息拿到,也有可能拿不到,必然系统没有提供等等原因,请知道的留言。

#include "stdio.h"

typedef struct {
        char a;
        char b;
}mystruct;
typedef union {
                short value;
                mystruct mysvalue;
        }union_t;

int main()
{
         union_t union_value={.value=0x0102};
         if(union_value.mysvalue.a==0x01)
         {
                 printf("big endian\n");
         }
         else if(union_value.mysvalue.a==0x02)
         {
                 printf("small endian\n");
         }
         else
         {
                printf("a is %#x and b is %#x\n",union_value.mysvalue.a,union_value.mysvalue.b);
         }
         return 0;
}
 

计算机系统中,大小端(endianness)是指多字节数据在内存中的存储顺序。大端模式(Big-endian)将高位字节存储在低地址中,而小端模式(Little-endian)则将低位字节存储在低地址中。利用联合体(union)可以高效地判断当前系统的字节序,因为联合体的所有成员共享同一块内存空间。 ### 联合体判断大小端的原理 联合体的特性是所有成员共享相同的内存空间,这意味着对一个成员的赋值会影响其他成员的值。例如,定义一个包含 `int` 类型和 `char` 类型的联合体,当向 `int` 成员写入一个值时,可以通过 `char` 成员访问其内存表示,从而判断系统的大小端模式。 ### 示例代码 以下是一个使用联合体判断大小端的完整 C 语言代码示例: ```c #include <stdio.h> int is_little_endian() { union { int i; char c; } test_union; test_union.i = 1; // 向int成员写入1 return test_union.c; // 如果char成员为1,则为小端;如果为0,则为大端 } int main() { if (is_little_endian()) { printf("当前系统为小端模式\n"); } else { printf("当前系统为大端模式\n"); } return 0; } ``` ### 内存分析 当 `test_union.i = 1` 时,整数 `1` 在内存中以 4 字节表示,其十六进制为 `0x00000001`。在小端模式下,低位字节存储在低地址,因此 `test_union.c` 会读取到 `0x01`;而在大端模式下,高位字节存储在低地址,因此 `test_union.c` 会读取到 `0x00` [^4]。 ### 扩展应用 除了使用联合体判断大小端外,还可以通过指针访问整数的字节来实现相同的目的。例如,将 `int` 类型的地址强制转换为 `char*` 类型,然后检查第一个字节的值。 ```c int is_little_endian_by_pointer() { int i = 1; char *c = (char*)&i; return *c; // 如果为1,则为小端;如果为0,则为大端 } ``` 这种方法的原理与联合体类似,都是基于内存的字节排列顺序来判断大小端模式。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值