【问题标题】:C pointer arithmetic without object of structure没有结构对象的 C 指针算术
【发布时间】:2014-01-30 11:35:05
【问题描述】:

我认为这在 C 中是不可能的,但要求验证这一点。是否可以在没有这种类型的实变量的结构成员之间进行算术运算?例如:

typedef struct _s1
{
  int a;
  int b;
  int c;
} T1;

我想查看“c”成员相对于结构开头的偏移量。如果我有变量,这很容易:

T1 str;

int dist = (int)&str.c - (int)&str;

但我的结构太大,它在 RAM 中没有成员(仅在 EEPROM 中)。我想进行一些地址计算但不定义 RAM 成员。我可以使用结构指针而不是结构变量来完成这项工作(它只需要 4 个字节),但这个案例对我来说很有趣。

【问题讨论】:

  • 定义假指针看看?
  • 这可能已经在这里得到了回答,并在其他地方讨论了无数次。我想答案应该是谷歌搜索。你谷歌了吗?
  • @EliasVanOotegem 考虑到 EEPROM 的提及,它很有可能在嵌入式系统上运行。据我们所知,它可能是 8 位或 16 位系统。
  • 旁白:您的示例代码应该是(char*)&str.c - (char*)&str。我手头没有参考资料,但我认为您实际上并不能保证将指针转换为int 然后进行算术运算会给出正确的结果。 (另外,如果你将指针转换为整数,你应该使用intptr_tuintptr_t

标签: c++ c pointers math


【解决方案1】:

使用offsetof,您可以在成员之间进行计算,而无需获取该类型的变量。您只需要类型本身和成员的名称。

注意为什么简单的计算可​​能无法解决:数据对齐。您不知道编译器将向您的结构添加多少填充,如果您更改结构,这可能会导致非常细微的错误或看似正确的计算错误。

【讨论】:

    【解决方案2】:

    首先,您将单个地址投射到ints。我不认为这是个好主意。 int 保证至少有 2 个字节或更多字节,地址/指针通常为 4 或 8 个字节。将这些投射到int 只是不正确。如果我要将它们转换成任何东西,我可能会使用 unsigned long 用于 32 位系统,unsigned long long 用于 64 位系统。为了安全起见,我会使用后者。
    也就是说,我根本不会转换地址,而只是使用 offsetof 宏。

    #include <stddef.h> 添加到您的文件中,并使用offsetof 宏,它将偏移值转换为size_t 类型,而不是int,请注意。它的工作原理是使用 0 作为结构的内存地址,然后返回给定成员的地址,给出实际的偏移量:

    size_t offset = offsetoff(T1, c);
                        //struct, member
    

    正如 Zack 在他的评论中指出的那样,答案的下一部分有点无关紧要,但是为了它包含的链接,并且为了完整起见,我将把它留在这里 - 因为 offsetof 是所有必需的实现:

    如果您由于某种原因没有 stddef.h(应该这样做,因为 offsetof 已经成为标准的一部分:C89 及更高版本),请自己定义宏,如下所示:

    #ifndef offsetof
    #define offsetof(s, m) ((size_t)&(((s *) 0)->m))
    #endif
    

    这应该可以解决问题,但要小心:这种实现可能会导致未定义的行为。 How and why is explained here
    我从the stddef.h source I found here 中获取了上面的offsetof 定义,因此您可以下载该文件并使用它,而不是在您自己的文件中定义宏,但请记住您使用的offsetof 不是100 % 可靠。

    不管怎样,现在就够了,更多信息在 Google 上,但这里有几个链接:

    【讨论】:

    • 定义offsetof 有点危险。您展示的实现是未定义行为的边界情况(我可以为此挖掘参考),但对于实现者来说这样做是可以的,因为他们知道(或必须确保)编译器的行为方式。
    • @pmr:在我的答案中添加了一些链接,谢谢你指出来。我不知道offsetof 的怪癖(我的C“知识”的主要来源是K&R,它根本没有提到宏)。无论如何,为您指出与 OP 的数据对齐的答案 +1
    • 现在没有任何理由无法提供 stddef.h,其中包含 offsetof - 自 C89 以来,所有实现(独立或其他)都需要它。
    【解决方案3】:

    你可以使用stddef.h中定义的offsetof

    我知道这超出了您的要求,但是 offsetof 有一个有趣的用法,例如在 linux 内核代码中使用的 container_of 宏。 container_of 可以返回父结构的地址,给定一个指向其成员之一的指针:

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

    你可以使用例如:

    // pointer_to_c points to a member of the struct, c in this case
    // and you want to get the address of the container
    T1 *container;
    container = container_of(pointer_to_c, T1, c);
    

    【讨论】:

    • 我喜欢container_of 宏,但(char *) 演员表对我来说有点legacy,如果你不介意我的话。我认为现在,我们会使用 void 指针
    • @EliasVanOotegem 我认为 (char *) 是必要的,因为后来 __mptr 用于指针算术。它需要精确地减少 offsetof(...) 个字节。你永远不应该对 viud 指针使用算术 stackoverflow.com/questions/3523145/…
    • 好点...我刚刚看到(char *)__mptr ,并没有停下来考虑void * - size_t 的含义:)
    【解决方案4】:

    stddef.h 有一个名为 offsetof 的宏,它给出了成员相对于结构开头的偏移量。

    size_t t = offsetof( T1 , c ) ;
    

    这样您就不必实际声明任何结构变量。

    【讨论】:

      【解决方案5】:

      看看Data structure alignment

      没错:

      size_t dist = offsetof(struct _s1, c);
      

      【讨论】:

        【解决方案6】:

        在标题stddef.h中有一个宏; offsetof。您可以像这样在结构上使用它:

        int dist = (int)offsetof(T1, c);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-07-30
          • 2020-08-19
          • 1970-01-01
          • 1970-01-01
          • 2012-07-27
          • 2021-11-03
          相关资源
          最近更新 更多