網站首頁 編程語言 正文
前言
本文以C語言實現,主要通過例題+說明的模式講解存儲模式:大小端字節序。
對于正整數而言,它的補碼 = 原碼 = 反碼;
對于負整數而言,它的補碼 = 原碼按位取反(就是反碼) + 1;
例如,當語句int a = 500被執行時,內存中會開辟 4字節(即32bit)的空間存儲整數20的二進制補碼(00000000 00000000 0000000111110100)。同時,我們也知道,內存中的每一個存儲單元的大小為 1字節(8bit)。
那么此時問題來了:int a = 500所占的總空間是4字節,然而每一個內存單元只存的下1字節的數據,換句話說,每一個整型數據需要4個存儲單元才能存的下——那么,這4個存儲單元在內存中到底是如何分布的呢?
是像數組一樣,用來存儲數據的4個內存單元彼此之間連續開辟,還是物理空間上其實并不連續、只是解析數據時把4個單元中的數據“拼”到一起,還原出連續的整型數呢?
每一個字節的數據,如何“被安排”存儲空間?
這個問題,本質上是數據在內存中的存儲模式問題。這就關系到我們今天要介紹的重點:大端數據存儲模式或小端數據存儲模式(即大端字節序與小端字節序)。
我們打開編譯器vs2019的內存監視,可以從監視窗口看到數值在內存中的存儲情況(顯示為16進制)。
注意:32位下,
20的16進制補碼為:00 00 00 14
-10的十六進制補碼為:ff ff ff f6
兩個16進制位恰好是一個字節8bit。我們將視圖調整為每行顯示4個字節(即一個int,這樣看得更清晰),于是可以發現,在內存中數據似乎是“倒著存”的。00 00 00 14在內存中的存儲顯示為了14 00 00 00.
事實上,這并不叫做“倒著存”,這正是我們上面提到過的:小端字節序的存儲模式。在閱讀完本文后,我們就能明白。
一、大小端介紹
當一個數值的大小超過 1字節 ,那么它要存到內存中,字節與字節之間就有存儲順序的問題。這個順序稱之為字節序。
以int類型為例。
理論上來說,即使存儲一個int型數據的4個存儲單元不連續,甚至天南海北,只要最終解析時能還原成一個正常的數即可。但是這么做屬實沒有必要:計算機中存儲肯定是采用最便捷的存儲方式,那就是連續存儲,一個整型的4個字節空間挨在一塊兒。
這時要考慮的問題便是,這四個挨在一塊的存儲空間,是由低地址向高地址存儲,還是由高地址向低地址存儲?
1. 大端字節序與小端字節序的概念
小端字節序:把一個數值的低位字節內容存放在低地址處;高位字節內容存放在高地址處。(低位存低地址,高位存高地址)。
大端字節序:把一個數值的低位字節內容存放在高地址處;高位字節內容存放在低地址處。(低位存高地址,高位存低地址)。
例如,要存儲一個十六進制數 0x11223344。該數中右端是低數位,左端是高數位(類比十進制數,十進制數10里面0是個位更低,1是十位更高)。
小端存儲情況如下:
小端存儲模式示意
小端存儲模式下,若取出該int型數據的首地址內容會發現,存的其實是0x44。
而大端存儲情況如下:
大端存儲模式示意
以上就是對大小端存儲模式的理解。
至于實際中如何存儲的,還取決于具體編譯器的選擇。現在大部分的編譯器選擇的是小端存儲模式,也就是我們上面看到的“倒著存”。
這時再參照引言中的例子,應該能對這兩種存儲模式有一個較好的理解了。
2. 為什么會有大小端之分?
這是因為在計算機系統中,我們以字節為單位,每個地址單元都對應著一個字節,一個字節為8 bit 。但是在 C 語言中,除了 8 bit 的 char 之外,還有 16 bit 的 short 型,32 bit 的 long 型(要看具體的編譯器);另外,對于位數大于 8 位的處理器,例如 16 位或者 32位的處理器,由于寄存器寬度大于一個字節,那么必然存在著一個如何將多個字節安排的問題。因此就導致了大端存儲模式和小端存儲模式。
例如,一個 16bit 的 short 型 x ,在內存中的地址為 0x0010 , x 的值為 0x1122 ,那么 0x11 為高字節,0x22 為低字節。
對于大端模式,就將 0x11 放在低地址中,即 0x0010 中, 0x22 放在高地址中,即 0x0011 中。 小端模式,剛好相反。我們常用的 X86 結構是小端模式,而 KEIL C51 則為大端模式。很多ARM , DSP 都為小端模式。有些 ARM 處理器還可以由硬件來選擇是大端模式還是小端模式。
3.一道和字節序相關的例題
題干
在小端機器中,下面代碼輸出的結果是:
#include <stdio.h>
int main()
{
int a = 0x11223344;
char *pc = (char*)&a;
*pc = 0;
printf("%x\n", a);
return 0;
}
思路
本題中的數值與我們上面討論的數值一樣,應該不難理解:
1. char*類型的指針變量pc指向的只能指向char類型的空間。如果是非char類型的空間,則必須將該空間的地址強轉為char*類型。
2. char *pc = (char*)&a; pc實際指向的是整形變量a的空間,即指針pc里放的是0x00405090,指向的值即0x44。
3. *pc=0,關鍵一步:究竟是將哪個存儲單元中的值置零了?由首個單元中存儲的內容可知,將0x44位置中內容改為了0。修改完成之后,a中內容為:0x11223300 (數值書寫肯定是高數位在左,低數位在右)
二、如何設計一個小程序判斷當前機器的字節序
百度2015年系統工程師筆試題
題干
請簡述大端字節序和小端字節序的概念,設計一個小程序來判斷當前機器的字節序。( 10 分)
解題
概念部分同上,在此不再贅述。
代碼部分:
1. 要檢測某一機器是大端還是小端其實并不難,我們可以直接用int類型的1來檢測。
2. 1在內存中的二進制補碼為:00000000000000000000000000000001
也就是說,我們只需要把首個內存單元的值取出來看看是0還是1,就能判斷它是大端還是小端。
因為如果是大端,則“高數位存在低地址”,我們取出最低地址的值,應當是最高數位上的數00000000;而若是小端,則“低數位存在低地址”,依然取出最低地址的值,應當是00000001
依照該思路,有以下代碼:
#include <stdio.h>
int check_sys()
{
int i = 1;
return (*(char *)&i);
}
int main()
{
int ret = check_sys();
if(ret == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
當然,也可以用共用體來實現檢查,這是更為便捷的一種方式:
int check_sys()
{
union
{
int i;
char c;
}un;
un.i = 1;
return un.c;
}
原文鏈接:https://blog.csdn.net/wyd_333/article/details/126248301
相關推薦
- 2022-04-01 Failed to execute goal com.github.eirslettfrontend
- 2022-11-10 Redis源碼設計剖析之事件處理示例詳解_Redis
- 2022-11-22 CentOS?7.9?安裝?docker20.10.12的過程解析_docker
- 2022-08-01 詳解Selenium中元素定位方式_python
- 2022-05-10 golang使用zookeeper進行增刪改查
- 2023-03-03 C語言malloc與calloc區別詳解_C 語言
- 2022-07-18 Linux如何設置SSH免密登錄
- 2023-02-23 Redis的setNX分布式鎖超時時間失效?-1問題及解決_Redis
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支