ALX_2002 писал(а):Надеюсь правильно интерпретировал
- Код: Выделить всё
Private Declare Function FreeMemoryA Lib "wbotho.dll" Alias "FreeMemory" (ByRef lpMemory As Long)
Нет.
ByRef lpMem As tagStatus_Check
или
ByVal lpMem As Long
С огромным удовольствием послушаю или почитаю ! Въехать в процесс для меня важнее чем просто тупо воспользоваться готовой функцией.
Представим себе, что VB компилирует на сразу, а переводит VB-код с C-style-код.
В сях есть два унарных оператора: * и &.
*xxx — значит (грубо) "то, что лежит по адресу, хранящемуся в xxx"
*123 — значит (грубо) "то, что лежит по адресу 123".
В отрыве от типа указателя такая запись имеет мало смысла (потому что компилятор не знает, а что должно лежать по адресу), но если xxx имеет правильный тип, то запись обретает смысл.
В частоности, по записи [/icode]*123[/icode] совершенно непонятно, идёт л речь о байте, лежащем по адресу 123, или же о структуре, лежащей по адресу 123. О чём идёт речь компилятор понимает, как я уже сказал, по типу операнда, поэтому достаточно скастовать операнд к правильному указательному типу, чтобы смысл возник:
Выражение
*( (char*)123) будет иметь смысл "байт, лежащий по адресу 123". Замечу, что * — это такой же унарный оператор как например - (минус), т.е. точно также, как можно писать "-a" или
-(a)[icode] или даже [icode]-(((a))), точно также абсолютно допустимой будет запись
*a,
*(a) или даже
*(((a))).
Так что вообще говоря, оператор * принимает в качестве операнда не адрес, а указатель. Так что если foo имеет тип "long*", то
*(foo) и
*(foo+1) обратятся к лонгам, отстающим в памяти друг от друга не на 1 байт, а на 1*sizeof(long). О как!
Кстати в сях
aaaaa[bbbb] всего лишь означает
*(aaaa + bbbb)Другой оператор & — возвращает указатель (типа указатель на
некоторыйтип), приняв в качестве аргумента выражение
некотороготипа.
Очевидно, что & и * — взаимообратны друг другу примерно так же, как [синус и арксинус] или [возведение в квадрат и квадратный корень].
Поэтому они "взаимоуничтожают" друг-друга, так что
*(&(fooo)) эквивалентно просто
fooo. Для примера: совершенно очевидно, что
&(*(123)) будет 123, потому как "адрес того, что лежит по адресу 123" будет 123.
Но опять же замечаю: * принимает не адрес, а указатель. & возвращает не адрес, а указатель.
Важно понимать, что указатель и адрес — разные вещи.
Закончили ликбез по сишный прелестям.
Так вот: в VB нет указателей, доступных пользователю, а значит нет и указательный типов (понятие "тип переменной/выражения" в VB гораздо уже, чем оно в С/С++), нет операторов, способных либо принять указатель на что-то и отдать это что-то, либо принять что-то и вернуть указатель на это что-то. (Но сами по себе указатели в VB есть, потому что их не может не быть при наличии хотя бы возможности передать что-то ByRef.)
Так вот, поскольку всего этого в VB нет, проиллюстрировать придётся на сишном примере.
Представим, что VB-компилятор не сразу же компилирует VB-код, а переводит его в сишный.
Рассматривать будем аргументы функций, имеющие тип long (потому что с более сложными всё обстоит несколько иначе).
В объявлении функции
ByVal foo As Long эквивалентно сишному
long foo.
В объявлении функции
ByRef foo As Long эквивалентно сишному
long* foo.
В коде функции, имеющей ByVal-аргумент "foo", всякое упоминание этого аргумента, то есть
foo, эквивалентно сишному
fooВ коде функции, имеющей ByRef-аргумент "foo", всякое упоминание этого аргумента, то есть такое же
foo, будет в сях эквивалентно
*fooВ коде функций, при вызове другой функции, когда что-то передаётся в качестве аргумента, и при том в "другой функции" этот аргумент ByRef-ный, то в сишном эквиваленте перед этим "что-то" будет стоять оператор &.
Вот такие правила. Теперь несколько примеров:
VB:
- Код: Выделить всё
Sub Alpha(ByVal AA As Long, ByRef BB As Long)
Dim CC As Long
Dim DD As Long
CC = AA
DD = BB
Omega AA, BB, CC, DD
End Sub
Sub Omega(ByVal param1 As Long, ByVal param2 As Long, ByRef param3 As Long, ByRef param4 As Long)
SomeOtherSub param1
SomeOtherSub param2
SomeOtherSub param3
SomeOtherSub param4
End Sub
Sub SomeOtherSub (ByVal xyz As Long)
...
End Sub
С++:
- Код: Выделить всё
void Alpha(long AA, long* BB)
{
long CC;
long DD;
CC = AA;
DD = *BB;
Omega(AA, *BB, &CC, &DD);
}
void Omega(long param1, long param2, long* param3, long* param4)
{
SomeOtherSub(param1);
SomeOtherSub(param2);
SomeOhterSub(*param3);
SomeOtherSub(*param4);
}
void SomeOtherSub(long xyz)
{
...
}
Теперь смотри то на эти два куска кода, то на правила "перевода" VB-кода в сишный.
Вот ещё один пример:
VB:
- Код: Выделить всё
Sub Ebru(ByVal xxx As Long, ByRef yyy As Long)
Kaya xxx, yyy, xxx, yyy
End Sub
Sub Kaya(ByVal aaa As Long, ByVal bbb As Long, ByRef ccc As Long, ByRef ddd As Long)
...
End Sub
C++:
- Код: Выделить всё
void Ebru(long xxx, long* yyy)
{
Kaya(xxx, *yyy, &(xxx), &(*yyy) ); // Здесь последнее &(*yyy) упрощается до yyy
}
void Kaya(long aaa, long bbb, long* ccc, long* ddd)
{
...
}
Т.е. смотри: у нас в Ebru в yyy — ByRef и в Kaya аргумент ddd — тоже ByRef. Поэтому выполняется сразу два правила "упоминание ByRef-аргумента" и "передача чего бы то ни было в качестве ByRef-аргумента". Поэтому добавляется и *, и &, которые взаимоуничтожают друг-друга.
Но многим требовалась в VB возможность всё-таки получить адрес, поэтому был сделан следующий трюк: создана функция VarPtr, которая просто возвращает число, которое её передали. Т.е. на самом деле функция объявлена
так:
- Код: Выделить всё
Function VarPtr (ByVal foo As Long)
VarPtr = foo
End Sub
или, если на С++:
- Код: Выделить всё
long VarPtr(long foo)
{
return foo;
}
Но VB заставили (нарочно неправильно объявив в TLB) думать, что VarPtr объявлена так:
- Код: Выделить всё
Function VarPtr(ByRef xxx As Any) As Long
Поэтому, когда мы пишем:
- Код: Выделить всё
Sub Russia(ByVal SomeParam As Long)
Dim SomeVar As Long
dim a as long
dim b as long
a = VarPtr(SomeParam)
b = VarPtr(SomeVar)
End Sub
То это эквивалентно:
- Код: Выделить всё
void Russia(long SomeParam)
{
long SomeVar;
long a, b;
a = VarPtr(&SomeParam);
b = VarPtr(&SomeVar);
}
long VarPtr(void* something) // Так себе представляет функцию VB
long VarPtr(long something) {return something;} // Такая она на самом деле
Как видишь, срабатывает правило "передаём <что-то> в другую функцию, имеющую ByRef-аргумент — добавляем & перед <чем-то>".
Учитывая то, что VarPtr возвращает то же, можно считать, что её вообще нет в коде (если её действительно убрать из кода, то будет
a = &SomeParam; и
a = &SomeVar; — что неправильно с точки зрения сей, потому как переменная
SomeVar имеет тип "long", тогда выражение
&SomeVar имеет тип "long*", и получается, что мы пытаемся присвоить значение одного ("long*") типа переменной другого (просто "long") типа, чего делать нельзя, не скастовав выражение к типу "long").
Теперь смотри что случится, когда мы в VarPtr передадим ByRef-аргумент:
VB:
- Код: Выделить всё
Sub Main()
Dim CoolVariable As Long
CoolVariable = 777
FirstAction CoolVariable
End Sub
Sub FirstAction(ByRef xxx As Long)
SecondActionAction xxx
End Sub
Sub SecondActionAction(ByRef xxx As Long)
ThirdAction xxx
End Sub
Sub ThirdAction(ByRef dunduck As Long)
Dim result As Long
result = VarPtr(xxx) ' И вот тут мы вдруг захотели получить адрес аргумента dunduck
End Sub
в С++:
- Код: Выделить всё
void Main()
{
long CoolVariable;
CoolVariable = 777;
FirstAction(&CoolVariable);
}
void FirstAction(long* xxx)
{
SecondActionAction(&(*xxx));
}
void SecondActionAction(long* xxx)
{
ThirdAction(&(*xxx));
}
void ThirdAction(long* dunduck)
{
long result;
result = VarPtr(&(*dunduck))
}
Тут мы видим, что кроме первого правила, сработало ещё и второе "всякое упоминание ByRef-аргумента
abc внутри функции выглядит как
*abc".
С учётом того, что половина упрощается:
- Код: Выделить всё
void Main()
{
long CoolVariable;
CoolVariable = 777;
FirstAction(&CoolVariable);
}
void FirstAction(long* xxx)
{
SecondActionAction(xxx);
}
void SecondActionAction(long* xxx)
{
ThirdAction(xxx);
}
void ThirdAction(long* dunduck)
{
long result;
result = VarPtr(dunduck)
}
Так вот, рассмотрим, что у нас происходит.
1) В переменную CoolVariable записывается 777.
2) Выражение
&CoolVariable "вычисляется". Результатом его вычисления является указатель на переменную CoolVariable, в которой лежит 777.
3) Этот указатель на переменную CoolVariable, в которой лежит 777, передаётся в FirstAction.
4) FirstAction ничего с этим указателем (на CoolVariable, в которой 777) не делает, передаёт его в SecondAction.
5) SecondAction ничего с этим указателем (на CoolVariable, в которой 777) не делает, передаёт его в ThirdAction.
6) ThirdAction принимает указатель на CoolVariable, в которой лежит 777, ничего с этим указателем не далает, передаёт его в VarPtr.
7) В VarPtr передаётся этот указатель, а фактически (физически) — адрес переменной CoolVariable. По этому адресу в памяти лежит число 777.
VarPtr тупо возвращает этот адрес как обычное число.
9) Этот адрес, как обычное число, записывается в long-переменную result.
В итоге: в переменной result у нас получается не адрес аргумента "dunduck", а адрес переменной SecondAction.
Так что с помощью VarPtr невозможно никакими трюками получить адрес ByRef-аргумента — всегда будешь получать его физическое значение.
(Пора бы завязыватьс с этой графоманией. На написание ответов трачу слишком много времени
)