Обучение
Модуль
Выбор правильного типа данных в коде на C# - Training
Выберите правильный тип данных для кода из нескольких основных типов, используемых в C#.
Этот браузер больше не поддерживается.
Выполните обновление до Microsoft Edge, чтобы воспользоваться новейшими функциями, обновлениями для системы безопасности и технической поддержкой.
В этом разделе описывается базовый двоичный интерфейс приложения (ABI) для x64, 64-разрядное расширение архитектуры x86. В ней рассматриваются такие темы, как соглашение о вызовах, макет типов, стек и регистрация использования и многое другое.
Два важных различия между x86 и x64:
Учитывая расширенный набор регистров, архитектура x64 использует соглашение о вызовах __fastcall и модель обработки исключений на основе RISC.
Соглашение __fastcall
использует регистры для первых четырех аргументов и кадр стека для передачи дополнительных аргументов. Подробные сведения о соглашении о вызовах x64, в том числе об использовании регистров, параметрах стека, возвращаемых значениях и раскрутке стека, см. в статье Соглашение о вызовах x64.
Дополнительные сведения о соглашении __vectorcall
о вызовах см. в статье __vectorcall
.
Следующий параметр компилятора позволяет оптимизировать приложение для архитектуры x64:
В этом разделе описывается хранилище типов данных для архитектуры x64.
Хотя можно получить доступ к данным с любым выравниванием, выровнять данные по своей естественной границе или несколько ее естественной границы, чтобы избежать потери производительности. Перечисления — это целочисленные константы, которые обрабатываются как 32-разрядные целые числа. В таблице ниже представлены определения типов и рекомендуемое место для хранения данных при использовании следующих значений выравнивания:
Скалярный тип | Тип данных C | Размер хранилища (в байтах) | Рекомендуемое выравнивание |
---|---|---|---|
INT8 |
char |
1 | Байт |
UINT8 |
unsigned char |
1 | Байт |
INT16 |
short |
2 | Word |
UINT16 |
unsigned short |
2 | Word |
INT32 |
int , long |
4 | Двойное слово |
UINT32 |
unsigned int , unsigned long |
4 | Двойное слово |
INT64 |
__int64 |
8 | Учетверенное слово |
UINT64 |
unsigned __int64 |
8 | Учетверенное слово |
FP32 (одиночная точность) |
float |
4 | Двойное слово |
FP64 (двойная точность) |
double |
8 | Учетверенное слово |
POINTER |
* | 8 | Учетверенное слово |
__m64 |
struct __m64 |
8 | Учетверенное слово |
__m128 |
struct __m128 |
16 | Увосьмеренное слово |
Другие типы, такие как массивы, структуры и объединения, предъявляют более строгие требования к выравниванию, обеспечивающие согласованное извлечение статических выражений и объединений. Ниже приведены определения для массивов, структур и объединений.
Массив
Содержит упорядоченную группу смежных объектов данных. Каждый объект называется элементом. Все элементы массива имеют одинаковый размер и тип данных.
Структура
Содержит упорядоченную группу объектов данных. В отличие от элементов массива, элементы структуры могут иметь различные типы и размеры данных.
Объединение
Объект, содержащий любой из наборов именованных элементов. Элементы именованного набора могут быть любого типа. Объем памяти, выделяемый для объединения, равен объему памяти, необходимому для хранения наибольшего его элемента, к которому прибавляется заполнение, требуемое для выравнивания.
В следующей таблице показано настоятельно рекомендуемое выравнивание скалярных членов профсоюзов и структур.
Скалярный тип | Тип данных в C | Требуемое выравнивание |
---|---|---|
INT8 |
char |
Байт |
UINT8 |
unsigned char |
Байт |
INT16 |
short |
Word |
UINT16 |
unsigned short |
Word |
INT32 |
int , long |
Двойное слово |
UINT32 |
unsigned int , unsigned long |
Двойное слово |
INT64 |
__int64 |
Учетверенное слово |
UINT64 |
unsigned __int64 |
Учетверенное слово |
FP32 (одиночная точность) |
float |
Двойное слово |
FP64 (двойная точность) |
double |
Учетверенное слово |
POINTER |
* | Учетверенное слово |
__m64 |
struct __m64 |
Учетверенное слово |
__m128 |
struct __m128 |
Увосьмеренное слово |
В отношении выравнивания статистического выражения действуют следующие правила:
Выравнивание массива совпадает с выравниванием одного из его элементов.
Выравнивание начала структуры или объединения соответствует максимальному выравниванию любого отдельного элемента. Каждый элемент структуры или объединения должен быть правильно выровнен в соответствии с приведенной выше таблицей, для чего может потребоваться неявное внутреннее заполнение в зависимости от предыдущего элемента.
Размер структуры должен быть кратен ее выравниванию, для чего может потребоваться заполнение после последнего элемента. Так как структуры и объединения могут объединяться в массивы, каждый элемент массива, представляющий собой структуру или объединение, должен начинаться и заканчиваться в ранее определенной позиции выравнивания.
Вы можете выровнять данные таким образом, чтобы быть больше, чем требования выравнивания до тех пор, пока сохраняются предыдущие правила.
Отдельный компилятор может настроить упаковку структуры с целью ограничения ее размера. Например, /Zp (выравнивание элементов структуры) позволяет настраивать упаковку структур.
В каждом из приведенных ниже четырех примеров объявляется выровненная структура или выровненное объединение, а на соответствующих рисунках показано размещение структуры или объединения в памяти. Каждый столбец на рисунке представляет байт памяти, а число в столбце соответствует смещению этого байта. Имя во второй строке на каждом рисунке соответствует имени переменной в объявлении. Затененные столбцы обозначают заполнение, необходимое для обеспечения указанного выравнивания.
// Total size = 2 bytes, alignment = 2 bytes (word).
_declspec(align(2)) struct {
short a; // +0; size = 2 bytes
}
// Total size = 24 bytes, alignment = 8 bytes (quadword).
_declspec(align(8)) struct {
int a; // +0; size = 4 bytes
double b; // +8; size = 8 bytes
short c; // +16; size = 2 bytes
}
// Total size = 12 bytes, alignment = 4 bytes (doubleword).
_declspec(align(4)) struct {
char a; // +0; size = 1 byte
short b; // +2; size = 2 bytes
char c; // +4; size = 1 byte
int d; // +8; size = 4 bytes
}
// Total size = 8 bytes, alignment = 8 bytes (quadword).
_declspec(align(8)) union {
char *p; // +0; size = 8 bytes
short s; // +0; size = 2 bytes
long l; // +0; size = 4 bytes
}
Битовые поля структуры ограничены 64 битами и могут иметь тип signed int, unsigned int, int64 или unsigned int64. При пересечении границы типа битовыми полями будут пропускаться биты для выравнивания поля по следующей границе. Например, целые битовые поля могут не пересекать 32-разрядную границу.
Типы данных, размер которых превышает 4 байта, не выравниваются автоматически в стеке при использовании компилятора x86 для компиляции приложения. Так как архитектура компилятора x86 — это 4 байтовой стек, все, что больше 4 байта, например 64-разрядное целое число, не может быть автоматически выровнено с 8-байтовым адресом.
Работа с невыровненными данными имеет два последствия.
Доступ к невыровненным данным может осуществляться медленнее, чем к выровненным.
Неуправляемые расположения нельзя использовать в переблокированных операциях.
Если требуется более строгое выравнивание, используйте __declspec(align(N))
в объявлениях переменных. В результате компилятор будет динамически выравнивать стек в соответствии с вашими спецификациями. Однако динамическая настройка стека во время выполнения может привести к более медленному выполнению приложения.
Архитектура x64 предоставляет 16 регистров общего назначения (которые далее называются целочисленными регистрами), а также 16 регистров XMM/YMM для значений с плавающей запятой. Переменные регистры — это оперативные регистры, которые вызывающий объект считает очищаемыми во время вызова. Неизменяемые регистры должны сохранять свое значение во время вызова функции и при использовании должны сохраняться вызываемым объектом.
Следующая таблица описывает использование каждого из регистров в вызовах функций.
Регистр | Состояние | Использование |
---|---|---|
RAX | Переменный | Регистр возвращаемого значения |
RCX | Переменный | Первый целочисленный аргумент |
RDX | Переменный | Второй целочисленный аргумент |
R8 | Переменный | Третий целочисленный аргумент |
R9 | Переменный | Четвертый целочисленный аргумент |
R10:R11 | Переменный | Должен сохраняться вызывающим объектом; используется в инструкциях syscall/sysret. |
R12:R15 | Неизменяемый | Должен сохраняться вызываемым объектом. |
RDI | Неизменяемый | Должен сохраняться вызываемым объектом. |
RSI | Неизменяемый | Должен сохраняться вызываемым объектом. |
RBX | Неизменяемый | Должен сохраняться вызываемым объектом. |
RBP | Неизменяемый | Может использоваться в качестве указателя фрейма; должен сохраняться вызываемым объектом. |
RSP | Неизменяемый | Указатель стека |
XMM0, YMM0 | Переменный | Первый аргумент FP; первый аргумент векторного типа при использовании __vectorcall |
XMM1, YMM1 | Переменный | Второй аргумент FP; второй аргумент векторного типа при использовании __vectorcall |
XMM2, YMM2 | Переменный | Третий аргумент FP; третий аргумент векторного типа при использовании __vectorcall |
XMM3, YMM3 | Переменный | Четвертый аргумент FP; четвертый аргумент векторного типа при использовании __vectorcall |
XMM4, YMM4 | Переменный | Должен сохраняться вызывающим объектом; пятый аргумент векторного типа при использовании __vectorcall |
XMM5, YMM5 | Переменный | Должен сохраняться вызывающим объектом; шестой аргумент векторного типа при использовании __vectorcall |
XMM6:XMM15, YMM6:YMM15 | Неизменяемый (XMM), переменный (верхняя половина YMM) | Должен сохраняться вызываемым объектом. Регистры YMM должны сохраняться вызывающим объектом. |
При выходе из функций и входе функций в вызовы библиотеки времени выполнения C и системные вызовы Windows флаг направления в регистре флагов ЦП должен сбрасываться.
Подробные сведения о выделении стека, выравнивании, типах функций и кадрах стека в архитектуре x64 см. в статье Использование стека для 64-разрядных систем.
Каждая функция, которая выделяет пространство стека, вызывает другие функции, сохраняет энергонезависимые регистры или использует обработку исключений, должна содержать пролог с ограничениями адресов, описанными в данных очистки, связанных с соответствующей записью таблицы функций, и эпилоги при каждом выходе из функции. Подробные сведения о необходимом коде пролога и эпилога в архитектуре x64 см. в статье Пролог и эпилог для 64-разрядных систем.
Сведения о соглашениях и структурах данных для реализации структурированной обработки исключений и обработки исключений C++ в архитектуре x64 см. в статье Обработка исключений в 64-разрядных системах.
Одно из ограничений компилятора x64 не поддерживает встроенный сборщик. Это означает, что функции, которые не могут быть записаны в C или C++, должны быть записаны как вложенные или как встроенные функции, поддерживаемые компилятором. Некоторые функции чувствительны к производительности, а другие — нет. Функции, для которых производительность важна, должны реализовываться как встроенные.
Встроенные компоненты, поддерживаемые компилятором, описаны в встроенных функциях компилятора.
Формат исполняемого образа x64 — PE32+. Размер исполняемых образов (как DLL, так и EXE) ограничен 2 гигабайтами, поэтому для адресации статических данных образов можно использовать относительную адресацию с 32-битным смещением. Эти данные включают в себя таблицу адресов импорта, строковые константы, статические глобальные данные и т. д.
Обучение
Модуль
Выбор правильного типа данных в коде на C# - Training
Выберите правильный тип данных для кода из нескольких основных типов, используемых в C#.