新聞中心
深入淺出:linux ntohl函數(shù)使用方法詳解

東山網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián),東山網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為東山超過千家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)營銷網(wǎng)站建設(shè)要多少錢,請找那個售后服務(wù)好的東山做網(wǎng)站的公司定做!
網(wǎng)絡(luò)字節(jié)序和主機字節(jié)序的轉(zhuǎn)換是網(wǎng)絡(luò)編程中不可避免的問題。ntohl函數(shù)是Linux中處理字節(jié)序轉(zhuǎn)換的重要函數(shù)之一。本文將深入淺出地講解ntohl函數(shù)的使用方法。
一、關(guān)于字節(jié)序
在計算機系統(tǒng)中,數(shù)據(jù)類型是以數(shù)值形式存儲在內(nèi)存中的。然而,在網(wǎng)絡(luò)傳輸中,數(shù)據(jù)需要以二進制流的形式進行傳輸。由于不同的系統(tǒng)和處理器所使用的字節(jié)序(大端序或小端序)不同,為了確保數(shù)據(jù)在網(wǎng)絡(luò)傳輸過程中能夠被正確地讀取,需要進行字節(jié)序的轉(zhuǎn)換。
網(wǎng)絡(luò)字節(jié)序是一種特定的字節(jié)序,用于在網(wǎng)絡(luò)中傳輸數(shù)據(jù)。網(wǎng)絡(luò)字節(jié)序采用大端字節(jié)序,在其中,高位字節(jié)存儲在低地址中,低位字節(jié)存儲在高地址中。
主機字節(jié)序則根據(jù)處理器的不同,有可能采用大端字節(jié)序,也有可能采用小端字節(jié)序。在大端字節(jié)序中,高位字節(jié)存儲在低地址,低位字節(jié)存儲在高地址;而在小端字節(jié)序中,則是恰恰相反。
例如,對于十六進制數(shù)0x12345678,在不同的字節(jié)序中所對應(yīng)的內(nèi)存存儲方式如下:
大端字節(jié)序:0x12 0x34 0x56 0x78
小端字節(jié)序:0x78 0x56 0x34 0x12
在網(wǎng)絡(luò)編程中,需要使用一些函數(shù)將主機字節(jié)序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序,或者將網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換成主機字節(jié)序,這就是ntohl函數(shù)發(fā)揮作用的時候了。
二、ntohl函數(shù)的介紹
ntohl函數(shù)的作用就是將網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換成主機字節(jié)序。ntohl函數(shù)在頭文件中被定義,它的原型如下:
unsigned long int ntohl (unsigned long int x);
該函數(shù)將一個網(wǎng)絡(luò)字節(jié)序的32位整數(shù)x轉(zhuǎn)換成一個主機字節(jié)序的32位整數(shù)。函數(shù)返回一個主機字節(jié)序的32位整數(shù)。
在ntohl函數(shù)的實現(xiàn)中,將x強轉(zhuǎn)成了unsigned char*類型,并按照網(wǎng)絡(luò)字節(jié)序的順序?qū)?個字節(jié)的數(shù)據(jù)寫入一個32位整數(shù)中,然后將其返回。
在ntohl函數(shù)中,大端字節(jié)序和小端字節(jié)序的數(shù)據(jù)處理方法是完全一致的,因此ntohl函數(shù)的使用與系統(tǒng)的字節(jié)序無關(guān)。
三、ntohl函數(shù)的使用
下面的代碼片段演示了如何使用ntohl函數(shù)將網(wǎng)絡(luò)字節(jié)序的IP地址轉(zhuǎn)換成字符串:
“`
#include
#include
#include
#include
int mn() {
uint32_t ip = 0x7f000001;
uint8_t *p = (uint8_t*) &ip;
struct in_addr ip_addr;
memcpy(&ip_addr, &ip, sizeof(ip_addr));
char ip_str[INET_ADDRSTRLEN];
memset(ip_str, ‘\0’, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &ip_addr, ip_str, INET_ADDRSTRLEN);
printf(“The IP address is: %s\n”, ip_str);
printf(“The IP address in host byte order is: %lu\n”, (unsigned long) ntohl(ip));
return 0;
}
“`
在這個代碼片段中,我們定義了一個32位的IP地址ip,這個IP地址是在網(wǎng)絡(luò)字節(jié)序下存儲的。我們使用了ntohl函數(shù)將它轉(zhuǎn)化為主機字節(jié)序,并輸出了它的十進制形式。
需要注意的是,在使用ntohl函數(shù)時,需要將網(wǎng)絡(luò)字節(jié)序的數(shù)據(jù)以某種形式存儲在內(nèi)存中,才能對其進行處理。例如在上述代碼片段中,我們將數(shù)字變量ip強制轉(zhuǎn)換為一個unsigned char指針,并使用memcpy函數(shù)將其值復制到了一個struct in_addr類型的變量中,以便于inet_ntop函數(shù)將其轉(zhuǎn)化為字符串形式。
四、
成都網(wǎng)站建設(shè)公司-創(chuàng)新互聯(lián)為您提供網(wǎng)站建設(shè)、網(wǎng)站制作、網(wǎng)頁設(shè)計及定制高端網(wǎng)站建設(shè)服務(wù)!
大端的計算機用語
端模式(Endian)的這個詞出自JonathanSwift書寫的《格列佛游記》。這本書根據(jù)將雞蛋敲開的方法不同將所有的人分為兩類,從圓頭開始將雞蛋敲開的人被歸為BigEndian,從尖頭開始將雞蛋敲開的人被歸為LittileEndian。小人國的內(nèi)戰(zhàn)就源于吃雞蛋時是究竟從大頭(Big-Endian)敲開還是從小頭(Little-Endian)敲開。在計算機業(yè)BigEndian和LittleEndian也幾乎引起一場戰(zhàn)爭。在計算機業(yè)界,Endian表示數(shù)據(jù)在存儲器中的存放順序。下文舉例說明在計算機中大小端模式的區(qū)別。
如果將一個32位的整數(shù)0x存放到一個整型變量(int)中,這個整型變量采用大端或者小端模式在內(nèi)存中的存儲由下表所示。為簡單起見,本書使用OP0表示一個32位數(shù)據(jù)的更高字節(jié)MSB(Most Significant Byte),使用OP3表示一個32位數(shù)據(jù)更低字節(jié)LSB(Least Significant Byte)。 ;地址偏移 ;大端模式 ;小端模式 0x00 12(OP0) 78(OP3) 0x01 34(OP1) 56(OP2) 0x02 56(OP2) 34(OP1) 0x03 78(OP3) 12(OP0) 如果將一個16位的整數(shù)0x1234存放到一個短整型變量(short)中。這個短整型變量在內(nèi)存中的存儲在大小端模式由下表所示。 ;地址偏移 ;大端模式 ;小端模式 0x00 12(OP0) 34(OP1) 0x01 34(OP1) 12(OP0) 由上表所知,采用大小模式對數(shù)據(jù)進行存放的主要區(qū)別在于在存放的字節(jié)順序,大端方式將高位存放在低地址,小端方式將高位存放在高地址。采用大端方式進行數(shù)據(jù)存放符合人類的正常思維,而采用小端方式進行數(shù)據(jù)存放利于計算機處理。到目前為止,采用大端或者小端進行數(shù)據(jù)存放,其孰優(yōu)孰劣也沒有定論。
有的處理器系統(tǒng)采用了小端方式進行數(shù)據(jù)存放,如Intel的奔騰。有的處理器系統(tǒng)采用了大端方式進行數(shù)據(jù)存放,如IBM半導體和Freescale的PowerPC處理器。不僅對于處理器,一些外設(shè)的設(shè)計中也存在著使用大端或者小端進行數(shù)據(jù)存放的選擇。
因此在一個處理器系統(tǒng)中,有可能存在大端和小端模式同時存在的現(xiàn)象。這一現(xiàn)象為系統(tǒng)的軟硬件設(shè)計帶來了不小的麻煩,這要求系統(tǒng)設(shè)計工程師,必須深入理解大端和小端模式的差別。大端與小端模式的差別體現(xiàn)在一個處理器的寄存器,指令集,系統(tǒng)總線等各個層次中。 從軟件的角度上,不同端模式的處理器進行數(shù)據(jù)傳遞時必須要考慮端模式的不同。如進行網(wǎng)絡(luò)數(shù)據(jù)傳遞時,必須要考慮端模式的轉(zhuǎn)換。有過Socket接口編程經(jīng)驗的程序員一定使用過以下幾個函數(shù)用于大小端字節(jié)序的轉(zhuǎn)換。
¨ #define ntohs(n) //16位數(shù)據(jù)類型網(wǎng)絡(luò)字節(jié)順序到主機字節(jié)順序的轉(zhuǎn)換
¨ #define htons(n) //16位數(shù)據(jù)類型主機字節(jié)順序到網(wǎng)絡(luò)字節(jié)順序的轉(zhuǎn)換
¨ #define ntohl(n) //32位數(shù)據(jù)類型網(wǎng)絡(luò)字節(jié)順序到主機字節(jié)順序的轉(zhuǎn)換
¨ #define htonl(n) //32位數(shù)據(jù)類型主機字節(jié)順序到網(wǎng)絡(luò)字節(jié)順序的轉(zhuǎn)換
其中互聯(lián)網(wǎng)使用的網(wǎng)絡(luò)字節(jié)順序采用大端模式進行編址,而主機字節(jié)順序根據(jù)處理器的不同而不同,如PowerPC處理器使用大端模式,而Pentuim處理器使用小端模式。
大端模式處理器的字節(jié)序到網(wǎng)絡(luò)字節(jié)序不需要轉(zhuǎn)換,此時ntohs(n)=n,ntohl =n;而小端模式處理器的字節(jié)序到網(wǎng)絡(luò)字節(jié)必須要進行轉(zhuǎn)換,此時ntohs(n) = __swab16(n),ntohl =__swab32(n)。__swab16與__swab32函數(shù)定義如下所示。 #define ___swab16(x)
{
__u16 __x = (x);
((__u16)(
(((__u16)(__x) & (__u16)0x00ffU) > 8)));
}
#define ___swab32(x)
{
__u32 __x = (x);
((__u32)(
(((__u32)(__x) & (__u32)0x000000ffUL) > 8) |
(((__u32)(__x) & (__u32)0xff000000UL) >> 24)));
} PowerPC處理器提供了lwbrx,lhbrx,stwbrx,sthbrx四條指令用于處理字節(jié)序的轉(zhuǎn)換以優(yōu)化__swab16和__swap32這類函數(shù)。此外PowerPC處理器中的rlwimi指令也可以用來實現(xiàn)__swab16和__swap32這類函數(shù)。在LinuxPowerPC中,定義了一系列有關(guān)字節(jié)序轉(zhuǎn)換的函數(shù),其詳細定義在./include/a-powerpc/byteorder.h文件中。
程序員在對普通文件進行處理也需要考慮端模式問題。在大端模式的處理器下對文件的32,16位讀寫操作所得到的結(jié)果與小端模式的處理器不同。讀者單純從軟件的角度理解上遠遠不能真正理解大小端模式的區(qū)別。事實上,真正的理解大小端模式的區(qū)別,必須要從系統(tǒng)的角度,從指令集,寄存器和數(shù)據(jù)總線上深入理解,大小端模式的區(qū)別。 除了4.2.1節(jié)中,軟件上對不同端模式編程上的差異,處理器在硬件上也由于端模式問題在設(shè)計中有所不同。從系統(tǒng)的角度上看,端模式問題對軟件和硬件的設(shè)計帶來了不同的影響,當一個處理器系統(tǒng)中大小端模式同時存在時,必須要對這些不同端模式的訪問進行特殊的處理。
PowerPC處理器主導網(wǎng)絡(luò)市場,可以說絕大多數(shù)的通信設(shè)備都使用PowerPC處理器進行協(xié)議處理和其他控制信息的處理,這也可能也是在網(wǎng)絡(luò)上的絕大多數(shù)協(xié)議都采用大端編址方式的原因。因此在有關(guān)網(wǎng)絡(luò)協(xié)議的軟件設(shè)計中,使用小端方式的處理器需要在軟件中處理端模式的轉(zhuǎn)變。而Pentium主導個人機市場,因此多數(shù)用于個人機的外設(shè)都采用小端模式,包括一些在網(wǎng)絡(luò)設(shè)備中使用的PCI總線,F(xiàn)lash等設(shè)備,這也要求硬件工程師在硬件設(shè)計中注意端模式的轉(zhuǎn)換。
本書中的小端外設(shè)是指這種外設(shè)中的寄存器以小端方式進行存儲,如PCI設(shè)備的配置空間,NOR FLASH中的寄存器等等。
對于有些設(shè)備,如DDR顆粒,沒有以小端方式存儲的寄存器,因此從邏輯上講并不需要對端模式進行轉(zhuǎn)換。在設(shè)計中,只需要將雙方數(shù)據(jù)總線進行一一對應(yīng)的互連,而不需要進行數(shù)據(jù)總線的轉(zhuǎn)換。
如果從實際應(yīng)用的角度說,采用小端模式的處理器需要在軟件中處理端模式的轉(zhuǎn)換,因為采用小端模式的處理器在與小端外設(shè)互連時,不需要任何轉(zhuǎn)換。
而采用大端模式的處理器需要在硬件設(shè)計時處理端模式的轉(zhuǎn)換。大端模式處理器需要在寄存器,指令集,數(shù)據(jù)總線及數(shù)據(jù)總線與小端外設(shè)的連接等等多個方面進行處理,以解決與小端外設(shè)連接時的端模式轉(zhuǎn)換問題。
在寄存器和數(shù)據(jù)總線的位序定義上,基于大小端模式的處理器有所不同。
一個采用大端模式的32位處理器,如基于E500內(nèi)核的MPC8541,將其寄存器的更高位m(mostsignificant bit)定義為0,更低位l(lease significantbit)定義為31;而小端模式的32位處理器,將其寄存器的更高位定義為31,低位地址定義為0。
與此向?qū)?yīng),采用大端模式的32位處理器數(shù)據(jù)總線的更高位為0,更高位為31;采用小端模式的32位處理器的數(shù)據(jù)總線的更高位為31,更低位為0。如圖4.5所示。 OP0 OP1 OP2 OP3 OP0 OP1 OP2 OP0 ;圖4.5大小端模式處理器的寄存器的定義 ;大端模式處理器寄存器位序定義 ;小端模式處理器寄存器位序定義 大小端模式處理器外部總線的位序也遵循著同樣的規(guī)律,根據(jù)所采用的數(shù)據(jù)總線是32位,16位和8位,大小端處理器外部總線的位序有所不同。
¨ 大端模式下32位數(shù)據(jù)總線的m是第0位,MSB是數(shù)據(jù)總線的第0~7的字段;而l是第31位,LSB是第24~31字段。小端模式下32位總線的m是第31位,MSB是數(shù)據(jù)總線的第31~24位,l是第0位,LSB是7~0字段。
¨ 大端模式下16位數(shù)據(jù)總線的m是第0位,MSB是數(shù)據(jù)總線的第0~7的字段;而l是第15位,LSB是第8~15字段。小端模式下16位總線的m是第15位,MSB是數(shù)據(jù)總線的第15~7位,l是第0位,LSB是7~0字段。
¨ 大端模式下8位數(shù)據(jù)總線的m是第0位,MSB是數(shù)據(jù)總線的第0~7的字段;而l是第7位,LSB是第0~7字段。小端模式下8位總線的m是第7位,MSB是數(shù)據(jù)總線的第7~0位,l是第0位,LSB是7~0字段。
由上分析,我們可以得知對于8位,16位和32位寬度的數(shù)據(jù)總線,采用大端模式時數(shù)據(jù)總線的m和MSB的位置都不會發(fā)生變化,而采用小端模式時數(shù)據(jù)總線的l和LSB位置也不會發(fā)生變化。
為此,大端模式的處理器對8位,16位和32位的內(nèi)存訪問(包括外設(shè)的訪問)一般都包含第0~7字段,即MSB。小端模式的處理器對8位,16位和32位的內(nèi)存訪問都包含第7~0位,小端方式的第7~0字段,即LSB。
由于大小端處理器的數(shù)據(jù)總線其8位,16位和32位寬度的數(shù)據(jù)總線的定義不同,因此需要分別進行討論在系統(tǒng)級別上如何處理端模式轉(zhuǎn)換。
在一個大端處理器系統(tǒng)中,需要處理大端處理器對小端外設(shè)的訪問。
1.1.2.1 大端處理器對32位小端外設(shè)進行訪問大端處理器采用32位總線與小端外設(shè)進行訪問時,大端處理器的32位數(shù)據(jù)總線的第0~7位用來處理OP0,第8~15位用來處理OP1,第16~23位用來處理OP2,第24~31位用來處理OP3。而32位的小端設(shè)備使用數(shù)據(jù)總線的第31~24位用來處理OP0,第23~16位用來處理OP1,第15~8位用來處理OP2,第7~0位用來處理OP3。
大端處理器,如MPC8541,使用stw,sth,stb和lwz,lhz,lbz指令對32位的外部設(shè)備進行訪問。在這些指令結(jié)束后,存放在外部設(shè)備的數(shù)據(jù)將被讀入MPC8541的通用寄存器中。為保證軟件的一致性,當訪問結(jié)束后,存放在通用寄存器的字節(jié)序,即OP0,OP1,OP2和OP3必須要和存放在小端外設(shè)的字節(jié)序一致。此時在使用大端處理器的數(shù)據(jù)總線連接小端外設(shè)時必須要進行一定的處理,按照某種拓撲結(jié)構(gòu)連接以保證軟件的一致性。大端處理器數(shù)據(jù)總線與小端外設(shè)進行連接的拓撲結(jié)構(gòu)如圖4.6所示。 OP0 OP1 OP2 OP 0 ;大端處理器的32位數(shù)據(jù)總線 ;小端設(shè)備的32位總線接口 ;圖4.6 大端處理器與小端外設(shè)的32位連接 OP0 OP1 OP2 OP3 如圖4.6所示,采用大端處理器訪問小端設(shè)備時,將各自的OP0~OP3字段直接相連。在大端處理器的32位數(shù)據(jù)總線的更高位為0,更低位為31;而小端設(shè)備的更高位為31,更低位為0。因此硬件工程師在進行信號連接時需要將采用大端處理器的0~31位分別與小端設(shè)備的31~0位一一對應(yīng),進行互連。
1.1.2.2 大端處理器對8,16位小端外設(shè)進行訪問大端處理器使用8位,16位數(shù)據(jù)總線對8位,16位的小端外設(shè)進行連接。對于32位處理器,用來連接外設(shè)的總線一般是32位。因此體系結(jié)構(gòu)工程師在進行大端處理器總線設(shè)計時有兩種選擇,是采用32位總線的高端部分(第0~15字段)還是低端部分(第16~31字段)連接小端設(shè)備。PowerPC處理器使用32位總線的高端部分,即數(shù)據(jù)總線的第0~15位連接16位的小端設(shè)備,使用0~7位連接8位的小端設(shè)備。
PowerPC處理器采用16位總線與16位的小端外設(shè)進行訪問時,PowerPC處理器的16位數(shù)據(jù)總線的第0~7位用來處理OP0,第8~15位用來處理OP1。而16位的小端設(shè)備使用數(shù)據(jù)總線的第15~8位用來處理OP0,第7~0位用來處理OP1。
PowerPC處理器采用8位總線與8位的小端外設(shè)進行訪問時,PowerPC處理器的8位數(shù)據(jù)總線的第0~7字段用來處理OP0。而8位的小端設(shè)備使用數(shù)據(jù)總線的第7~0位用來處理OP1。大端處理器與小端外設(shè)的連接關(guān)系如圖4.7所示。 OP0 OP1 OP0 OP ;大端處理器的8/16位數(shù)據(jù)總線 ;小端設(shè)備的8/16位總線接口 ;圖4.7 大端處理器與小端外設(shè)的8/16位連接 OP0 OP 與32位總線接口類似,PowerPC處理器可以使用stw,sth,stb和lwz,lhz,lbz指令對32位的外部設(shè)備進行訪問,并將數(shù)據(jù)存放在相應(yīng)的通用寄存器中。當訪問結(jié)束后,存放在通用寄存器的字節(jié)序,即OP0,OP1必須要和存放在小端外設(shè)的字節(jié)序一致。
PowerPC處理器對8位的小端外設(shè)進行訪問時,一個總線周期只能訪問8位數(shù)據(jù),如果處理器使用stw或者lwz指令訪問8位的小端設(shè)備內(nèi)的32位數(shù)據(jù)時,在數(shù)據(jù)總線上將OP0,OP1,OP2和OP3依次傳遞到PowerPC的通用寄存器中。
PowerPC處理器對16位的小端外設(shè)進行訪問時,一個總線周期只能訪問16位數(shù)據(jù),如果處理器使用stw或者lwz指令訪問16位的小端設(shè)備內(nèi)的32位數(shù)據(jù)時,在數(shù)據(jù)總線上將OP0~1和OP2~3依次傳遞到PowerPC的通用寄存器中。
PowerPC處理器使用sth或者lhz指令訪問16位的小端設(shè)備時,16位的小端設(shè)備將數(shù)據(jù)的第15~0位,傳遞到PowerPC處理器的總線的第0~15位,然后再將數(shù)據(jù)最終傳遞給相應(yīng)的通用寄存器。這里有許多讀者會感到困惑,因為為了保證軟件的一致性,PowerPC處理器使用lhz指令訪問16位的小端設(shè)備的16位寄存器時,需要將結(jié)果保存在通用寄存器的第16~31位,而不是0~15位。究竟PowerPC處理器是如何將系統(tǒng)總線中0~15位的數(shù)據(jù)搬移到寄存器的第16~31位中的呢?為此我們需要對lhz指令進行分析。 lhz rD,d(rA)
if rA = 0 then b ← 0
else b ← (rA)
EA ← b + EXTS(d)
rD ← (24)0 || MEM(EA,1) 由lhz指令的以上描述得知lhz指令將來自數(shù)據(jù)總線上的OP0與OP1直接存入寄存器的第16~31位,而將第0~15位直接清零。
PowerPC處理器使用stb或者lbz指令訪問8位的小端設(shè)備時,8位的小端設(shè)備將數(shù)據(jù)的第7~0位,傳遞到PowerPC處理器的總線的第0~7位,然后再將數(shù)據(jù)最終傳遞給相應(yīng)的通用寄存器,lbz指令的描述如下所示。 lbz rD,d(rA)
if rA = 0 then b ← 0
else b ← (rA)
EA ← b + EXTS(d)
rD ← (24)0 || MEM(EA,1) 由lhz指令的以上描述得知lhz指令將來自數(shù)據(jù)總線上的OP0直接存入寄存器的第24~31位,而將第0~23位清零。
文章二:
大端(big-endian)和小端(little-endian)
:36 ;補:x86機是小端(修改分區(qū)表時要注意),單片機一般為大端 今天碰一個關(guān)于字節(jié)順序的問題,雖然看起來很簡單,但一直都沒怎么完全明白這個東西,索性就找了下資料,把它弄清楚. 因為現(xiàn)行的計算機都是以八位一個字節(jié)為存儲單位,那么一個16位的整數(shù),也就是C語言中的short,在內(nèi)存中可能有兩種存儲順序big-endian和 litte-endian.考慮一個short整數(shù)0x3132(0x32是低位,0x31是高位),把它賦值給一個short變量,那么它在內(nèi)存中的存 ;儲可能有如下兩種情況:
大端字節(jié)(Big-endian):
—>>>>>>>>;內(nèi)存地址增大方向
short變量地址
0x1000 0x1001
_____________________________| || 0x31 | 0x32|_______________ | ________________
高位字節(jié)在低位字節(jié)的前面,也就是高位在內(nèi)存地址低的一端.可以這樣記住(大端->;高位->;在前->;正常的邏輯順序)
小端字節(jié)(little-endian):
—>>>>>>>>;內(nèi)存地址增大方向
short變量地址
0x1000 0x1001
_____________________________| || 0x32 | 0x31|________________ | ________________低位字節(jié)在高位字節(jié)的前面,也就是低位在內(nèi)存地址低的一端.可以這樣記?。ㄐ《?>;低位->;在前->;與正常邏輯順序相反)
可以做個實驗
在windows上下如下程序
#include
#include
void main(void)
{ short test; FILE* fp; test = 0x3132; //(31ASⅡC碼的’1’,32ASⅡC碼的’2’)
if ((fp = fopen (c:\\test.txt,wb)) == NULL)
assert(0); fwrite(&test,sizeof(short),1,fp); fclose(fp);
}
然后在C盤下打開test.txt文件,可以看見內(nèi)容是21,而test等于0x3132,可以明顯的看出來x86的字節(jié)順序是低位在前.如果我們把這段 ;同樣的代碼放到(big-endian)的機器上執(zhí)行,那么打出來的文件就是12.這在本機中使用是沒有問題的.但當你把這個文件從一個big- endian機器復制到一個little-endian機器上時就出現(xiàn)問題了.
如上述例子,我們在big-endian的機器上創(chuàng)建了這個test文件,把其復制到little-endian的機器上再用fread讀到一個 short里面,我們得到的就不再是0x3132而是0x3231了,這樣讀到的數(shù)據(jù)就是錯誤的,所以在兩個字節(jié)順序不一樣的機器上傳輸數(shù)據(jù)時需要特別小 ;心字節(jié)順序,理解了字節(jié)順序在可以幫助我們寫出移植行更高的代碼.
正因為有字節(jié)順序的差別,所以在網(wǎng)絡(luò)傳輸?shù)臅r候定義了所有字節(jié)順序相關(guān)的數(shù)據(jù)都使用big-endian,BSD的代碼中定義了四個宏來處理:
#define ntohs(n) //網(wǎng)絡(luò)字節(jié)順序到主機字節(jié)順序 n代表net,h代表host,s代表short
#define htons(n) //主機字節(jié)順序到網(wǎng)絡(luò)字節(jié)順序 n代表net,h代表host,s代表short
#define ntohl(n) //網(wǎng)絡(luò)字節(jié)順序到主機字節(jié)順序 n代表net,h代表host,l代表 long
#define htonl(n) //主機字節(jié)順序到網(wǎng)絡(luò)字節(jié)順序 n代表net,h代表host,l代表 long
舉例說明下這其中一個宏的實現(xiàn):#define sw16(x) \ ((short)(\ (((short)(x) & (short)0x00ffU) > 8)))
這里實現(xiàn)的是一個交換兩個字節(jié)順序.其他幾個宏類似.
我們改寫一下上面的程序
#include
#include
#define sw16(x) \
((short)(\ (((short)(x) & (short)0x00ffU) > 8)))
// 因為x86下面是低位在前,需要交換一下變成網(wǎng)絡(luò)字節(jié)順序
#define htons(x) sw16(x)
void main(void)
{ short test; FILE* fp;
test = htons(0x3132); //(31ASⅡC碼的’1’,32ASⅡC碼的’2’) if ((fp = fopen (c:\\test.txt,wb)) == NULL)
assert(0); fwrite(&test,sizeof(short),1,fp); fclose(fp);
}
如果在高字節(jié)在前的機器上,由于與網(wǎng)絡(luò)字節(jié)順序一致,所以我們什么都不干就可以了,只需要把#define htons(x) sw16(x)宏替換為 #define htons(x) (x).
一開始我在理解這個問題時,總在想為什么其他數(shù)據(jù)不用交換字節(jié)順序?比如說我們write一塊buffer到文件,最后終于想明白了,因為都是unsigned char類型一個字節(jié)一個字節(jié)的寫進去,這個順序是固定的,不存在字節(jié)順序的問題,夠笨啊.. 文章三:
big-endian和little-endian這兩個術(shù)語來自Jonathan Swift在十八世紀的嘲諷作品Gulliver’s Travels。Blefuscu帝國的國民被根據(jù)吃雞蛋的方式劃分為兩個部分:一部分在吃雞蛋的時候從雞蛋的大端(bigend)開始,而另一部分則從雞蛋的小端(little end)開始。
x86的CPU使用的是LE(Windows中稱為“主機字節(jié)序”),而SocksAddr中使用的則是BE(就是“網(wǎng)絡(luò)字節(jié)序”),所以在使用網(wǎng)絡(luò)編程時需要使用htns,htnl,nths,nthl來倒字節(jié)序。
其實對匯編熟了就清楚了,慘,我的匯編很慘的LE little-endian 更符合人的思維的字節(jié)序 地址低位存儲值的低位 地址高位存儲值的高位 怎么講是更符合人的思維的字節(jié)序,是因為從人的之一觀感來說 低位值小,就應(yīng)該放在內(nèi)存地址小的地方,也即內(nèi)存地址低位 反之,高位值就應(yīng)該放在內(nèi)存地址大的地方,也即內(nèi)存地址高位 BE big-endian 最直觀的字節(jié)序 地址低位存儲值的高位 地址高位存儲值的低位 為什么說直觀,不要考慮對應(yīng)關(guān)系 只需要把內(nèi)存地址從左到右按照由低到高的順序?qū)懗?把值按照通常的高位到低位的順序?qū)懗?兩者對照,一個字節(jié)一個字節(jié)的填充進去 例子:在內(nèi)存中雙字0x(DWORD)的存儲方式 內(nèi)存地址LEBEMSDN中關(guān)于LE和BE的解釋Byte Ordering Byte ordering Meaning big-endian The most significant byte is on the left end of a word. little-endian The most significant byte is on the right end of a word. 這里這個最重要的字節(jié)可以解釋成值的更高位,如果換成是錢的話就是最值錢的那一位 比如我有1234元人民幣,最值錢的是1000元,最不值錢的是4元,那么這個1就是最重要的字節(jié)
Big endian machine: It thinks the first byte it reads is the biggest.Little endian machine: It thinks the first byte it reads is the littlest.舉個例子,從內(nèi)存地址0x0000開始有以下數(shù)據(jù)0x0000 0x120x0001 0x340x0002 0xab0x0003 0xcd如果我們?nèi)プx取一個地址為0x0000的四個字節(jié)變量,若字節(jié)序為big-endian,則讀出結(jié)果為0x1234abcd;若字節(jié)序位little-endian,則讀出結(jié)果為0xcdab3412.如果我們將0x1234abcd寫入到以0x0000開始的內(nèi)存中,則結(jié)果為 big-endian little-endian0x0000 0x12 0xcd0x0001 0x23 0xab0x0002 0xab 0x340x0003 0xcd 0x12x86系列CPU都是little-endian的字節(jié)序。
關(guān)于linux ntohl函數(shù)的介紹到此就結(jié)束了,不知道你從中找到你需要的信息了嗎 ?如果你還想了解更多這方面的信息,記得收藏關(guān)注本站。
創(chuàng)新互聯(lián)(cdcxhl.com)提供穩(wěn)定的云服務(wù)器,香港云服務(wù)器,BGP云服務(wù)器,雙線云服務(wù)器,高防云服務(wù)器,成都云服務(wù)器,服務(wù)器托管。精選鉅惠,歡迎咨詢:028-86922220。
分享標題:「深入淺出」Linuxntohl函數(shù)使用方法詳解(linuxntohl函數(shù))
分享URL:http://m.fisionsoft.com.cn/article/cdogcee.html


咨詢
建站咨詢
