Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Типы данных




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

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

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

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

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

· Байт.

· Слово.

· Двойное слово.

· Учетверное слово.

Байт — 8 последовательно расположенных битов, пронумерованных от 0 до 7, при этом бит 0 является самым младшим значащим битом;

Слово — последовательность из 2 байт, имеющих последовательные адреса. Размер слова — 16 бит; биты в слове нумеруются от 0 до 15. Байт, содержащий нулевой бит, называется младшим байтом, а байт, содержащий 15-й бит - старшим байтом. Микропроцессоры Intel имеют важную особенность — младший байт всегда хранится по меньшему адресу. Адресом слова считается адрес его младшего байта. Адрес старшего байта может быть использован для доступа к старшей половине слова.

Двойное слово — последовательность из 4 байт (32 бита), расположенных по последовательным адресам. Нумерация этих бит производится от 0 до 31. Слово, содержащее нулевой бит, называется младшим словом, а слово, содержащее 31-й бит, - старшим словом. Младшее слово хранится по меньшему адресу. Адресом двойного слова считается адрес его младшего слова. Адрес старшего слова может быть использован для доступа к старшей половине двойного слова.

Учетверенное слово — последовательность из 8 байт (64 бита), расположенных по последовательным адресам. Нумерация бит производится от 0 до 63. Двойное слово, содержащее нулевой бит, называется младшим двойным словом, а двойное слово, содержащее 63-й бит, — старшим двойным словом. Младшее двойное слово хранится по меньшему адресу. Адресом учетверенного слова считается адрес его младшего двойного слова. Адрес старшего двойного слова может быть использован для доступа к старшей половине учетверенного слова.

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

Двоичные числа. Целый тип без знака — двоичное значение без знака, размером 8, 16, 32 и 64 бита.

Целый тип со знаком — двоичное значениесо знаком в старшем бите, размеры 8, 16, 32 и 64 бита. Ноль в знаковом бите в операндах соответствует положительному числу, а единица — отрицательному. Отрицательные числа представляются в дополнительном коде.

 

BCD – двоично-десятичное представление. Оно использует двочное представление десятичных цифр 0 – 9. Одна десятичная цифра в двоичном коде требует 4 бита (полбайта). Различают подтипы:

· Неупакованный. Неупакованные десятичные числа хранятся как байтовые значения без знака по одной цифре в каждом байте. Значение цифры определяется младшим полубайтом. Старший полубайт содержит 0000.

· Упакованный. Каждая цифра хранится в своем полубайте. Цифра в старшем полубайте (биты 4–7) является старшей

Указатели на память. Различаются у казатели на память двух типов:

· ближнего типа (NEAR)— 32-разрядный логический адрес, представляющий собой относительное смещение в байтах от начала сегмента. Эти указатели могут также использоваться в сплошной (плоской) модели памяти, где сегментные составляющие одинаковы;

· дальнего типа (FAR) — 48-разрядный логический адрес, состоящий из двух частей: 16-разрядной сегментной части — селектора, и 32-разрядного смещения.

Цепочка.

Цепочка представляет собой некоторый непрерывный набор байтов, слов или двойных слов максимальной длины до 4 Гбайт. Цепочка может содержать набор байтов, слов и двойных слов.

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

Непосредственные данные и данные простого типа являются элементарными, или базовыми; работа с ними поддерживается на уровне системы команд микропроцессора. Используя данные этих типов, можно формализовать и запрограммировать практически любую задачу. Но насколько это будет удобно — вот вопрос.

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

Директивы резервирования и инициализации данных TASM и MASM. В TASM и MASM определены несколько директив резервирования и инициализации данных. Машинного эквивалента этим директивам нет.

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

Директивы резервирования и инициализации данных простых типов имеют формат.

На рисунке использованы следующие обозначения:

·? показывает, что содержимое поля не определено, то есть при задании директивы с таким значением выражения содержимое выделенного участка физической памяти изменяться не будет. Фактически, создается неинициализированная переменная;

· Значение инициализации — значение элемента данных, которое будет занесено в память после загрузки программы. Фактически, создается инициализированная переменная, в качестве которой могут выступать константы, строки символов, константные и адресные выражения в зависимости от типа данных. Подробная информация приведена в приложении 1;

· Имя — некоторое символическое имя метки или ячейки памяти в сегменте данных, используемое в программе.

· Выражение с оператором повторения dup. В памяти резервируется под значение после dup столько раз, сколько определено в количестве повторений.

Например, в переменной типа DWORD можно сохранить любое 32-разрядное целое значение. Однако на некоторые типы накладываются более жесткие ограничения. Например, переменной типа REAL4 можно присвоить только вещественную константу.

Размер резервируемой памяти определяет диапазон представимых данных:

· Для чисел без знака от 0 до 2Биты – 1.

· Для чисел со знаком от -2Биты-1. до 2Биты-1 – 1.

· Для строк это Биты/8. Каждый символ в одном байте.

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

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

· Сегмент (seg) — адрес начала сегмента, содержащего переменную;

· Смещение (offset) в байтах от начала сегмента с переменной;

· Тип (type) — определяет количество памяти, выделяемой переменной в соответствии с директивой объявления переменной.

Получить и использовать значение этих атрибутов в программе можно с помощью рассмотренных нами операторов ассемблера seg, offset и type.

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

[имя] директива инициализатор [,инициализатор]..

Инициализаторы. При определении данных должен быть указан хотя бы один инициализатор, даже если переменной не назначается какого-то конкретного значения (в этом случае значение инициализатора равно?). Все дополнительные инициализаторы перечисляются через запятую. Для целочисленных типов данных инициализатор является целочисленной константой либо выражением, значение которого соответствует размеру определяемых данных (BYTE, WORD, и т.д.).

Независимо от используемого формата чисел, все инициализаторы автоматически преобразовываются ассемблером в двоичную форму. Другими словами, в результате компиляции инициализаторов 00110010b, 32h и 50d будет получено одинаковое двоичное значение.

Определение переменных типа BYTE и SBYTE. Директивы BYTE (определяет беззнаковый байт) и SBYTE (определяет знаковый байт) используются в операторах определения данных, с помощью которых в программе выделяется память под одну или несколько знаковых или беззнаковых переменных длиной 8 битов. Каждый инициализатор должен быть либо 8-разрядным целочисленным выражением или символьной константой. Например:

value1 BYTE 'A'; Символьная константа

value2 BYTE 0; Наименьшее беззнаковое байтовое значение

value3 BYTE 255; Наибольшее беззнаковое байтовое значение

value4 SBYTE -128; Наименьшее знаковое байтовое значение

value5 SBYTE + 127; Наибольшее знаковое байтовое значение

Для наглядности мы выделили ключевые слова BYTE и SBYTE прописными буквами, но вы с тем же успехом можете записать их и строчными буквами.

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

value6 BYTE?

Имена переменных. Имя переменной является меткой, значение которой соответствует смещению данной переменной относительно начала сегмента, в котором она расположена. Например, предположим, что переменная valuel1 расположена в сегменте данных со смещением 0 и занимает один байт памяти. Тогда переменная value2 будет располагаться в том же сегменте со смещением 1:

.data

valuel BYTE 10h

value2 BYTE 20h

Множественная инициализация. Если в одном и том же операторе определения данных используется несколько инициализаторов, то присвоенная этому оператору метка относится только к первому байту данных. В приведенном ниже примере подразумевается, что метке list соответствует смещение 0. Тогда значение 10 располагается со смещением 0 относительно сегмента данных, значение 2 0 — со смешением 1, 3 0 — со смещением 2 и 4 0 — со смещением 3:

.data

list BYTE 10,20,30,40

На рисунке эта последовательность байтов показана наглядно вместе с соответствующим значением смещения.

Определение строк. Чтобы определить в программе текстовую строку, нужно составляющую ее последовательность символов заключить в кавычки. Чаще всего в программах используются так называемые нуль-завершенные (null-terminated) строки, или строки, оканчивающиеся нулевым байтом, т.е. байтом, значение которого равно двоичному нулю. Этот тип строк используется в таких популярных языках программирования, как С/С++? и Java, а также передается в качестве параметров функциям системы Microsoft Windows. Ниже приведен пример нуль-завершенной строки:

Privetstvie BYTE "Добрый день!"

Каждый символ данной строки занимает один байт памяти.

Определение переменных типа WORD и SWORD.

С помощью директив WORD (определить слово) и SWORD (определить слово со знаком) в программах выделяется память для хранения 16-разрядных целых значений. Например:

word1 WORD 65535; Наибольшее беззнаковое значение

word2 SWORD -32768; Наименьшее знаковое значение

word3 WORD 7; Неинициализированное беззнаковое значение

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

myList WORD 1, 2, 3, 4,.5

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

Определение переменных типа DWORD и SDWORD.

С помощью директив DWORD (определить двойное слово) и SDWORD (определить двойное слово со знаком) в программах выделяется память для хранения 32-разрядных целых значений. Например:

val1 DWORD 12345678h; Беззнаковое значение

val2 SDWORD -2147483648; Знаковое значение

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

myList DWORD 1, 2, 3, 4,.5

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

Определение переменных типа QWORD. С помощью директивы QWORD (определить учетверенное слово) в программах выделяется память для хранения 64-разрядных целых значений. Например:

quad1 QWORD 1000000000123456789Ah

Определение переменных типа TBYTE. С помощью директивы TBYTE (определить 10 байтов) в программах выделяется память для хранения 80-разрядных целых значений. Этот тип данных в основном используется для хранения десятичных упакованных целых чисел (двоично-кодированных целых чисел). Для работы с этими числами используется специальный набор команд математического сопроцессора. Вот пример определения: Например:

val1 TBYTE 1000000000123456789Ah

Определение переменных вещественного типа. Директива REAL4 определяет в программе 4-байтовую переменную вещественного типа одинарной точности. Директива REAL8 определяет 8-байтовую переменную вещественного типа двойной точности, а REAL10 — 10-байтовую переменную вещественного типа расширенной точности. После каждой из директив необходимо указать один или один или несколько инициализаторов, значение которых должно соответствовать длине выделяемого участка памяти под переменную:

rval1 REAL4 -2.1 з

rval2 REAL8 3.2E-260

rval3 REAL10 4.6E+4096

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

Данные сложного типа. MASM и TASM поддерживают следующие сложные типы данных:

· массивы;

· структуры;

· объединения;

· записи.

Разберемся более подробно с тем, как определить данные этих типов в программе и организовать работу с ними.

Массив (MASsiv) - структурированный тип данных, состоящий из некоторого числа элементов одного типа.

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

· Перечислением элементов массива в поле операндов одной из директив описания данных. При перечислении элементы разделяются запятыми. К примеру:

; массив из 5 элементов.Размер каждого элемента 4 байта

mas dd 1,2,3,4,5

· Используя оператор повторения dup. К примеру:

; массив из 5 нулевых элементов

; Размер каждого элемента 2 байта

mas dw 5 dup (0)

· Используя директивы label и rept. Пара этих директив может облегчить описание больших массивов в памяти и повысить наглядность такого описания. Директива rept относится к макросредствам языка ассемблера и вызывает повторение указанное число раз строк, заключенных между директивой и строкой endm.

· Использование цикла для инициализации значений области памяти, которую можно будет впоследствии трактовать как массив.

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

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

Структура (STRUCture) - это тип данных, состоящий из фиксированного числа элементов разного типа.

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

Для использования структур в программе необходимо выполнить три действия:

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

· Определить экземпляр структуры. Этот этап подразумевает инициализацию конкретной переменной заранее определенной (с помощью шаблона) структурой.

· Организовать обращение к элементам структуры.

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

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

· Определить структуру — значит, дать указание транслятору выделить память и присвоить этой области памяти символическое имя.

Описать структуру в программе можно только один раз, а определить — любое количество раз.

Пример.

worker struc; информация о сотруднике, начало

name db 30 dup (' '); фамилия, имя, отчество, 30 пустых полей рахмером 1 байт

sex db 'м'; пол, по умолчанию 'м' — мужской

position db 30 dup (' '); должность, 30 пустых полей рахмером 1 байт

age db 2 dup(‘ ’); возраст, 2 пустых поля рахмером 1 байт

standing db 2 dup(‘ ’); стаж, 2 пустых поля рахмером 1 байт

salary db 4 dup(‘ ’); оклад в рублях, 4 пустых поля рахмером 1 байт

birthdate db 8 dup(‘ ’); дата рождения, 8 пустых полей рахмером 1 байт

worker ends; информация о сотруднике, конец

Объединение (UNION) - тип данных, позволяющий трактовать одну и ту же область памяти как имеющую разные типы и имена.

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

Пример структуры с вложенным объединением.

pnt struc; структура pnt, содержащая вложенное объединение

union; описание вложенного в структуру объединения

offs_16 dw?; объект размером 2 байта

offs_32 dd?; объект размером 4 байта

ends; конец описания объединения

segm dw?

ends; конец описания структуры

Запись (RECORD) - структурный тип данных, состоящий из фиксированного числа элементов длиной от одного до нескольких бит. При описании записи для каждого элемента указывается его длина в битах и, что необязательно, некоторое значение. Суммарный размер записи определяется суммой размеров ее полей и не может быть более 8, 16 или 32 бит. Если суммарный размер записи меньше указанных значений, то все поля записи “прижимаются” к младшим разрядам.

Подумаем, зачем тратить под некоторый программный индикатор со значением “включено-выключено” целых 8 разрядов, если вполне хватает одного? А если таких индикаторов несколько, то расход оперативной памяти может стать весьма ощутимым. Когда мы знакомились с логическими командами, то говорили, что их можно применять для решения подобной проблемы. Но это не совсем эффективно, так как велика вероятность ошибок, особенно при составлении битовых масок.

TASM предоставляет нам специальный тип данных, использование которого помогает решить проблему работы с битами более эффективно. Речь идет о специальном типе данных — записях.

Использование записей в программе, так же, как и структур, организуется в три этапа:

· Задание шаблона записи, то есть определение набора битовых полей, их длин и, при необходимости, инициализация полей.

· Определение экземпляра записи. Так же, как и для структур, этот этап подразумевает инициализацию конкретной переменной типом заранее определенной с помощью шаблона записи.

· Организация обращения к элементам записи.

Для использования шаблона записи в программе необходимо определить переменную с типом данной записи, для чего применяется следующая синтаксическая конструкция:

Пример.

iotest record; Создание записи с именем iotest

i1:1,i2:2=11,i3:1,i4:2=11,i5:2=00; Список компонент записи с указанием числа бит для каждого

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

[{+ I -.}] цифры [основание]

Если основание в целочисленной константе не указано, предполагается, что число десятичное. Примеры:

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

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

[Знак]Цифры 1. [Цифры 2] [степень]

Ниже приведены определения понятий знаки степень:

· Знак – это плюс (+) или минус (-).

· Цифры 1 – целая часть мантиссы.

· Точка – разделитель целой и дробной частей мантиссы.

· Цифры 2 – дробная часть мантиссы. Может пропускаться.

· степень - E [ + или - ]Цифры 3.

· E – символ.

· Цифры 3 – порядок..

Поле знака является необязательным, в котором может находиться математический знак + или -. Примеры правильных вещественных констант: 2, + 3.0, -44.2E+05, 26.E5.

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

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

Символьные конствнты — это последовательности символов, заключенные в одинарные или двойные кавычки. Ассемблер автоматически заменяет символьную константу на соответствующий ей ASCII-код. Вот несколько примеров: 'A', "d".

Строковые константы. Строковой константой называется последовательность символов, заключенных в одинарные или двойные кавычки. Вместо нее ассемблер автоматически подставляет последовательность ASCII-кодов, соответствующих каждому символу строковой константы. Вот несколько примеров: 'ABC', 'X', "Привет, Вася!"




Поделиться с друзьями:


Дата добавления: 2014-10-17; Просмотров: 982; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.614 сек.