8

我是 C 语言的新手,刚刚了解了结构和指针。

我的问题与offsetof我最近看到的宏有关。我知道它是如何工作的以及其背后的逻辑。

<stddef.h>文件中定义如下:

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

我的问题是,如果我有如下所示的结构:

struct test {
    int field1:
    int field2:
};

struct test var;

为什么我不能直接获取field2as的地址:

char * p = (char *)&var;
char *addressofField2 = p + sizeof(int);

而不是写这样的东西

field2Offset = offsetof (struct test, field2);

然后将偏移值添加到 var 的起始地址?

有什么区别吗?使用offsetof效率更高吗?

4

3 回答 3

4

C 编译器通常会在 a 的成员之间添加额外的填充位或字节,struct以提高效率并保持整数字对齐(在某些体系结构中需要避免总线错误,而在某些体系结构中需要避免效率问题)。例如,在许多编译器中,如果你有这个struct

struct ImLikelyPadded {
    int x;
    char y;
    int z;
};

您可能会发现是 12,而不是 9,因为编译器会在 one-byte 的末尾和 word-sizedsizeof(struct ImLikelyPadded)之间插入三个额外的填充字节。这就是它如此有用的原因——它可以让你确定事情在哪里真正考虑到了填充字节,并且具有高度的可移植性。char yint zoffsetof

于 2016-02-07T21:18:35.547 回答
0

与数组不同,struct 的内存布局并不总是连续的。编译器可能会添加额外的字节,以对齐内存。这被称为padding

由于填充,我们很难手动找到成员的位置。这也是为什么我们总是使用 sizeof 来查找结构体大小的原因。

Offsetof ,宏让你找出结构的成员与结构的分层位置的距离,偏移量。

container_of如果在 Linux 内核的宏中看到 offsetof,则一种智能用途。这个宏让你在一个通用的包含双向链表中找出给定成员地址的节点的起始位置

于 2016-02-07T21:29:02.537 回答
0

正如其他答案中已经提到的,填充是原因之一。我不会重复已经说过的话。

使用宏而不是手动计算偏移量的另一个好理由offsetof是您只需编写一次。想象一下,如果您需要更改 . 的类型field1或插入或删除field2. 使用您的手工计算,您必须找到并更改它的所有出现。缺少其中一个会产生难以发现的神秘错误。

在这种情况下,使用编写的代码offsetof不需要任何更新。编译器会在下一次编译时处理所有事情。

更重要的是,使用的代码offsetof更加清晰。宏是标准的,其功能已记录在案。阅读代码的程序员同行会立即理解它。理解手工编写的代码尝试的内容并不容易。

于 2016-02-07T21:38:46.930 回答