New и строковые поля.

В PascalABC.NET возникают большие проблемы при использовании динамических переменных, создаваемых в традиционном стиле с помощью вызова New, в случае, когда создаются записи, содержащие поля типа string. Совершенно корректная программа, работающая в Turbo Pascal, Delphi, и даже в PascalABC (без .NET) перестает работать в ABC.NET: в какой-то момент поля string превращаются в nil. При не очень большом объеме данных может и работать, но только при очень небольшом. Вот пример ситуации, когда возникают проблемы: type tPtr = ^tNode; tNode = record ___word: string; ___left, right: tPtr; end;

var ___tree: tPtr; …

New(tree); …

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

Версия 3.0

Строки string собираются сборщиком мусора. Используйте shortstring, короткие строки фиксируются в памяти и не собираются сборщиком мусора (если используются в неуправляемой памяти)

Спасибо. Попробую. Но с какой стати они собираются сборщиком, если на эти строки есть ссылки? Они никакой не мусор.

Не помогает, однако. И, в общем, я эти штучки давно пробовал. Не используя слово shortstring, но объявляя тип как string[n].

Приведите программу пожалуйста

Вот:

Dictionary.pas (1,8 КБ)

А можно неприличный вопрос? Для чего изобретать очередной велосипед, если словарь Dictionary уже есть готовый? Ну не нравится язык, где много готовых компонентов - пишите и отлаживайте на FPC сутками то, что тут можно за полчаса сделать… Зачем в среду .NET лезть со всеми этими пещерными ссылками и указателями?

Согласен, вопрос неприличный. Видите ли, готовые словари, кто-то когда-то написал. И этот кто-то изучал когда-то, как это все устроено. Этот процесс еще называют образованием.

Давайте разбираться.

Вот две фразы из справки PascalABC.NET:

procedure New(var p: ^T); Выделяет динамическую память размера sizeof(T) и возвращает в переменной p указатель на нее. Тип T должен быть размерным

и

Отметим, что динамическая память, выделяемая процедурой New, не находится под управлением сборщика мусора, поэтому нуждается в явном освобождении вызовом процедуры Dispose. Именно поэтому работа с обычными указателями считается в PascalABC.NET устаревшей и не рекомендуется к использованию.

Собственно, самое главное здесь: Тип T должен быть размерным. А у Вас в записи - ссылочный тип.

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

Ну то есть, Вы должны с этим что-то сделать. Например, сказать, что PascalABC.NET вместе с его сборщиком мусора - плохая система - и не пользоваться ей. Или - принять новые правила игры. Например, заменить string на integer или использовать только ссылки:

http://pascalabc.net/stati-po-pascalabc-net/osobennosti-yazika/18-svyaznye-spiski-novyj-stil

Если изучать “как устроено”, милости просим в мир С++. Там Вы “по самое нехочу” наиграетесь в ссылки, указатели, сборку мусора, утечки памяти и т.д… Рискну предположить, что основа PascalABC.NET написана не на Паскале с последующим бутстрэппингом, а на совсем ином языке))

Что и требовалось доказать. Ибо как нефиг.

Вы что-то путаете. В Си++ нет автоматического сбора мусора.

Как раз не путаю. Именно потому, что нет автоматической сборки, я и и пишу “наиграетесь”. О чем свидетельствует дальнейшее “утечки памяти”. Их нет при автоматической сборке.

Давайте, Вы воздержитесь от дальнейшего троллинга. Ответа на заданный вопрос Вы не знаете. А давать советы про то, зачем мне эта программа, я не просил. Пока, пока.

Троллинг я тоже не оправдываю.

Ещё раз повторю по сути вопроса - надо понять, что с этим делать.

Два мира - неуправляемые указатели и управляемые ссылки - невозможно помирить между собой. Поэтому нужны какие-то компромиссы. Компромисс, который предлагает PascalABC.NET, - не использовать эти два мира совместно.

Строки в любых их воплощениях - это ссылки.

1 лайк

Станислав Станиславович, я, признаться, не понимаю, в чем тут технические проблемы и почему “невозможно помирить”. Но, видимо, я понимаю не все. В любом случае огорчительно, что программа. которая корректно работает во всех известных мне системах программирования на Паскале, не работает в PascalABC.NET. Разумеется, я могу это обойти. Но программа учебная, сюжет классический и хотелось бы, чтобы классические примеры на Паскале не переставали работать.

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

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

В Вашем случае это несомненно плохо. Обеспечить обратную совместимость именно в этом случае не удалось.

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

2 лайка

Почему нельзя сделать, чтобы New давала указатель на управляемую память и выделяла место там? При этом даже Dispose можно оставить.

Рискну предположить, что если позволить “вручную” создавать указатели на произвольные области в управляемой памяти, то сборщик мусора (GC), вызванный по нехватке памяти, не сможет разобраться, “жив” указатель или нет. А если не разберется - вот она, утечка управляемой памяти по ошибках в программе.

А это прямой путь к обвалу всех приложений в CLR

Можно было сделать всё - это несомненно. Но New и неуправляемая память реально нужны для ряда приложений. Например, в модуле Graph они активно используются для взаимодействия с неуправляемым кодом. Так что, поэтому было принято другое решение.

Ваше решение - хорошее, но оно затрагивает половину компилятора и его реализация приведет к обеднению Паскаля - в нём нельзя будет работать с неуправляемым кодом

А что такое неуправляемая память? Та, что не подчиняется сборщику мусора? Но ведь память, предоставляемая через New, тоже должна каким-то образом диспетчеризоваться. А что такое неуправляемый код?

Извините за назойливость.