[ Уменьшение размера программ написанных на C++ ]
В этой статье я хочу затронуть одну уже вдоль и поперек изъезженную тему – уменьшение размера
приложений написанных на C++. В частности я создаю эту тему для того, чтобы поделиться с вами
найденными мною функциями, заменяющими стандартные RTL-функции C++ при разработке на чистом
WinAPI. Также надеюсь, что вы пополните этот список, а так же по возможности оптимизируете
существующий. Ну и, конечно же, надеюсь, что статья окажется полезной новичкам.
И так допустим, вы написали или хотите написать, какую-либо программу на C++ при этом вы хотите,
чтобы она «весила» как можно меньше, и не потребляла много ресурсов. Если вы уже написали программу,
и у вас нет желания переписывать код на WinAPI можете просто добавить следующие строчки, которые
уменьшат размер программы, в начало кода:
#pragma optimize("gsy",on)
#pragma comment(linker,"/RELEASE")
#pragma comment(linker,"/MERGE:.rdata=.text /MERGE:.data=.text")
#pragma comment(linker, "/SECTION:.text,EWR")
#pragma comment(linker,"/FILEALIGN:512")
Либо эти же строчки можно вынести в отдельный заголовочный файл (.h), а затем подключить к своему
проекту через #include. Теперь немного расскажу, что означают эти строки.
#pragma optimize("gsy",on) – позволим компилятору самому оптимизировать код:
g - global optimizations (глобальная оптимизация), s - favor small code (оптимизировать в пользу
уменьшения кода, при этом может незначительно снизиться быстродействие), y - no frame pointers.
#pragma comment(linker,"/RELEASE") – компилируем релиз версию, чтобы не было никакой отладочной
информации, увеличивающей размер.
#pragma comment(linker,"/MERGE:.rdata=.text /MERGE:.data=.text") – объединяем секции, код
уменьшается за счет того, что теперь нет свободного пространства между секциями.
#pragma comment(linker, "/SECTION:.text,EWR") – выставляем необходимые права на секцию
.text: Execute (выполнение), Write (запись), Read (чтение).
#pragma comment(linker,"/FILEALIGN:512") – уменьшаем величину выравнивания секций до 512 байт,
по умолчанию 4 Кб. При размере 4 Кб приложение загружается немного быстрее.
Еще более оптимизировать приложение можно, отключив библиотеку времени исполнения
(Run-Time Library, RTL). При этом все стандартные функции, такие как strcmp, strlen, memcpy,
CopyMemory, malloc, new и другие, станут не доступны. Их можно заменить на схожие функции Win API,
а некоторые написать самому.
И так если вы решили писать на WinAPI, то добавьте к предыдущим строчкам еще эти:
#pragma comment(linker,"/NODEFAULTLIB")
#pragma comment(linker,"/ENTRY:main")
#pragma comment(linker,"/SUBSYSTEM:windows")
#pragma comment(linker,"/NODEFAULTLIB") – подключает к приложению только те библиотеки, которые
указаны явно.
#pragma comment(linker,"/ENTRY:main") - отключает RTL и переназначает точку входа.
#pragma comment(linker,"/SUBSYSTEM:windows") – нужно, чтобы линкер определил систему
Теперь функция main не передает Instance и командную строку, но их можно получить, используя
функции WinAPI - GetModuleHandle() и GetCommandLine(). Функции malloc, free можно заменить на
HeapAlloc, HeapFree. Функциям strlen, strcpy, strcmp соответствуют схожие по названию, но с
буквой "l" вначале – lstrlen, lstrcpy, lstrcmp.
Теперь то, для чего собственно это все и затевалось – замены стандартным RTL-функциям, а также
другие полезные функции:
static void WINAPI ZeroMemory(LPVOID lpMem, DWORD dwSize)
{
_asm
{
mov edi,[lpMem]
mov ecx,[dwSize]
xor eax,eax
rep stosb
}
}
static void WINAPI MemCpy(LPVOID lpMem, LPVOID lpMem2, DWORD dwLen)
{
_asm
{
mov edi,[lpMem]
mov esi,[lpMem2]
mov ecx,[dwLen]
rep movsb
}
}
static void WINAPI MemSet(void *szBuffer, DWORD dwLen, DWORD dwSym)
{
_asm
{
pushad
mov edi,[szBuffer]
mov ecx,[dwLen]
mov eax,[dwSym]
rep stosb
popad
}
}
// Функция ищет подстроку Str2 в строке Str1
int InStr(char* Str1, char* Str2)
{
int cStr1=0,cStr2=0,i;
while (Str1[cStr1]) cStr1++; // считаем размер
while (Str2[cStr2]) cStr2++; // строк, можно и через lstrlen
if ((!cStr1)||(!cStr1)||(cStr1<cStr2)) return 0;
for (i=0; i<(cStr1-cStr2+1); i++) // search Str2 in string Str1
if(CompareString(LOCALE_USER_DEFAULT,0,Str1+i,cStr2,Str2,cStr2)==2) return i+1;
return 0;
}
// Функция заменяет в строке Src подстроку Str1 на Str2
char* Replace(char* Src,char* Str1, char* Str2)
{
int cStr1=0,cStr2=0,i;
char* buffer;
buffer = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,lstrlen(Src)*2);
while (Str1[cStr1]) cStr1++; // считаем размер
while (Str2[cStr2]) cStr2++; // строк, можно и через lstrlen
i = InStr(&Src[0],&Str1[0]);
while(i != 0)
{
lstrcpyn(&buffer[lstrlen(buffer)],&Src[0],i);
lstrcat(&buffer[0],&Str2[0]);
lstrcpy(&Src[0],&Src[i+cStr1-1]);
i = InStr(&Src[0],&Str1[0]);
}
lstrcat(&buffer[0],&Src[0]);
lstrcpy(&Src[0],&buffer[0]);
HeapFree(GetProcessHeap(),HEAP_NO_SERIALIZE,buffer);
return Src;
}
// Функция определяющая размер файла
long FileLen(LPCSTR lpszFileName)
{
WIN32_FIND_DATA wfd;
long len;
HANDLE hFindFile = FindFirstFile(lpszFileName,&wfd);
if (hFindFile == INVALID_HANDLE_VALUE)
len = 0;
else
{
len = wfd.nFileSizeLow;
FindClose(hFindFile);
}
return len;
}
(с) Geograph