有这个宏offsetof在C/C++,让您获得地址的POD结构成员的偏移.有关C FAQ的示例:
struct foo {
int a;
int b;
};
struct foo;
/* Set the b member of foo indirectly */
*(int *)((char *)foo + offsetof(b)) = 0xDEADBEEF;
Run Code Online (Sandbox Code Playgroud)
现在这对我来说似乎是邪恶的,我看不出这个宏的许多合法用途.
我看到的一个合法的例子是它在Linux内核中的container_of宏中用于获取嵌入式结构父对象的地址:
/* 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);
Run Code Online (Sandbox Code Playgroud)
这个宏有什么其他合法用途?你什么时候应该不会使用这个宏?
一个合法用途offsetof()是确定类型的对齐方式:
#define ALIGNMENT_OF( t ) offsetof( struct { char x; t test; }, test )
Run Code Online (Sandbox Code Playgroud)
需要对象的对齐可能有点低级,但无论如何我认为这是合法的用法.
我在嵌入式系统中使用它的方法之一是我有一个表示非易失性存储器(例如EEPROM)布局的结构,但我不想在RAM中实际创建这个结构的实例.您可以使用各种漂亮的宏技巧来允许您从EEPROM读取和写入特定字段,其中offsetof用于计算结构中字段地址的工作.
关于'邪恶',你必须记住,传统上在'C'编程中完成的许多东西,特别是在资源有限的平台上,从现代计算的豪华环境来看,现在看起来像邪恶的hackery.
嗯……在 C 语言中,它对于任何需要代码来描述数据结构的地方都非常有用。我已经使用它来执行运行时生成的 GUI:用于设置选项。
它的工作原理如下:需要选项的命令定义一个保存其选项的本地结构,然后将该结构描述为生成 GUI 的代码,用于offsetof指示字段所在的位置。使用偏移量而不是绝对地址允许 GUI 代码适用于该结构的任何实例,而不仅仅是一个。
在示例中快速绘制草图有点困难(我尝试过),但由于注释表明示例是有序的,所以我会再试一次。
假设我们有一个独立的模块,称为“命令”,它在应用程序中实现某些操作。该命令有一堆控制其一般行为的选项,应通过图形用户界面向用户公开。出于该示例的目的,假设应用程序是文件管理器,并且命令可以是例如“复制”。
这个想法是,复制代码位于一个 C 文件中,GUI 代码位于另一个 C 文件中,并且 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. */
Run Code Online (Sandbox Code Playgroud)
然后,复制命令将其配置设置注册到 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);
}
Run Code Online (Sandbox Code Playgroud)
然后,假设用户要求设置复制命令的选项。然后,我们可以首先复制当前选项以支持取消,并向 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;
}
}
Run Code Online (Sandbox Code Playgroud)
当然,这段代码假设设置是纯值类型,因此我们可以使用简单的结构体赋值来复制结构体。如果我们还支持动态字符串,则需要一个函数来进行复制。不过,对于配置数据,任何字符串可能最好在结构中表示为静态大小的char数组,这很好。
请注意,GUI 模块只知道以偏移量表示的每个值所在的位置,这一事实使我们能够为对话框函数提供临时的堆栈副本。如果我们改为使用指向每个字段的直接指针来设置 GUI 模块,则这是不可能的,而且灵活性会大大降低。