亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解C語言的結(jié)構(gòu)體中成員變量偏移問題

 更新時間:2016年04月20日 11:42:47   投稿:goldensun  
這篇文章主要介紹了C語言的結(jié)構(gòu)體中成員變量偏移問題,以講解如何編寫宏來對成員變量進行修改為主,需要的朋友可以參考下

c語言中關(guān)于結(jié)構(gòu)體的位置偏移原則簡單,但經(jīng)常忘記,做點筆記以是個記憶的好辦法

原則有三個:

a.結(jié)構(gòu)體中的所有成員其首地址偏移量必須為器數(shù)據(jù)類型長度的整數(shù)被,其中第一個成員的首地址偏移量為0,

例如,若第二個成員類型為int,則其首地址偏移量必須為4的倍數(shù),否則就要“首部填充”;以此類推

b.結(jié)構(gòu)體所占的總字節(jié)數(shù)即sizeof()函數(shù)返回的值必須是最大成員的長度的整數(shù)倍,否則要進行“末尾填充”;

c.若結(jié)構(gòu)體A將結(jié)構(gòu)體B作為其成員,則結(jié)構(gòu)體B存儲的首地址的偏移量必須為B中所含成員數(shù)據(jù)長度最大值的整數(shù)倍,

如若B中成員為int,double,char,則B的偏移量要為8的整數(shù)倍;否則進行“中間填充”。

相信大家在c語言程序開發(fā)的過程一定都使用過結(jié)構(gòu)體,那么不知你對結(jié)構(gòu)體中成員變量偏移這塊是如何理解的?本文將和大家一起分享下,本人最近關(guān)于c語言中結(jié)構(gòu)體偏移的一些思考和總結(jié)。

示例1

我們先來定義一下需求:

已知結(jié)構(gòu)體類型定義如下:

struct node_t{
 char a;
 int b;
 int c;
};

且結(jié)構(gòu)體1Byte對齊

#pragma pack(1)

求:

結(jié)構(gòu)體struct node_t中成員變量c的偏移。

注:這里的偏移量指的是相對于結(jié)構(gòu)體起始位置的偏移量。

看到這個問題的時候,我相信不同的人腦中浮現(xiàn)的解決方法可能會有所差異,下面我們分析以下幾種可能的解法:

方法1

如果你對c語言的庫函數(shù)比較熟悉的話,那么你第一個想到的肯定是offsetof函數(shù)(其實只是個宏而已,先姑且這樣叫著吧),我們man 3 offsetof查看函數(shù)原型如下:

 #include <stddef.h>

  size_t offsetof(type, member);

有了上述的庫函數(shù),我們用一行代碼就可以搞定:

offsetof(struct node_t, c);

當然這并非本文探討的重點,請繼續(xù)閱讀。


方法2

當我們對c語言的庫函數(shù)不熟悉的時候,此時也不要著急,我們依然可以使用我們自己的方法來解決問題。

最直接的思路是:【結(jié)構(gòu)體成員變量c的地址】 減去 【結(jié)構(gòu)體起始地址】

我們先來定義一個結(jié)構(gòu)體變量node:

struct node_t node;

接著來計算成員變量c的偏移量:

(unsigned long)(&(node.c)) - (unsigned long)(&node)

&(node.c)為結(jié)構(gòu)體成員變量c的地址,并強制轉(zhuǎn)化為unsigned long;

&node為結(jié)構(gòu)體的起始地址,也強制轉(zhuǎn)化為unsigned long;

最后我們將上述兩值相減,得到成員變量c的偏移量;

方法3

按照方法2的思路我們在不借助庫函數(shù)的情況下,依然可以得到成員變量c的偏移量。但作為程序員,我們應該善于思考,是不是可以針對上面的代碼做一些改進,使我們的代碼變得更簡潔一些?在做具體的改進之前,我們應該分析方法2存在哪些方面的問題。

相信不用我多說,細心的你一定已經(jīng)察覺到,方法2中最主要的一個問題是我們自定義了一個結(jié)構(gòu)體變量node,雖然題目中并未限制我們可以自定義變量,但當我們遇到比較嚴且題目中不允許自定義變量的時候,此時我們就要思考新的解決方法。

在探討新的解決方法之前,我們先來探討一個有關(guān)偏移的小問題:

小問題

這是一道簡單的幾何問題,假設在座標軸上由A點移動到B點,如何計算B相對于A的偏移?這個問題對于我們來說是非常的簡單,可能大部分人都會脫口而出并得到答案為B-A。

那么這個答案是否完全準確呢?比較嚴謹?shù)哪阌X得顯然不是,原因在于,當A為坐標原點即A=0的時候,上述答案B-A就直接簡化為B了。

這個小小的簡單的問題,對于我們來說有什么啟示呢?

我們結(jié)合方法2的思路和上述的小問題,是不是很快就得到了下面的關(guān)聯(lián):

(unsigned long)(&(node.c)) - (unsigned long)(&node)

B - A
我們小問題的思路是當A為坐標原點的時候,B-A就簡化為B了,那么對應到我們的方法2,當node的內(nèi)存地址為0即(&node==0)的時候,上面的代碼可簡化為:

(unsigned long)(&(node.c))

由于node內(nèi)存地址==0了,所以

node.c  //結(jié)構(gòu)體node中成員變量c

我們就可以使用另外一種方式來表達了,如下:

((struct node_t *)0)->c

上述代碼應該比較好理解,由于我們知道結(jié)構(gòu)體的內(nèi)存地址編號為0,所以我們就可以直接通過內(nèi)存地址的方式來訪問該結(jié)構(gòu)體的成員變量,相應的代碼的含義就是 獲取內(nèi)存地址編號為0的結(jié)構(gòu)體struct node_t的成員變量c。

注:此處只是利用了編譯器的特性來計算結(jié)構(gòu)體偏移,并未對內(nèi)存地址0有任何操作,有些同學對此可能還有些疑問,詳細的了解該問題可參考關(guān)于c語言結(jié)構(gòu)體成員變量訪問方式的一點思考。

此時,我們的偏移求法就消除了struct node_t node這個自定義變量,直接一行代碼解決,:

(unsigned long)(&(((struct node_t *)0)->c))

上述的代碼相對于方法2是不是更簡潔了一些。

這里我們將上面的代碼功能定義為一個宏,該宏的作用是用來計算某結(jié)構(gòu)體內(nèi)成員變量的偏移(后面的示例會使用該宏):

#define OFFSET_OF(type, member) (unsigned long)(&(((type *)0)->member))

使用上面的宏,就可以直接得到成員變量c在結(jié)構(gòu)體struct node_t中的偏移為:

OFFSET_OF(struct node_t, c)

示例2

和示例1一樣,我們先定義需求如下:

已知結(jié)構(gòu)體類型定義如下:

struct node_t{
 char a;
 int b;
 int c;
};

int *p_c,該指針指向struct node_t x的成員變量c

結(jié)構(gòu)體1Byte對齊

#pragma pack(1)
求:

結(jié)構(gòu)體x的成員變量b的值?

拿到這個問題的時候,我們先做一下簡單的分析,題目的意思是根據(jù)一個指向某結(jié)構(gòu)體成員變量的指針,如何求該結(jié)構(gòu)體的另外一個成員變量的值。

那么可能的幾種解法有:

方法1

由于我們知道結(jié)構(gòu)體是1Byte對齊的,所以這道題最簡單的解法是:

*(int *)((unsigned long)p_c - sizeof(int))
上述代碼很簡單,成員變量c的地址減去sizeof(int)從而得到成員變量b的地址,然后再強制轉(zhuǎn)換為int *,最后再取值最終得到成員變量b的值;

方法2

方法1的代碼雖然簡單,但擴展性不夠好。我們希望通過p_c直接得到指向該結(jié)構(gòu)體的指針p_node,然后通過p_node訪問該結(jié)構(gòu)體的任意成員變量了。

由此我們得到計算結(jié)構(gòu)體起始地址p_node的思路為:

【成員變量c的地址p_c】減去【c在結(jié)構(gòu)體中的偏移】

由示例1,我們得到結(jié)構(gòu)體struct node_t中成員變量c的偏移為:

(unsigned long)&(((struct node_t *)0)->c)

所以我們得到結(jié)構(gòu)體的起始地址指針p_node為:

(struct node_t *)((unsigned long)p_c - (unsigned long)(&((struct node_t *)0)->c))

我們也可以直接使用示例1中定義的OFFSET_OF宏,則上面的代碼變?yōu)椋?/p>

(struct node_t *)((unsigned long)p_c - OFFSET_OF(struct node_t, c))

最后我們就可以使用下面的代碼來獲取成員變量a,b的值:

p_node->a

p_node->b

我們同樣將上述代碼的功能定義為如下宏:

#define STRUCT_ENTRY(ptr, type, member) (type *)((unsigned long)(ptr)-OFFSET_OF(type, member))
該宏的功能是通過結(jié)構(gòu)體任意成員變量的指針來獲得指向該結(jié)構(gòu)體的指針。

我們使用上面的宏來修改之前的代碼如下:

STRUCT_ENTRY(p_c, struct node_t, c)

p_c為指向結(jié)構(gòu)體struct node_t成員變量c的指針;

struct node_t結(jié)構(gòu)體類型;

c為p_c指向的成員變量;

注:

上述示例中關(guān)于地址運算的一些說明:

int a = 10;
int * p_a = &a;

p_a == 0x95734104;

以下為編譯器計算的相關(guān)結(jié)果:

p_a + 10 == p_a + sizeof(int)*10 =0x95734104 + 4*10 = 0x95734144

(unsigned long)p_a + 10 == 0x95734104+10 = 0x95734114

(char *)p_a + 10 == 0x95734104 + sizeof(char)*10 = 0x95734114

從上述三種情況,相信你應該能體會到我所要表達的意思了。(注:后續(xù)某博文將從編譯器的角度對該問題進行詳細的闡述)

結(jié)論

本文通過幾個示例描述了c語言結(jié)構(gòu)體有關(guān)偏移的一些有意思的事情,希望能夠?qū)δ阌兴鶐椭?。為什么會有上述思考,相信有些同學已經(jīng)看出一些端倪,這也正是后續(xù)博文將要描述的主題。

如文中有錯誤之處,歡迎指出。

相關(guān)文章

  • C++?opencv利用grabCut算法實現(xiàn)摳圖示例

    C++?opencv利用grabCut算法實現(xiàn)摳圖示例

    這篇文章主要為大家介紹了C++?opencv利用grabCut算法實現(xiàn)摳圖的代碼示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-05-05
  • C++實現(xiàn)的求解多元一次方程示例

    C++實現(xiàn)的求解多元一次方程示例

    這篇文章主要介紹了C++實現(xiàn)的求解多元一次方程,涉及C++矩陣運算相關(guān)操作技巧,需要的朋友可以參考下
    2018-01-01
  • C++動態(tài)內(nèi)存管理詳解

    C++動態(tài)內(nèi)存管理詳解

    今天小編就為大家分享一篇關(guān)于關(guān)于C++動態(tài)分配內(nèi)存的介紹,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2021-08-08
  • 一起來學習C語言的字符串轉(zhuǎn)換函數(shù)

    一起來學習C語言的字符串轉(zhuǎn)換函數(shù)

    這篇文章主要為大家詳細介紹了C語言的字符串轉(zhuǎn)換函數(shù),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • 用C實現(xiàn)PHP擴展 Fetch_Url 類數(shù)據(jù)抓取的方法

    用C實現(xiàn)PHP擴展 Fetch_Url 類數(shù)據(jù)抓取的方法

    該擴展是基于libcurl基礎實現(xiàn)的網(wǎng)頁數(shù)據(jù)抓取
    2013-04-04
  • CMake語法及CMakeList.txt簡單使用小結(jié)

    CMake語法及CMakeList.txt簡單使用小結(jié)

    Cmake主要用于開發(fā)跨平臺的C++項目,本文主要介紹了CMake語法及CMakeList.txt簡單使用小結(jié),具有一定的參考價值,感興趣的可以了解一下
    2022-05-05
  • c++ const引用與非const引用介紹

    c++ const引用與非const引用介紹

    const引用是指向const對象的引用,可以讀取ref,但不能修改所以也就有將const變量賦值給非const引用是非法的,感興趣的朋友可以了解下,或許本文對你有所幫助
    2013-01-01
  • C++獲取指定目錄下的所有文件

    C++獲取指定目錄下的所有文件

    這篇文章主要為大家詳細介紹了C++獲取指定目錄下所有文件的相關(guān)代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • N叉樹的三種遍歷(層次遍歷、前序遍歷、后序遍歷)

    N叉樹的三種遍歷(層次遍歷、前序遍歷、后序遍歷)

    本文主要介紹了N叉樹的三種遍歷(層次遍歷、前序遍歷、后序遍歷),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-04-04
  • C++中的vector使用詳解及重要部分底層實現(xiàn)

    C++中的vector使用詳解及重要部分底層實現(xiàn)

    本篇文章會對vector的語法使用進行詳解,同時,還會對重要難點部分的底層實現(xiàn)進行講解,其中有vector的迭代器失效和深拷貝問題,希望本篇文章的內(nèi)容會對你有所幫助
    2023-07-07

最新評論