当前位置:首页 > > 小麦大叔
[导读]先说内存 柿子捡软的捏,以前做项目的时候被大小端的问题坑过,那种酸爽就像蓝天白云,晴空万里忽然暴风雨,突如其来的BUG,让原本不充裕的时间更加雪上加霜;虽然很基础,但是能力有限,也难免出现错误和纰漏,请各位大佬们在讨论中无情指正我。

先说内存

柿子捡软的捏,以前做项目的时候被大小端的问题坑过,那种酸爽就像蓝天白云,晴空万里忽然暴风雨,突如其来的BUG,让原本不充裕的时间更加雪上加霜;虽然很基础,但是能力有限,也难免出现错误和纰漏,请各位大佬们在讨论中无情指正我。


先说内存

程序运行在内存中,计算机中的最小存储单位是Bit,即10的二进制,它可以识别的机器码就是以二进制形式存储的;

内存由多个存储单元组成,每个存储单元都有一个唯一的数字地址字节可寻址内存。每个存储位置可以包含固定数量的二进制数字。

在大多数的现代计算机上,地址的最小数据的长度为8位,称为字节(1 Byte = 8 Bit);

一般计算机中用户程序直接访问的地址是虚拟内存的地址,操作系统内核会根据用户程序访问的虚拟地址,找出页表中对于的物理地址,最终寻址到所需要的数据;

具体如下图所示;

转自wiki

然而,在MCU等裸机开发的环境中,没有MMU,则程序直接访问的是物理内存,所以无论是计算机还是MCU在程序运行中都需要内存作为载体,保存数据和运行程序。

那么,下面再来看是程序以及数据在内存中是以何种形式存储的?

字节

前面提到过,在大多数的现代计算机上,地址的最小数据的长度为8位,称为字节(1 Byte = 8 Bit);至于为什么是8位?看起来似乎有点玄学,并且很吉利的一个数字,但是老外好像没有数字迷信,这里的原因大概是因为这几点;

  • 由于计算机内部最本质需要实现的操作是加法,减法,乘法,除法等运算都能通过加法实现,另外由于最早期设计的加法器是8位;

  • 另外一个原因可以追溯到1956年,IBM公司最早提出字节的概念,随着IBM的壮大,字节便专门用来表示二进制数,其中也包括不少优点;易于以BCD码形式保存;用于保存文本也非常合适,另外世界上大部分语言都可以用小于256个字符(一个字节宽度)来表示,如果一个不够,那就两个,比如中文;

字节和位

字节顺序

在说大小端之前,要先提一下字节顺序(Endianness),它是描述数据以字节为一组在计算机内存中存储顺序的术语。

字节顺序可以是大端顺序(big-endian)或者小端顺序(little-endian);在对多字节数据进行存储时,一般遵循以下规则;

  • 小端:数据的最后一个字节先存储,即 LSB
  • 大端:数据的第一个字节先存储,即 MSB

数据0x01020304分别在大端机器和小端机器中的存储形式,具体如下图所示;

在大多数情况下,编译器会处理字节顺序,从而避免出现大小端不一致的问题,但是在以下情况下字节顺序就会成为一个问题。

在通讯中,例如网络编程:假设在小端机器上向文件写入整数,然后将此文件传输到大端机器上。如果没有做大小端转换,那么大端机器就会以相反的顺序读取文件

TCP/IP协议中,默认使用的是大端顺序,它与具体的CPU类型、操作系统等无关;

那么如何在程序中快速的区分大小端呢?

大端和小端的区分

下面介绍几种通过C语言实现大小端判断的方法;

第一种通过指针的内存对齐来实现;

函数的形式;

unsigned char check_endianvoid )
{
    int test_var = 1;
    unsigned char *test_endian = (unsigned char*)&test_var;

    return (test_endian[0] == 0);
}

宏定义的形式;

static uint32_t endianness = 0xdeadbeef
enum endianness { BIG, LITTLE };

#define ENDIANNESS ( *(const char *)&endianness == 0xef ? LITTLE \
                   : *(const char *)&endianness == 0xde ? BIG \
                   : assert(0))

更加简洁;

#define IS_BIG_ENDIAN (!*(unsigned char *)&(uint16_t){1})

第二种通过结构体和联合体的内存对齐来实现;

#ifndef ORDER32_H
#define ORDER32_H

#include 
#include 

#if CHAR_BIT != 8
#error "unsupported char size"
#endif

enum
{
    O32_LITTLE_ENDIAN = 0x03020100ul,
    O32_BIG_ENDIAN = 0x00010203ul,
    O32_PDP_ENDIAN = 0x01000302ul,      /* DEC PDP-11 (aka ENDIAN_LITTLE_WORD) */
    O32_HONEYWELL_ENDIAN = 0x02030001ul /* Honeywell 316 (aka ENDIAN_BIG_WORD) */
};

static const union { unsigned char bytes[4]; uint32_t value; } o32_host_order =
    { { 0123 } };

#define O32_HOST_ORDER (o32_host_order.value)

#endif

当然具体的方法还有很多,本文就先讲到这里。


—— The End —


长按识别二维码关注获取更多内容




免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭