1. 程式人生 > >c中結構體指標的強制型別轉換

c中結構體指標的強制型別轉換

1、結構體到結構體指標之間的轉換

a、結構體指標的強制型別轉換即結構體的資料成員在記憶體段當中的重新分配,結構體的儲存“記憶體對齊”請參看上篇部落格,該論述例項如下:

#include <iostream>
#include <stdlib.h>
struct A{
    int num;
 };
struct B{
    int num;
    char type;
    int age;
};
int main()
{
    struct A a;
    a.num = 5;
    char* tmp1 = (char *)(&(a.num));
    tmp1 = tmp1 + 4;
    *tmp1 = 'a';

    int *tmp2 = (int *)(&(a.num));
    tmp2 = tmp2 + 2;
    *tmp2 = 32;
    struct B *b = (struct B *)(&a);
    std::cout << b->num << " " << b->age << " " << b->type << std::endl;

    std::cout << sizeof(*b) << std::endl;
    system("pause");
    return 0;
}

輸出結果:   5 32 a       12   

結果分析:按照結構體B在記憶體當中的儲存方式應該如下:1111   1xxx   1111,也就是sizeof(*b)的值為12(詳細計算看上篇)。

1、結構體定義了一個結構體A a,它的儲存方式是1111,共佔用4個位元組,儲存的值為5;

2、tmp1指標所指向的位置為第8個位元組的位置,儲存的值為a;tmp2指向的位置為第6個位元組位置;

3、將A a強制轉換成B b,即需要按照結構體B對該a所對應的塊記憶體進行重新取值並賦值給B所對應的欄位,得到的會是如下結果:1~4位元組儲存的是5,5~8位元組儲存的是10,9~12位元組儲存的是a

理解如下圖:


b、型別轉換也可以根據如下理解:

例如:b->num : 將 struct A a 所指向的記憶體單元以 struct B b的分佈方式(利用 b)取得偏移為num 的值。
結構體宣告如何記憶體的分佈,
結構體指標宣告結構體的首地址,結構體成員宣告該成員在結構體中的偏移地址。

解釋如下:

    1、首先要明確struct B b本身有一個值(在32位機上一般是32位),這個值代表著某個記憶體地址,比如這個首地址值是0x30000000,由於b被定義Struct B型別,那麼編譯器在編譯原始檔時就會認為b指向的是一個struct B的結構,這個結構的內容由一段連續的記憶體儲存,(b->num)(b->type)(b->age)在struct B的定義中肯定是存在num、type、age三個的,這幾個變數對一個struct B結構的地址偏移量是固定的,也就是說只要知道一個struct B結構的起始地址,就能計算出這個結構所包含的num、type、age的地址,由於B b的地址已知(由A a強制轉換而來,所以編譯器知道這個地址即a的首地址),b所含的num離b的偏移量已知,那麼就能通過b實現對num的取值,在編譯器編譯C檔案的時候並不管b中的nun是否定義

    2、b被定義為struct B,而B中又包含b,那麼編譯器就認為b所指向的結構中存在num
    3、b->num如果不發生保護錯誤,總是能取到一個值(這個值就是對b偏移一定距離的記憶體單元中的值),只是這個值不能確定是否是你期望的值
    4、再次提醒,變數的值是儲存在記憶體中的,每個記憶體位元組對應一個記憶體地址,而記憶體儲存的值本身是沒有整型,指標,字元等的區別的,區別的存在是因為我們對它們有不同的解讀,b的值就是一個32位值,並且儲存在某個記憶體單元中,通過這個32位值就能找到b所指向的結構的起始地址,通過這個起始地址和各個結構所包含變數離起始地址的偏移對這些變數進行引用,b->num只是這種引用更易讀的寫法,只要b是指向num 的指標,那麼b的值就肯定存在,b存在,偏移量已知,那麼b->num就肯定存在,只是要記住,b->num只是代表了對num一定偏移地址的值

     5、假如b->num所對應的這段記憶體從未有進行過操作,那麼其值就是系統上電啟動之後的記憶體的預設值,如果有其他程式使用過,那麼其值就是其他程式執行後在這段記憶體中留下的值,總之,每個正常工作的記憶體單元中都是有值的,無論這些值是否是我們所需要的,它們始終是存在的。你可以把記憶體想象成一個大陣列,當你在程式中定義了一個數組,但是並沒有初始化,但是你也可以引用陣列元素,只是引用的值是未知的,道理是類似的    

2、將int強制型別轉換為結構體型別

typedef struct eg
{
     int x;
     int y;
}eg;
int point = 0x30000000;
eg *peg = (eg*)point;
可以看到point本身只是個整型變數,但是這樣的賦值是合法的,peg->x的值是0x30000000開始的四位元組,peg->y是0x30000004開始的四位元組。

解釋如下:

     不是說某個地址有那個結構體你才能引用,即使沒有,你也能引用,因為你已經告訴了編譯器param變數就是指向一個PAINT_PARAM結構體的變數並且指明瞭param的值,機器碼的眼中是沒有資料結構一說的,它只是機械的按照指令的要求從記憶體地址取值,那剛才的例子來說,peg->x,peg->y的引用無論0x30000000是否存在一個eg結構體都是合法的,如果0x30000000開始的8個位元組存在eg結構體,那麼引用的就是這個結構體的值,如果這個位置是未定義的值,那麼引用的結果就是這8個位元組中的未定義值,記憶體位置總是存在的,而對記憶體中值的引用就是從這些記憶體位置對應的記憶體單元取值。