Кодерские tips’n’tricks
В данной теме делимся программерскими трюками. Это могут быть советы по оптимизации, нестандартные приемы, интересные (но не слишком большие) куски кода. Не стоит превращать тему в свалку исходников, выбирайте наиболее интересные и полезные участки кода. Желательно откомметировать трюк, чтобы всем было понятно.
Delphi
Оптимизация
[Передача аргументов] Никогда не передавай функции в качестве параметра структуру, лучше передавать указатель на нее
Код:
//Ни в коем случае не делай так.
Procedure code (Data:TStructure);
//Правильный вариант
Procedure code (PData:PStructure); //гда PStructure = ^TStrucrure
Старайся передавать своим функциям не более трех параметров, т.к. они передаются через регистры (по соглашению fastcall, принятом по умолчанию в Delphi), а все остальные через стек.
[Функции - инварианты] Довольно распространенная ошибка программистов – присутствие функций - инвариантов в цикле.
Код:
//Неоптимизированный вариант
While i<= lstrlen(str) do
Begin
X:=x+ord(str[i]);
Inc(i);
End;
Очевидно, что длина str не меняется, но компилятор считает, что все, что передается по ссылке сожжет быть изменено, и lstrlen вычисляется много раз. Оптимизированный вариант выглядит так.
Код:
//Так лучше
N:=lstrlen(str);
While i<= n do
Begin
X:=x+ord(str[i]);
Inc(i);
End;
[Экономия памяти] Когда класс располагается в памяти, то между полями появляются пустые ячейки памяти. Это происходит потому, что Delphi, оптимизируя код, каждое поле располагает от предыдущего со сдвигом в 4 байта.
Код:
// Неоптимизированный вариант
TMyClass = class
private
field1: boolean;//1 байт
field2: longint; //4 байт
field3: char; //1 байт
field4: string; //4 байт
field5: byte; //1 байт
field6: integer; //4 байт
field7: byte; //1 байт
public
procedure code;
end;
Реально этот экземпляр класса будет занимать 28 байт. Если мы изменим порядок полей, то сможем добиться уменьшения размера до 16 байт. В нашем примере после field1 размером 1 байт идет field2 размером 4 байта, значит, мы теряем 3 байта на выравнивание. Если же размер field2 не превышал 3 байт, то Delphi не стал бы выравнивать, а поместил бы это поле сразу после первого.
Код:
// Оптимизированный вариант
TMyClass = class
private
field1: boolean; //1 байт
field3: char; //1 байт
field5: byte; //1 байт
field7: byte; //1 байт
field2: longint; //4 байт
field4: string; //4 байт
field6: integer; //4 байт
public
procedure code;
end;
[Компиляция без RTL (Run Time Library)] Как известно минимальный размер скомпилированной в Delphi программы с настройками по умолчанию равен 13,5 Кб. Виной тому принудительно подключаемая Delphi RTL, в которой реализованы некоторые возможности языка Delphi.
Для уменьшения размера скомпилированных прог исправим модели System.pas и SysInit.pas, удалив все «лишнее». Затем перекомпилируем их и полученные dcu-файлы поместим в папку с прогой.
Минимальный System.pas
Код:
unit System;
interface
procedure _HandleFinally;
type
TGUID = record
D1: LongWord;
D2: Word;
D3: Word;
D4: array [0..7] of Byte;
end;
PInitContext = ^TInitContext;
TInitContext = record
OuterContext: PInitContext;
ExcFrame: Pointer;
InitTable: pointer;
InitCount: Integer;
Module: pointer;
DLLSaveEBP: Pointer;
DLLSaveEBX: Pointer;
DLLSaveESI: Pointer;
DLLSaveEDI: Pointer;
ExitProcessTLS: procedure;
DLLInitState: Byte;
end;
implementation
procedure _HandleFinally;
asm
end;
end.
Минимальный SysInit.pas
Код:
unit SysInit;
interface
procedure _InitExe;
procedure _halt0;
procedure _InitLib(Context: PInitContext);
var
ModuleIsLib: Boolean;
TlsIndex: Integer = -1;
TlsLast: Byte;
const
PtrToNil: Pointer = nil;
implementation
procedure _InitLib(Context: PInitContext);
asm
end;
procedure _InitExe;
asm
end;
procedure _halt0;
asm
end;
end.
Компиляция
Код:
Dcc32.exe – Q System.pas SysInit.pas –M –Y –Z -$D- -O
Некоторые трюки
[Обмен] Обмен значений между двумя переменными без привлечения третьей.
Код:
x:=x xor y;
y:=y xor x;
x:=x xor y;