文章程式碼顯示

2018年2月12日 星期一

《筆記》C語言 - 07_5:指標強制轉整數(cast)、數據分割取值、big&little endian

指標強制轉換為普通型態

#include "stdio.h"

int main(void) {

 int x = 10;
 int* xPtr = &x;

 printf("x = %d, address = %p\n", x, xPtr);

 printf("xPtr to unsigned long = %d(dec) %x(hex)", (unsigned long)xPtr, (unsigned long)xPtr );

}


將 int* 的指標強制轉型為 unsigned long


指標型態之間強制轉換

#include "stdio.h"

int main(){

    int i; //a counter that avoid exceed the memory
    unsigned char *ptr; // 8 bits

    short short_num = 32767; // 16 bits
    int int_num = 2147483647; // 32 bits
    long long long_long_num = 9223372036854775807; // 64 bits

    printf("short_num = %d (0x%x), address = 0x%p \n", short_num, short_num, &short_num);
    ptr = &short_num; //將 ptr 指向 short_num
    printf("ptr 指向 short_num, ptr = %x, ptr + 1 = %x \n", ptr, ptr+1);

    for(i = 0; i < sizeof(short_num); i++){
        printf("address 0x%x = 0x%x \n", ptr+i,*(ptr + i)); //拆解成 8 bits , 8 bits
    }

    printf("\n");

    printf("int_num = %d (0x%x), address = 0x%p \n", int_num, int_num, &int_num);
    ptr = &int_num;
    printf("ptr 指向 int_num, ptr = %x, ptr + 1 = %x \n", ptr, ptr+1);
    for(i = 0; i < sizeof(int_num); i++){
      printf("address 0x%x = 0x%x \n", ptr+i,*(ptr + i)); //拆解成 8 bits , 8 bits
    }

    printf("\n");

    printf("long_long_num = %lld (0x%llx), address = 0x%p \n", long_long_num, long_long_num, &long_long_num);
    ptr = &long_long_num;
    printf("ptr 指向 long_long_num, ptr = %x, ptr + 1 = %x \n", ptr, ptr+1);
    for(i = 0; i < sizeof(long_long_num); i++){
      printf("address 0x%x = 0x%x \n", ptr+i,*(ptr + i)); //拆解成 8 bits , 8 bits
    }
}


先前我們提到過,一個數值在電腦裡面是以 byte 為單位來進行儲存的,也就是 8 bits 為一個單位。

但在數據型態上我們又常常用到如 char (1byte) 、 short (2bytes) 、 int (4bytes) 、 long long (8bytes) 在電腦中又是如何存放的呢?

首先我們針對 short_num 來看,我們先宣告它的數值為 32767

以十六進制來表示十進制的 32767 為 0x7fff (也就是 short 型別可表示的最大正整數)

而 short_num 在電腦中所存放的記憶體位址為 0x0028FF36

那這個 0x0028FF36 就存放著 32767 (0x7fff) 的數值嗎? 事實上如我們前面所說,一個記憶體位址只能存放 1 byte 的資料,所以他必須用到 "兩個記憶體位址"

也就是他將 0x0028FF36 以及 0x0028FF37 這兩個 2bytes 合併起來,用來表示一個 2 bytes 的數據

第六行我們宣告一個指向 unsigned char* 的指標變數 ptr

我們思考一下,先前我們說過當我們宣告一個 *int door 指標變數時,表示其應該要用來存放一個 int 型態數據的位址(或說 door 將會指向一個 int 型別的變數)。

而在第六行宣告一個指標變數 ptr ,其指向一個 unsigned char 變數又是什麼意思?

事實上這樣宣告後,當我們使用 *(米字號) 做為 "(依位址)取值運算子" 時,它就只會取 1 byte 的資料長度。以此類推,若我們宣告另一個指標變數 ptr1 為 short* 時,使用取址運算子對一個數據進行取值,就會一次取到 2 bytes 的資料(文末補充資料有宣告為 short* 時的結果)。

在第十三行的地方我們將 ptr 直接指向 short_num ,如此一來他就會指到 0x0028FF36 ( short_num 的位址開頭)

我們在 16 ~ 18 行列印出 *ptr 以及 *( ptr + 1 )

得到分割後的數據

記憶體位址 0x0028FF36 = 0xff
記憶體位址 0x0028FF37 = 0x7f


下圖中的上圖(!?) 為一個 16 bits ( 2 bytes ) 的數據,每一格代表一個 bit 。
第 0 格的地方我們稱為最低位元(LSB)
第 15 格的地方我們稱為最高位元(MSB)




下圖中的下圖(!?)

這樣的數據分割方式稱為 little endian ( Intel 系統使用 )

另一種數據分割方式為 big endian ,其被應用的系統更為廣泛的

分割後的排列的順序與上述正好相反




由上圖可以看出

若有一個數據以十六進制表示為 0x7FFFFFFF (總共使用 4 bytes 長度),且從記憶體位址 0x0028ff36 開始擺放這個數據



little endian : 

其數據分割的方式為 "從低位元擺上去" ,也就是從雞蛋中的小邊打破這個雞蛋

big endian :

其數據分割的方式為 "從高位元往下擺",也就是從雞蛋中的大邊打破這個雞蛋


結論 :

顯然我的電腦是使用 little endian (Intel 系統)


關於 big & little endian 有一個有趣的歷史小故事,可參考此連結



補充 : 

#include "stdio.h"

int main(){

    int i; //a counter that avoid exceed the memory
    unsigned short *ptr; // 16 bits
    printf("The address of pointer = %p \n\n", &ptr);

    short short_num = 32767; // 16 bits
    int int_num = 2147483647; // 32 bits
    long long long_long_num = 9223372036854775807; // 64 bits

    printf("short_num = %d (0x%x), address = 0x%p \n", short_num, short_num, &short_num);
    ptr = &short_num; //將 ptr 指向 short_num
    printf("ptr 指向 short_num, ptr = %x, ptr + 1 = %x \n", ptr, ptr+1);

    for(i = 0; i < sizeof(short_num); i++){
        printf("address 0x%x = 0x%x \n", ptr+i,*(ptr + i)); //拆解成 16 bits , 16 bits
    }

    printf("\n");

    printf("int_num = %d (0x%x), address = 0x%p \n", int_num, int_num, &int_num);
    ptr = &int_num;
    printf("ptr 指向 int_num, ptr = %x, ptr + 1 = %x \n", ptr, ptr+1);
    for(i = 0; i < sizeof(int_num); i++){
      printf("address 0x%x = 0x%x \n", ptr+i,*(ptr + i)); //拆解成 16 bits , 16 bits
    }

    printf("\n");

    printf("long_long_num = %lld (0x%llx), address = 0x%p \n", long_long_num, long_long_num, &long_long_num);
    ptr = &long_long_num;
    printf("ptr 指向 long_long_num, ptr = %x, ptr + 1 = %x \n", ptr, ptr+1);
    for(i = 0; i < sizeof(long_long_num); i++){
      printf("address 0x%x = 0x%x \n", ptr+i,*(ptr + i)); //拆解成 16 bits , 16 bits
    }
}


#include "stdio.h"

int main(){

    int i; //a counter that avoid exceed the memory
    unsigned int *ptr; // 32 bits
    printf("The address of pointer = %p \n\n", &ptr);

    short short_num = 32767; // 16 bits
    int int_num = 2147483647; // 32 bits
    long long long_long_num = 9223372036854775807; // 64 bits

    printf("short_num = %d (0x%x), address = 0x%p \n", short_num, short_num, &short_num);
    ptr = &short_num; //將 ptr 指向 short_num
    printf("ptr 指向 short_num, ptr = %x, ptr + 1 = %x \n", ptr, ptr+1);

    for(i = 0; i < sizeof(short_num); i++){
        printf("address 0x%x = 0x%x \n", ptr+i,*(ptr + i)); //拆解成 32 bits , 32 bits
    }

    printf("\n");

    printf("int_num = %d (0x%x), address = 0x%p \n", int_num, int_num, &int_num);
    ptr = &int_num;
    printf("ptr 指向 int_num, ptr = %x, ptr + 1 = %x \n", ptr, ptr+1);
    for(i = 0; i < sizeof(int_num); i++){
      printf("address 0x%x = 0x%x \n", ptr+i,*(ptr + i)); //拆解成 32 bits , 32 bits
    }

    printf("\n");

    printf("long_long_num = %lld (0x%llx), address = 0x%p \n", long_long_num, long_long_num, &long_long_num);
    ptr = &long_long_num;
    printf("ptr 指向 long_long_num, ptr = %x, ptr + 1 = %x \n", ptr, ptr+1);
    for(i = 0; i < sizeof(long_long_num); i++){
      printf("address 0x%x = 0x%x \n", ptr+i,*(ptr + i)); //拆解成 32 bits , 32 bits
    }
}


↓↓↓ 連結到部落格方針與索引 ↓↓↓

Blog 使用方針與索引