2008-12-30 1300 views

回答

8

這是由GCC編譯器提供一個內置實現offsetof宏由該C和C++標準規定:

GCC - offsetof

它返回以字節偏移一個POD結構的成員/工會在。

樣品:

struct abc1 { int a, b, c; }; 
union abc2 { int a, b, c; }; 
struct abc3 { abc3() { } int a, b, c; }; // non-POD 
union abc4 { abc4() { } int a, b, c; }; // non-POD 

assert(offsetof(abc1, a) == 0); // always, because there's no padding before a. 
assert(offsetof(abc1, b) == 4); // here, on my system 
assert(offsetof(abc2, a) == offsetof(abc2, b)); // (members overlap) 
assert(offsetof(abc3, c) == 8); // undefined behavior. GCC outputs warnings 
assert(offsetof(abc4, a) == 0); // undefined behavior. GCC outputs warnings 

@Jonathan提供的,你可以用它一個很好的例子。我記得曾經看到它用來實現入侵列表(列表的數據項包括next和prev指針本身),但我不記得在實現它的過程中有什麼幫助,可悲的是。

+0

我猜在哪裏有用的是,入侵的節點包含指向「下一個」對象中的節點的指針。在使用列表時,需要從節點到對象的基部,因此可以從指針值和reinterpret_cast中減去offsetof(某些)字節。 – 2008-12-31 01:07:56

+0

當然,所有在C++中都不可移植,但在C中做這項工作。 – 2008-12-31 01:09:12

2

As @litb,說:結構/類成員的字節偏移量。在C++中,有些情況下它是未定義的,以防編譯器投訴。 IIRC,一個方法來實現它(在C,至少)是做

#define offsetof(type, member) (int)(&((type *)0)->member) 

但我敢肯定有問題,但我會留給有興趣的讀者指出...

+0

未定義的行爲,即使在C中也是如此。多重原因,甚至:重新定義std宏和deref NULL。雖然在stdlib中很常見,但由於它受到不同規則的約束。 – MSalters 2009-01-02 14:29:58

12

正如@litb指出的和@JesperE所示,offsetof()提供了一個以字節爲單位的整數偏移量(作爲size_t的值)。

什麼時候可以使用它?

可能相關的一種情況是表格驅動的操作,用於從文件中讀取大量不同的配置參數並將這些值填充到同樣巨大的數據結構中。減少巨大的瑣碎(並忽略了各種必要的現實世界的做法,例如在標題中定義結構類型),我的意思是一些參數可能是整數和其他字符串,代碼可能看起來很像:

#include <stddef.h> 

typedef stuct config_info config_info; 
struct config_info 
{ 
    int parameter1; 
    int parameter2; 
    int parameter3; 
    char *string1; 
    char *string2; 
    char *string3; 
    int parameter4; 
} main_configuration; 

typedef struct config_desc config_desc; 
static const struct config_desc 
{ 
    char *name; 
    enum paramtype { PT_INT, PT_STR } type; 
    size_t offset; 
    int min_val; 
    int max_val; 
    int max_len; 
} desc_configuration[] = 
{ 
    { "GIZMOTRON_RATING", PT_INT, offsetof(config_info, parameter1), 0, 100, 0 }, 
    { "NECROSIS_FACTOR", PT_INT, offsetof(config_info, parameter2), -20, +20, 0 }, 
    { "GILLYWEED_LEAVES", PT_INT, offsetof(config_info, parameter3), 1, 3, 0 }, 
    { "INFLATION_FACTOR", PT_INT, offsetof(config_info, parameter4), 1000, 10000, 0 }, 
    { "EXTRA_CONFIG",  PT_STR, offsetof(config_info, string1), 0, 0, 64 }, 
    { "USER_NAME",  PT_STR, offsetof(config_info, string2), 0, 0, 16 }, 
    { "GIZMOTRON_LABEL", PT_STR, offsetof(config_info, string3), 0, 0, 32 }, 
}; 

現在,您可以編寫一個通用函數來讀取配置文件中的行,放棄註釋和空白行。然後隔離參數名稱,並在desc_configuration表中查找(您可以對其進行排序,以便您可以執行二分搜索 - 多個SO問題解決該問題)。當它找到正確的config_desc記錄時,它可以將找到的值和config_desc條目傳遞給兩個例程之一 - 一個用於處理字符串,另一個用於處理整數。

的那些功能的關鍵部分是:

static int validate_set_int_config(const config_desc *desc, char *value) 
{ 
    int *data = (int *)((char *)&main_configuration + desc->offset); 
    ... 
    *data = atoi(value); 
    ... 
} 

static int validate_set_str_config(const config_desc *desc, char *value) 
{ 
    char **data = (char **)((char *)&main_configuration + desc->offset); 
    ... 
    *data = strdup(value); 
    ... 
} 

這避免了必須寫一個單獨的函數對於該結構的每個單獨部件。

7

內置__offsetof運算符的用途是編譯器供應商可以繼續#define offsetof()宏,但它可以與定義一元運算符&的類一起使用。當((&左值))返回該右值的地址時,offsetof()的典型C宏定義才起作用。即

#define offsetof(type, member) (int)(&((type *)0)->member) // C definition, not C++ 
struct CFoo { 
    struct Evil { 
     int operator&() { return 42; } 
    }; 
    Evil foo; 
}; 
ptrdiff_t t = offsetof(CFoo, foo); // Would call Evil::operator& and return 42