【问题标题】:struct similarity in CC中的结构相似性
【发布时间】:2015-07-29 21:10:09
【问题描述】:

考虑下面的两个structs:

struct A {
    double x[3];
    double y[3];
    int z[3];

    struct A *a;
    int b;
    struct A *c;
    unsigned d[10];
};

struct B {
    double x[3];
    double y[3];
    int z[3];
};

注意struct Bstruct A 的严格子集。现在,我想将成员.x.y.zstruct A 的实例复制到struct B 的实例。我的问题是:根据标准,这样做是否有效:

struct A s_a = ...;
struct B s_b;
memcpy(&s_b, &s_a, sizeof s_b);

即是否保证成员的填充,在他们出现的顺序中,将是相同的,这样我就可以“部分地”memcpystruct Astruct B

【问题讨论】:

  • 一点也不。我相信有人会找到标准,但填充取决于实现,除非代码中有非常具体的标志。
  • 如前所述,填充将成为方式和实现依赖。你试过你的系统吗?计算机很有趣,因为当您尝试时没有任何问题
  • 我认为可能要求BA 的严格前缀,而不仅仅是一个子集。
  • @Pynchia 这只会告诉你它是否适用于特定情况,而不是泛化。
  • @Jashaszun 虽然填充依赖于实现,但我认为实现必须保持一致。

标签: c struct standards


【解决方案1】:

不保证struct A 的布局开始时与struct B 的布局相同。

然而,当且仅当他们都是工会的成员:

union X
{
    struct A a; 
    struct B b;
};

then it is guaranteed公共初始序列布局相同。

我从未听说过任何编译器会在检测到结构是联合的成员时以不同方式布局结构,所以实际上你应该是安全的!

【讨论】:

  • 如果struct B 只是struct B 的第一个字段也是一样的。无论哪种方式,您都需要另一个字段名称,为此它保证的(而不仅仅是常见的做法)。
  • @Olaf 我不知道你想说什么
  • union X x; x.a.x[0]; 需要选择正确的结构 .astruct A { struct B b; ...} a; a.b.x[0] 同上。
  • @Olaf 如果struct A 是这样定义的,那么它没有成员x[0]
  • 正确:它确实有一个成员 b 和成员 x。反过来,你有包装联合x。在我看来,这两种方式都不是很优雅(没有批评)
【解决方案2】:

使用struct B 作为struct Aanonymous struct 成员怎么样。但是,这需要-fms-extensions 用于 gcc(顾名思义,VC 应该有类似的扩展名):

struct B {
    double x[3];
    double y[3];
    int z[3];
};

struct A {
    struct B;

    struct A *a;
    int b;
    struct A *c;
    unsigned d[10];
};

这允许使用struct A 中的字段,例如:

struct A as;

as.x[2] = as.y[0];

等等。这保证了相同的布局(标准不允许在结构的开头进行填充,因此内部结构保证从与外部相同的地址开始)并且struct Astruct B 兼容。

还有:

struct A as;
struct B bs;
memcpy(&as, &bs, sizeof(bs));

【讨论】:

  • (struct B)as = bs; 是非法的,没有结构类型的转换
  • 您的代码不包含任何匿名结构成员。它们是一个没有标签的结构,但是B 是一个结构标签。
  • @MattMcNabb:抱歉,已编辑。但是,现在不确定严格的别名。至少 'memcpy` 应该是安全的。
  • 不起作用,gcc 抱怨:warning: declaration does not declare anything struct B;,然后:error: ‘struct A’ has no member named ‘x’。 Clang 说了类似的话。
  • 事实上 struct B; 在 ISO C 中是非法的。这是微软的扩展。在 gcc for windows 中,它默认启用(大概是为了与 windows 头文件兼容),以获得一致的行为使用 -fno-ms-extensions
【解决方案3】:

我认为标准不会禁止实现在s_a 中包含比s_b 更多的填充,即使它的成员是s_b 的子集,前者实际上更大。这样的行为会很奇怪,我想不出编译器为什么会这样做,但我认为不会被禁止。

如果复制的字节数是sizeof s_asizeof s_b中的较小者,则memcpy操作将保证复制所有公共字段,但不一定会留下s_b后面的字段不受干扰。在典型的机器上,如果声明是:

struct A { uint32_t x; char y; };
struct B { uint32_t x; char y,p; uint16_t q; };

第一个结构将包含 5 个字节的数据和 3 个字节的填充,而第二个结构将包含 8 个字节的数据,没有填充。如您的代码所示,使用memcpy 会将s_a 中的填充复制到s_b 中的数据上。

如果您需要复制初始结构成员,同时保持结构的平衡不受干扰,您应该计算最后一个感兴趣成员的偏移量和大小,并将其用作要复制的字节数。在我上面给出的示例中,y 的偏移量为 4,大小为 1,因此memcpy 将忽略结构的一部分,这些结构在A 中用作填充但可能将数据保存在B.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多