Jump to content

смещение

С offsetof() Макрос — это функция библиотеки ANSI C, найденная в stddef.h . Он оценивает смещение (в байтах) данного члена внутри структуры или типа объединения , выражение типа размер_т . offsetof() Макрос принимает два параметра : первый — это имя структуры или объединения, а второй — имя подобъекта структуры/объединения, который не является битовым полем . Его нельзя назвать прототипом C. [ 1 ]

Выполнение

[ редактировать ]

«Традиционная» реализация макроса полагалась на то, что компилятор получал смещение члена путем указания гипотетической структуры, которая начинается с нулевого адреса:

#define offsetof(st, m) \
    ((size_t)&(((st *)0)->m))

Это можно понимать как получение нулевого указателя структуры типа. st , а затем получение адреса участника м внутри указанной структуры. Хотя эта реализация работает правильно во многих компиляторах, она вызвала некоторые споры относительно того, является ли это поведение неопределенным в соответствии со стандартом C. [ 2 ] поскольку это, по-видимому, предполагает разыменование нулевого указателя (хотя, согласно стандарту, раздел 6.6 Константные выражения, параграф 9, операция не обеспечивает доступ к значению объекта). Это также имеет тенденцию выдавать запутанную диагностику компилятора, если один из аргументов написан с ошибкой. [ нужна ссылка ]

Альтернатива:

#define offsetof(st, m) \
    ((size_t)((char *)&((st *)0)->m - (char *)0))

Это можно указать таким образом, поскольку стандарт не определяет, что внутреннее представление нулевого указателя находится по нулевому адресу. Поэтому необходимо определить разницу между адресом участника и базовым адресом.

Некоторые современные компиляторы (например, GCC ) вместо этого определяют макрос, используя специальную форму (как расширение языка), например [ 3 ]

#define offsetof(st, m) \
    __builtin_offsetof(st, m)

Эта встроенная функция особенно полезна для классов C++, которые объявляют собственный унарный тип. оператор & . [ 4 ]

Использование

[ редактировать ]

Это полезно при реализации общих структур данных на языке C. Например, ядро ​​Linux использует offsetof() для реализации Container_of() , который позволяет чему-то вроде типа миксина найти структуру, которая его содержит: [ 5 ]

#define container_of(ptr, type, member) ({ \
                const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                (type *)( (char *)__mptr - offsetof(type,member) );})

Этот макрос используется для извлечения охватывающей структуры из указателя на вложенный элемент, например, в этой итерации связанного списка. my_struct объекты :

struct my_struct {
    const char *name;
    struct list_node list;
};

extern struct list_node * list_next(struct list_node *);

struct list_node *current = /* ... */
while (current != NULL) {
    struct my_struct *element = container_of(current, struct my_struct, list);
    printf("%s\n", element->name);
    current = list_next(&element->list);
}

РеализацияContainer_of в ядре Linux использует расширение GNU C, называемое выражениями операторов . [ 6 ] Возможно, выражение оператора использовалось для обеспечения безопасности типов и, следовательно, устранения потенциальных случайных ошибок. Однако существует способ реализовать то же поведение без использования выражений операторов, сохраняя при этом безопасность типов:

#define container_of(ptr, type, member) ((type *)((char *)(1 ? (ptr) : &((type *)0)->member) - offsetof(type, member)))

На первый взгляд эта реализация может показаться более сложной, чем необходимо, а необычное использование условного оператора может показаться неуместным. Возможна более простая реализация:

#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)))

Эта реализация также будет служить той же цели, однако в исходной реализации ядра Linux есть фундаментальное упущение. Тип ptr никогда не сверяется с типом члена, это то, что может уловить реализация ядра Linux.

В вышеупомянутой реализации с проверкой типов проверка выполняется необычным использованием условного оператора. Ограничения условного оператора указывают, что если оба операнда условного оператора являются указателями на тип, они оба должны быть указателями на совместимые типы. В этом случае, несмотря на то, что значение третьего операнда условного выражения никогда не будет использовано, компилятор должен выполнить проверку, чтобы убедиться, что (ptr) и &((type *)0)->member оба являются совместимыми типами указателей.

Ограничения

[ редактировать ]

Использование offsetof ограничено типами POD в C++98 , классами стандартной компоновки в C++11 , [ 7 ] и больше случаев условно поддерживаются в C++17 , [ 8 ] в противном случае он имеет неопределенное поведение. Хотя большинство компиляторов выдают правильный результат даже в случаях, которые не соответствуют стандарту, существуют крайние случаи, когда offsetof либо выдаст неверное значение, либо выдаст предупреждение или ошибку во время компиляции, либо приведет к полному сбою программы. Особенно это касается виртуального наследования. [ 9 ] Следующая программа выдаст несколько предупреждений и выведет явно подозрительные результаты при компиляции с gcc 4.7.3 на архитектуре amd64:

#include <stddef.h>
#include <stdio.h>

struct A
{
    int  a;
    virtual void dummy() {}
};

struct B: public virtual A
{
    int  b;
};

int main()
{
    printf("offsetof(A, a) : %zu\n", offsetof(A, a));
    printf("offsetof(B, b) : %zu\n", offsetof(B, b));
    return 0;
}

Выход:

offsetof(A, a) : 8
offsetof(B, b) : 8
  1. ^ «смещение ссылки» . MSDN . Проверено 19 сентября 2010 г.
  2. ^ «Вызывает ли &((имя структуры *)NULL -> b) неопределенное поведение в C11?» . Проверено 7 февраля 2015 г.
  3. ^ «Смещение ссылки GCC» . Фонд свободного программного обеспечения . Проверено 19 сентября 2010 г.
  4. ^ «Какова цель и тип возвращаемого значения оператора __builtin_offsetof?» . Проверено 20 октября 2012 г.
  5. ^ Грег Кроа-Хартман (июнь 2003 г.). "container_of()" . Linux-журнал . Проверено 19 сентября 2010 г.
  6. ^ «Утверждения и объявления в выражениях» . Фонд свободного программного обеспечения . Проверено 1 января 2016 г.
  7. ^ «смещение ссылки» . cplusplus.com . Проверено 1 апреля 2016 г.
  8. ^ «смещение ссылки» . cppreference.com . Проверено 20 июля 2020 г.
  9. ^ Стив Джессоп (июль 2009 г.). «Почему вы не можете использовать offsetof для структур, отличных от POD, в C++?» . Переполнение стека . Проверено 1 апреля 2016 г.
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: cb3d1466ae6ffcbde6626f41ba82bf0e__1714112040
URL1:https://arc.ask3.ru/arc/aa/cb/0e/cb3d1466ae6ffcbde6626f41ba82bf0e.html
Заголовок, (Title) документа по адресу, URL1:
offsetof - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)