1. 程式人生 > >與struct相關的巨集定義(成員變數偏移和獲取struct起始地址)

與struct相關的巨集定義(成員變數偏移和獲取struct起始地址)

學過C語言的人都知道,我們可以通過struct定義自己的資料集合,從而實現對資料的封裝功能。

在實際的專案實踐中,struct定義的結構隨處可見。使用struct並不難,網上也有很多介紹struct的文章。

從如構定義到如何使用,講得很詳細。在此我就不多講。

這裡主要介紹在struct中如何獲取成員變數的偏移以及通過成員變數的地址獲取struct的起始地址。

第二個問題是緊密依賴第一個問題的。

1. 獲取成員變數的偏移。

直接上程式碼吧:

#define offset_of(type, field) ( (unsigned int)&(((type *)(0))->field) )

這個巨集對於剛開始學習C語言的人來說是比較困難的。

要理解這個巨集,首先要理解巨集定義到底是怎麼一回事,還有struct是怎麼一回事,還有指標的用法。

簡單解釋一下這個巨集:

如果我們定義一個struct:

typedef struct{

int a;

int b;

} A;

那麼offset_of(A, b)就會輸出b相對於起始地址的偏移。

& -- 這是取地址符號,表示獲取當前變數的地址。

0 -- 這是這個巨集中最核心的地方。我們知道,對於一個struct, 比如說上面的A,  &A->b代表了b的地址, 如果我們想得到b相對於A的偏移,那麼&A->b - &A就可以得到。

       但如是&A就是0呢。那麼&A->b不就是偏移了嗎?

好了,當我們有了獲取成員變數相對偏移的巨集。如果已知成員變數的地址,那我們也可以輕鬆得獲取該成員所在結構體的首地址。

程式碼如下:

#define container_of(ptr, type, field) (type *)((char *)ptr - offset_of(type, field))
我們只需要把該成員變數的地址減去你相對於首地址的偏移,不就是該結構體的首地址了麼?

好了,不多說了,結出一個完整的例項:

// testConsole.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <time.h>
#include <stdio.h>
#include <stdlib.h>

#define offset_of(type, field) ( (unsigned int)&(((type *)(0))->field) )
#define container_of(ptr, type, field) (type *)((char *)ptr - offset_of(type, field))

typedef struct{
    int     a;
    int     b;
    char    c;
    int     d;
}package_t;

int main()
{
    package_t pkg;
    int *p;

    p = (int *)&pkg.d;
    printf("offset of 'd' is %d.\n", offset_of(package_t, d));
    printf("The addr of pkg = 0x%x.\n", &pkg);
    printf("The addr of d's container = 0x%x.\n", container_of(p, package_t, d));
    return 0;
}




相關推薦

struct相關巨集定義成員變數偏移獲取struct起始地址

學過C語言的人都知道,我們可以通過struct定義自己的資料集合,從而實現對資料的封裝功能。 在實際的專案實踐中,struct定義的結構隨處可見。使用struct並不難,網上也有很多介紹struct的文章。 從如構定義到如何使用,講得很詳細。在此我就不多講。 這裡主要介紹在

C語言裡面的行內函數inline巨集定義#define探討

在 C 中,你可以通過在結構中設定一個 void* 來得到“封裝的結構”,在這種情況下,指向實際資料的 void* 指標對於結構的使用者來說是未知的。因此結構的使用者不知道如何解釋void*指標所指內容,但是存取函式可以將 void* 轉換成適當的隱含型別。這樣給出了封裝的一種形式。

C語言常規優化策略——引數傳遞、巨集定義、全域性變數彙編

C語言常規優化策略 4 引數傳遞、巨集定義、全域性變數與彙編 按照結構化程式設計的原則,一種語言,如果具有賦值、選擇與迴圈三種結構,並嚴格按照這三種結構 來組織程式,避免使用象goto語句這類使程式控制發生跳轉的語言成分,在每一個程式塊(如選擇塊、循 環塊)中保持單向的輸入流

簡單的C語言巨集定義結合全域性變數的方法實現微控制器串列埠實現透傳模式

何謂透傳? 根據百度百科給出的定義如下:        透傳,即透明傳輸(pass-through),指的是在通訊中不管傳輸的業務內容如何,只負責將傳輸的內容由源地址傳輸到目的地址,而不對業務資料內容做任何改變。        在現實微控制器產品開發過程中,如果存在多個

C++ 類靜態成員變數宣告定義

C++悠悠然 君不見黃河之水天上來,東流到海不復回 //1)在靜態記憶體區中 //2)所有類物件共用這一個變數,只有唯一一個 //3)必須在類外面顯示定義,顯示定義的時候不加static //4)可以通過類物件訪問,也可以通過類名加作用域訪問 #includ

C語言基礎之巨集定義附程式碼

C語言基礎之巨集定義 巨集定義:是C語言提供的三種預處理功能的其中一種,這三種預處理包括:巨集定義、檔案包含、條件編譯。巨集定義和操作符的區別是:巨集定義是替換,不做計算,也不做表示式求解。#define預處理指令可以用來定義巨集。 巨集定義的形式,分為帶引

qt *.pro 原始碼通用巨集定義如條件編譯

*.pro中: DEFINES += _qt_msvc_2015 contains(DEFINES,_qt_msvc_2015){ QT += webengine QT

Java中接口裡定義成員變數

在interface裡面的變數都是public static final 的。所以你可以這樣寫:public static final int i=10;或則int i=10;(可以省略掉一部分) 注意在宣告的時候要給變數賦予初值 解釋: 首先你要弄清介面的含義.介面就是提供

接口裡定義成員變數必須是public static final型別

在interface裡面的變數都是public static final 的。所以你可以這樣寫:   public static final int i=10;   或則   int i=10;(可以省略掉一部分)   注意在宣告的時候要給變數賦予初值   解釋:   首先

介面當中定義"成員變數"

package Extends.InterfaceFinalConst; public interface InterfaceFinalConst { /* 介面當中也可以定義"成員變數"但是必須使用public static final三個關鍵字進行修飾 從效果上看,這其實是介面的常量

行內函數帶引數巨集區別筆記

1.行內函數呼叫時,會進行型別檢查,要求實參和形參的型別一致,另外行內函數會先對實參表示式進行求值,然後傳遞給形參;而巨集呼叫時只用實參簡單地替換形參。 2.行內函數實在編譯的時候,在呼叫的地方將程式碼展開,而巨集則是在預處理時進行替換的。 3.C++中建議採用inli

java中的變數成員變數、本地變數、類變數

Java中的變數: 1、成員變數(例項變數,屬性) 2、本地變數(區域性變數) 3、類變數(靜態屬性) 一、成員變數(例項變數,屬性) 1.1-成員變數:(在類中定義,    訪問修飾

資料結構演算法之線性表簡單定義順序結構儲存查,增,刪

線性表(List) 由零個或多個數據元素組成的有限序列,它是一個序列,也就是說元素之間是有個先來後到的,若元素存在多個,則第一個元素無前驅,最後一個元素無後繼,其他元素有且只有一個前驅和後繼,另外,線性表強調是有限的,事實上無論計算機發展多強大,它處理的元素都是有限的。

適配IPhone X相關巨集定義

// MARK: 適配IPhone X #define AppStatusBarHeight [[UIApplication sharedApplication] statusBarFrame

你真的瞭解巨集嗎:淺談巨集定義#define語句

寫在前面: 本文所有程式碼均在Linux環境下執行 Linux版本為CentOS 7.4 巨集定義 語法 #define name Stuff #define PI 3.14 //定義一個M,值為3.14 #def

Linux 核心中 likely unlikely 的巨集定義解析

在 2.6 核心中,隨處可以見到 likely() 和 unlikely() 的身影,那麼為什麼要用它們?它們之間有什麼區別? 首先要明確: if(likely(value)) 等價於 if(value) if(unlikely(

Python中定義“私有”成員變數成員函式

在學習Python的過程中發下,它把類(class)中所有的成員函式和成員變數都看做是"Public"的,作為C++出身的程式設計師們可能就不習慣了。 Python的官方教程中如是說:““Private” instance variables that cannot b

詳解巨集定義#define

C語言中用到巨集定義的地方很多,如在標頭檔案中為了防止標頭檔案被重複包含,則用到: #ifndef cTest_Header_h #define cTest_Header_h //標頭檔案內容 #endif 在我們常用的 stdio.h 標頭檔案中也可以

巨集定義#ifndef+#define+#endif的作用

本文介紹#ifndef、#define、#endif三者一起使用的巨集定義的作用。 在標頭檔案中,我們經常會在標頭檔案中見到#i

按鈕相關屬性設置按鈕文字位置 圖片位置設置

idt target cal sta brush 位置 icontrol tle 屬性 - (UIButton *)navSearBtn { if (!_navSearBtn) { _navSearBtn = [[UIButton alloc]in