【问题标题】:C struct size alignmentC 结构体大小对齐
【发布时间】:2012-06-23 05:06:04
【问题描述】:

我希望 C 结构的大小是 16 字节的倍数(16B/32B/48B/..)。 它的大小无关紧要。它只需要是 16 字节的倍数。

我怎样才能强制编译器这样做?

【问题讨论】:

  • 您的标题使用了对齐这个词,但是您的问题主体谈到了结构的大小。你真正想要完成的是什么?我也很好奇为什么。
  • 这里有一篇现有的文章,它提供了一种使用联合填充到特定大小的解决方案。但是,该解决方案需要知道结构 Size of Struct 的大小,但是您需要做的是对 char 数组使用大小计算,例如 (sizeof(struct it)/16+1)*16 而不是数字常量.
  • @RichardChambers 原因是因为我有一个 16B 对齐的数据块,并且这个结构将位于块的开头,我仍然想要块的实际数据部分的开头(在结构之后)对齐到 16B 以获得良好的 SSE 性能。
  • @RichardChambers 是的。标题似乎有些误导。我关心的是结构的大小,而不是对齐方式。

标签: c struct alignment


【解决方案1】:

C 结构的大小取决于结构的成员、它们的类型以及它们的数量。实际上没有标准的方法来强制编译器使结构成为某个大小的倍数。一些编译器提供了一个编译指示,允许您设置对齐边界,但这确实是另一回事。并且可能有一些会有这样的设置或提供这样的 pragma。

但是,如果您坚持使用这种方法,那就是对结构进行内存分配并强制内存分配向上舍入到下一个 16 字节大小。

如果你有这样的结构。

struct _simpleStruct {
   int iValueA;
   int iValueB;
};

然后您可以执行以下操作。

{
    struct _simpleStruct *pStruct = 0;
    pStruct = malloc ((sizeof(*pStruct)/16 + 1)*16);
    // use the pStruct for whatever
    free(pStruct);
}

就您而言,这将把大小推高到下一个 16 字节大小。然而,内存分配器所做的可能是也可能不是给你一个实际上是那个大小的块。内存块实际上可能比您的请求大。

如果你打算对此做一些特殊的事情,例如,假设你要将此结构写入文件并且你想知道块大小,那么你将不得不进行与 malloc 中使用的相同的计算() 而不是使用 sizeof() 运算符来计算结构的大小。

所以接下来的事情就是使用宏来编写自己的 sizeof() 运算符。

#define SIZEOF16(x) ((sizeof(x)/16 + 1) * 16)

据我所知,没有可靠的方法可以从指针中提取已分配块的大小。通常,指针将有一个内存分配块,由内存堆管理函数使用,该函数将包含各种内存管理信息,例如分配的块大小实际上可能大于请求的内存量。然而,这个块的格式以及它相对于提供的实际内存地址的位置将取决于 C 编译器的运行时间。

【讨论】:

  • 很好的答案,但看起来如果类型已经是 16 字节的倍数,那么这个宏将在其大小上增加额外的 16 字节,可能会浪费内存。这是使用三元运算符来消除这种情况的替代方法:#define SIZEOF16(x) ((sizeof(x) & 0x0F) ? (sizeof(x) + (0x10 - (sizeof(x) & 0x0F))) : sizeof(x))
  • @rsethc 说得好。我想我担心的是宏参数的多次使用。大多数人都知道不要在宏中使用修改诸如x++ 之类的表达式,并且大多数看起来都是大写的函数通常都是宏,所以我想它可能是相当无害的。我想inline 函数也可以。
  • 宏中的多次使用是一个有效的问题,尽管sizeof 是在编译时计算出来的。我无法想象它会像这样使用:SIZEOF16 = (x++),但如果是这样,我认为可能有某种方法可以告诉 GCC 只评估一次宏参数(无法回忆起它)。
【解决方案2】:

对于 Microsoft Visual C++:

#pragma pack(push, 16)

struct _some_struct
{
     ...
}

#pragma pack(pop)

对于 GCC:

struct _some_struct { ... } __attribute__ ((aligned (16)));

例子:

#include <stdio.h>

struct test_t {
    int x;
    int y;
} __attribute__((aligned(16)));

int main()
{
    printf("%lu\n", sizeof(struct test_t));
    return 0;
}

使用gcc -o main main.c 编译将输出16。其他编译器也是如此。

【讨论】:

  • 对于 gcc attribute 对齐,它实际上是否强制结构大小为 16B 的倍数?还是只是保证struct的起始地址对齐到16B?
  • 结构的起始地址和大小都会对齐到16B。这样做是为了确保结构数组的元素正确对齐。
  • 这不是真的。 __attribute(aligned(x))) 宏在 x 字节边界上分配结构,与结构的大小无关gcc.gnu.org/onlinedocs/gcc-3.2.3/gcc/Variable-Attributes.html
  • 这是真的(暗示)。自己检查一下。我已经用一个例子更新了我的答案。
【解决方案3】:

您也许可以做一个双重结构,将您的实际结构包装在第二个可以添加填充的结构中:

struct payload {
  int a;    /*Your actual fields. */
  float b;
  char c;
  double d;
};

struct payload_padded {
  struct payload p;
  char   padding[16 * ((sizeof (struct payload) + 15) / 16)];
};

然后你可以使用填充结构:

struct payload_padded a;

a.p.d = 43.3;

当然,您可以利用结构的第一个成员从结构开始的 0 字节开始这一事实,并将指向 struct payload_padded 的指针视为指向 struct payload 的指针(因为它是):

float d_plus_2(const struct payload *p)
{
  return p->d + 2;
}

/* ... */

struct payload_padded b;
const double dp2 = d_plus_2((struct payload *) &b);

【讨论】:

    【解决方案4】:

    这完全取决于编译器和其他工具,因为在 ISO C 标准中没有详细说明对齐(它指定对齐可能在编译器的要求下发生,但没有详细说明如何执行它)。

    您需要查看编译器工具链的特定于实现的东西。它可能会提供一个#pragma pack(或align 或其他东西),您可以将其添加到您的结构定义中。

    它也可以将其作为语言扩展提供。例如,gcc 允许您向定义添加属性,其中之一控制对齐:

    struct mystruct { int val[7]; } __attribute__ ((aligned (16)));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-06
      • 2011-05-13
      • 1970-01-01
      • 2020-07-26
      • 2016-08-30
      相关资源
      最近更新 更多