- Всех программистов можно разделить на три категории:
- Программисты, которым не нужен прямой доступ к памяти.
- Программисты, которым часто требуется прямой доступ к памяти, и выбранный ими язык позволяет легко это осущеставлять.
- Программисты, которым часто требуется прямой доступ к памяти, но выбранный язык не позволяет им легко производить его.
- Всех VB-программистов можно отнести к последней категории. Здесь надо уточнить, что "часто требуется" - это понятие относительное, и у каждого это "часто" своё.
- Все случаи комирования -- это либо копирование блоков (причём иногда довольно больших), либо копирование значений каких-то переменных. В последнем случае, копируется обычно 4 байта, т.е. DWORD аля переменная типа Long (или String, ну или Single, но это редко).
Относительно этого всех программистов можно разелить на три категории:- Программисты, которые для копирования 4 байт используют GetMem4.
- Программисты, которые используют для копирования 4 байт памяти CopyMemory и не знают о GetMem4.
- Программисты, которые знают о GetMem4, но всё равно используют CopyMemory для копирования 4 байт.
- С первыми все ОК, вторых достаточно направить в сторону описания функции GetMem4, а вот третьи...
- Я не могу знать, почему третья категория делает так, но предполагаю, что причина в том, что люди не видят разницу, - считают, что это одно и то же.
- Посему, давайте посмотрим, что делает GetMem4.
На заметку
Для тех, кто не в курсе: регистр - это такая ячейка в процессоре, способная хранить данные.
(Обе функции принимают src и dst -- адреса откуда копировать и куда копировать. CopyMemory принимает ещё и length -- сколько копировать).
Что делает GetMem4:- В eax записывается src.
- В ecx записывается dst
- В eax записываются 4 байта по адресу, хранящемуся в eax.
- В память, по адресу, хранящемуся в ecx, записывается значение (те самые 4 байта), которые хранятся в eax.
А теперь, что делает CopyMemory:- Сохраняет значение esi в памяти
- Сохраняет значение edi в памяти
- В esi записывается src
- В edi записывается dst
- В ecx копируется length
- Число в esi сравнивается с числом в edi (т.е. по сути сравниваются src и dst).
- Если esi оказывается меньше и равен edi, то выполнение переходит на пункт 18.
- Число из ecx копируется в edx.
- К числу в edx применяется битовая and-маска 3. Таким образом в edx получается остаток от деления прежнего значения edx на 4.
- Запускается циклическое копирование блоков по 4 байта из esi в edi по ecx блоков. При этом, ecx на каждом шаге уменьшается на единицу. И по когда в ecx получается число 0, циклическое копирование заканчивается.
- В ecx записывается значение edx.
- Проверяется, а не равен ли случайно edx нулю. И если равен (т.е. если размер копируемого блока был кратен 4 и всё уже скопировалось), то выполнение переходит к пункту 15.
- Запускается циклическое копирование байтов из esi+ecx в edi + ecx. ecx при этом, опять же, уменьшается на единицу, и так происходит, пока ecx не станет равен нулю.
- Осуществляетя переход к пункту 15.
- Из памяти достаётся и записывается прежнее значение edi.
- Из памяти достаётся и записывается прежнее значение esi.
- Всё, CopyMemory на этом заканчивается.
- Хранящееся в ecx число (т.е. length) сранивается с 0. И если оно равно нулю, выполнение переход к пункту 15.
- В eax копируется edi (а там у нас dst).
- От eax отнимается esi (а там - src)
- Результат, получившийся в eax, сравнивается с ecx (а там - длина копируемого блока). Таким нехитрым образом проверяется, пересекаются ли копируемые области.
- И если ecx был меньше и равен результата в eax, то (то блоки пересекаются) выполнение передаётся на пункт 8.
- В противном случае (если блоки пересекаются), устанавливается флаг DF, который определяет, что циклическое копирование будет выполнено справа навлево.
- К esi прибавляется ecx.
- К edi прибавляется ecx.
- Осуществляется циклическое побайтовое копирование (Ахтунг! Именно побайтовое. Глючит либо меня, либо программистов MS, которые этот код писали. Что если мне нужно скопировать блок размером в мегабайт, и область [откуда] с областью [куда] пересекается всего-лишь одним байтом? Весь мегабайт будет копироваться побайтно?)
- Флаг DF сбрасывается.
- Выполнение переходит на пункт 15.
- Чтобы чуть лучше показать вам разницу, позволю себе вставить картинку с блок-схемами обеих функций (картинка хоть и большая, весит всего 24 кб), сравните:
- А теперь скажите мне, те, кто используют вместо GetMem4 функцию CopyMemory. Вы хоть отдаёте себе отчёт, сколько лишних действий вы производите?