C:在运行时动态合成聚合数据类型

时间:2013-08-16 03:27:15

标签: c struct memory-alignment

免责声明:这个问题是关于一般概念的。我已经“愚蠢”了这个问题,以便我可以在这里清楚地提出这个问题 - 而不需要提供它的实际应用的整个背景。我已经可以预见到一堆评论,比如“为什么你会这样做?”但是,如果这个问题只能按照面值进行,我会很感激!

假设您希望在运行时从一些预定义的结构中动态合成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。)考虑到运行时合成的限制,有没有更好的方法呢?

1 个答案:

答案 0 :(得分: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;
}

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

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

您应该可以将此概括为任意数量的成员。

请注意,如果您想确定合成结构的整体大小(sizeof()可能会返回),以便您可以创建这些合成结构的数组,那么您需要获得组合对齐要求并roundUp这个因素的最终定位。组合对齐将是各个比对的最不常见的倍数。