Вопрос по c, struct, alignment – Выравнивание размера структуры C

8

Я хочу, чтобы размер структуры C был кратным 16 байтам (16B / 32B / 48B / ..). Неважно, к какому размеру он подходит, он должен быть кратным 16B. Как я могу заставить компилятор сделать это? Благодарю.

Что за компилятор? Joe
@Joe Я сейчас использую gcc Neo Hpcer
А вот существующая статья, которая предлагает решение, использующее объединение для заполнения до определенного размера. Однако это решение требует знания размера структурыSize of Struct однако вам нужно будет использовать вычисление размера для массива символов, например (sizeof (struct it) / 16 + 1) * 16, а не числовую константу. Richard Chambers
В вашем названии используется слово выравнивание, однако основная часть вашего вопроса говорит о размере структуры. Что вы на самом деле хотите достичь? И мне также любопытно, почему. Richard Chambers
@RichardChambers Причина в том, что у меня есть выровненный фрагмент данных размером 16B, и эта структура будет в начале фрагмента, и я все еще хочу выровнять начало фактической части данных фрагмента (после структуры) до 16B для хорошей производительности SSE. Neo Hpcer

Ваш Ответ

4   ответа
12

#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, То же самое касается других компиляторов.

Да, это так :) .. мой плохой
И начальный адрес, и размер структуры будут выровнены по 16B. Это делается для того, чтобы обеспечить правильное выравнивание элементов массива структур.
Для gccattribute выровненный, это фактически заставляет размер структуры быть кратным 16B? Или это просто гарантирует, что начальный адрес структуры выровнен по 16B? Neo Hpcer
Это правда (это подразумевается). Проверьте это сами. Я обновил свой ответ с примером.
Это неправда. Макрос __attribute (align (x))) размещает структуру на границе х-байтов и не имеет ничего общего с размером структурыgcc.gnu.org/onlinedocs/gcc-3.2.3/gcc/Variable-Attributes.html
5

поскольку выравнивание не указано глубоко в стандарте ISO C (оно указывает, что выравнивание может происходить по заданию компилятора, но не вдавается в подробности о том, как его применять).

Вам нужно изучить специфические для реализации вещи для вашей цепочки инструментов компилятора. Это может обеспечить#pragma pack (или жеalign или что-то другое), что вы можете добавить к определению вашей структуры.

Это может также обеспечить это как расширение языка. Например, gcc позволяет добавлять атрибуты к определению, один из которых управляет выравниванием:

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

их типов и их количества. На самом деле не существует стандартного способа заставить компилятор создавать структуры, кратные некоторому размеру. Некоторые компиляторы предоставляют прагму, которая позволит вам установить границу выравнивания, однако это действительно другая вещь. И могут быть некоторые, которые будут иметь такую обстановку или обеспечить такую прагму.

Однако, если вы настаиваете на этом, одним из методов будет выделение памяти структуры и принудительное округление памяти до следующих 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, что он должен оценивать макро-аргумент только один раз (не могу вспомнить его из головы).
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);

Похожие вопросы