当前位置: 首页 > 知识库问答 >
问题:

'偏移量(结构派生,超.x) == 偏移量(结构基数,x)'在 C 中是否成立?

黄朗
2023-03-14

我不确定安德烈·卡隆在这里是什么意思:

C中的虚拟函数

...其中一些代码依赖于(正式的)非标准行为,这些行为“恰好”在大多数编译器上工作。主要问题是代码假设

< code>m的类型为< code>struct meh *。类型为< code>struct foo *的对象f通过向< code>struct meh *的强制转换被赋给m。< code>struct meh具有< code>struct foo类型的成员< code > base (< code > struct foo meh::base = foo::bar )。为什么不能保证< code >

结构梅赫 * m = (结构梅赫*)f;变为结构梅赫 * m = (结构梅赫*)f-

struct meh
{
   /* inherit from "class foo". MUST be first. */
   struct foo base;
   int more_data;
};

下面,我列出了相关的ISO C90/C 98从我的研究摘录。我还创建了一个代码示例。示例代码可以通过< code >-fsanitize = undefined-STD = c 98-O0-Wall-Wextra-wpe dantic-Wconversion-Wundef 用Clang编译。

这是:

https://godbolt.org/z/qo9f8KnYM

节选

来自ISO C90 (ANSI C89):

对象的存储值只能由具有以下类型之一的左值访问: /28/

...

  • 在其成员中包括上述类型之一的聚合或联合类型(递归地包括
    子聚合或包含的联合的成员),或

指向结构对象的指针,经过适当的强制转换,指向其初始成员(如果该成员是位字段,则指向其所在的单元),反之亦然。因此,结构对象中可能存在未命名的孔,但在其开始处可能没有,这是实现适当对齐所必需的。

来自ISO C 98:

16如果POD联合包含两个或多个共享公共初始序列的POD结构,并且POD联合对象当前包含其中一个POD结构时,允许检查其中任何一个的公共初始部分。如果对应的成员具有一个或多个初始成员序列的布局兼容类型(对于位字段,宽度相同),则两个POD结构共享一个公共的初始序列。17指向POD结构对象的指针(使用reinterpret_cast进行适当转换)指向其初始成员(如果该成员是位字段,则指向其所在的单元),反之亦然。[注意:因此,POD结构对象中可能有未命名的填充,但不是在其开始处,这是实现适当对齐所必需的。]

代码示例

#include <iostream>

struct A {
  int m1;
};

struct B {
  int m1;
  int m2;
};

struct C {
  struct A super;
  int m3;
};

int main(void) {
  struct A a = {42};
  struct C c = {{666}, 1984};

  // Access A::m1 through pointer of type B
  std::cout << ((B *)&a)->m1 << std::endl; // 42

  // Access A::m1 through pointer of type C
  std::cout << ((C *)&a)->super.m1 << std::endl; // 42

  // Access C::super::A::m1 through pointer of type A.
  std::cout << ((A *)(&c))->m1 << std::endl; // 666

  return 0;
}

编辑1:让我在这个编辑部分重写这个问题。我将忽略C,因为评论中的人告诉我不要使问题复杂化。如果这个编辑比原来的更有用,那么也许你可以考虑用这个编辑替换原来的文章。或者我或其他人可以直接“删除”原来的。或者,如果你对如何改进我的问题有更好的想法,请告诉我。毫无疑问,这是我第二次尝试提出这个问题:

我指的是这里张贴的答案:

C中的虚拟函数

  struct Base {
    int x;
  };

  struct Derived {
    struct Base super;
  };

如果offsetof(struct Derived,super.x)==0offsetof?

AndréCaron建议使用指向派生对象的额外指针。显然,依赖offsetof(struct Derived,super.x)==offsetof。

尽管这样做是可行的,但是您依赖编译器扩展来进行类型双关,这可能会导致未定义的行为blablabla。事实上,这在海湾合作委员会和MSVC行得通。

事实上,对齐功能依赖于编译器扩展。您可以使用structfoo中指向“派生对象”的额外void*指针使其可移植。然而,该技术在知名图书馆中非常流行,因此被认为是“可移植的”。任何造成这种代码中断的编译器都会收到客户的大量投诉。

我很难理解为什么偏移量(结构派生,超级.x)!=偏移量(结构基数,x)可能会这样。我没有在C标准中找到澄清。因此,我要求进一步澄清这一点。

13:26,重申我的假设:

假设offsetof(struct Derived, Super. x)!=offsetof(struct Base, x)

  struct Base {
    int x;
    void *hook;
  };

  struct Derived {
    struct Base super;
  };

根据上述假设,考虑:

  struct Base base = {42};
  struct Derived derived;
  base.hook = &base; /* Assuming offsetof(struct Base, x) == 0 */
  derived.super = base;

(struct Base*)(derived.super.hook)==

#include <stddef.h>
#include <stdio.h>

struct Base {
  int x;
  void *hook;
};

struct Derived {
  struct Base super;
};

int main(void) {
  struct Base base = {42};
  struct Derived derived;
  base.hook = &base; /* Assuming offsetof(struct Base, x) == 0 */
  derived.super = base;

  printf("Offset Base x: %lu\n", offsetof(struct Base, x));
  printf("Offset Derived super: %lu\n", offsetof(struct Derived, super));
  printf("Offset Derived super.x: %lu\n", offsetof(struct Derived, super.x));
  printf("Offset Derived super.hook: %lu\n",
         offsetof(struct Derived, super.hook));
  printf("derived.super.hook == &base, yields %d",
         (struct Base *)(derived.super.hook) == &base);

  return 0;
}

共有1个答案

李敏学
2023-03-14

然而,为什么一个POD结构必须有另一个指针void*foo::钩子?

这不是必需的。从最初的问答:

这种技术更可靠,尤其是如果您计划用C语言编写“派生结构”并使用虚函数。在这种情况下,第一个成员的偏移量通常不是0,因为编译器在那里存储运行时类型信息和类的v表。

具有虚函数的 c 结构/类不是 POD。任何非 POD 结构/类都可以具有数据成员的非 0 偏移量,这就是钩子要处理的情况。

 类似资料:
  • 这个问题是关于使用带结构偏移量的指针算法派生的指针。 考虑以下程序: 这个程序似乎可以工作,并在我使用默认设置和C 11标准测试的编译器上给出预期的结果。 (一个密切相关的程序,其中我们使用< code>void *而不是< code>char *和< code>static_cast而不是< code>reinterpret_cast,并没有被普遍接受。< code>gcc 5.4发布了一个关于

  • 可以从输入主题的特定偏移量到结束偏移量进行Kafka流处理吗? 我有一个Kafka流应用程序消耗输入主题,但由于某种原因失败了。我修复了问题并再次启动它,但它从输入主题的最新偏移量开始消耗。我知道应用程序已处理的输入主题的偏移量。现在,我如何将输入主题从一个偏移量处理到另一个偏移量。我正在使用合流平台5.1.2。

  • 问题内容: 在MySQL中存储时区偏移的正确数据类型/结构是哪一种?我只想存储数字值(城市和国家显然存储在其他列中)。 例子: -5:00瓜亚基尔(ECU) -4:30委内瑞拉加拉加斯 0:00一些城市 2:00德国波恩 问题答案: 您应该使用。这是完成任务的正确数据类型:您已设置格式并可以进行计算。此外,根据文档,还应该将其用作两个时刻之间差异的结果,这实际上就是时区。 从文档: MySQL检索

  • 在这两个示例中,通过偏移其他成员的指针来访问结构成员是否会导致未定义/未指定/实现定义的行为? C11§6.7.2.1第14段似乎表明,这应该是实施定义: 结构或联合对象的每个非位字段成员都以适合其类型的实现定义方式对齐。 后来又说: 结构对象中可能有未命名的填充,但在其开头没有。 但是,如下所示的代码似乎相当常见: 该标准似乎保证 与 和< code >( 原始应用程序正在考虑从一个结构字段到另

  • 为什么实际主题中的偏移值与同一主题中的偏移值不同?PFB偏移位置以及使用的命令。 我错过了什么?

  • 问题内容: 是否可以跳过X个第一行,并在一个查询中选择所有其他行?像那样: 它将选择:pqr,stu,vwx,yz 我尝试使用LIMIT和OFFSET完成此操作,但是问题是表是动态的,而且我不知道应该输入哪个LIMIT(我不知道表中有多少行)。 问题答案: 如果只需要最后N行,请尝试以下操作: 这会根据的顺序为您提供最后几条记录。 您可以使用自动递增的主键(希望有一个主键)来确定行的顺序(如果无法