C语言程序设计 | 大小端

发布于:2023-01-24 ⋅ 阅读:(19) ⋅ 点赞:(0) ⋅ 评论:(0)

目录

一、什么是大小端

什么是大小端:

为什么会有大小端:

二、如何确定大小端

两种不同的求大小端的方式

 常见的求大小端错误的方式:


一、什么是大小端

假如定义一个 int 类型的变量 a,让 a = 12345678(十六进制)

我们知道,数据在计算机中存储的单位是字节,1 Byte = 8 Bit = 2 个十六进制位。

而这四个字节在内存中与两种不同的存储方式

什么是大小端:

大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;
小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地址中。

为什么会有大小端:

至于为什么要区分大小端,这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为 8bit。但是在C语言中除了 8bit 的 char 之外,还有 16bit 的 shor t型,32 bit 的 long 型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。例如一个16bit的short型x,在内存中的地址为0x0010,x的值为0x1122,那么0x11为高字节,0x22为低字节。对于 大端模式,就将0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,刚好相反。我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以随时在程序中(在ARM Cortex 系列使用REV、REV16、REVSH指令)进行大小端的切换。

二、如何确定大小端

对于我们经常使用的x86架构的pc机来说,数据在内存中的存储方式为小端存储。

两种不同的求大小端的方式

//方法一

#include <stdio.h>

int check_sys()
{
	int i = 1;
	//转换为二进制补码:00 00 00 01
	//小端:01 00 00 00
	//大端:00 00 00 01

	return (*(char*)&i);
	//取出 i 的首字节数据
	//小端:01 -> 1
	//大端:00 -> 0
}

int main()
{
	int ret = check_sys();
	if (ret == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

//方法二

#include <stdio.h>

int check_sys()
{
	union//共用体共用一段内存
	{
		int i;
		char c;
	}un;
	un.i = 1;
	//小端:10 00 00 00
	//大端:00 00 00 01

	return un.c;
	//小端:10 -> 1
	//大端:00 -> 0
}

int main()
{
	int ret = check_sys();
	if (ret == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

 常见的求大小端错误的方式:

//强制类型转换

#include <stdio.h>

int main()
{
    int a = 1;
    char b;

    b = (char)a;
    //a的补码:00 00 00 01  ->  1
    //强制类型转换时发生数据截断
    //此时 b 的补码:01  ->  1

    printf("b = %d\n", b);
    //无论大小端,结果都是1

    return 0;
}
//移位

#include <stdio.h>
int main()
{
    int a, b;

    a = 1;
    //a 的补码:0000 0000 0000 0000 0000 0000 0000 0001  -> 1
    
    b = a >> 1;
    //移位后 b 的补码:0000 0000 0000 0000 0000 0000 0000 0000  -> 0

    printf("b = %d\n", b);
    //无论大小端,结果都是0

    return 0;
}

//位于

#include <stdio.h>

int main()
{
    int a = 1;
    //a 的补码:0000 0000 0000 0000 0000 0000 0000 0001

    int b = a & 0xff;
    //  0000 0000 0000 0000 0000 0000 0000 0001
    // &1111 1111 1111 1111 1111 1111 1111 1111
    // =0000 0000 0000 0000 0000 0000 0000 0001  -> 1

    //char b = a & 0xff  也是同理

    printf("b = %d\n", b);
    //无论大小端,结果都是1

    return 0;
}

总结:

对于取地址(指针)和联合体类型的操作都是基于内存层次的运算,这两种方法可以判断数据在内存上的存储方式。而对于强制类型转换和位于、移位的操作都是基于二进制补码层次的运算,这是高于内存层次的运算,也就是说将内存里面存放的数据拿出来再进行的运算,此时无法判断数据在内存中存储的方式。


网站公告

欢迎关注微信公众号

今日签到

点亮在社区的每一天
签到