Вопрос, связанный с выравниванием - PullRequest
2 голосов
/ 16 февраля 2010

Является ли хорошей практикой группировать все переменные одного типа вместе, объявляя в локальной области внутри функции? Если да, то почему? Решает ли это проблемы с выравниванием памяти?

Ответы [ 5 ]

6 голосов
/ 16 февраля 2010

Я думаю, что это имело значение с компилятором VAX C, который я использовал 20 лет назад, но не с каким-либо современным компилятором. не безопасно предполагать, что локальные переменные будут в каком-то определенном порядке, конечно, небезопасно предполагать, что они будут в том порядке, в котором вы их объявили. Я определенно видел, как компилятор MSVC переупорядочивает их.

Группирование переменных одного типа помогает , когда они являются полями структуры, поскольку порядок полей структуры гарантированно соответствует порядку объявления.

3 голосов
/ 16 февраля 2010

зависит от компилятора; то есть компилятор будет размещать память по своему усмотрению. Так что, кроме хорошего стиля, он не имеет никакого эффекта (по крайней мере, в любых современных компиляторах, которые я использовал)

1 голос
/ 16 февраля 2010

В общем случае это не поможет для локальных переменных. Существуют правила оптимизации, которые могут применяться компилятором, и дополнительные директивы "pragma", которые можно использовать для управления выравниванием.

1 голос
/ 16 февраля 2010

Это не решит проблемы с выравниванием, поскольку проблем с выравниванием быть не должно - компилятор правильно выровняет ваши локальные переменные, поэтому проблем с выравниванием быть не должно.

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

Если вы собираетесь «печатать» элементы в стеке, вам нужно будет использовать те же методы для безопасности выравнивания, которые вы использовали бы для данных вне стека - возможно, больше, так как память выделяется malloc() или new гарантированно выровнены соответствующим образом для любого типа - эта гарантия не предоставляется для хранилища, выделенного для автоматических переменных.

«Наказание по типу» - это когда вы обходите систему типов. например, путем доступа к байтам в массиве char как int путем приведения char* к int*:

int x;
char data[4];

fill_data( data, sizeof(data));

int x = *(int*) data;

Поскольку требование выравнивания char[] может отличаться от int, вышеуказанный доступ от data к int* может быть не «безопасным для выравнивания». Однако, поскольку malloc() указано для возврата указателей, соответствующим образом выровненных для любого типа, следующие не должны иметь проблем с выравниванием:

int x;
char* pData = malloc( 4);

if (!pData) exit(-1);

fill_data( pData, 4);

x = *(int*) pData;

Тем не менее, обратите внимание, что sizeof(int) может не иметь значения 4, а типы int могут быть с прямым или младшим порядком байтов, поэтому в приведенном выше коде все еще есть проблемы с переносимостью, но не проблемы с выравниванием. Существуют и другие способы выполнения типов, в том числе доступ к данным через различные члены union, но у них могут быть свои собственные проблемы с переносимостью, в частности, то, что доступ к члену, который не был последним записанным членом, является неопределенным поведением.

0 голосов
/ 16 февраля 2010

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

Многие компиляторы C выравнивают элементы структуры, вставляя между ними байты заполнения. Например, если у вас есть структура S {int a; символ b; int c; char d; int e; }, и целевое оборудование требует, чтобы целые числа были выровнены по 4-байтовым границам, тогда компилятор вставит три байта заполнения между b и c и между d и e, тратя впустую 6 байтов памяти на экземпляр. С другой стороны, если элементы были в порядке ac, e, b, d, то он вставит два байта заполнения в конце (так, чтобы размер S в целом был кратен 4, поэтому члены будут быть правильно выровненным в массивах), тратя только 2 байта на экземпляр. Правила очень сильно зависят от платформы и компилятора; некоторые компиляторы могут переставлять элементы, чтобы избежать заполнения, а некоторые имеют расширения для управления правилами заполнения и выравнивания в случае, если вам нужна двоичная совместимость. В общем, вы должны заботиться о выравнивании, только если вы либо читаете / пишете структуры напрямую и в зависимости от того, что они имеют одинаковую структуру (что обычно является плохой идеей), либо ожидаете, что у вас будет много экземпляров, а память стоит дорого .

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