Legit Использование смещения макроса в C / C ++ - PullRequest
8 голосов
/ 21 сентября 2009

В C / C ++ есть этот макрос offsetof , который позволяет получить смещение адреса члена в структуре POD. Для примера из C FAQ :

struct foo {
int a;
int b;
};

struct foo;

/* Set the b member of foo indirectly */
*(int *)((char *)foo + offsetof(b)) = 0xDEADBEEF;

Теперь мне это кажется злым, и я не вижу много законных применений этого макроса.

Один законный пример, который я видел, это использование в макросе container_of в ядре Linux для получения адреса родительского объекта встроенных структур:

/* get the address of the cmos device struct in which the cdev
   structure which inode points to is embedded */
struct cmos_dev *cmos_devp = 
     container_of(inode->i_cdev, struct cmos_dev, cdev);

Какие еще допустимые варианты использования этого макроса существуют? Когда не следует использовать этот макрос?

РЕДАКТИРОВАТЬ Пока этот ответ на другой вопрос SO - лучший, который я когда-либо видел.

Ответы [ 5 ]

6 голосов
/ 21 сентября 2009

Одно допустимое использование offsetof() - определение выравнивания типа:

#define ALIGNMENT_OF( t ) offsetof( struct { char x; t test; }, test )

Может потребоваться выравнивание объекта на низком уровне, но в любом случае я бы посчитал это законным использованием.

5 голосов
/ 21 сентября 2009

Один из способов, которыми я использовал его во встроенных системах, - это когда у меня есть структура, которая представляет макет энергонезависимой памяти (например, EEPROM), но где я не хочу на самом деле создавать экземпляр этой структуры в оперативной памяти Вы можете использовать различные полезные трюки макросов, чтобы вы могли читать и записывать определенные поля из EEPROM, где offsetof выполняет работу по вычислению адреса поля в структуре.

Что касается «зла», вы должны помнить, что многие вещи, которые традиционно делались в программировании на «С», особенно на платформах с ограниченными ресурсами, теперь выглядят как злые хакерские атаки, если смотреть на них из роскошного окружения современных вычислений.

4 голосов
/ 21 сентября 2009

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

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

В примере немного сложно быстро набросать (я пытался), но, поскольку комментарии указывают, что пример в порядке, я попробую еще раз.

Предположим, у нас есть автономный модуль, называемый «командой», который реализует некоторое действие в приложении. Эта команда имеет несколько параметров, которые управляют ее общим поведением, которые должны быть доступны пользователю через графический интерфейс пользователя. Для целей этого примера предположим, что приложение является файловым менеджером, и команда может быть, например, "Копировать".

Идея состоит в том, что код копии находится в одном файле C, а код GUI - в другом, и код GUI не нужно жестко кодировать для «поддержки» параметров команды копирования. Вместо этого мы определяем параметры в файле копирования, например:

struct copy_options
{
  unsigned int buffer_size;     /* Number of bytes to read/write at a time. */
  unsigned int copy_attributes; /* Attempt to copy attributes. */
  /* more, omitted */
};

static struct copy_options options; /* Actual instance holding current values. */

Затем команда copy регистрирует свои параметры конфигурации с помощью модуля GUI:

void copy_register_options(GUIModule *gui)
{
  gui_command_begin(gui, "Copy");
  gui_command_add_unsigned_int(gui, "Buffer size", offsetof(struct copy_options, buffer_size));
  gui_command_add_boolean(gui, "Copy attributes", offsetof(struct copy_options, copy_attributes));
  gui_command_end(gui);
}

Тогда, скажем, пользователь просит установить параметры команды копирования. Затем мы можем сначала скопировать текущие параметры для поддержки отмены и запросить в модуле GUI диалоговое окно, содержащее элементы управления, созданные во время выполнения, подходящие для редактирования параметров этой команды:

void copy_configure(GUIModule *gui)
{
  struct copy_options edit = options;

  /* Assume this opens a modal dialog, showing proper controls for editing the
   * named command's options, at the address provided. The function returns 1
   * if the user clicked "OK", 0 if the operation was cancelled.
  */
  if(gui_config_dialog(gui, "Copy", &edit))
  {
    /* GUI module changed values in here, make edit results new current. */
    options = edit;
  }
}

Конечно, этот код предполагает, что настройки являются чисто типами значений, поэтому мы можем скопировать структуру, используя простое присвоение структуры. Если бы мы также поддерживали динамические строки, нам понадобилась бы функция для копирования. Для данных конфигурации, однако, любая строка, вероятно, лучше всего будет выражаться в виде массива char статического размера в структуре, что будет хорошо.

Обратите внимание, что тот факт, что модуль GUI знает только, где находится каждое значение, выраженное в виде смещения, позволяет нам предоставить функции диалога временную копию в стеке. Если бы мы вместо этого настроили модуль GUI с прямыми указателями на каждое поле, это было бы невозможно, что было бы гораздо менее гибким.

2 голосов
/ 21 сентября 2009

По сути, все, что вы будете делать с указателем на член (T::*) в C ++, является хорошим кандидатом для использования offsetof в C. По этой причине offsetof намного реже в C ++.

Теперь это, конечно, немного кругло, поэтому вот несколько примеров:

  • Полугодовые функции сортировки для структур. qsort использует обратный вызов, который не идеален. Часто вам просто нужно отсортировать по естественному порядку одного члена, например, третий int в структуре. Гипотетический qsort_int может принять аргумент offsetof для этой цели.
  • Точно так же можно написать макрос extract такой, что вы можете сказать int out[10]; extract(int, &MyFoo[0], &MyFoo[10], out, offsetof(struct Foo, Bar));
1 голос
/ 21 сентября 2009

offsetof относительно часто используется для программирования драйвера устройства, когда вам обычно приходится писать на простом C, но иногда нужны некоторые «другие» функции. Предположим, у вас есть функция обратного вызова, которая получает указатель на некоторую структуру. Теперь эта структура сама является членом другой более крупной «внешней» структуры. с помощью «offsetof» у вас есть возможность изменять элементы «внешней» структуры, когда у вас есть доступ только на «внутренний» член.

Примерно так:

struct A
{
 int a1;
 int a2;
};

struct B
{
 int b1;
 int b2;
 A a;
};

void some_API_callback_func(A * a)
{
 //here you do offsetof 
 //to get access to B members
}

Конечно, это опасно, если есть вероятность, что структура A используется не как часть структуры B. Но во многих местах, где инфраструктура для "some_API_callback_func" хорошо документирована, это прекрасно работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...