Сколько байт занимает тип Word

Все простые типы, за исключением вещественных, называются порядковыми типами. Для величин порядковых типов определены стандартные процедуры и функции: Dec, Inc, Ord, Pred, Succ (см. п. 13.1).

.1. Целые типы

В отличие от языка Паскаль, где определен единственный целый тип Integer, в Turbo Pascal имеется пять стандартных типов целых чисел: Shortint, Integer, Longint, Byte, Word. Характеристики этих типов приведены в табл. 2.

Таблица 2. Целые типы данных

ТипДиапазонФорматРазмер в байтах
Shortint-128 .. 127Знаковый1
Integer-32768 .. 32767Знаковый2
Longint-2147483648 .. 2147483647Знаковый4
Byte0 .. 255Беззнаковый1
Word0 .. 65535Беззнаковый2

.2. Логический тип

Стандартный логический тип Boolean (размер — 1 байт) представляет собой тип данных, любой элемент которого может принимать лишь два значения: True и False. При этом справедливы следующие условия: False

.3. Символьный тип

Стандартный символьный тип Char определяет полный набор ASCII-символов. Функция Ord от величины типа Char дает код соответствующего символа. Сравниваются величины символьного типа по своим кодам.

.4. Перечисляемый тип

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

Важно, в каком порядке перечислены идентификаторы при определении типа, т. к. первому идентификатору присваивается порядковый номер 0, второму — 1 и т. д. Один и тот же идентификатор можно использовать в определении только одного перечисляемого типа. Функция Ord от величины перечисляемого типа дает порядковый номер ее значения.

Пример. Перечисляемый тип.

type Operat = (Plus, Minus, Mult, Divide);

Логический тип является частным случаем перечисляемого типа:

type Boolean = (False, True);

.5. Тип-диапазон

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

Минимальное значение при определении такого типа не должно быть больше максимального.

Пример. Определение типов-диапазонов.

type Dozen = 1..12; AddSub = Plus..Minus;

.6. Вещественные типы

В отличие от стандарта языка Паскаль, где определен только один вещественный тип Real, в Turbo Pascal имеется пять стандартных вещественных типов: Real, Single, Double, Extended, Соmр. Характеристики этих типов см. в табл. 3. Таблица 3. Вещественные типы данных

ТипДиапазонЧисло значащих цифрРазмер в байтах
Real2.9*10-39..1.7*103811-126
Single1.5*10-45..3.4*10387-84
Double5.0*10-324.-1.7*1030815-168
Extended3.4*10-4932..1.1*10493219-2010
Comp-263+1..263-119-208

Тип Comp фактически является типом целых чисел увеличенного диапазона, однако порядковым не считается.

Типы Single, Double, Extended и Comp можно использовать в программах только при наличии арифметического сопроцессора или при включенном эмуляторе сопроцессора (см. пп. 17.5.8 и 17.7.1).

Работа с данными и памятью

Все используемые в программе данные имеют определенный тип, который определяется разрядностью данных. Архитектура Intel x86-64 поддерживает следующие типы данных:

  • byte : 8-разрядное целое число
  • word : 16-разрядное целое число или слово
  • dword : 32-разрядное целое число или двойное слово
  • qword : 64-разрядное целое число или четверное слово

Например, 8-разрядный регистр AL по сути представляет байт, 16-разрядный AX — слово, 32-разрядный EAX — двойное слово, а 64-разрядный регистр RAX — четверное слово.

Определение данных в программе

Ассемблер NASM позволяет определять в программе объекты, которые хранят некоторые данные, и на протяжении программы мы можем использовать эти данные, изменять их. Подобные данные еще называются переменными. Объявления данных имеют следующую форму:

re>label directive value

label или метка представляет название переменной, которая может представлять произвольный идентифкатор. После названия переменной идет directive — директива, которая устанавливает тип данных. Это может быть одна из следующих директив:

  • db (define byte): определяет целое число размером в 1 байт
  • dw (define word): определяет целое число размером в 2 байта (слово)
  • dd (define dword): определяет целое число размером в 4 байта (так называемое двойное слово)
  • dq (define qword/quad): определяет целое число размером в 8 байт (четверное слово)

После директивы данных идет собственно значение. Например:

re>number dq 22

Стоит отметить, что после названия переменной можно указывать двоеточие, как для обычной метки, но оно в принципе необязательно:

re>number: dq 22

Здесь определена переменная number, которая имеет тип qword , то есть представляет 8 байт, и которая равна 22.

С каждой переменной ассемблер будет ассоциировать некоторый свободный участок памяти. Например, переменная number имеет тип qword и занимает 8 байт, соответственно ассемблер найдет в памяти свободный 8 байт и ассоциирует их с этой переменной.

Расположение и использование данных

Где определяются данные? В программе на ассемблере можно опредлять данные в различных секциях.

Определение данных в секции .text

Например, в секции кода .text :

re>global _start section .text number: dq 123 ; определяем объект number внутри секции .text _start: ; остальной код программы

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

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

Стоит сразу сказать, что имя переменной, например, "number", само по себе представляет ее адрес, а не непосредственное значение. Например, возьмем следующую программу на Linux:

re>global _start section .text number: dq 124 ; определяем объект number внутри секции .text _start: mov rdi, number mov rax, 60 syscall

Здесь в регистр RDI помещается адрес переменной number, но не само значение. Чтобы получить значение, надо поместить название переменной в квадратные скобки:

re>[название_переменной]

Например, возьмем следующую программу на Linux :

re>global _start section .text number: dq 124 ; определяем объект number внутри секции .text _start: mov rdi, [number] ; rdi = 124 mov rax, 60 syscall

Инструкция mov rdi, [number] помещает в регистр rdi значение переменной number, то есть число 124.

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

re>global _start section .text number: db 124 ; определяем объект number внутри секции .text _start: mov rax, [number] ret

При использовании компоновщика ld , который идет в комплекте GCC, мы столкнемся с ошибкой релокаций:

re>c:asm>nasm -f win64 hello.asm -o hello.o c:asm>ld hello.o -o hello.exe hello.o:hello.asm:(.text+0x5): relocation truncated to fit: IMAGE_REL_AMD64_ADDR32 against `.text’ c:asm>

Если мы используем компоновщик от Microsoft — link.exe , то мы также столкнемся с ошибкой релокаций, хотя само сообщение об ошибке будет выглядеть несколько иначе:

re>c:asm>nasm -f win64 hello.asm -o hello.o c:asm>link hello.o /entry:_start /subsystem:console /out:hello.exe Microsoft (R) Incremental Linker Version 14.37.32824.0 Copyright (C) Microsoft Corporation. All rights reserved. hello.o : error LNK2017: ‘ADDR32’ relocation to ‘.text’ invalid without /LARGEADDRESSAWARE:NO LINK : fatal error LNK1165: link failed because of fixup errors c:asm>

Суть ошибки: мы пытаемся создать 64-разрядное приложение, которое использует 32-разрядные адреса. В принципе ошибка подсказывает один из вариантов решений — добавить к вызову компоновщика дополнительную опцию — /LARGEADDRESSAWARE:NO :

re>link hello.o /entry:_start /subsystem:console /out:hello.exe /LARGEADDRESSAWARE:NO

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

re>[rel название_переменной]

Так, изменим программу для Windows:

re>global _start section .text number: dq 124 ; определяем объект number внутри секции .text _start: mov rax, [rel number] ; используем адрес number относительно регистра RIP ret

Теперь при использовании компоновщика от Microsoft у нас не возникнет проблем:

re>c:asm>nasm -f win64 hello.asm -o hello.o c:asm>link hello.o /entry:_start /subsystem:console /out:hello2.exe Microsoft (R) Incremental Linker Version 14.37.32824.0 Copyright (C) Microsoft Corporation. All rights reserved. c:asm>hello2.exe c:asm>echo 124 124 c:asm>

Тем не менее при использовании компоновщика ld от GCC мы хоть и скомпонуем программу, но получим некорреткные результаты:

re>c:asm>nasm -f win64 hello.asm -o hello.o c:asm>ld hello.o -o hello.exe c:asm>hello.exe c:asm>echo -1073741819 -1073741819 c:asm>

Таким образом, в зависимости от используемого компоновщика мы можем получить разные результаты. Мы можем играть с параметрами компоновщиков, но в любом случае секция .text не слишком хорошо подходит для хранения данных.

Секция .data

Секция данных задается с помощью директивы .data . Эта директива сообщает ассемблеру, что дальше идут объявления данных. Например, программа на Linux:

re>global _start section .data ; определение секции .data number dq 125 ; определяем объект number внутри секции .data section .text _start: mov rdi, [number] ; получаем данные из секции .data mov rax, 60 syscall

Аналогичная программа для Windows:

re>global _start section .data ; определение секции .data number dq 125 ; определяем объект number внутри секции .data section .text _start: mov rax, [rel number] ret

Стоит отметить, что в отличие от программы для Linux, здесь применяется оператор rel . Тем не менее и компоновщик ld от GCC, и компоновщик от Microsoft успешно скомпонуют данную программу.

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

re>section .data i64 dq 8 i32 dd 4 i16 dw 2 i8 db 1

Для переменной i8 выделяется 1 байт, для i16 — 2 байта, для i32 — 4 байта и для i64 — 8 байт. В итоге для секции .data будет выделен блок памяти, который занимает не менее 15 байт, где переменные будут располагаться следующим образом:

Секция data в ассемблере GNU

Допустим, для переменной i64 выделено 8 байт по адресу 0x0000, тогда i32 располагается по адресу 0x0008, а i16 — по адресу 0x000С и i8 по адресу 0x000E.

Изменение данных

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

re>global _start section .text number dq 124 ; определяем объект number внутри секции .text _start: mov rdx, 111 mov [number], rdx ; пытаемся записать в number число 111 mov rdi, [number] mov rax, 60 syscall

Здесь мы пытаемся поместить значение регистра RDX в переменную number:

re>mov [number], rdx

Однако объект number определен в секции .text , поэтому он не изменяем. И хотя ассемблер может успешно скомпилировать программу, Linux сгенерирует общую ошибку защиты (general protection fault), если мы попытаемся сохранить какие-либо данные в секции кода:

Собственно при определении объектов в секции .text у нас получаются константы — объекты, значения которых нельзя изменить. Поэтому для определения переменных и в целом для определения данных больше подходят другие секции, в частности, секция .data. Изменим программу, перенеся данные в секуцию .data :

re>global _start section .data ; определение секции .data number dq 125 ; определяем объект number внутри секции .data section .text _start: mov rdx, 111 mov [number], rdx ; пытаемся записать в number число 111 mov rdi, [number] mov rax, 60 syscall

Теперь при выполнении проблем не возникнет:

Аналогичный пример для Windows:

re>global _start section .data ; определение секции .data number dq 125 ; определяем объект number внутри секции .data section .text _start: mov rdx, 111 mov [rel number], rdx ; пытаемся записать в number число 111 mov rax, [rel number] ret

Определение массивов данных

Ассемблер NASM позволяет определить набор данных. Если нам известны все элементы набора, то мы их можем перечислить через запятую:

re>nums dq 11, 12, 13, 14, 15, 16, 17

Здесь переменная nums представляет набор из 7 чисел, каждое из которых занимает 8 байтов. При при обращении к этому набору по имени переменной мы фактически обращаемся к первому элементу этого набора:

re>global _start section .data nums dq 11, 12, 13, 14, 15, 16, 17 ; набор из 7-ми 8-разрядных чисел section .text _start: mov rdi, [nums] ; rdi = 11 mov rax, 60 syscall

С помощью директивы times можно определить массив, в котором некоторое значение повторяется определенное количество раз:

re>times количество тип значение

Первый параметр директивы указывает на количество — сколько определить чисел. Второй параметр представляет тип чисел, например, db , dq и т.д. Третий параметр указывает на само значение. Например:

re>numbers: times 10 db 2

Здесь переменная numbers представляет массив из 10 чисел, каждое из которых равно 2 и представляет 1 байт (тип db )

Для упрошения определения наборов большего размера NASM имеет дополнительные директивы resX . Вместо X указывается суффикс, обозначающий тип данных:

  • resb : выделяет некоторое количество байт
  • resw : выделяет некоторое количество слов (2-х байтовых чисел)
  • resd : выделяет некоторое количество двойных слов (4-х байтовых чисел)
  • resq : выделяет некоторое количество четверных слов (8-х байтовых чисел)

В качестве параметра этим директивам передается количество чисел в создаваемом наборе. По умолчанию каждое такое число будет инициализировано нулем. Например:

re>buffer resb 10

Определяет переменную buffer — набор из 10 байт. Другой пример:

re>numbers resq 5

Определяет набор из 5 четверных слов.

Дополнительные секции данных

Кроме собственно секции .data в программе на ассемблере может использоваться еще ряд секций. Секция .rodata содержит данные, которые нельзя изменить (то есть по сути константы). Он загружается в память при загрузке приложения и помечается как доступный только для чтения. Попытки записи в эту память приведут к остановке программы.

re>global _start section .rodata ; определение секции .rodata number dq 125 ; число можно получить, но нельзя изменить section .text _start: mov rdi, [number] ; mov [number], rdi ; при попытке записи — Segmentation fault mov rax, 60 syscall

Секция .bss (сокращение от Block Started by Symbol) содержит неинициализированные данные, для которых известен размер, но неизвестно значение. Это экономит место в исполняемом файле, особенно если здесь большой объем данных. Операционная система инициализирует раздел .bss всеми нулями. Можно зарезервировать данные в разделе .bss, используя директивы resb/resw/resd/resq . Например, если нам нужен массив, значения которого в начале программы не столь важны и который инициализируется уже по ходу программу, например, мы в него загружаем данные из файла, то подобный массив как раз можно определить в секции .bss:

re>global _start section .bss buffer resq 1024 ; массив из 1024-х 8-байтовых чисел section .text _start: mov rdi, [buffer] ; rdi = 0 mov rax, 60 syscall

В данном случае в секции .bss определен массив из 1024-х 8-байтовых чисел, который в сумме занимает 1024 * 8 = 8184 байт. Однако поскольку этот массив размещен в секции .bss , то эти байты не входят в размер файла, они будут выделяться при запуске программы.

Преобразования данных

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

re>global _start section .data number db 12 ; однобайтовое число section .text _start: mov rdi, [number] mov rax, 60 syscall

Здесь значение 8-разрядного числа number помещается в 64-разрядный регистр RDI. При компиляции и выполнении программ у нас не возникнет особых проблем:

Аналогично не возникнет проблем с программой для Windows:

re>global _start section .data number db 12 ; однобайтовое число section .text _start: mov rax, [rel number] ret

re>c:asm>nasm -f win64 hello.asm -o hello.o c:asm>ld hello.o -o hello.exe c:asm>hello.exe c:asm>echo %ERRORLEVEL% 12 c:asm>

Изменим программу для Windows следующим образом:

re>global _start section .data nums db 1, 2, 0, 0, 0, 0, 0, 0 ; 8 однобайтовых числа section .text _start: mov rax, [rel nums] ret

Тепепь nums представляет массив из 8 байтов. Имя переменной — адрес первого элемента массива, соответственно мы ожидаем получить в регистр RAX первый элемент массива — число 1. Однако статусный код возврата покажет что-то не то:

re>c:asm>nasm -f win64 hello.asm -o hello.o c:asm>ld hello.o -o hello.exe c:asm>hello.exe c:asm>echo %ERRORLEVEL% 513 c:asm>

Я специально выбрал версию для Windows, потому что на Linux в принципе мы можем и не столкнуться с этой проблемой. Почему мы получаем число 513?

Число 513 в двоичной системе равно 1000000001 или добавим в начало ненужные нули и получим 00000010_00000001 . Младший байт этого числа 00000001 равен 1, а следующий байт 00000010 в десятичной системе равен 2. То есть 8 байтов массива были последовательно загружены в регистр RAX. Что неудивительно, так как регистр RAX имеет размер 8 байт. Но, допустим, нам надо загрузить только первый один единственный байт. В этом случае нам надо применить преобразования. Преобразования производятся с помощью операторов-названий типов данных:

  • byte : преобразует в байт
  • word : преобразует в слово
  • dword : преобразует в двойное слов
  • qword : преобразует в четверное слово

re>mov rax, [rel nums]

нам надо изменить на строку

re>movzx rax, byte [rel nums]

Выражение byte [rel nums] или byte [nums] получает из массива nums первый байт, а инструкция movzx помещает этот байт в регистр (в младший байт), заполняя остальные байты регистра нулями

Аналогично когда мы помещаем в переменную или по некоторому адресу в памяти непосредственное значение, то нам надо указать размер данных:

re>mov byte [nums], 101

Например, в программе для Linux поместим байт в массив:

re>global _start section .data nums db 12, 13, 14, 15 ; 4 однобайтовых числа число section .text _start: mov byte [nums], 101 ; nums = 101, 13, 14, 15 movzx rdi, byte [nums] ; rdi = 101 mov rax, 60 syscall

Разработка програмного обеспечения

Программирование на Delphi. Типы данных.

При объявлении переменной программисту необходимо указать тип данных. Соглпсно типу данных компилятор определяет как хранить информацию в программе.

Из простых типов данных порядковые — самые простые. В этих типах данных информация представляется в виде отдельных элементов. Связь между отдельными элементами и их представлением в памяти определяет естественные отношения порядка между этими элементами. Отсюда и название порядковые

Одно из ценных свойств порядковых и целых типов является в том, что они состоят из строго определенных значений в неизменном порядке.

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

В переменных целых типов информация представляется в виде целых чисел, не имеющих дробных частей. В Delphi wелые типы подразделяются на физические и логические.

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

Integer Тип данных Integer общий (generic) знаковый тип. На 32 разрядных системах Integer может содержать содержать значения от -2147483648 до 2147483647. Все очень просто, это минимальное и максимальные значения которые можно выразить (закодировать) 32 разрядами.

Int64 Соответственно для 64 разрядов минимальное значение int64 = -9223372036854775808, максимальное значение int64 = 9223372036854775807

Cardinal Тип Cardinal (0..4294967295) это Integer, размер которого не гарантируется. Это основное целое число без знака, и в настоящее время имеет такую же вместимость как LongWord : 32 бита.

ShortInt Тип ShortInt (-128..127) наименьшая форма целого числа со знаком, занимающая 8 бит (1 байт) памяти.

Byte Тип Byte (0..255) является наименьшей формой целого числа, занимая 8 битов (1 байт) памяти.

SmallInt Тип SmallInt (-32768..32767) 16 битное целое число со знаком.

Word Тип Word (0..65535) — целое число, поддерживающее положительные значения до 65535. Это занимает 16 бит памяти.

LongInt Тип LongInt (-2147483648..2147483647) — целое 32 битное число со знаком. Этот размер фиксирован, и не будет изменяться в будущих выпусках Delphi. В настоящее время он имеет тот же самый размер как тип Integer.

LongWord Тип LongWord — целые числа, имеющие положительные значения до 4294967295. Он занимает 32 бита памяти.

Символьные типы обеспечивают хранение отдельных символов.

Char Тип Char (#0..#255) простой тип переменной, используемый для хранения простого символа.

В результате получаем следующее окно

AnsiChar

Тип AnsiChar (#0..#255) используется для содержания простых символов. Гарантируется, что его размер 8 бит.

WideChar

Тип WideChar (#0..#65535) имеет размерность 16 бит, обеспечивая поддержку многобайтовых наборов Интернациональных символов типа Китайского, который содержит большое число символов в своём словаре

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

в Delphi создаются упорядоченные перечени элементов.

С помощью поддиапазонного типа данных на основе стандартных типов (Boolean, Char, Integer) или ранее сформированных (Перечисляемый тип) образуются поддиапазоны значений, которые становятся новыми типами. Рассмотрим создание поддиапазонного типа на примере переменной описывающей температуру воды.

Real Тип Real является устаревшим. Рекомендуется использовать его эквивалент — Double.

Real48 Тип Real48 как и Real так же устарел. Его текущий эквивалент — Extended.

Single Тип Single самый быстрый тип с плавающей запятой в Delphi. Он также имеет самые низкие требования памяти — 32 бита (1 для знака, 8 для экспоненты, и 23 для мантиссы). Он поддерживает приблизительно 7 цифр точности в диапазоне от 1,18 x 10-38 до 3,4 x 1038.

В результате получаем следующее окно

Оцените статью
InternetDoc.ru
Добавить комментарий