Чтение онлайн

на главную - закладки

Жанры

О чём не пишут в книгах по Delphi

Григорьев Антон Борисович

Шрифт:

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

Листинг 3.45. Быстрый (в среднем) способ получения строки через буфер

const

 StatBufSize = ...; // Размер, подходящий для данного случая

var

 StatBuf: array[0..StatBufSize - 1] of Char;

 Buf: PChar;

 RealLen: Integer;

begin

 //
Пытаемся разместить строку в буфере StatBuf

 RealLen := GetString(StatBuf, StatBufSize);

 if RealLen > StatBufSize then

 begin

// Если StatBuf оказался слишком мал, динамически выделяем буфер

// нужного размера и вызываем функции еще раз

Buf := StrAlloc(RealLen);

GetString(Buf, RealLen);

 end

 else

// Размера статического буфера хватило. Пусть Buf указывает

// на StatBuf, чтобы нижеследующий код мог в любом случае

// обращаться к буферу через переменную Buf

Buf := StatBuf;

 // Что-то делаем с содержимым буфера

 ...

 // Если выделяли память, ее следует очистить

 if Buf <> StatBuf then StrDispose(Buf);

end;

Следует также упомянуть о еще одной альтернативе передачи строк в DLL — типе

WideString
, который хранит строку в кодировке Unicode и является, по сути, оберткой над системным типом
BSTR
. Работать с
WideString
так же просто, как и с
AnsiString
, перекодирование из ANSI в Unicode и обратно выполняется автоматически при присваивании значения одного типа переменной другого. В целях совместимости с СОМ и OLE при работе с памятью дли строк
WideString
используется специальный системный менеджер памяти (через API-функции
SysAllocString
,
SysFreeString
и т.п.), поэтому передавать эти строки из DLL в главный модуль и обратно можно совершенно безопасно даже без
ShareMem
. Правда, при этом не стоит забывать о расходовании процессорного времени на перекодировку, если основная работа идет не с Unicode, а с ANSI.

Отметим одну ошибку, которую делают новички, прочитавшие комментарий про

ShareMem
, но не умеющие работать с
PChar
. Они пишут, например, такой код для функции, находящейся в DLL и возвращающей строку (листинг 3.46).

Листинг 3.46. Неправильный способ возврата строки из DLL

function SomeFunction(...): PChar;

var

 S: string;

begin

 // Здесь присваивается значение S

 Result := PChar(S);

end;

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

S
за пределы области видимости память, которую
занимала эта строка, освободилась. Менеджер памяти может в любой момент вернуть эту память системе (тогда обращение к ней вызовет Access violation) или задействовать для других целей (тогда новая информация уничтожит содержащуюся там строку). Проблема маскируется тем, что обычно результат используется немедленно, до того как менеджер памяти что-то сделает с этим блоком. Тем не менее полагаться на это и писать такой код не следует.

3.4. Прочие "подводные камни"

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

3.4.1. Порядок вычисления операндов

Эта проблема связана с тем, что у человека есть определенные интуитивные представления о порядке выполнения действий программой, однако компилятор не всегда им соответствует. Рассмотрим следующий код (листинг 3.47, пример OpOrder на компакт-диске).

Листинг 3.47. "Неправильный" порядок вычисления операндов

var

 X: Integer;

function GetValueAndModifyX: Integer;

begin

 X := 1;

 Result := 2;

end;

procedure TForm1.Button1Click(Sender: TObject);

var

 A1, A2: Integer;

begin

 X := 2;

 A1 := X + GetValueAndModifyX;

 X := 2;

 А2 := GetValueAndModifyX + X;

 Label1.Caption := IntToStr(A1);

 Label2.Caption := IntToStr(A2);

end;

Суть этого примера заключается в том, что функция

GetValueAndModifyX
имеет побочный эффект — изменяет значение глобальной переменной
X
. И эту же переменную мы используем при вычислении выражения, в которое входит также вызов
GetValueAndModifyX
. При вычислении
A1
в выражении сначала упоминается
X
, а потом
GetValueAndModifyX
, при вычислении
А2
— наоборот. Логично было бы предположить, что
A1
получит значение 4,
А2
— 3, т.к. вычисление первого операнда должно выполняться раньше второго. В действительности же обе переменные получат значение 3, поскольку компилятор сам выбирает порядок вычисления операндов независимо от того, в каком порядке они упоминаются в выражении. То же самое касается любых коммутативных операций: умножения, арифметических
and
,
or
и
xor
. Посмотрим, что будет для некоммутативных операций, например, для деления (листинг 3.48).

Листинг 3.48. "Неправильный" порядок вычисления операндов при делении

procedure TForm1.Button2Click(Sender: TObject);

var

 A1, A2: Extended;

begin

 X := 2;

 A1 := X / GetValueAndModifyX;

 X := 2;

 A2 := GetValueAndModifyX / X;

 Label1.Caption := FloatToStr(A1);

 Label2.Caption := FloatToStr(A2);

Поделиться:
Популярные книги

Барон нарушает правила

Ренгач Евгений
3. Закон сильного
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Барон нарушает правила

Измена. Не прощу

Леманн Анастасия
1. Измены
Любовные романы:
современные любовные романы
4.00
рейтинг книги
Измена. Не прощу

Жребий некроманта 3

Решетов Евгений Валерьевич
3. Жребий некроманта
Фантастика:
боевая фантастика
5.56
рейтинг книги
Жребий некроманта 3

Город Богов 2

Парсиев Дмитрий
2. Профсоюз водителей грузовых драконов
Фантастика:
юмористическое фэнтези
городское фэнтези
попаданцы
5.00
рейтинг книги
Город Богов 2

Сумеречный Стрелок 3

Карелин Сергей Витальевич
3. Сумеречный стрелок
Фантастика:
городское фэнтези
попаданцы
аниме
5.00
рейтинг книги
Сумеречный Стрелок 3

Сердце для стража

Каменистый Артем
5. Девятый
Фантастика:
фэнтези
боевая фантастика
9.20
рейтинг книги
Сердце для стража

Ваше Сиятельство

Моури Эрли
1. Ваше Сиятельство
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Ваше Сиятельство

Черный дембель. Часть 3

Федин Андрей Анатольевич
3. Черный дембель
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Черный дембель. Часть 3

Сирота

Ланцов Михаил Алексеевич
1. Помещик
Фантастика:
альтернативная история
5.71
рейтинг книги
Сирота

Сотник

Ланцов Михаил Алексеевич
4. Помещик
Фантастика:
альтернативная история
5.00
рейтинг книги
Сотник

Скандальная свадьба

Данич Дина
1. Такие разные свадьбы
Любовные романы:
современные любовные романы
эро литература
5.00
рейтинг книги
Скандальная свадьба

Последний из рода Демидовых

Ветров Борис
Фантастика:
детективная фантастика
попаданцы
аниме
5.00
рейтинг книги
Последний из рода Демидовых

Бестужев. Служба Государевой Безопасности. Книга 5

Измайлов Сергей
5. Граф Бестужев
Фантастика:
городское фэнтези
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Бестужев. Служба Государевой Безопасности. Книга 5

Мастер Разума IV

Кронос Александр
4. Мастер Разума
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Мастер Разума IV