C語(yǔ)言大小端模式、判斷大小端、大小端轉(zhuǎn)換方法詳解
1. 什么是大端和小端
對(duì)于一個(gè)存儲(chǔ)空間大于 1 個(gè)字節(jié)的數(shù)據(jù),在內(nèi)存中有兩種存儲(chǔ)模式,
大端模式 (big-endian):數(shù)據(jù)的高字節(jié)在內(nèi)存的低地址存放,數(shù)據(jù)的低字節(jié)在內(nèi)存的高地址存放
小端模式 (little-endian):數(shù)據(jù)的高字節(jié)在內(nèi)存的高地址存放,數(shù)據(jù)的低字節(jié)在內(nèi)存的低地址存放
簡(jiǎn)單記憶:
大端模式:大高低
小端模式:小低低
比如,比如數(shù)字:0x12345678,大小端存儲(chǔ)模式如下:


2.為什么會(huì)存在大小端的問(wèn)題
在 C 語(yǔ)言中,有占 1 個(gè)字節(jié)內(nèi)存空間的 char 類型,占 2 個(gè)字節(jié)內(nèi)存空間的 short int 類型,以及占 4 個(gè)字節(jié)內(nèi)存空間的 int,double 類型和占 8 個(gè)字節(jié)內(nèi)存空間的 double 類型等等,那么對(duì)于大于 1 個(gè)字節(jié)空間的數(shù)據(jù)類型的數(shù)據(jù)在內(nèi)存中存儲(chǔ)時(shí),就必然產(chǎn)生一個(gè)數(shù)據(jù)在內(nèi)存中應(yīng)該將其低字節(jié)數(shù)據(jù)安排在內(nèi)存的低地址還是高地址呢?,對(duì)這個(gè)問(wèn)題做的不同選擇,就形成了大小端問(wèn)題。
3. 判斷主機(jī)字節(jié)序 (主機(jī)大小端)
3.1 使用聯(lián)合體 (union)
聯(lián)合體的每一個(gè)成員共用一個(gè)內(nèi)存地址,修改其中一個(gè)成員的數(shù)據(jù),可能會(huì)影響其它成員的數(shù)據(jù)的值。比如以下聯(lián)合體,成本變量 i 和 c 的內(nèi)存地址是相同的,它的內(nèi)存分布示意圖為
union {
int i;
char c;
}un; // 匿名聯(lián)合體
#include <stdio.h>
int is_little_endian() {
union {
int i;
char c;
}un; // 匿名聯(lián)合體
un.i = 1;
return un.c; // 小端:返回 1,說(shuō)明數(shù)據(jù)的低字節(jié)在內(nèi)存的低地址存放
// 大端:返回 0,說(shuō)明數(shù)據(jù)的低字節(jié)在內(nèi)存的高地址存放
}
int main() {
if (is_little_endian())
printf("little-endian\n");
else
printf("big-endian\n");
return 0;
}3.2 使用指針
#include <stdio.h>
int is_little_endian() {
int i = 1;
// 等同于 char* p = (char*)&i; return *p;
return *(char*)&i; // 小端:返回 1,說(shuō)明數(shù)據(jù)的低字節(jié)在內(nèi)存的低地址存放
// 大端:返回 0,說(shuō)明數(shù)據(jù)的低字節(jié)在內(nèi)存的高地址存放
}
int main() {
if (is_little_endian())
printf("little-endian\n");
else
printf("big-endian\n");
return 0;
}3.3 強(qiáng)制轉(zhuǎn)為 char 類型法
#include <stdio.h>
int is_little_endian() {
int i = 1;
// (char)i : 強(qiáng)轉(zhuǎn)為 char 類型,實(shí)際就是取 i 在內(nèi)存中第一個(gè)字節(jié)的數(shù)據(jù)
return (char)i; // 小端:返回 1,說(shuō)明數(shù)據(jù)的低字節(jié)在內(nèi)存的低地址存放
// 大端:返回 0,說(shuō)明數(shù)據(jù)的低字節(jié)在內(nèi)存的高地址存放
}
int main() {
if (is_little_endian())
printf("little-endian\n");
else
printf("big-endian\n");
return 0;
}4. 大小端轉(zhuǎn)換
1. 編寫大小端轉(zhuǎn)化函數(shù),并打印本機(jī)字節(jié)序下的數(shù)字:0x11223344,分別在小端模式和大端模式下是什么數(shù)據(jù)。
#include <stdio.h>
int is_little_endian();
int to_opposite_endian(int data);
int to_big_endian(int data);
int to_little_endian(int data);
int main() {
int i = 0x11223344;
printf("little_endian: %x\n", to_little_endian(i));
printf("big_endian: %x\n", to_big_endian(i));
return 0;
}
int is_little_endian() {
int i = 1;
return (char)i; // 小端:返回 1,大端:返回 0
}
// 大小端轉(zhuǎn)換
int to_opposite_endian(int data) {
int size = sizeof(data);
int des = 0; // 目標(biāo)數(shù)據(jù)
int mask = 0xff; // 掩碼
int temp;
for (int i = 0; i < size; i++) {
des = des << 8; // 左移8位
temp = data & mask; // 取值
temp = temp >> i * 8; // 移到低8位
des |= temp;
mask = mask << 8; // 掩碼左移8位,為下一次取數(shù)據(jù)用
}
return des;
}
int to_big_endian(int data) {
if (!is_little_endian()) // 大端
return data;
return to_opposite_endian(data);
}
int to_little_endian(int data) {
if (is_little_endian()) // 小端
return data;
return to_opposite_endian(data);
}
2. 在網(wǎng)絡(luò)編程中,可以使用 htonl, htons, ntohl, ntohs 等函數(shù)
- htonl(uint32_t hostlong) // host to network long,32位無(wú)符號(hào)整型的主機(jī)字節(jié)序轉(zhuǎn)成網(wǎng)絡(luò)字節(jié)序
- htons(uint16_t hostshort) // host to network short,16位無(wú)符號(hào)短整型的主機(jī)字節(jié)序轉(zhuǎn)成網(wǎng)絡(luò)字節(jié)序
- ntohl(uint32_t netlong) // network to host long,32位無(wú)符號(hào)整型的網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)成主機(jī)字節(jié)順序的
- ntohs(uint16_t netshort) //16位無(wú)符號(hào)短整型的網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)成主機(jī)字節(jié)序
網(wǎng)絡(luò)字節(jié)序:大端模式(big-endian),即數(shù)據(jù)的高字節(jié)在內(nèi)存的高地址存放,數(shù)據(jù)的低字節(jié)在內(nèi)存的低地址存放。
附:大小端轉(zhuǎn)換函數(shù)
直接通過(guò)對(duì)地址的操作來(lái)實(shí)現(xiàn) 傳入的變量為32位的變量
中間變量ptr是傳入變量的地址
void swap16(void * p)
{
uint16_t *ptr=p;
uint16_t x = *ptr;
x = (x << 8) | (x >> 8);
*ptr=x;
}
void swap32(void * p)
{
uint32_t *ptr=p;
uint32_t x = *ptr;
x = (x << 16) | (x >> 16);
x = ((x & 0x00FF00FF) << 8) | ((x >> 8) & 0x00FF00FF);
*ptr=x;
}
void swap64(void * p)
{
uint64_t *ptr=p;
uint64_t x = *ptr;
x = (x << 32) | (x >> 32);
x = ((x & 0x0000FFFF0000FFFF) << 16) | ((x >> 16) & 0x0000FFFF0000FFFF);
x = ((x & 0x00FF00FF00FF00FF) << 8) | ((x >> 8) & 0x00FF00FF00FF00FF);
*ptr=x;
}總結(jié)
到此這篇關(guān)于C語(yǔ)言大小端模式、判斷大小端、大小端轉(zhuǎn)換的文章就介紹到這了,更多相關(guān)C語(yǔ)言判斷大小端、大小端轉(zhuǎn)換內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)銷售管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)銷售管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
一篇文章徹底弄懂C++虛函數(shù)的實(shí)現(xiàn)機(jī)制
C++中的虛函數(shù)的作用主要是實(shí)現(xiàn)了多態(tài)的機(jī)制,基類定義虛函數(shù),子類可以重寫該函數(shù),在派生類中對(duì)基類定義的虛函數(shù)進(jìn)行重寫時(shí),需要在派生類中聲明該方法為虛方法,這篇文章主要給大家介紹了關(guān)于如何通過(guò)一篇文章徹底弄懂C++虛函數(shù)的實(shí)現(xiàn)機(jī)制,需要的朋友可以參考下2021-06-06
利用C語(yǔ)言來(lái)求最大連續(xù)子序列乘積的方法
這篇文章主要介紹了利用C語(yǔ)言來(lái)求最大連續(xù)子序列乘積的方法,基本的思路以外文中還附有相關(guān)ACM題目,需要的朋友可以參考下2015-08-08
C++?通過(guò)pqxxlib庫(kù)鏈接?PostgreSql數(shù)據(jù)庫(kù)的詳細(xì)過(guò)程
這篇文章主要介紹了C++?通過(guò)pqxxlib庫(kù)鏈接?PostgreSql數(shù)據(jù)庫(kù),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04
C++調(diào)用C#的DLL實(shí)現(xiàn)方法
這篇文章主要介紹了C++調(diào)用C#的DLL實(shí)現(xiàn)方法,很有實(shí)用價(jià)值,需要的朋友可以參考下2014-07-07
C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之?dāng)U展字符詳解
掌握C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)的關(guān)鍵在于理解其核心概念,擴(kuò)展字符作為其中的重要一環(huán),對(duì)于編程人員來(lái)說(shuō)至關(guān)重要,本指南將為您深入剖析擴(kuò)展字符的相關(guān)知識(shí),帶您輕松掌握C語(yǔ)言數(shù)據(jù)結(jié)構(gòu),讓我們一起探索這個(gè)令人著迷的領(lǐng)域吧!2024-03-03
淺析C/C++中的可變參數(shù)與默認(rèn)參數(shù)
C支持可變參數(shù)的函數(shù),這里的意思是C支持函數(shù)帶有可變數(shù)量的參數(shù),最常見的例子就是我們十分熟悉的printf()系列函數(shù)。我們還知道在函數(shù)調(diào)用時(shí)參數(shù)是自右向左壓棧的2013-09-09

