Выравнивание элемента данных C++ и упаковка массива

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

Со всем LINQ-совершенством, лямбдой и всеми, некоторые проблемные области не могут быть представлены UML, мы все еще должны освободить дорогу вокруг сгенерированного кода для компьютера для выполнения наших указаний.

, Как мы могли представить в UML, уже не говоря о XML, следующей проблеме? LINQ к SQL с помощью GROUP BY и СЧИТАЮТ (ОТЛИЧНЫМИ)

сумма ответов, к которым простая проблема говорит о многом, что UML, SQL (самый важный ассемблер, независимо от того, что те парни ORM говорят Вам иначе), XML не являются суждением XOR. Мы будем все еще использовать комбинации их технология, не используя только одного из них исключая других.

17
задан Adam Holmberg 4 November 2009 в 20:24
поделиться

8 ответов

Массив объектов должен быть непрерывным, поэтому между объектами никогда не будет отступов, хотя они могут быть добавлены в конец объекта (что дает почти такой же эффект).

Учитывая, что вы работаете с char, предположения, вероятно, чаще всего верны, но стандарт C ++ определенно не гарантирует этого. Использование другого компилятора или даже просто изменение флагов, переданных вашему текущему компилятору, может привести к вставке отступов между элементами структуры или после последнего элемента структуры, либо к тому и другому вместе.

19
ответ дан 30 November 2019 в 10:19
поделиться

Определенно было бы безопаснее сделать:

sizeof(foo) * SOME_NUM
22
ответ дан 30 November 2019 в 10:19
поделиться

Если вы копируете свой массив таким образом, вы должны использовать

memcpy(pBuff,listOfFoos,sizeof(listOfFoos));

Это всегда будет работать, пока вы назначили pBuff того же размера. Таким образом, вы вообще не делаете никаких предположений о заполнении и выравнивании.

Большинство компиляторов выравнивают структуру или класс в соответствии с требуемым выравниванием самого большого включенного типа. В вашем случае с символами это означает отсутствие выравнивания и заполнения, но если вы добавите сокращение, например, ваш класс будет иметь размер 6 байтов с одним байтом заполнения, добавленным между последним символом и вашим сокращением.

6
ответ дан 30 November 2019 в 10:19
поделиться

I think the reason that this works because all of the fields in the structure are char which align one. If there is at least one field that does not align 1, the alignment of the structure/class will not be 1 (the alignment will depends on the field order and alignment).

Let see some example:

#include <stdio.h>
#include <stddef.h>

typedef struct {
    unsigned char a;
    unsigned char b;
    unsigned char c;
} Foo;
typedef struct {
    unsigned short i;
    unsigned char  a;
    unsigned char  b;
    unsigned char  c;
} Bar;
typedef struct { Foo F[5]; } F_B;
typedef struct { Bar B[5]; } B_F;


#define ALIGNMENT_OF(t) offsetof( struct { char x; t test; }, test )

int main(void) {
    printf("Foo:: Size: %d; Alignment: %d\n", sizeof(Foo), ALIGNMENT_OF(Foo));
    printf("Bar:: Size: %d; Alignment: %d\n", sizeof(Bar), ALIGNMENT_OF(Bar));
    printf("F_B:: Size: %d; Alignment: %d\n", sizeof(F_B), ALIGNMENT_OF(F_B));
    printf("B_F:: Size: %d; Alignment: %d\n", sizeof(B_F), ALIGNMENT_OF(B_F));
}

When executed, the result is:

Foo:: Size: 3; Alignment: 1
Bar:: Size: 6; Alignment: 2
F_B:: Size: 15; Alignment: 1
B_F:: Size: 30; Alignment: 2

You can see that Bar and F_B has alignment 2 so that its field i will be properly aligned. You can also see that Size of Bar is 6 and not 5. Similarly, the size of B_F (5 of Bar) is 30 and not 25.

So, if you is a hard code instead of sizeof(...), you will get a problem here.

Hope this helps.

5
ответ дан 30 November 2019 в 10:19
поделиться

Все сводится к выравниванию памяти. Типичные 32-битные машины читают или записывают 4 байта памяти за попытку. Эта структура защищена от проблем, потому что она легко подпадает под эти 4 байта без запутанных проблем с заполнением.

Теперь, если бы структура была такой:

class foo {
   unsigned char a;
   unsigned char b;
   unsigned char c;
   unsigned int i;
   unsigned int j;
}

Логика ваших коллег, вероятно, привела бы к

memcpy(pBuff,listOfFoos,11*SOME_NUM);

(3 символа = 3 байта , 2 ints = 2 * 4 байта, поэтому 3 + 8)

К сожалению, из-за заполнения структура фактически занимает 12 байтов. Это связано с тем, что вы не можете вместить три символа и int в это 4-байтовое слово, и поэтому там есть один байт заполненного пространства, который вставляет int в его собственное слово. Это становится все более серьезной проблемой по мере того, как становятся более разнообразными типы данных.

2
ответ дан 30 November 2019 в 10:19
поделиться

Я бы перестраховался и заменил магическое число 3 на sizeof (foo) , как мне кажется.

Я предполагаю, что этот код оптимизирован для будущих архитектур процессоров вероятно, будет введена какая-то форма заполнения.

И пытаться отследить такую ​​ошибку - настоящая боль!

2
ответ дан 30 November 2019 в 10:19
поделиться

Как уже говорили другие, использование sizeof (foo) является более безопасной ставкой. Некоторые компиляторы (особенно эзотерические во встроенном мире) добавляют к классам 4-байтовый заголовок. Другие могут проделывать забавные трюки с выравниванием памяти, в зависимости от настроек вашего компилятора.

Для основной платформы вы, вероятно, в порядке, но это не гарантия.

1
ответ дан 30 November 2019 в 10:19
поделиться

В ситуациях, когда используются подобные вещи, и я не могу этого избежать, я пытаюсь прервать компиляцию, когда предположения больше не выполняются. Я использую что-то вроде следующего (или Boost.StaticAssert , если позволяет ситуация):

static_assert(sizeof(foo) <= 3);

// Macro for "static-assert" (only usefull on compile-time constant expressions)
#define static_assert(exp)           static_assert_II(exp, __LINE__)
// Macro used by static_assert macro (don't use directly)
#define static_assert_II(exp, line)  static_assert_III(exp, line)
// Macro used by static_assert macro (don't use directly)
#define static_assert_III(exp, line) enum static_assertion##line{static_assert_line_##line = 1/(exp)}
2
ответ дан 30 November 2019 в 10:19
поделиться
Другие вопросы по тегам:

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