Page 10 of 12

Re: Начинающий

Posted: Sat Jan 05, 2013 2:06 pm
by Artyom
Leency wrote:1. Помогите разобраться: абстрактный класс - это класс на основе которого нельзя создать объект, а можно только наследовать и уже тогда через дочерний класс работать с ним?

2. Зачем нужен интерфейс, если есть классы? Что он даёт? Только, пожалуйста, по простому.
Leency, ты решил Java освоить?
1. Всё верно;
2. Интерфейс это в своём роде абстракция класса, но с более расширенными возможностями.
В общем на эту тему есть куча интернет-литературы (.pdf, ...).

Re: Начинающий

Posted: Sat Jan 05, 2013 2:16 pm
by Artyom
Вообще-то, значение Интерфейсов сложно объяснить словами, каждый для себя его воспринимает по-разному.
Если задуматься о самом значении слова Интерфейс, то это нечто соединяющие (связующие) какой-то объект с другим объектом.

Re: Начинающий

Posted: Sat Jan 05, 2013 10:46 pm
by Freeman
Интерфейсы используются в тех языках, авторы которых не смогли решить проблему множественного наследования и, перестраховавшись, разрешили его только от интерфейсов.

Re: Начинающий

Posted: Fri Jan 11, 2013 10:39 pm
by GerdtR
Не понимаю, как динамические библиотеки используют память? Типа как
mov eax,[var]
var dd 123
Они же динамические. Как они определяют куда писать/читать.

Re: Начинающий

Posted: Sat Jan 12, 2013 1:12 am
by IgorA
GerdtR wrote:Они же динамические. Как они определяют куда писать/читать.
Как в Windows такой код себя поведет не знаю. А в Колибри если сделать подобное, то сначала будет создаваться локальная копия всей бибилиотеки для конкретного приложения. И меняться будет данные в локальной версии библиотеки. Это расходует память, т. к. создается копия не только для переменных а и для програмного кода. Потому подобных ситуаций следует избегать, но во многих библиотеках они встречаються.

Re: Начинающий

Posted: Sun Jan 13, 2013 6:32 pm
by GerdtR
Т.е. мне лучше просто выделить динамически память и использовать её через указатель в качестве переменных? Кстати далёкие переходы тоже лучше не делать? Только относительные?

Re: Начинающий

Posted: Mon Jan 14, 2013 12:06 pm
by CleverMouse
GerdtR, при компиляции команды типа mov eax,[var] fasm генерирует код команды и запоминает, что на таком-то месте был адрес var. В двоичном формате это знание успешно игнорируется, но в более сложных форматах в той или иной форме есть список перемещаемых элементов aka relocations, в котором записано, что там-то и там-то находятся адреса. Загрузчик библиотеки уже знает, где именно в памяти размещена библиотека, и проходит по списку перемещаемых элементов, корректируя адреса. В результате после загрузки mov eax,[var] читает откуда надо.
IgorA, неверно. Нет никакой локальной копии всей библиотеки для конкретного приложения.
GerdtR, глобальных переменных действительно лучше избегать, но по причинам, не связанным с библиотеками: глобальные переменные, к которым много обращений, могут существенно усложнить выяснение "какая собака здесь порылась, что теперь у переменных какие-то странные значения"; с глобальными переменными обязательно будут проблемы, если к ним есть обращение из двух потоков параллельно. Глобальные константы - возможно, вычисляемые и записываемые один раз при инициализации, но не при работе - пожалуйста. И, между прочим, каждый адрес глобальной переменной - 4 байта в коде и лишняя запись в таблице перемещаемых элементов. Лучше использовать локальные переменные:

Code: Select all

include 'proc32.inc'
proc MyAwesomeProc
locals
var1 dd ?
var2 dd ?
endl
mov [var1],eax
ret
endp
Для справки: это транслируется примерно в

Code: Select all

if used MyAwesomeProc
MyAwesomeProc:
push ebp
mov ebp, esp
sub esp, 8 ; allocate space for two dword variables
mov [ebp-4], eax
leave
ret
end if
Локальные переменные создаются на стеке, для их адресации используется регистр ebp. Если регистров не хватает и очень хочется задействовать ebp для вычислений, то можно адресовать и напрямую через esp:

Code: Select all

proc MyAwesomeProc
sub esp, 8 ; allocate space for two dwords
virtual at esp
.var1 dd ?
.var2 dd ?
end virtual
mov [.var1], eax
add esp, 8 ; restore the stack
ret
endp
но тогда придётся очень внимательно следить за всеми операциями со стеком, которые сбивают esp; кроме того, адресация через esp экономит на прологе push ebp/mov ebp,esp, но теряет по байту на каждом обращении к переменным.

Re: Начинающий

Posted: Mon Jan 14, 2013 7:29 pm
by IgorA
CleverMouse wrote:IgorA, неверно. Нет никакой локальной копии всей библиотеки для конкретного приложения.
Странно, мне Марио раньше сказал совсем другое, вот цитата из моего ЛС:
Только сейчас заметил, то что ты сделал в SVN r. 1489.
Я не знаю сам ли ты додумался или кто подсказал столь гениальное решение, но ты в курсе что записывая в область памяти библиотеки ты автоматом создаешь дублирующую копию всей библиотеки Box_Lib в памяти приложения вызвавшего Lib_Init?

Программа перестает пользоваться спроецированной копией библиотеки и следовательно каждая программа будет потреблять лишнюю память в размере 60 Кб (если не ошибаюсь). С одной стороны немного, но с другой это нехорошая тенденция.

Изменяемые данные должны храниться только в области специально предназначенной для работы - work area, обычно так ее называют. Она не является частью библиотеки и на нее передается указатель через стек. Твои изменения сломали этот подход.

Re: Начинающий

Posted: Mon Jan 14, 2013 7:50 pm
by CleverMouse
Я не знаю, откуда Марио это взял. В действительности там постраничный copy-on-write: viewtopic.php?p=29192#p29192

Re: Начинающий

Posted: Tue Jan 15, 2013 8:24 pm
by GerdtR
За идею с локальными переменными спасибо, в некоторых случаях подойдёт. Хотя глобальные всё равно надо. Хотя я использовал их как в обычной проге. Запустилось и сбоев никаких. Только лишь хотелось как-то поэкономней с размером библиотеки. Затраты опер. памяти пока особо не волнует.

Re: Начинающий

Posted: Wed Jan 16, 2013 12:06 am
by GerdtR
И ещё вопрос))) Надо чтобы во время работы окна MsgBox основное приложение останавливалось и ждало результата. Хотел всё основать на проверке первого байта в структуре сообщения на 0, но оказалось, что при закрытии бокса(я про крестик, разумеется) там так и остаётся ноль. И функцию на крестик нельзя приделать(не понимаю, зачем так сделано) Можно ли всё-таки обойтись библиотекой или свою мини-версию делать?
И ещё) Когда выделяешь память, то она забита нулями или как получится?
И ещё)). Вин функции сохраняют регистры, поэтому приходится делать тоже самое. Я делаю это при помощи pushad/popad. Но когда функция должна вернуть значение приходится как-то сохранять значение. Пока я это делал так:
mov [var],eax
popad
mov eax,[var]
Теперь, конечно, сделаю var локальным, но вообще как такое обычно делают? Нырял в исходники ядра, что бы посмотреть на сам код int40h, но пока ни разу до шёл)))

Re: Начинающий

Posted: Wed Jan 16, 2013 10:36 am
by IgorA
GerdtR wrote:И функцию на крестик нельзя приделать(не понимаю, зачем так сделано) Можно ли всё-таки обойтись библиотекой или свою мини-версию делать?
Чтобы сделать функцию на крестик нужно дорабатывать код самой msgbox. Это не было сделано потому что такое никто не просил сделать. На форуме есть тема посвященная бибилотеке msgbox :
viewtopic.php?f=45&t=1241
С нее можно скачать исходные коды бибилиотеки, чтобы доработать ее. Вот только на svn исходных кодов нет, там сразу файл msgbox.obj идет.
GerdtR wrote:Когда выделяешь память, то она забита нулями или как получится?
В виндовсе в большинстве случаев да, но может быть и мусор. Как в Колибри не знаю, наверное также.
GerdtR wrote:Вин функции сохраняют регистры, поэтому приходится делать тоже самое. Я делаю это при помощи pushad/popad.
Вин функции возвращают значение через регистр eax, потому скорее всего в них делается push/pop для всех изменяемых регистров кроме eax.
Есть еще один способ возвращения значения, через стек. Но этот способ сложноват, потому что нужно правильно вычислить место в стеке где будет возвращаемое значение.

Re: Начинающий

Posted: Wed Jan 16, 2013 12:26 pm
by Insolor
IgorA wrote: Вин функции возвращают значение через регистр eax, потому скорее всего в них делается push/pop для всех изменяемых регистров кроме eax.
По соглашениям stdcall и cdecl функция может менять eax, ecx, edx, об их сохранности должен заботиться вызывающий код. Пруф

Re: Начинающий

Posted: Wed Jan 16, 2013 12:38 pm
by Asper

Code: Select all

push ebx ecx edx esi ;registers saved
;...
pop esi edx ecx ebx ;registers restored

Re: Начинающий

Posted: Wed Jan 16, 2013 1:25 pm
by CleverMouse
GerdtR wrote:И ещё) Когда выделяешь память, то она забита нулями или как получится?
Зависит от того, что значит "выделять память". Функции ядра типа 68.12 обнуляют память при выделении. Но они оперируют целыми страницами, и если нужно выделять мелкие куски типа нескольких десятков байт, получается излишний расход памяти. В подобных случаях нужно использовать какой-нибудь user-mode менеджер, выделяющий, условно говоря, одну страницу из системы, какую-то часть этой страницы для поддержки собственной информации, остальное нарезающий кусками по запросу; user-mode менеджеры, как правило, ничего не гарантируют - новый кусок вполне может содержать данные, оставшиеся от ранее выделенного и освобождённого куска.
GerdtR wrote:И ещё)). Вин функции сохраняют регистры, поэтому приходится делать тоже самое. Я делаю это при помощи pushad/popad. Но когда функция должна вернуть значение приходится как-то сохранять значение. Пока я это делал так:
mov [var],eax
popad
mov eax,[var]
Теперь, конечно, сделаю var локальным, но вообще как такое обычно делают? Нырял в исходники ядра, что бы посмотреть на сам код int40h, но пока ни разу до шёл)))
pushad/popad относительно медленные, хотя и короткие. Лучше сохранять и восстанавливать только те регистры, которые нужно восстановить - например, только ebx, esi, edi. Именно в такой постановке можно написать

Code: Select all

mov [esp+1Ch],eax
popad