3

免责声明:这个问题是关于一个一般概念。我已经“简化”了这个问题,以便我可以在这里清楚地问它——无需提供它实际应用的整个上下文。我已经可以预见到一堆评论,比如“你为什么要那样做?” 但是,如果这个问题可以从表面上看,我将不胜感激!

假设您想在运行时从一些预定义的结构中动态合成 C 中的一些数据结构。

我知道如何提出这个问题的最好方法是通过代码示例。

在下文中,我们定义了两个结构体:FooBar. 我还定义了一个结构FooBar来说明编译时生成的复合类型和运行时生成的“动态合成”类型之间的至少一个区别。

#include <stdlib.h>
#include <stdio.h>

typedef struct Foo {
    char junk1;
    char junk2;
} Foo;

typedef struct Bar {
    int junk3;
    int junk4;
} Bar;

typedef struct FooBar {
    Foo foo;
    Bar bar;
} FooBar;

int main()
{
    printf("Sizes: %li, %li, %li\n", sizeof(Foo), sizeof(Bar), sizeof(FooBar));
    // Prints: Sizes: 2, 8, 12
    // Because Foo is aligned on 1-byte boundaries and has total size of 2 bytes.
    // Bar is aligned on 4-byte boundaries and has total size of 8 bytes.
    // But FooBar is aligned on 4-byte boundaries due to the ints in Foo. Therefore,
    // the compiler added 2-bytes of padding after the foo member.

    // The following "works", but only allocates 10 bytes, and
    // "Bar" members are now "misaligned":
    void * syntheticFooBar = malloc(sizeof(Foo) + sizeof(Bar));
    ((Foo*)syntheticFooBar)->junk1 = 1;
    ((Foo*)syntheticFooBar)->junk2 = 2;
    ((Bar*)(syntheticFooBar + sizeof(Foo)))->junk3 = 3;
    ((Bar*)(syntheticFooBar + sizeof(Foo)))->junk4 = 4;

    free(syntheticFooBar);
    return 0;
}

所以我的问题是:

1.) 缺乏正确的数据对齐会对性能造成多大的影响?考虑到访问合成结构的“成员”所涉及的开销,数据对齐甚至是一个重要的促成因素吗?

2.) 考虑到运行时综合的限制,有没有更好的方法来做到这一点?

4

1 回答 1

1

1.) 缺乏正确的数据对齐会对性能造成多大的影响?考虑到访问合成结构的“成员”所涉及的开销,数据对齐甚至是一个重要的促成因素吗?

这完全取决于 CPU 架构和编译器。在某些系统上,您可能只会受到性能损失,但在其他系统上,您可能会崩溃。

2.) 考虑到运行时综合的限制,有没有更好的方法来做到这一点?

这是一个如何在运行时创建正确对齐的合成结构的示例:

#include <stdio.h>
#include <stdlib.h>

typedef struct {
  char junk1;
  char junk2;
} A;

typedef struct {
  int junk3;
  int junk4;
} B;

typedef struct {
  double junk5;
  char junk6;
} C;

static size_t roundUp(size_t value,size_t factor)
{
  return value+factor-1-((value+factor-1)%factor);
}

#define alignof(type) (sizeof(struct {type a;char b;})-sizeof(type))

int main(int argc,char **argv)
{
  size_t offsets[3];
  size_t pos = 0;

  pos = roundUp(pos,alignof(A));
  offsets[0] = pos;
  pos += sizeof(A);

  pos = roundUp(pos,alignof(B));
  offsets[1] = pos;
  pos += sizeof(B);

  pos = roundUp(pos,alignof(C));
  offsets[2] = pos;
  pos += sizeof(C);

  {
    char *foobar = malloc(pos);
    A *a = (A *)(foobar + offsets[0]);
    B *b = (B *)(foobar + offsets[1]);
    C *c = (C *)(foobar + offsets[2]);
    a->junk1 = 1;
    a->junk2 = 2;
    b->junk3 = 3;
    b->junk4 = 4;
    c->junk5 = 5;
    c->junk6 = 6;
    free(foobar);
  }
  return 0;
}

特定结构的对齐方式是通过创建具有原始结构和额外字符的结构来确定的。如果您要使用这些结构的数组,编译器将自动添加足够的填充以确保保留必要的对齐方式,因此通过测量大小的差异,我们可以获得正确的对齐方式。

使用对齐信息,我们可以创建一个表,其中显示了合成结构的每个成员相对于开头的位置。我们只需要通过将位置四舍五入到最接近的对齐倍数来确保每个成员的位置具有正确的对齐方式。

您应该能够将其推广到任意数量的成员。

请注意,如果您想确定合成结构的整体大小(sizeof() 可能返回什么),以便您可以创建这些合成结构的数组,那么您需要获得组合对齐要求并将最终 pos 向上舍入到那个因素。组合对齐将是单个对齐的最小公倍数。

于 2013-08-16T04:44:48.780 回答