Массивы, структуры, типы, классы переменной длины
Предыдущие рассуждения об объектах переменной длины мы для простоты связывали со строками.
Как правило, строки — это массив типа «char» (так реализовано во многих языках).
Ничто не мешает использовать массивы других типов:
// Конструктор массива типа Double
DoubleArr :: DoubleArr (DoubleArr* ptr, ...) {
ptr = Выделить память в другом стеке (требуемый размер);
ptr -> Инициализация объекта DoubleArr (...);
}
Ранее мы рассмотрели, как объекты переменной длины можно разместить в стеке функции.
Встаёт вопрос: этот объект является экземпляром какого типа?
Раз объект может иметь неизвестную во время компиляции длину,
то и тип или класс этого объекта может быть достаточно произволен по своей структуре.
Например, тип/класс объекта может иметь не только поля переменной длины,
но переменное количество этих полей.
Массивы могут иметь не только произвольное количество элементов, но и произвольную размерность.
Это открывает достаточно широкие перспективы при построении языка программирования, обладающего большой гибкостью.
Однако предстоит обдумать, как правильно сочетать такую гибкость и контроль за всеми сущностями программы.
Но можно уже сейчас порассуждать о некоторых возможных проблемах. Возьмём, к примеру, структуру:
cтруктура ФИО {
строка Фамилия;
строка Имя;
строка Отчество;
};
Если бы строковые поля этой структуры имели бы фиксированный размер,
то программе было бы легко определить, в каком месте начинается поле «Имя».
Зная во время компиляции размер поля «Фамилия», мы знаем смещение поля «Имя».
Если размер полей не фиксирован и не известен во время компиляции, то нужно иметь механизмы получения адресов полей структуры.
Значит, в самой структуре должны иметься (в скрытом виде) указатели на поля структуры.
Эффективным будет такое решение.
-
Поля фиксированного размера размещаются в начале структуры.
Для них нет нужды вычислять смещение — оно известно во время компиляции.
-
Первое поле переменного размера тоже имеет смещение, известное во время компиляции.
Для него нет нужды создавать указатель.
-
Последующие поля переменного размера должны иметь скрытый указатель на себя.
Эти указатели имеют фиксированный размер и под них отводится место в начале структуры — там же,
где и остальные объекты постоянного размера. Для N полей переменного размера понадобится N-1 указателей.
Конструктор, создающий экземпляр такой структуры, должен заполнить указатели на поля переменной длины.
При копировании экземпляров структур адреса, содержащиеся в таких структурах,
должны приводиться к правильным; в С++ этим занимается конструктор копирования.
Теперь рассмотрим случай, когда структура имеет переменной количество полей.
К примеру, во многих странах нет понятия «отчество», а в Исландии нет фамилий:
исландцы носят только имя и отчество. В некоторых странах люди могут иметь несколько имён и несколько фамилий.
Всё это может привести к такому выводу: структура может иметь внутри себя безымянные поля.
Переменное количество полей не позволяет дать им имена, известные во время компиляции.
Вывод такой: надо иметь возможность обращаться к полям массива по номеру, как если бы это был массив. Например, так:
ФИО.[3]
Такая свобода имеет и обратную сторону медали.
Когда должен проверяться тип поля: во время компиляции или это откладывается на время выполнения программы?
Это зависит от такого, какую типизацию будет иметь язык — статическую или динамическую.
В случае статической типизации возможно решение, аналогичное тому,
как в языке C определяются типы аргументов функций с переменным числом аргументов:
void f (float a, char* b, int c, ...)
Третий и последующие аргументы имеют тип «int».
Так же и в структурах: последнее описанное поле можно сделать определяющим тип последующих полей.
Приведённые выше рассуждения можно распространить не только на структуры, но и на классы.
Читаем далее следующую статью:
О хранении данных в стеке — вместо заключения.
Почитайте ещё:
Опубликовано: 2014.07.27, последняя правка: 2018.10.29 15:54
Отзывы
✅ 2016/05/06 14:07, utkin #0
Если бы строковые поля этой структуры имели бы фиксированный размер, то программе было бы легко определить, в каком месте начинается поле «Имя». Зная во время компиляции размер поля «Фамилия», мы знаем смещение поля «Имя». Если размер полей не фиксирован и не известен во время компиляции, то нужно иметь механизмы получения адресов полей структуры. Значит, в самой структуре должны иметься (в скрытом виде) указатели на поля структуры. Эффективным будет такое решение. Так и есть. Программа "знает" кто и где заканчивается. Потому что поля всегда выравниваются к какому-нибудь кратному блоку. Например, 8 байт. И если там в структуре в строке 5 символов (и нуль в сишной), то 2 байта будут лежать "вхолостую". Поэтому чисто теоретически СТАТИЧЕСКИЕ записи (типа struct или record) в стек запихуемы. А вот как это реально в стеке или куче я точно сказать не могу. Насчет строк, которые должны меняться, конечно все не так однозначно. Вероятно, там находится ссылка, то есть адрес, где расположены символы. Но в таком случае размер тем более известен и Вы смело можете пихать любую структуру в стек. А вот как расположить в стеке данные, изменения которых нужно проводить динамически, это большой вопрос.Поля фиксированного размера размещаются в начале структуры. Для них нет нужды вычислять смещение — оно известно во время компиляции. Ну в свете вышесказанного смещение в структуре известно всегда. И во время компиляции и после.✅ 2016/05/07 11:12, Автор сайта #1
А вот как расположить в стеке данные, изменения которых нужно проводить динамически, это большой вопрос. Конечно, большой вопрос. Поэтому в функциональном программировании так любят неизменяемые данные. Но, между прочим, на верхушке стеке можно иметь объект с динамически изменяемым размером. Он, конечно, единственный. Но тут есть интересный момент. Обычно, заголовочная часть данных размещается в начале этих данных, т.е. в области с наименьшим адресом:struct X { int len; char str[??]; }; В архитектуре x86 стек растёт вниз, в сторону уменьшения адресов. Т.е. заголовочная часть (поле len) данных окажется внизу. Если удлиняем стек, чтобы увеличить длину данных (чтобы строка str имела теперь большую длину), то адрес вершина стека уменьшится. Но тогда мы вынуждены переместить заголовочную часть данных (len), сами данные (str), и только потом на освободившееся место записываем дополнительные данные.✅ 2016/05/11 09:17, utkin #2
Но тут есть интересный момент. Обычно, заголовочная часть данных размещается в начале этих данных, т.е. в области с наименьшим адресом: Это Паскалевская строка такая. А вот Сишная просто до нулевого байта и длина строки, до тех пор пока Вы не посчитаете реальный размер дойдя до конца, неизвестна.✅ 2016/05/11 10:42, Автор сайта #3
Вот поэтому мне не нравится строки в Си, но нравятся в Паскале. Хорошо на эту тему писал Джоэл Спольски в «Назад, к основам». Я Вам когда-то давал ссылку. Добавить свой отзыв
Написать автору можно на электронную почту mail(аt)compiler.su
|