【问题标题】:c struct grabbing data by offsetc struct 按偏移量抓取数据
【发布时间】:2010-11-15 14:07:53
【问题描述】:

假设我有这个结构:

typedef struct nKey {
    int num;
    widget* widget;
} NUMBER_KEY;

还有一个功能:

void dialKey(widget* widget) {
    // Need to print 'num' of the struct that this widget resides in
}

我该如何实现呢?我试过类似的东西:

    printf("%d", * (int *) widget - sizeof(int)); // Failure.org

编辑:假设被传递的小部件实际上是 NUMBER_KEY 结构的成员是安全的

编辑:寻找问题的解决方案而不是另一种方法。

【问题讨论】:

  • 您的代码不一定会按原样工作,因为结构成员之间可能存在实现定义的填充。
  • 最重要的是指针指向小部件,而不是小部件指针。此外,从 (int *) 中减去 sizeof(int) 会将指针移回 sizeof(int) 个整数,而不仅仅是一个。更不用说填充的可能性了。

标签: c struct offset


【解决方案1】:

C 标准定义了 offsetof() 宏(在 stddef.h 标头中定义),这在您的情况下很方便。

#include <stddef.h>

void DialKey(widget** foo) {
    printf("%d", *((char *)foo - offsetof(struct nKey, widget)));
}

请注意,您必须传递结构字段的地址,而不是值!

【讨论】:

    【解决方案2】:

    正如迈克尔在他的回答中解释的那样,你不能在给定的约束下做到这一点,因为没有办法“走回”指针图。为了让事情更明显,让我画一张涉及的对象图(用 C 术语,而不是 OOP 意义上的):

    +- NUMBER_KEY --+
    |  ...          | points to      
    | widget field -+-----------+
    |  ...          |           |
    +---------------+           |   + widget +
                                +-->|  ...   |
                                    |  ...   |
                                +-->|  ...   |
                                |   +--------+
                      points to |
    [widget argument]-----------+
    

    注意箭头。它们是单向的——你可以从一个指向指向值的指针“走”,但你不能“走”回来。因此,您可以遵循函数的 widget 参数以获取 widget 对象,但是一旦到达那里,就无法判断还有谁指向它 - 包括 NUMBER_KEY 结构的任何实例。想一想:如果你有十几个指向同一个widget 的其他指针,其中一些来自不同的NUMBER_KEY 对象怎么办?如果不在widget 对象中保留所有指针的列表,它怎么可能跟踪它?如果你真的需要这个,这就是你必须做的——让widget指向它拥有的NUMBER_KEY

    【讨论】:

      【解决方案3】:

      鉴于只有一个 widgit* 而不是 widgit** 被传递到 dialKey,没有办法做你想做的事(widgit* 值与 NUMBER_KEY 结构没有关系)。假设您的真正意思是:

      void dialKey(widget** ppWidget) 
      {    
          // Need to print 'num' of the struct that this widget resides in
      }
      

      Microsoft 有一个漂亮的宏来做这类事情(它有助于在 C 中使用一般操作链表的例程):

      #define CONTAINING_RECORD(address, type, field) ((type *)( \
                                     (PCHAR)(address) - \
                                     (ULONG_PTR)(&((type *)0)->field)))
      

      你可以这样使用:

      NUMBER_KEY* pNumberKey = CONTAINING_RECORD( *ppWidgit, NUMBER_KEY, widgit);
      
      printf( "%d", pNumberKey->num);
      

      【讨论】:

      • +1 for widget * vs. widget **,所有现有答案都错过了。 CONTAINING_RECORD 显然是一个 hack,但至少它隐藏了血淋淋的细节......
      • 很好,我错过了结构声明中的指针。我会相应地修正我的答案。
      • @RBertig - 这可能是一个 hack,但它非常有用。
      • 您可以将 CONTAINING_RECORD 宏简化为 ((char *)(address) - offsetof(type, field))。 offsetof() 是来自 stddef.h 的标准 C 宏。
      【解决方案4】:

      结构的内存布局由您的编译器定义,您的代码只有在结构成员之间有 0 字节填充时才能工作。通常不建议这样做 b/c 0 字节填充会使您的结构覆盖大多数处理器的标准读取大小。

      一些对您的问题有用的宏:

      #define GET_FIELD_OFFSET(type, field)    ((LONG)&(((type *)0)->field))
      #define GET_FIELD_SIZE(type, field)      (sizeof(((type *)0)->field))
      

      示例:

      NUMBER_KEY key;
      key.num = 55;
      int nOffSetWidget = GET_FIELD_OFFSET( NUMBER_KEY, widget);
      int *pKeyNumAddress = (int *) &(key.widget) - nOffSetWidget );
      
      printf("%d", * pKeyNumAddress );    // Should print '55'
      

      【讨论】:

      • 如果你#include ,offsetof() 宏是标准的。
      • 我认为它不会按您期望的方式工作。您将 key.widget 的地址转换为 *int,因此使用 sizeof(int) 作为算术单元的指针数学,因此您将扣除 sizeof(int)*nOffSetWidget 字节数,而不是扣除 nOffSetWidget 字节数。并且有标准的宏偏移量 - 不需要发明它。
      【解决方案5】:

      最安全的解决方案是在小部件中保留指向相关nKey结构的指针

       printf("%d", widget->myKey->num);
      

      所有其他方法都是不安全的

      【讨论】:

      • 说“所有其他方法都不安全”是一种大胆的夸大其词。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-09
      • 1970-01-01
      相关资源
      最近更新 更多