Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Hello, World!
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  11:20[Войти] | [Зарегистрироваться]

Обсуждение материала
Тонкости работы со строками
Полный текст материала


Другие публикации автора: Антон Григорьев

Цитата или краткий комментарий:

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


Важно:
  • Страница предназначена для обсуждения материала, его содержания, полезности, соответствия действительности и так далее. Смысл не в разборке, а в приближении к истине :о) и пользе для всех.
  • Любые другие сообщения или вопросы, а так же личные эмоции в адрес авторов и полемика, не относящаяся к теме обсуждаемого материала, будут удаляться без предупреждения авторов, дабы не мешать жителям нормально общаться.
  • При голосовании учитывайте уровень, на который расчитан материал. "Интересность и полезность" имеет смысл оценивать относительно того, кому именно предназначался материал.
  • Размер одного сообщений не должен превышать 5К. Если Вам нужно сказать больше, сделайте это за два раза. Или, что в данной ситуации правильнее, напишите свою статью.
Всегда легче осудить сделанное, нежели сделать самому. Поэтому, пожалуйста, соблюдайте правила Королевства и уважайте друг друга.



Добавить свое мнение.

Результаты голосования
Оценка содержания

  Содержит полезные и(или) интересные сведения
[1]2291.7%
 
  Ничего особенно нового и интересного
[2]14.2%
 
  Написано неверно (обязательно укажите почему)
[3]14.2%
 
Всего проголосовали: 24

Оценка стиля изложения

  Все понятно, материал читается легко
[1]1982.6%
 
  Есть неясности в изложении
[2]417.4%
 
  Непонятно написано, трудно читается
[3]00%
 
Всего проголосовали: 23




Смотрите также материалы по темам:
[Строки] [Работа с PChar]

Комментарии жителей
Отслеживать это обсуждение

Всего сообщений: 85

05-04-2012 14:09
Хотел бы добавить ещё одну полезную ссылку:
»вопрос КС №45926«

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


04-02-2011 10:19
Кажется, нашёл ещё одну тонкость при работе с PChar (в Delphi 7), которая здесь ещё не рассматривалась.
Начну издалека:

const
  S: string = 'ABC';
var
  P: PChar;
begin
  P := PChar(S);
  WriteLn(P);
end.


Здесь всё нормально, выводится 'ABC'.
Меняем пример:

const
  S = 'ABC';
// ... далее, как раньше


Здесь уже S - "истинная" строковая константа, и, по идее, PChar(S) уже не нужен, т.к. компилятор автоматически распознаёт такие присваивания. Тем не менее, пример работает, как ожидается.
Меняем пример дальше:

const
  S = 'A';
// ... далее, как раньше


К моему изумлению, код перестал работать (может быть AV, либо вывод пустой строки). Что произошло? А вот что: результатом операции PChar('A') является не адрес памяти, по которому лежит нуль-завершённая строка 'A', а адрес $00000041 (код символа 'A', приведённый к типу Pointer). Вот такие вот пирожки с котятами. У компилятора, конечно, могут быть резоны делать именно так (константа-Char не то же самое, что константа-string), но на мой взгляд, если уж так, то такое преобразование типов должно быть запрещено. Или, по крайней мере, компилятор должен был предупреждение выдать.
Сообщение не подписано


02-02-2011 06:38
Спасибо за совет.

К сожалению на Delphi не специализируемся, но стараемся поддерживать, а тут вышло так,
что после Delphi 7 не обновлялись, клиенты тоже, а тут новый клиент скачал демо-версию...

Про вопросы не в той темы - извините. Запутался в терминах в стиле "королевства"(


02-02-2011 04:01
http://www.transl-gunsmoker.ru/2009/09/pchars.html#d2009

P.S. "ВНЕЗАПНО", ага. Я вот не понимаю, ну как можно умудриться проспать переход на Unicode при обновлении Delphi до 2009 и выше.
P.P.S. На будущее - здесь идет обсуждение статьи. Это не место для задания вопросов. Вопросы задаются на Круглом Столе.


02-02-2011 02:05
Здравствуйте, возникла следующая проблема.
Есть dll написанная в Visual Studio C++ 6.0. Был сделан delphi-хедер для неё, с помощью утилиты coff2omf переделывал lib и спокойно отдавал клиентам, у всех работало без проблем.
Затем появился Delphi 2010 и в нём возникли проблемы со строками. Почему-то строки обрезаются при передаче внутрь (порой только один символ), а если смотреть возвращаемые, то там абракадабра.

Объявление функции передающей строку внутрь такое:
function LibSetupContext(
   hInstance: LibInstanceHandle;              // хендл инстанса библиотеки
    szContextIniFileName: PChar = nil       // контекстные настройки; nil-контекст по умолчанию
  ): boolean; stdcall;
   external LibDllPath name '_LibSetupContext@8';

Объявление функции возвращающей строку такое:
function LibGetVersion(
  ): PChar; stdcall;  // адрес временной переменной, содержащей строку с версией ScAPI
   external LibDllPath name '_LibGetVersion@0';
LibInstanceHandle и LibDllPath определены так:
const
   LibDllPath = 'libpr.dll';
type
    LibInstanceHandle= longword;    // внутренний контекст исполнения библиотеки


04-04-2010 03:49
например, Integer, тоже необходимо учитывать правильность работы с памятью? А то везде упоминаются только строки как то...

Нет, с Integer никаких проблем не будет, но строго говоря не только строки подвержены таким проблемам. При работе с любым типом, будь то простой указатель, динмассив или объект, где выделяется и освобождается динамическая память с использованием менеджера памяти Delphi. Например, если создать объект в DLL, передать его в EXE и там попытаться уничтожить - будет то же самое: менеджер EXE память для объекта не выделял, у него нет никаких данных чтобы правильно ее освободить.
PS: Но объекты не только из-за этого не рекомендуется передавать между приложением и библиотекой
 Ins


03-04-2010 19:14
А при передачи между программой и подключаемой длл других типов данных, например, Integer, тоже необходимо учитывать правильность работы с памятью? А то везде упоминаются только строки как то...


02-04-2010 05:52
>>> WideString потребует дополнительного процессорного времени для преобразования из Unicode в Ansi и обратно
Справедливо.

Хочу только заметить, что подобная дополнительная нагрузка не смущает использующих версии до Delphi 2009. Ведь при вызове WinAPI функций Delphi 7 и Delphi 2007 вызывают ANSI версии этих функций - что означает ровно такую же конвертацию ANSI<->Unicode. Поскольку большинство просто не принимает это в расчёт, то логично предположить, что они так же отнесутся и к более медленному выполнению предложенного варианта.


02-04-2010 04:26
сообщение от автора материала
sprofxx:

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

А PChar - это сам по себе указатель, нет нужды делать указатель на указатель.

Если же вас заинтересовал совет Александра, то учтите, что WideString потребует дополнительного процессорного времени для преобразования из Unicode в Ansi и обратно, если у вас версия Delphi более ранняя, чем D2009.


02-04-2010 03:31
Посмотрите также Менеджеры памяти в программах.


02-04-2010 02:16
>>> приведите простейший код передачи строк из dll в основную прогу и обратно
Просто используйте WideString.

DLL:

function DoSomething(const A: WideString): WideString; stdcall;
begin
  Result := A + 'sdf' + A;
end;



exe:

function DoSomething(const A: WideString): WideString; stdcall; external 'Project2.dll';

procedure TForm1.Button1Click(Sender: TObject);
begin
  Caption := DoSomething(IntToStr(5));
end;



Это будет работать по той простой причине, что WideString - это (на самом деле) системный тип BSTR, у которого есть обязательное условие: любые операции с памятью для него должны идти через системный менеджер памяти. Что означает, что и в DLL и в exe у вас для WideString будет выделять и освобождать память один и тот же код.

P.S. А в коде у вас написан немножко бред (хоть в данном случае он будет работать). Проблема №1: вы не знаете, сколько выделить памяти. Проблема №2: у вас на один уровень косвенности больше, чем нужно.


01-04-2010 19:51
Вот что сам наваял после безумно долгих мучений:


//Код dll
...

procedure DllProc(str1: pointer); stdcall;
begin
  StrPCopy(PChar(str1^),'Текст из dll');
end;

...




//Код программы
...

procedure TForm1.Button1Click(Sender: TObject);
var Lib: Cardinal;
    DllProc: procedure(str1: pointer); stdcall;
    str:PChar;
begin
  Lib := LoadLibrary('MyDll.dll');
  if Lib <> 0 then
  try
    DllProc := GetProcAddress(Lib, 'DllProc');
    GetMem(str,25);
    DllProc(@str);
    Label1.Caption:=str;
    FreeMem(str);
  finally
    FreeLibrary(Lib);
  end;
end;

...



Здесь правильно используется память?

Возьмем например API функцию GetWindowText, одним из параметров в ней является переменная типа PChar (т.е. ссылка на Char'ы). Тогда каким образом функция вызванная из dll и получив этот указатель, сможет выделить место для этой переменной в области памяти основной программы так, чтобы после выгрузки dll переменная и ее значение еще были живы и вообще, как dll может управлять чужой областью памяти в этом случае? Как вообще работает эта API и подобные ей, в них даже не @PChar передается как у меня, а вообще просто PChar (тела подобных функций не нашел)?

Если можно, пожалуйста, приведите простейший код передачи строк из dll в основную прогу и обратно.


14-03-2010 05:26
сообщение от автора материала
sprofxx:

Прошу прощения за задержку с ответом: сразу ответить не было времени, а потом забыл.

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

Посмотрите, как работают функции WinAPI типа GetWindowText, которые возвращают строковые значения, и действуйте по аналогии. Или используйте SimpleShareMem и не мучайтесь - начиная с BDS 2006 есть такой модуль, который работает так же, как ShareMem, но нужды таскать за собой дополнительную dll при его использовании нет.


08-03-2010 19:52
Вроде решение было найдено, сутки провозился до этого, а тут быстро пришел к этому:

Код dll без изменений.


//код приложения
procedure TForm1.Button1Click(Sender: TObject);
var Lib: Cardinal;
    DllProc: procedure(str: PPChar); stdcall;
    str:PChar;
begin
  Lib := LoadLibrary('MyDll.dll');
  if Lib <> 0 then
  try
    DllProc := GetProcAddress(Lib, 'DllProc');
    DllProc(@str);
    Label1.Caption:=str;
  finally
    FreeLibrary(Lib);
  end;
end;



Это верно?


08-03-2010 19:28
Еще в догонку: и вообще не в фрилайбрери дело... Как правильно реализовать?


08-03-2010 19:16
В догонку: только написал, и этот способ почему то перестал работать без sharemem, хотя вроде орудование памятью не нарушается.

Объясните пожалуйста, как заполнить строковую переменную, находящейся в основной программе из подключаемой dll? Хотелось бы нормальный способ узнать. Как, например, функции API заполняют результатом своих действий переданный им буфер строкой?


08-03-2010 19:03
Так и не понятно как передавать в простейшем варианте строки из функций dll.

А по большому счету не понятно как присвоить значение в ячейки памяти (строку), на которые указывает указатель PChar, что то вроде:


var s:PChar;

begin
  s^:='лалала';
end;



Много колдовал, перерыл весь инет, а примера так и нет...

Все что удалось получить:


//код dll

...
type
  PPChar = ^PChar;

procedure DllProc(str: PPChar); stdcall;
begin
  str^:='Текст из dll!';
end;

exports
  DllProc;
begin
end.




//Код программы
type
  PPChar = ^PChar;

procedure TForm1.Button1Click(Sender: TObject);
var Lib: Cardinal;
    DllProc: procedure(str: PPChar); stdcall;
    str:PPChar;
    t:string;
begin
  str := @t;
  Lib := LoadLibrary('MyDll.dll');
  if Lib <> 0 then
  try
    DllProc := GetProcAddress(Lib, 'DllProc');
    DllProc(str);
    Label1.Caption:=str^;
  finally
    FreeLibrary(Lib);
  end;
end;



1. Этот вариант верный и единственный?
2. И почему после успешного выполнения при вызове FreeLibrary возникает ошибка?


13-01-2010 12:56
Всем откликнувшимся на мой вопрос большое спасибо.


13-01-2010 11:48
просходит ли автоматическое освобождение памяти занимаемой строками
можешь также не беспокоиться о такой структуре
var MyArray: array of TMyRec; где
TMyRec = record
  v1: integer;
  v2: string;
  ...
end;
но если какой-либо указатель Pointer, PChar, TObject, ... и данные "хранящиеся в них" больше нигде не используются, то их(эти указатели) надо освобождать.
 riff


13-01-2010 11:39
сообщение от автора материала
Юрий:

Да, происходит. Delphi освобождает память для строк всегда и везде, когда это надо. Когда программист не вмешивается в перераспределение памяти для управляемых типов какими-то грубыми низкоуровневыми методами, причин для самостоятельного освобождения какой-либо памяти для них просто не существует.


13-01-2010 11:39
просходит ли автоматическое освобождение памяти занимаемой строками происходит.
 riff


13-01-2010 10:42
В программе использую динамический массив
var
  Param : array of AnsiString;

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

При выполнении процедуры SetLength(Param, 0) просходит ли автоматическое освобождение памяти занимаемой строками составляющими массив Param (имея в виду что они типа AnsiString) или нужно это делать самому?

Прошу вас ответить на этот вопрос.


22-12-2009 05:03
сообщение от автора материала
Виноват, не заметил, что это идёт передача между различными процессами.

Да, тогда такое решение в корне неверно. Это только в старых 16-разрядных программах, где все приложения выполнялись в одном адресном пространстве, можно было обмениваться данными через DLL. А в 32-разрядных это и в самом деле нельзя, так как все адреса - виртуальные, и и каждого процесса виртуальное адресное пространство своё.


22-12-2009 04:48
Видимо, единственный правильный метод пересылки строк, это совет Torbins использовать WM_COPYDATA, т.к. передача указателя между различными процессами в корне неверна (используется разное адресное пространство)...


22-12-2009 02:01
1) Watch Name Value
fn $BA20F4 {'?'}
2) Watch Name Value
FileName $BA20F4 {'?'}
3) Watch Name Value
Msg.WParam $BA20F4

Кстати, попробовал "параметр функции GetFileName лучше сделать параметром-значением, а не переменной" - AV нет, но результирующая строка содержит набор случайных символов. То же самое и при параметре-константе.


22-12-2009 01:45
сообщение от автора материала
1. Чему равно численное значение указателя fn после вызова StrAlloc?
2. Какое значение показывает отладчик в GetFileName?
3. Чкму равен WParam при обработке сообщения?


22-12-2009 01:23
Антон Григорьев,
AV происходит в функции
function StrPCopy(Dest: PWideChar; const Source: UnicodeString): PWideChar;
а в ней при вызове

function StrLCopy(Dest: PWideChar; const Source: PWideChar; MaxLen: Cardinal): PWideChar;
var
  Len: Cardinal;
begin
  Result := Dest;
  Len := StrLen(Source);
  if Len > MaxLen then
    Len := MaxLen;
  Move(Source^, Dest^, Len * SizeOf(WideChar)); // AV here!
  Dest[Len] := #0;
end;



22-12-2009 00:58
сообщение от автора материала
А под отладчиком пройти не пробовали? В какой строке ошибка, какие значения получают переменные?


21-12-2009 23:48
Антон Григорьев,
Что-то навскидку не могу заметить в вашем коде ошибку
и тем не менее я получаю "Access violation at address 00404536 in module 'my_project.exe'. Write of address 00BA20F4." при выполнении Button1Click :(
не забыли ли вы поставить stdcall при импорте GetFileName в приложении
Нет, не забыл.
Даже и не знаю уже в чем еще может быть быть причина ошибки? Delphi 2010.

Torbins,
А что это за WM_SM_GETFILENAME такой?
const
  WM_SM_GETFILENAME = WM_USER + 1009;


21-12-2009 13:47
А что это за WM_SM_GETFILENAME такой? Я понимаю, если бы это WM_COPYDATA был.


21-12-2009 08:15
сообщение от автора материала
Виктор:

Что-то навскидку не могу заметить в вашем коде ошибку. Разве что параметр функции GetFileName лучше сделать параметром-значением, а не переменной. Но это просто немного снижает эффективность, к ошибке приводить не должно. Кроме того, когда размер буфера известен на этапе компиляции, и он не очень большой, лучше выделять место под него в стеке, а не в динамической памяти. А так, единственное что приходит в голову - не забыли ли вы поставить stdcall при импорте GetFileName в приложении.


21-12-2009 08:00
Уважаемый автор!
А подскажите как быть в таком случае:
Одна программа (Pr1) должна через вспомогательную dll получить строку из другой программы (Pr2) так (схематично):


//в Pr1
procedure TForm1.Button1Click(Sender: TObject);
var
  fn: PChar;
begin
  fn := StrAlloc(MAX_PATH);
  GetFileName(fn);
  MessageBox(0, fn, PChar(Caption), 0);
  StrDispose(fn);
end;

// в dll
function GetFileName(var FileName: PChar): Integer; stdcall;
begin
  Result := SendMessage(HWND, WM_SM_GETFILENAME, integer(FileName), 0);
end;

//в обработчике сообщений Pr2
  case Msg.Msg of
    WM_SM_GETFILENAME:
      begin
        StrPCopy(PChar(Msg.WParam), 'test');
        Handled := True;
      end;
  end;



Этот код неверный, как правильно реализовать желаемое?
Спасибо!


15-08-2009 03:32
Руслан Примак: как правильно передать в функцию dll запись, содержащей строку неограниченной длинны.

//программа
type
  PMyRec = ^TMyRec;
  TMyRec = record
    I: integer;
    S: string;
  end;

procedure TForm1.Button1Click(Sender: TObject);
var
  r: TMyRec;
  Lib: Cardinal;
  AddRec: procedure(MyRec: PMyRec); stdcall;
begin
  r.i := 100;
  r.s := 'Long String';
  Lib := LoadLibrary('mylib.dll');
  if Lib <> 0 then
  try
    AddRec := GetProcAddress(Lib, 'AddRec');
    AddRec(@r);
  finally
    FreeLibrary(Lib);
  end;
end;



//dll
type
  PMyRec = ^TMyRec;
  TMyRec = record
    I: integer;
    S: string;
  end;

procedure AddRec(MyRec: PMyRec); stdcall;
var
  r: TMyRec;
begin
  r.i := MyRec.i;
  SetString(r.s, PChar(MyRec.s), Length(MyRec.s));
  //r.s := MyRec.s; //ни в коем случае
  //а с r.s делай уже что хочешь
end;

 riff


14-08-2009 12:58
сообщение от автора материала
Поэтому у меня вопрос: как правильно передать в функцию dll строку неиограниченной длинны из основного приложения

Есть несколько вариантов решения этой проблемы.

1. Воспользоваться системным менеджером памяти, который предназначен для COM. Выделять и освобождать память при этом придётся вручную, но зато DLL и приложение могут перераспределять память, выделенную друг другом, без проблем, так как системный менеджер памяти у них один. Подробнее этот менеджер памяти описан здесь: http://www.delphikingdom.com/asp/viewitem.asp?catalogid=1363

2. Воспользоваться строками WideString (не путать с UnicodeString, появившимися в Delphi 2009). Это обёртка над типом BSTR из OLE, поэтому такие строки пользуются тем менеджером памяти, который описан в п.1, только компилятор, как и в случае со string, сам заботится о выделении и освобождении памяти. Недостаток этого способа - использование двухбайтной кодировки, что влечёт за собой потерю производительности и возможные ошибки из-за перекодировки в ANSI, которую компилятор выполняет автоматически при присваиваниях между WideString и AnsiString. Зато думать вообще ни о чём не надо, всё само делается.

3. Возложить все операции с памятью только на один модуль. Обычно - на приложение. В этом случае получение строки выполняется в два шага: на первом приложение запрашивает у DLL длину строки, на втором выделяет требуемое количество памяти и передаёт этот буфер в DLL. DLL никакую память не выделяет, а только заполняет этот буфер. Естественно, освобождает буфер потом само приложение.

И есть ещё один важный момент. Начиная с Delphi 2006 появился модуль (unit) SimpleShareMem. Использовать его надо точно так же, как и ShareMem, за исключением того, что никаких дополнительных DLL таскать за собой не нужно. SimpleShareMem использует возможности нового менеджера памяти, который может быть расшарен для использования несколькими модулями (модуль здесь - это не unit, а приложение или DLL). Каждый модуль, при компиляции которого использовался SimpleShareMem, начинает свою работу с проверки, есть ли расшаренный менеджер памяти. Если он есть, то модуль будет использовать его, если нет, то расшарит свой. Таким образом, все модули, использующие SimpleShareMem, будут пользоваться одним менеджером памяти, и никаких проблем при передаче строк, разумеется, не возникнет.

тоже самое касается и записи, содержащей строку неограниченной длинны.

Не понял, в чём вопрос. Нет никакой разницы в передаче в DLL строки как отдельной переменной и как поля записи. Методы решения проблем те же.


14-08-2009 12:54
2Geo
Хотя, на самом деле не пять, а шесть, так как там посередине еще были дырочки более мелкого размера, которые служили грнаицей раздела между двумя частями кода, чтобы можно было определить, где верх, а где низ.

Это немного не то. Эти маленькие дырочки - транспортные отверстия. Специальная шестерня из шипов протягивает перфоленту по тракту считывающего устройства. Верх и низ тута вовсе не канают.


14-08-2009 09:45
Спасибо за материал, время от времени перечитываю, т.к. сразу все в голове не помещается :)
Но есть замечание. В последнем разделе, где рассматривается sharemem автор  рассказывает как ненужно делать, но при этом не показывает как правильно передать сроку неограниченной (а попросту - неизвестной) длинны. Или я не доглядел (но я старался :)). Тоже самое касается и записей.
Поэтому у меня вопрос: как правильно передать в функцию dll строку неиограниченной длинны из основного приложения, тоже самое касается и записи, содержащей строку неограниченной длинны.


13-09-2008 16:41
Продолжение (не уместилось в 5 кб):

Примечание
Тип PChar - это не просто указатель на Char, а тип, специально предназначенный для работы с сишными (нуль-терминированными) строками, который интерпретируется именно как строка, поэтому он имеет ряд особенностей. Одной из особенностей является то, что нуль-терминированная строка всегда должна оканчивается символом #0. Если строка пуста, то указатель будет ссылаться непосредственно на этот символ #0, который должен находиться где-то в памяти и под него должна быть выделена память. Некоторые процедуры, работающие с нуль-терминированными строками, могут интерпретировать указатель на nil как пустую строку, но это скорее является "фичей" на грани "бага", нежели штатным поведением. Отсюда следует не совсем очевидный момент: если S - пустая строка AnsiString (которая, по существу, является указателем  на nil), то:
Pointer(S) = nil
PChar(S) <> nil
При этом, если вывести "свой" тип - указатель на Char (в примере - PtrChar), то:
PtrChar(S) = nil,
т.к. этот указатель не интерпретируется как нуль-терминированная строка и при преобразовании с ним не производится дополнительных действий.

P.S. Если Антон найдёт в этом примере что-то интересное, что позволит добавить в его книгу пару абзацев, буду только рад.


13-09-2008 16:38
>>> А есть ли способ прочитать счетчик ссылок переменной AnsiString?

Посмотрите »вопрос КС №62357«
Там есть пример и пояснения для динамических массивов.
Хотел ограничиться этой ссылкой, но решил таки наваять небольшой обучающий пример для строк, который и предлагаю вниманию почтенной публики:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
type
  PtrChar = ^Char;  // "Чистый" указатель на Char

procedure ShowStringInfo(const S: String; Comment: String);
var
  ptr: Pointer;  // Нетипизированный указатель
  pCh: PChar;    // Указатель на Char, предназначенный для работы с нуль-терминированными строками
  ptrCh: PtrChar; // Обычный указатель на Char без дополнительных особенностей
  QRef, QElem: Integer;
begin
  ptr := Pointer(S);
  pCh := PChar(S);
  ptrCh := PtrChar(S);
  if ptr = nil then
  begin
    ShowMessage( Format(
      '%S'#13#10 +
      '------------------------------'#13#10 +
      'S = %s <интерпретируется как пустая>'#13#10#13#10 +
      'Pointer(S) =  %p'#13#10 +
      'PtrChar(S) = %p'#13#10 +
      'PChar(S) =    %p'#13#10#13#10 +
      'Ссылок:    <данные отсутствуют>'#13#10 +
      'Символов: <данные отсутствуют>'#13#10,
      [ Comment, QuotedStr(S), ptr, ptrCh, Pointer(pCh)] ));
    Exit;
  end;

  QRef := PInteger(pCh - SizeOf(Integer) * 2 )^;
  QElem := PInteger(pCh - SizeOf(Integer))^;
  ShowMessage( Format(
    '%S'#13#10 +
    '------------------------------'#13#10 +
    'S = %s'#13#10#13#10 +
    'Pointer(S) =  %p'#13#10 +
    'PtrChar(S) = %p'#13#10 +
    'PChar(S) =    %p'#13#10#13#10 +
    'Ссылок:    %d'#13#10 +
    'Символов: %d'#13#10,
    [ Comment, QuotedStr(S), ptr, ptrCh, Pointer(pCh), QRef, QElem] ));
end;

procedure TForm1.Button1Click(Sender: TObject);
const
  cnst = 'Named constant';
var
  a, b: String;
begin
  ShowStringInfo('Text constant', 'Неименованная константа');

  ShowStringInfo(cnst, 'Именованная константа');

  ShowStringInfo(a, 'a - хотя и локальная, но при объявлении инициализирована nil');

  a := 'Test';
  ShowStringInfo(a, 'a - присвоена константа');

  b := a;
  ShowStringInfo(a, 'a - после присвоения b := a (a и b ссылаются на одну константу)');
  ShowStringInfo(b, 'b - ссылается на ту-же константу');

  a := a + 'New';
  ShowStringInfo(a, 'a - присвоена не константа');

  b := a;
  ShowStringInfo(a, 'a - после присвоения b := a (a и b ссылаются на одни данные)');
  ShowStringInfo(b, 'b - ссылается на те-же данные');

  a := a + 'Other';
  ShowStringInfo(a, 'a - присвоено новое значение, ссылается на новые данные');
  ShowStringInfo(b, 'b - ссылается на прежние данные, количество ссылок уменьшилось');

  ShowMessage('А теперь - внимание!'#13#10'Строке присваиваем значение, возвращаемое функцией');

  a := DateTimeToStr(Now);
  ShowStringInfo(a, 'a - присвоено значение, возвращённое функцией (количество ссылок - 2, а не 1)');

  a := '';
  ShowStringInfo(a, 'a - присвоена пустая строка');
end;

end.



12-09-2008 16:59


  asm
    mov eax, Str
    mov eax, [eax-8]
    mov RefCount, eax
  
end;



12-09-2008 11:20
А есть ли способ прочитать счетчик ссылок переменной AnsiString?


25-06-2008 03:53
>>> А я так и не считал, просто забрел туда когда искал таблицы символов.
Ну, если нашли, то тогда это символ NUL по таблице ASCII ;-)

Но ни в коем случае не путать ни с NULL в базах данных, ни с NIL в паскале :D
 Geo


25-06-2008 03:29
А Википедия - не самый лучший учебник программирования на Delphi, всё-таки у энциклопедии совсем другие задачи.
А я так и не считал, просто забрел туда когда искал таблицы символов.


25-06-2008 00:01
А символ #0 - пробел?
Пробел имеет вполне конкретный код, как и любой символ. В кодировке Windows 1251 (как и в старой ASCII по Вашей ссылке) его код 32 (20h)


24-06-2008 10:59
>>> На перфолентах 1 представлялась дырочкой, 0 — отсутствием дырочки. Поэтому пустые части перфоленты до начала и после конца сообщения состояли из таких символов.
Если уж на то пошло, то дырочка на перфоленте -- это не символ. Дырочка (или отсутствие дырочки) -- это один бит. В то время как символ -- это несколкьо бит (то есть несколько дырочек). Сколько именно -- зависит от системы кодирования. Например, на телеграфных перфолентах использовался пятизначный равномерный код, в котором один симовл кодировался пятью битами. То есть, есои Вам попадется в руки такой раритет, как телеграфная перфолента, то Вы увидите на ней вертикально в ряд пять дырочек (или недырочек). Хотя, на самом деле не пять, а шесть, так как там посередине еще были дырочки более мелкого размера, которые служили грнаицей раздела между двумя частями кода, чтобы можно было определить, где верх, а где низ.

Что-то меня понесло ;-)

Присоединяюсь к Антону: не изучайте серьезные вещи по Википедии. Не только в программировании.
 Geo


24-06-2008 09:42
сообщение от автора материала
Символ #0 - это символ с кодом 0, т.е. в ячейке, которая хранит код этого символа, если интерпретировать её значение не как символ, а как число, будет 0. А Википедия - не самый лучший учебник программирования на Delphi, всё-таки у энциклопедии совсем другие задачи.


24-06-2008 09:04
Извините, конечно, дурака!
А символ #0 - пробел?или NULL? Так вроде его можно заменить на #10( - #14)?
Для чего вам вообще этот символ в строке (если, конечно это не пробел)?
С Википедии http://ru.wikipedia.org/wiki/ASCII
NUL, 00 — Null, пустой. Всегда игнорировался. На перфолентах 1 представлялась дырочкой, 0 — отсутствием дырочки. Поэтому пустые части перфоленты до начала и после конца сообщения состояли из таких символов. Сейчас используется во многих языках программирования как конец строки. (Строка понимается как последовательность символов.) В некоторых операционных системах NUL — последний символ любого текстового файла.


14-11-2007 22:26
>>>Ввел PChar, навел мышку и подсказка мне написала PChar=^Char. Вот так однако :)
PChar это не просто указатель на Char в своё время (когда string = string[255]) он был специально включён для упрощения работы с длинными строками заканчивающиеся на #0, и основное отличие PChar  от просто ^Char это совместимость PChar с массивами Char начинающиеся с 0 индекса

type
  PInteger = ^Integer;
  PChr = ^Char;

procedure TestI(H: PInteger);
begin

end;

procedure TestC(H: PChar);
begin

end;

procedure TestChr(CH: PChr);
begin

end;

procedure TForm1.Button1Click(Sender: TObject);
var
  M: array[0..10]of Integer;
  C: array[0..10]of Char;
begin
//  TestI(M); - ошибка компиляции
  TestC(C); // всё прекрасно компилируется
//  TestChr(C); - ошибка компиляции
end;



14-11-2007 17:12
Для Антона Григорьева:

Полностью согласен, явно тип не определен, может подсказка неправильная? :)


14-11-2007 06:33
...существуют менеджеры памяти сторонних разработчиков, которые решают ту же проблему, что и ShareMem, но без использования библиотеки. Эти менеджеры памяти просто все запросы по работе с памятью передают системному менеджеру памяти, что избавляет их от необходимости хранить какую-либо информацию о выделенных блоках. Такое решение вполне работоспособно, но менее эффективно, особенно если нужно многократно выделять и освобождать небольшие блоки памяти.

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

unit FastShareMem;

interface

var
  GetAllocMemCount: function: integer;
  GetAllocMemSize : function: integer;

implementation

uses Windows;

const ClassName  = '_com.codexterity.fastsharemem.dataclass';

type
  TFastSharememPack = record
    MemMgr: TMemoryManager;
    _GetAllocMemSize  :function :integer;
    _GetAllocMemCount :function :integer;
  end;

  function _GetAllocMemCount: Integer;
  begin
    Result := System.AllocMemCount;
  end;

  function _GetAllocMemSize: Integer;
  begin
    Result := System.AllocMemSize;
  end;

var
  MemPack: TFastSharememPack;
  OldMemMgr: TMemoryManager;
  wc: TWndClass;
  isHost: boolean;

initialization

  if (not GetClassInfo(HInstance, ClassName, wc)) then
  begin
    GetMemoryManager(MemPack.MemMgr);
    MemPack._GetAllocMemCount := @_GetAllocMemCount;
    MemPack._GetAllocMemSize  := @_GetAllocMemSize;
    GetAllocMemCount := @_GetAllocMemCount;
    GetAllocMemSize  := @_GetAllocMemSize;
    FillChar(wc, sizeof(wc), 0);
    wc.lpszClassName := ClassName;
    wc.style := CS_GLOBALCLASS;
    wc.hInstance := hInstance;
    wc.lpfnWndProc := @MemPack;
    if RegisterClass(wc)=0 then
    begin
      MessageBox( 0, 'Shared Memory Allocator setup failed: Cannot register class.', 'FastShareMem', 0 );
      Halt;
    end;
    isHost := true;
  end else
  begin
    GetMemoryManager(OldMemMgr); // optional
    SetMemoryManager(TFastSharememPack(wc.lpfnWndProc^).MemMgr);
    GetAllocMemCount := TFastSharememPack(wc.lpfnWndProc^)._GetAllocMemCount;
    GetAllocMemSize  := TFastSharememPack(wc.lpfnWndProc^)._GetAllocMemSize;
    isHost := false;
  end;

finalization
  if isHost then UnregisterClass(ClassName, HInstance)
  else SetMemoryManager(OldMemMgr); // optional

end.

 Ins


14-11-2007 00:56
сообщение от автора материала
Алексею Денисову:

Подсказка - это хорошо, но я привык доверять исходникам. В Delphi 7 тип PChar нигде в модуле System не объявляется, но используется. Попробуйте встать на PChar и выбрать в контекстном меню Find declaration - увидите, что среда просто перейдёт на модуль System, но объявление типа не найдёт. Кстати, хинт в семёрке для PChar пишет "type System.PChar: PAnsiChar", для PAnsiChar - "System.PAnsiChar: ^Char", но явного объявления PAnsiChar в System тоже нет.


14-11-2007 00:46
Познавательно, однако есть маленькая деталь


В литературе нередко можно встретить утверждение, что PChar=^Char, однако это неверно


только что посмотерл (Delphi 6), что именно так этот тип и обозначен.
Ввел PChar, навел мышку и подсказка мне написала PChar=^Char. Вот так однако :)


13-10-2007 01:46
сообщение от автора материала
Не худо бы добавить про PString и PWideString.

А что про них добавлять-то? На мой взгляд, совершенно бесполезные типы. Передавать указатель на указатель смысла практически никакого нет, а возни с появлением ссылки, не учитываемой счётчиком, будет много. Ни разу не использовал эти типы и совершенно не представляю, где они могли бы понадобиться.


12-10-2007 07:12
Не худо бы добавить про PString и PWideString. В плане передачи параметров это было бы интересно.


16-09-2007 05:47
2 АГ:
Спасибо большое, думаю после прочтения http://rsdn.ru/article/Delphi/memmanager.xml про менджер мне станет чуть-чуть понятней ;)


16-09-2007 05:27
сообщение от автора материала
Но тут вновь и вновь повторятся выделения именно одного и того же куска памяти сначала для LocalRec.Str, потом для S, почему ?

Всё очень просто. Сначала менеджер памяти выделяет память для строки "Hello". Алгоритм детерминирован, поэтому при данном состоянии кучи выделяется строго определённый кусок памяти. Затем эта строка освобождается, и состояние динамической памяти возврящается к тому, которое было до выделения памяти под "Hello". Затем возникает необходимость выделения памяти под строку "Good buy". Запускается тот же самый алгоритм при тех же самых начальных условиях. Естественно, что он даёт тот же самый результат, т.е. выделяет под строку ту же область памяти.


16-09-2007 04:12
Я только что узнал, что UniqueString выделяет память, только тогда когда счетчик ссылок больше чем 1, т.е. убеждается, но тогда возникает вопрос:

"Почему менджер памяти выделяет тот же самый кусок памяти под переменную S, такую которая раньше использовалась под LocalRec.Str ?"

Насколько я понимаю string это тип динамическим временем жизни. Т.е. менеджер памяти сам его порождает\убивает, следовательно может выделиться любой кусок памяти для строки! Но тут вновь и вновь повторятся выделения именно одного и того же куска памяти сначала для LocalRec.Str, потом для S, почему ?


16-09-2007 02:17
Статья отличная, но хотел бы предложить небольшое улучшение. В виду того что этот материал для новичков, то следует в коментариях "на заметку" раскрывать нюанся, к примеру до прочтения этой статьи я не знал, что такое "LStr", а это сокращение от Long String, можно встретить в LStrToPChar и других.

2 АГ:
Спасибо тебе большое, спасибо за то что ты старался пролить свет на многие прикрытые водой камни. Если тебе не сложно, и тебя появится время, я бы хотел чтобы ты ответил на такой вопрос:

В примере RecordCopy:

  S := 'Good bye';
  UniqueString(S);


Мне не понятно, почему UniqueString всегда размещает строку "Gool bye" в том же месте, где была строка "Hello" ? Ведь насколько я понял, эта ф-ция работает с динамической памятью и следовательно может выделиться, по идее, любой кусок памяти, а потом копирование в него строки. А тут наоборот постоянно выделяется один и тот же кусок памяти, почему? Чего я еще не понимаю ? ;)


01-07-2007 00:17
сообщение от автора материала
Проверялось всё в BDS2006(Delphi for Win32). В Delphi 7 всё действительно работает как вы описали

В BDS используется другой менеджер памяти - FastMem. Я его ещё не щупал, пока не могу сказать, чем он отличается от обычного менеджера памяти.


29-06-2007 08:25
Так возьмите и расширьте ;-)

Не обладаю должной фундаментальностью, усидчивостью и ... слогом :)))


29-06-2007 08:05
>>> Думаю, только стоит расширить его описанием тонкостей WideString, PWideChar и преобразований соответсвующих <...>
Так возьмите и расширьте ;-)
 Geo


29-06-2007 06:41
Хороший материал. Подробный. Думаю, только стоит расширить его описанием тонкостей WideString, PWideChar и преобразований соответсвующих.
Например, о том что в WideString не использутеся счетчик ссылок и копирование строки происходит всегда с выделением памяти под копию. Или про приведение к PChar WideString'ов.

А так же механизмы компилятора, используемые им  при выборе overload функций типа:

someproc(s1, s2: string); overload;
someproc(s1, s2: WideString); overload;
someproc(s1, s2: PChar); overload;
someproc(s1, s2: PWideChar); overload;
someproc(s1: string; s2: PChar); overload;
someproc(s1: string; s2: WideString); overload;


и т.д. по вариантам (+ с const и var)

при разных типах фактических параметрах (string, widestring, pchar, pwidechar, char, widechar, строковые констаныт и литералы). Однажды пришлось составлять очень большой тест.


28-06-2007 10:47
Статья отличная, читается легко и понятно, правда я незадолго до этого прочитал статью с рсдна, на которую тут также была дана ссылка в коментариях. Но у меня возникла пара вопросов по одному из примеров, а именно в примере RecordCopy при нажатии кнопки 1. Во первых, почему если строку в записи сделать больше чем строка, хранимая в S, то отображается именно строка записи, а точнее в лэйбл выдаётся всегда именно та строка, которая больше по длине(при первом же нажатии на кнопку после запуска программы). И второе, вы утверждаете что при отсутствие строки Pointer(Rec.Str) := nil; возникает ошибка, но она не возникает.

P.S. Проверялось всё в BDS2006(Delphi for Win32). В Delphi 7 всё действительно работает как вы описали, разве что при отсутствии Pointer(Rec.Str) := nil; ошибка возникает не при выходе из процедуры а при завершении приложения.


02-04-2007 10:26
Отличная статья, раньше про константы и не задумывался


07-11-2006 12:05
сообщение от автора материала
Недавно в одном из своих ответов DRON дал ссылку на статью http://www.rsdn.ru/article/Delphi/dynarrays.xml , в которой тоже рассматриваются некоторые тонкости работы со строками. Кое-что из этого я не знал. Всем очень рекомендую, статья интересная.


12-10-2006 06:28
Отличная статья, настоящее научное исследование! До многого я доходил сам, путём многочисленных AV и дебагов, но систематизировать всё это не удавалось. Отдельная благодарность за то, что пролили свет на механизм работы с памятью при присваивании строк.

P.S. Во избежание траблов с длл-ками, имхо, легче всего действовать по схеме win API aka "Тебе нужно - ты и выделяй" :). По крайней мере, там, где это возможно.


06-05-2006 07:52
Автору большое спасибо, изложение максимально простое.

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


11-04-2006 03:53
сообщение от автора материала
К ShortString это тоже частично относится - строка хранится в самой переменной, но если присвоить переменной константу, то эта константа тоже должна где-то хранится, прежде чем будет скопирована в эту переменную. Вопрос, где и как хранится эта константа, в статье тоже затронут.


11-04-2006 03:38
Вот теперь я понял. :-)
Только это ведь не относится к ShortString или как его там?
 KIA


11-04-2006 02:34
сообщение от автора материала
то что "s" объявленна переменной, говорит компилятору о том ГДЕ надо хранить помещаемые в неё данные

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


11-04-2006 02:25
Вот именно это я и имею ввиду, то что "s" объявленна переменной, говорит компилятору о том ГДЕ надо хранить помещаемые в неё данные, говорит о том, что они будут именяться.
 KIA


10-04-2006 12:41
сообщение от автора материала
'Text' - константа, но ведь s - переменная, и работа происходит именно с переменной, так что не вижу точек соприкосновения.

Эти четыре символа, составляющие константу 'Text', где-то надо хранить. Перечитайте внимательно, весь раздел посвящён именно тому, где и как сохраняются именно эти символы. Переменные в данном случае служат лишь для того, чтобы это проиллюстрировать.


10-04-2006 09:39
'Text' - константа, но ведь s - переменная, и работа происходит именно с переменной, так что не вижу точек соприкосновения.
 KIA


10-04-2006 09:25
сообщение от автора материала
Я не понял примеров, почему там речь везде идёт о константах, когда явным образом прописанно Var?

Когда пишется что-то вроде S:='Text', то 'Text' - это строковая константа. Именно о таких константах и идёт речь.

неужели так плохо в Дельфи с UTF-8/16, что об этом здесь даже не упоминается?

Просто кодировки - это отдельная тема, которая выходит за рамки этой статьи. Здесь рассматриваются только вопросы о том, как строки хранятся в памяти.


10-04-2006 06:36
Я не понял примеров, почему там речь везде идёт о константах, когда явным образом прописанно Var? А в целом приятная статья, особенно для тех, кто программил в обычном Паскале и даже бы не подумал читать про всякие строки, пока не приспичит, а приспичило бы довольно скоро, судя по статье и не в самом приятном виде..
Хотя не понятно почему, но неужели так плохо в Дельфи с UTF-8/16, что об этом здесь даже не упоминается?
 KIA


27-03-2006 04:07
сообщение от автора материала
И я так и не понял можно ли освобождать PChar в dll с помошью FreeMem если память под него была занята GetMem в основной программе?

Можно, но только в том случае, если используется ShareMem.


27-03-2006 03:55
Хорошая статья, меня только смущает "backward compatibility routines" в которых находятся StrNew и StrDispose. И я так и не понял можно ли освобождать PChar в dll с помошью FreeMem если память под него была занята GetMem в основной программе?
Для полноты картины нужно было написать про TStringStream - являющимся серъёзной подмогой при многократном наращивании строки.
Иногда не хватает и скорости TStringStream, и для тех же целей приходиться использовать TMemoryStream.

P.S. Такое ощущение, что я это сегодня ночью уже писал... почему-то не дошло...


21-03-2006 12:43
GEO, я не голосовал, если вы на меня подумали. :)

Джа подсказывает мне, что использование арифметических операторов тоже может быть проблемой. АГ уже рассказывал о подводных камнях использования арифметики, основанной на вычислениях с плавающей запятой. Была б, мне кажется, очень интересной статья об устойчивых вычислениях. Где они нужны, какие варианты есть... Тем более, что инфы по этому делу совсем мало, а в рунете вообще нет. Так что с каждой шутки можно наковырять че-нигобудь серъёзного обязатьельо.,.

В последнее время среди дельфийских программистов стало актуально завершать любой пост кличем: "НАПАСАРАН КОМРАДЫ"! К чему бы это? :)

P.S.
Почему мне всегда загадки про животных попадаются?


21-03-2006 11:25
Очень хорошая статья. Не хотел голосовать, так как статья не для меня, но очень удивлен одним голосом за "ничего особенно нового и интересного" и решил все же несколько "поправить положение". Господа, прежде чем голосовать, обращайте внимание на раздел, в котором материали публикуется. Может быть не стоит давать такие отзывы, если Вы уже не новичок, а материал из Hallo, World!

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

От себя хот5л бы добавить, что если начинающий программист чувствует себя уже достаточно уверенно, чтобы начать экспериментировать с перескакиваниями с одной разновидности на другую, то это допустимо. Но нужно быть очень аккуратным и хорошо себе представлять все особенности (статья в этом поможет). Например, добавление символа #0 в конец ShortString поможет избежать глюков с переходом к PChar (а иногда такой подход при вызове API-функций более удобен, чем работа с динамической памятью)

var
  S : ShortStringl;
  P : PChar;
begin
  S:='This is a string' + #0; // добавляем в конец строки #0
  P:=@S[1];  // присваиваем ссылку на первый символ строки
end;



P.S. Вообще-то, из комментариев оытных пользователей на данную статью можно создать еще одну статью, типа "Маленькие хитрости по борьбе с проблемпми строк" ;-)
 Geo


21-03-2006 10:36
Отличная работа, Антон!
Более чем подробно.


21-03-2006 10:14
Для полноты картины нужно было написать про TStringStream - являющимся серъёзной подмогой при многократном наращивании строки. Типа

str = str + "fdgsdfg";


в цикле.

P.S.
И кстати, хватит нахваливать Антона, а то он скоро совсем сбрякнется и начнёт писать статьи типа "Особенности использования операторов арифметических операций (+, -, *, /) в языке Object Pascal" :) Шучу.


21-03-2006 02:47
Отличная статья!


19-03-2006 01:57
Превосходная статья. Как всегда удивляешься тому, что вроде было понятно... Антон Григорьев снова показал мастер-класс:)))


18-03-2006 03:32
Увлекательнейшее чтение, оторваться невозможно ;-)

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

И ведь такие языки существуют. Например, в старом добром FoxPro (DOS) все обстояло именно таким образом. Второй пример - Perl, в котором кроме того добавлена прозрачная поддержка Unicode и сериализация строк в составе сложных структур. А в Delphi, как мне кажется, в погоне за универсальностью, эффективностью, обратной совместимостью и системной совместимостью одновременно сильно занижен уровень абстракции языка в этом плане. Помесь Pascal и Assembler - немножно ежики, немножно черепахи.


17-03-2006 10:22
Очень подробный материал. Спасибо автору за проделанную работу.


17-03-2006 05:08
Есть еще одна интересная штука:

S := S1 + S2;


будет по-разному работать для String[127] и String[128] (во втором случае будет преобразование в AnsiString, конкатенация и усечение результата). Хотя, например, в Delphi 2 работало одинаково.


Добавьте свое cообщение

Вашe имя:  [Войти]
Ваш адрес (e-mail):На Королевстве все адреса защищаются от спам-роботов
контрольный вопрос:
Вода мокрая или сухая?
в качестве ответа на вопрос или загадку следует давать только одно слово в именительном падеже и именно в такой форме, как оно используется в оригинале.
Надоело отвечать на странные вопросы? Зарегистрируйтесь на сайте.

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


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

Текст:
Жирный шрифт  Наклонный шрифт  Подчеркнутый шрифт  Выравнивание по центру  Список  Заголовок  Разделительная линия  Код  Маленький шрифт  Крупный шрифт  Цитирование блока текста  Строчное цитирование
  • вопрос Круглого стола № XXX

  • вопрос № YYY в тесте № XXX Рыцарской Квинтаны

  • сообщение № YYY в теме № XXX Базарной площади
  • обсуждение темы № YYY Базарной площади
  •  
     Правила оформления сообщений на Королевстве
      
    Время на сайте: GMT минус 5 часов

    Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
    Функция может не работать в некоторых версиях броузеров.

    Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
    Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

     
    © При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
    Все используемые на сайте торговые марки являются собственностью их производителей.

    Яндекс цитирования