какова цель и тип возвращаемого значения оператора __builtin_offsetof?
Вопрос
Какова цель оператора __builtin_offsetof (или оператора _FOFF в Symbian) в C++?
Кроме того, что он возвращает?Указатель?Количество байт?
Решение
Это встроенная функция, предоставляемая компилятором GCC для реализации offsetof
макрос, определенный стандартом C и C++:
Он возвращает смещение в байтах, в котором находится член структуры/объединения 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), но, к сожалению, я не могу вспомнить, где это было полезно при его реализации.
Другие советы
Как указывает @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);
...
}
Это позволяет избежать написания отдельной функции для каждого отдельного члена структуры.
Цель встроенного оператора __offsetof состоит в том, чтобы поставщик компилятора мог продолжать #определять макрос offsetof(), но при этом использовать его для работы с классами, определяющими унарный оператор&.Типичное определение offsetof() в макросе C работало только тогда, когда (&lvalue) возвращало адрес этого rvalue.Т.е.
#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
Как сказал @litb:смещение в байтах члена структуры/класса.В C++ бывают случаи, когда он не определен, на случай, если компилятор будет жаловаться.IIRC, один из способов реализовать это (по крайней мере, на C) - это сделать
#define offsetof(type, member) (int)(&((type *)0)->member)
Но я уверен, что здесь есть проблемы, но я оставлю это на усмотрение заинтересованного читателя...