Почему компилятор выделяет больше места, чем sizeof (MyClass) для объекта в стеке? [Дубликат]

Простой способ нарисовать текст в текущем графическом контексте - это draw(at:withAttributes:) NSString.

Пример:

class MyView : NSView {
    override func draw(_ dirtyRect: NSRect) {
        let atts = [NSFontAttributeName:NSFont.init(name: "Georgia", size: 30)]
        ("Hello world" as NSString).draw(
             at: NSMakePoint(100,100), 
             withAttributes: atts)
    }
}

Результат:

39
задан Stu Thompson 11 November 2009 в 16:19
поделиться

4 ответа

Выравнивание переменных в памяти (короткая история).

На прошлых компьютерах была 8-битная шина данных. Это означает, что каждый такт 8 бит информации может быть обработан. Тогда это было хорошо.

Затем появились 16-разрядные компьютеры. Из-за нисходящей совместимости и других проблем был сохранен 8-битный байт, и было введено 16-битное слово. Каждое слово было 2 байта. И каждый такт 16 бит информации может быть обработан. Но это создавало небольшую проблему.

Давайте посмотрим на карту памяти:

+----+
|0000| 
|0001|
+----+
|0002|
|0003|
+----+
|0004|
|0005|
+----+
| .. |

. На каждом адресе есть байт, к которому можно получить доступ индивидуально. Но слова могут быть получены только по четным адресам. Поэтому, если мы читаем слово в 0000, мы читаем байты в 0000 и 0001. Но если мы хотим прочитать слово в позиции 0001, нам нужны два обращения к чтению. Сначала 0000,0001, а затем 0002 0003, и мы сохраняем только 00010002.

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

Например, если у нас есть структура с байтовым полем (B) и полем слов (W) (и очень наивный компилятор ), получаем следующее:

+----+
|0000| B
|0001| W
+----+
|0002| W
|0003|
+----+

Это не весело. Но при использовании выравнивания слов мы находим:

+----+
|0000| B
|0001| -
+----+
|0002| W
|0003| W
+----+

Здесь память приносится в жертву для скорости доступа.

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

97
ответ дан derpda 21 August 2018 в 09:10
поделиться
  • 1
    Отличное описание выравнивания стека! – Shaun Bouckaert 23 March 2009 в 08:42
  • 2
    Я пытаюсь учиться сбору, и я изо всех сил пытаюсь понять выравнивание. Это полностью отвечает на мои вопросы! – joek1975 13 January 2010 в 17:15
  • 3
    Всегда рад помочь кому-то :-). – Toon Krijthe 13 January 2010 в 20:49
  • 4
    Это объясняет очень приятно, почему массив слов должен быть выровнен. Поскольку для доступа к определенному элементу в противном случае потребовалось бы два чтения. Но в примере с ударом, содержащим байт и слово: если вы читаете полную структуру, то в обоих случаях вы должны прочитать оба слова в любом случае. – Michael Lehn 26 April 2014 в 15:58

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

// Some compilers won't align this as it's on the stack...
int __declspec(align(32)) needsToBe32Aligned = 0;
// Change to
static int __declspec(align(32)) needsToBe32Aligned;
needsToBe32Aligned = 0;

В качестве альтернативы найдите компилятор, который выравнивает переменные в стеке. Очевидно, что синтаксис выравнивания «__declspec», который я использовал здесь, может быть не таким, каким использует ваш компилятор.

2
ответ дан Dan Olson 21 August 2018 в 09:10
поделиться
  • 1
    Компиляторы do сопоставляют переменные в стеке с требованиями / требованиями к выравниванию для этого типа, указанного в ABI. Обычно это означает естественное выравнивание: alignment = width, поэтому 4-байтовый int получает 4-байтовое выравнивание. Поддержание выравнивания по 16 байт для самого указателя стека позволяет выравнивать переменные в стеке на 16, 8, 4 или 2 без дополнительных затрат. – Peter Cordes 31 March 2018 в 01:02

IIRC, выравнивание стека - это когда переменные помещаются в стек, «выровненный» с определенным количеством байтов. Поэтому, если вы используете выравнивание по 16 бит, каждая переменная в стеке начнет с байта, который состоит из двух байтов из текущего указателя стека внутри функции.

Это означает, что если вы используете переменную, которая равна & lt; 2 байта, например char (1 байт), между ним и следующей переменной будет 8 бит неиспользуемого «дополнения». Это позволяет определенные оптимизации с допущениями на основе местоположений переменных.

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

Кажется, что скомпилированный код msvc не согласен выравнивание переменных. Попробуйте выполнить компиляцию при выключенных оптимизациях.

10
ответ дан MSalters 21 August 2018 в 09:10
поделиться
  • 1
    sizeof (char) всегда 1 байт, который всегда не менее 8 бит ... не байтов. Выравнивание зависит от платформы компилятора, и (x86, в любом случае), как правило, 4 байта для 32-битных архитектур, 8 байт для 64-битных арков. – snemarch 23 March 2009 в 08:52
  • 2
    Спасибо, был мозгом действительно размером с байт: P. Я выбрал 16 байтов в качестве произвольного примера, но использование меньшего примера делает его более понятным. – Shaun Bouckaert 23 March 2009 в 20:54
  • 3
    Нет, выравнивание стека заключается в поддержании выравнивания самого указателя стека. Однобайтовые локальные переменные в стеке могут быть по любому адресу. Если есть только один, то перед следующей переменной будет добавление, потому что большинство ABI выравнивают основные типы, такие как int, до их собственной ширины (естественное выравнивание). Только для прохода arg в стеке являются однобайтовыми объектами, дополненными «шириной стека». или слот (размер одной команды push). – Peter Cordes 31 March 2018 в 00:40

Некоторые архитектуры процессоров требуют конкретного выравнивания различных типов данных и будут генерировать исключения, если вы не соблюдаете это правило. В стандартном режиме x86 не требует этого для базовых типов данных, но может пострадать от штрафов за производительность (см. Www.agner.org для советов по оптимизации низкого уровня).

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

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

] EDIT : что касается исключения, то процедура в DLL, вероятно, хочет использовать инструкции SSE для некоторых временных данных стека и терпит неудачу, потому что два разных компилятора не согласны с вызовами конвенций.

11
ответ дан snemarch 21 August 2018 в 09:10
поделиться
Другие вопросы по тегам:

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