При вызове строковых функций надо внимательно относиться к неявному
преобразованию типов. Простой пример. Функция Pos, как ты знаешь, находит
вхождение одной строки в другую и возвращает позицию первого символа искомой
строки. Но что если надо найти положение не строки, а всего одного символа: Pos(TargetChar,
SomeString). Дык вот в этом случае единичный символ сначала автоматически
преобразуется в длинную строку, а потом уже подставляется в функцию. Значит из
кучи будет выделена дополнительная память и будет написан дополнительный код
инициализации и финализации скрытой строки (да еще с блоком обработки ошибок).
Если эта операция будет производиться многократно (к цикле например), то потеря
производительности будет довольно существенной. И все тут же примутся грешить на
кривой компилятор у Дельфей ;)
Чтобы избежать этого косяка можно написать свою функцию поиска символа в строке,
где не будет лишних преобразований.
function PosOfChar(Ch: AnsiChar; const S: String): Integer;
var
i: Integer;
begin
Result := 0;
for i := 1 to Length(S) do
if S[i] = Ch then
begin
Result := i;
Exit;
end;
end;
Эта функция будет работать в 5 РАЗ быстрее, чем Pos с единичными символами. Кстати операция конкатенации (т.е. соединения) строк "+" работает тоже работает только со строками и единичные символы тоже будут приведены к строкам.
Посмотри еще раз на код который приведен выше. Строковый параметр S передается в
функцию, как const. Зачем я так сделал? Потому что S в коде не изменяется, а я
хочу повысить производительность. Объясняю.
Если ты читал заметку о длинной строке AnsiString, то видел, что это довольно
сложная структура данных, и у нее кроме всего прочего имеется счетчик ссылок (RefCount).
Если ты передашь строку в подпрограмму без слова const, то компилятор решит, что
эта строка может в подпрограмме измениться. Он создаст на строку дополнительную
ссылку (увеличит счетчик на 1) в начале, а в конце эту ссылку удалит (уменьшит
счетчик ссылок на 1). Кроме того, чтобы гарантировать, что этот счетчик
действительно уменьшится, в код скрытно от тебя вставляется блок обработки
ошибок try..finally. Как ты понимаешь, производительность это не улучшает.
Чтобы всего этого не было, ты должен явно сказать компилятору, что эта
переменная в коде функции изменяться не будет, т.е. передать параметр как const.
Кстати, если ты все-таки попытаешься эту строку изменить, то вылетит ошибка
(константы на то и константы, чтобы не менялись). Вот такая маленькая хитрость.
Как ты знаешь, в Delphi с помощью переменной Result можно оперировать значением,
которое возвращает функция. Фактически это не локальная переменная, а скрытый
параметр var. Если функция возвращает длинную строку (или динамический массив),
то переменная Result должна быть инициализирована. Но Delphi почему-то делает
это не всегда. Поэтому ты сам должен всегда инициализировать Result пустой
строкой.
Орехов Роман aka tripsin
Опубликована на VR-online в 2006 г.