Рассылка закрыта
При закрытии подписчики были переданы в рассылку "Сетевой адаптер: осваиваем Интернет" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
Все, что вы не знали, но хотели бы узнать о Delphi N9
Выпуск №9 Раздел: Язык Программирования Delphi Подраздел: Работа с указателями+ работа с множествами
Уважаемый подписчик, О чем будет следующий раздел - решать вам. Варианты: VCL Системные функции и Winapi Базы данных Работа с файловой системой Репортинг, работа с принтером Работа с сетью, Интернетом, протоколами Работа с графикой, мультимедиа
Ваши предложения высылайте на В этом выпуске: Избежать использования неактуальных указателей
{ Автор: David S. Becker (dsb@plaza.ds.adp.com) Дата: 1/27/97 Авторские права: Нет Дистрибутивные права: Свободные, неограниченное использование, в случае любых изменений кода с вашей стороны или наличия каких-либо замечаний или предложений пришлите их пожалуйста мне. Данный модуль создавался для помощи в управлении указателями и объектами. Так как компилятор не инициализирует указатели и объекты в nil и не сбрасывает их в nil при освобождении, существует вероятность применения неактуального указателя. По этой причине я рекомендую добавление секции 'initialization' во все модули и вызове Nilify() для всех указателей/объектов в данном модуле. Это позволит быть уверенным, что все указатели/объекты стартуют как nil. Кроме того, вместо стандартных аналогов, вы можете использовать NilFree (для объектов), NilDispose (для указателей, создаваемых с помощью New), и NilFreeMem (для указателей, создаваемых с помощью GetMem). Эти процедуры безопасны при вызове nil-вых указателей/объектов, так как перед выполнением любых действий они проверяют их на nil. После освобождения распределенной указателем/объектом памяти они сбрасываются в nil. Строгое соблюдение функций модуля значительно снижает риск использования неактуального указателя. (Конечно, вы еще можете получить неактуальные указатели из VCL, т.к. они, естественно, не используют данные функции.) } interface { Проверка указателя на nil } { ПРИМЕЧАНИЕ: Данная функция отличается от Assigned() тем, что Assigned() } { требует переменную, а IsNil() нет. } function IsNil(const p: Pointer): Boolean;{ Устанавливает указатель в nil } procedure Nilify(var p);{ Освобождает не-nil объект и устанавливает его в nil } procedure NilFree(o: TObject);{ Освобождает не-nil указатель, созданный с помощью New и устанавливает его в nil } procedure NilDispose(var p: Pointer);{ Освобождает не-nil указатель и устанавливает его в nil } procedure NilFreeMem(var p: Pointer; size: Word); implementation function IsNil(const p: Pointer): Boolean; begin Result := (p = nil); end; procedure Nilify(var p); begin Pointer(p) := nil; end; procedure NilFree(o: TObject); begin if not IsNil(o) then begin o.Free; Nilify(o); end; end; procedure NilDispose(var p: Pointer); begin if not IsNil(p) then begin Dispose(p); Nilify(p); end; end; procedure NilFreeMem(var p: Pointer; size: Word); begin if not IsNil(p) then begin FreeMem(p, size); Nilify(p); end; end; end.
Сначала короткое объяснение арифметике указателя. Когда вы имеете дело с динамическими страницами памяти, то все, что вы имеете - это указатели на начала блоков памяти. Вы хотите просмотреть всю строку памяти, чтобы понять какие функции необходимы для работы с данными, хранящимися в памяти? Это возможно путем изменения места в памяти, на которое указывает указатель. Это называется арифметикой указателя. Основополагающая идея при занятиях арифметикой с указателем - указатель должен быть увеличен на значение корректного приращения. (Корректное приращение определяется размером объекта, на который показывает указатель. Например, char = 1 байт; integer = 2 байта; double = 8 байт и т.д.) Функции Inc() и Dec() изменяют значение корректного приращения. (Компилятор знает правильный размер объекта.) Если вы осуществляете динамическое распределение памяти, то делать это можно примерно так: uses WinCRT; procedure TForm1.Button1Click(Sender: TObject); var MyArray: array[0..30] of char; b: ^char; i: integer; begin StrCopy(MyArray, 'Дельфи - рулез фарева!'); {помещаем что-то в память для организации указателя} b := @MyArray; { назначаем указатель на текущую позицию памяти } for i := StrLen(MyArray) downto 0 do begin write(b^); { пишем символ в текущую позицию указателя. } inc(b); { перемещаем указатель на следующий байт памяти } end; end; Нижеследующий код демонстрирует работу функций Inc() и Dec(), увеличивающих или уменьшающих указатель на размер соответствующего типа: var P1, P2: ^LongInt; L: LongInt; begin P1 := @L; { назначаем оба указателя на одно и то же место } P2 := @L; Inc(P2); { Увеличиваем один } { Здесь мы получаем разницу между смещениями двух указателей. Поскольку первоначально они указывали на одно и то же место памяти, то результатом данного вызова будет разница между двумя указателями после вызова Inc(). } L := Ofs(P2^) - Ofs(P1^); { L = 4; т.е. sizeof(longInt) } end; Вы можете изменить тип объекта, на который указывает P1 и P2, на какой-то другой и убедиться, что (SizeOf(P1^)) всегда возвращает величину корректного приращения (проще сказать, что это размер объекта - В.О.).
...мне также понадобилось в подпрограмме получить ссылку на дочернее MDI-окно без сообщения подпрограмме с каким конкретно классом MDI необходимо работать. Что я сделал: я передавал в виде параметров тип дочернего MDI-окна и ссылку как нетипизированную переменную и затем обрабатывал это в подпрограмме. Вот пример. Эта подпрограмма работает с дочерним окном, которое может иметь только один экземпляр. Если оно не открыто, подпрограмма создаст его, если оно открыто, оно переместит его на передний план. procedure FormLoader(FormClassType: TFormClass; var FormName); begin if TForm(FormName) = nil then begin Application.CreateForm(FormClassType, FormName); end else begin TForm(FormName).BringToFront; TForm(FormName).WindowState := wsNormal; end; end; Вот как это вызывать: procedure TfrmTest.sbOpenClick(Sender: TObject); begin FormLoader(TfrmTest, frmTest); end;
Сначала вы должны создать тип: Type Pinteger : ^Integer; Var MyPtr : Pinteger; Мне кажется, что в начале вы использовали плохой пример, имеет смысл использовать 32-битный указатель для 16-битной величины или распределять 10 байт для переменной. Pascal позволяет вам использовать методы NEW и DISPOSE, которые автоматически распределяют и освобождают правильные размеры блока. Например, NEW(MyPtr) = GetMem(MyPtr, Sizeof(MyPtr)) Возможно, вы захотите подсчитать количество целочесленных переменных. В этом случае ознакомьтесь с возможностями TList. Пока лучше используйте линейный массив (или указатель на первый элемент, чтобы вычислить их количество, достаточно разделить количество занимаемой массивом памяти на количество элементов). Для полноты, это должно быть: NEW(MyPtr) = GetMem(MyPtr, SizeOf(MyPtr^)); SizeOf(MyPtr) всегда будет равен 4 байта, как 16-битный указатель. Если я правильно разобрался в том, что вы хотите (динамический массив целых, количество элеметнов которого может быть известно только во время выполнения приложения), вы можете сделать так: Type pIntArr = ^IntArr; IntArr = Array[1..1000] of Integer; Var MyPtr : pIntArr; Begin GetMem(MyPtr, 10); { 10 = SizeOf(Integer) * 5 !!) { MyPtr[2]:=1; } // <<<< Заполняем массив >>>> MyPtr[2]^:=1; FreeMem(MyPtr,10); End; Технология похожа на ту, которуя Delphi использует при работе с pchar. Синтаксис очень похож: type intarray = array[0..20000] of integer; procedure TForm1.Button1Click(Sender: TObject); var xptr: ^IntArray; begin GetMem(xptr, 10); xptr^[idx] := 1; { где idx от 0 до 4, поскольку мы имеем 10 байте = 5 целых } FreeMem(xptr, 10); end; Обратите внимание на то, в вам в действительности нет необходимости распределять массив для 20,000 элементов, но проверка диапазона Delphi не будет работать, если диапазон равен 20,000. (Предостережение будущим пользователям!)
// Converting method pointers into function pointers // Often you need a function pointer for a callback function. But what, if you want to specify a method as // an callback? Converting a method pointer to a function pointer is not a trivial task; both types are // incomatible with each other. Although you have the possibility to convert like this "@TClass.SomeMethod", // this is more a hack than a solution, because it restricts the use of this method to some kind of a class // function, where you cannot access instance variables. If you fail to do so, you'll get a wonderful gpf. // But there is a better solution: run time code generation! Just allocate an executeable memory block, and // write 4 machine code instructions into it: 2 instructions loads the two pointers of the method pointer // (code & data) into the registers, one calls the method via the code pointer, and the last is just a return // Now you can use this pointer to the allocated memory as a plain funtion pointer, but in fact you are // calling a method for a specific instance of a Class. type TMyMethod = procedure of object; function MakeProcInstance(M: TMethod): Pointer; begin // allocate memory GetMem(Result, 15); asm // MOV ECX, MOV BYTE PTR [EAX], $B9 MOV ECX, M.Data MOV DWORD PTR [EAX+$1], ECX // POP EDX MOV BYTE PTR [EAX+$5], $5A // PUSH ECX MOV BYTE PTR [EAX+$6], $51 // PUSH EDX MOV BYTE PTR [EAX+$7], $52 // MOV ECX, MOV BYTE PTR [EAX+$8], $B9 MOV ECX, M.Code MOV DWORD PTR [EAX+$9], ECX // JMP ECX MOV BYTE PTR [EAX+$D], $FF MOV BYTE PTR [EAX+$E], $E1 end; end; procedure FreeProcInstance(ProcInstance: Pointer); begin // free memory FreeMem(ProcInstance, 15); end; // After all, you should not forget to release the allocated memory. // "TMyMethod" can be modified according your specific needs, e.g. add some parameters for a WindowProc. // N.B.: Yes, I know, Delphi has those "MakeProcInstance" function in its forms unit. // But this works a little bit different, has much more overhead, // and most important, you have to use the forms unit, which increases the size of your exe drastically, // if all other code doesn't use the VCL (e.g. in a fullscreen DirectX/OpenGl app). // Wer noch Fragen hat / if you have questions: Florian.Benz@t-online.de
Множества - это наборы однотипных логически связанных друг с другом объектов. Характер связей между объектами лишь подразумевается программистом и никак не контролируется Object Pascal. Количество элементов, входящих в множество, может меняться в пределах от 0 до 256 (множество, не содержащее элементов, называется пустым). Именно непостоянством количества своих элементов множества отличаются от массивов и записей. Два множества считаются эквивалентными тогда и только тогда, когда все их элементы одинаковы, причем порядок следования элементов в множестве безразличен. Если все элементы одного множества входят также и в другое, говорят о включении первого множества во второе. Пустое множество включается в любое другое. Пример определения и задания множеств: type digitChar = set of '0'..'9'; digit = set of 0. .9; var sl,s2,s3 : digitChar; s4,s5,s6 : digit; begin si := ['1', '2', '3']; s2 := ['3', '2', '1']; s3 := ['2', '3']; s4 := [0..3, 6]; s5 := [4, 5]; s6 := [3..9]; end. В этом примере множества si и s2 эквивалентны, а множество S3 включено в s 2 , но не эквивалентно ему. Описание типа множества имеет вид: <имя типа> = set of <базовый тип>; Здесь <имя типа> - правильный идентификатор; set, of - зарезервированные слова (множество, из); <базовый тип> - базовый тип элементов множества, в качестве которого может использоваться любой порядковый тип, кроме Word, Integer, Longint, Int64. Для задания множества используется так называемый конструктор множества: список спецификаций элементов множества, отделенных друг от друга запятыми; список обрамляется квадратными скобками. Спецификациями элементов могут быть константы или выражения базового типа, а также тип-диапазон того же базового типа. Над множествами определены следующие операции: * пересечение множеств; результат содержит элементы, общие для обоих множеств; например, s4*s6 содержит [3], s4*s5 -пустое множество (см. выше); + объединение множеств; результат содержит элементы первого множества, дополненные недостающими элементами из второго множества: S4+S5 содержит [0,1,2,3,4,5,6]; S5+S6 содержит [3, 4, 5, 6, 7, 8, 9] ; разность множеств; результат содержит элементы из первого множества, которые не принадлежат второму: S6-S5 содержит [3,6,7,8,9]; S4-S5 содержит [0,1, 2, 3, 6] ; = проверка эквивалентности; возвращает True, если оба множества эквивалентны; <> проверка неэквивалентности; возвращает True, если оба множества неэквивалентны; <= проверка вхождения; возвращает True, если первое множество включено во второе; >= проверка вхождения; возвращает True, если второе множество включено в первое; in проверка принадлежности; в этой бинарной операции первый элемент - выражение, а второй - множество одного и того же типа; возвращает True, если выражение имеет значение, принадлежащее множеству: 3 in s 6 возвращает True; 2*2 in si возвращает False. Дополнительно к этим операциям можно использовать две процедуры. include - включает новый элемент во множество. Обращение к процедуре: Include(S,I) Здесь s - множество, состоящее из элементов базового типа TSet Base; I - элемент типа TSetBase, который необходимо включить во множество. exclude - исключает элемент из множества. Обращение: Exclude(S,I) Параметры обращения - такие же, как у процедуры include. В отличие от операций + и -, реализующих аналогичные действия над двумя множествами, процедуры оптимизированы для работы с одиночными элементами множества и поэтому отличаются высокой скоростью выполнения. Учебная программа PRIMSET В следующем примере, иллюстрирующем приемы работы с множествами, реализуется алгоритм выделения из первой сотни натуральных чисел всех простых чисел[ Простыми называются целые числа, которые не делятся без остатка на любые другие целые числа, кроме 1 и самого себя. К простым относятся 1, 2, 3, 5, 7, 11, 13 и т. д.. ]. В его основе лежит прием, известный под названием "решето Эратосфена". В соответствии с этим алгоритмом вначале формируется множество BeginSet, состоящее из всех целых чисел в диапазоне от 2 до N. В множество primerset (оно будет содержать искомые простые числа) помещается 1. Затем циклически повторяются следующие действия: взять из BeginSet первое входящее в него число Next и поместить его В PrimerSet; удалить из BeginSet число Next и все другие числа, кратные ему, Т. е. 2*Next, 3*Next И Т.Д. Цикл повторяется до тех пор, пока множество BeginSet не станет пустым. Эту программу нельзя использовать для произвольного N, так как в любом множестве не может быть больше 256 элементов. procedure TfmExample.bbRunClick(Sender: TObject); // Выделение всех простых чисел из первых N целых const N = 255; // Количество элементов исходного множества type SetOfNumber = set of 1..N; var n1,Next,i: Word; // Вспомогательные переменные BeginSet, // Исходное множество PrimerSet: SetOfNumber; // Множество простых чисел S : String; begin BeginSet := [2..N]; // Создаем исходное множество PrimerSet:= [1]; // Первое простое число Next := 2; // Следующее простое число while BeginSet о [ ] do // Начало основного цикла begin nl := Next; //nl-число, кратное очередному простому (Next) // Цикл удаления из исходного множества непростых чисел: while nl <= N do begin Exclude(BeginSet, nl); n1 := nl + Next // Следующее кратное end; // Конец цикла удаления Include(PrimerSet, next); // Получаем следующее простое, которое есть первое // число, не вычеркнутое из исходного множества repeat inc(Next) until (Next in BeginSet) or (Next > N) end; // Конец основного цикла // Выводим результат: S := '1'; for i := 2 to N do if i in PrimerSet then S := S+', '+IntToStr(i); mmOutput.Lines.Add(S) end; Перед тем как закончить рассмотрение множеств, полезно провести небольшой эксперимент. Измените описание типа SetOfNumber следующим образом: type SetOfNumber = set of 1..1; и еще раз запустите программу из предыдущего примера. На экран будет выведено 1, 3, 5, 7 Множества BeginSet и PrimerSet состоят теперь из одного элемента, а программа сумела поместить в них не менее семи! Секрет этого прост: внутреннее устройство множества таково, что каждому его элементу ставится в соответствие один двоичный разряд (один бит); если элемент включен во множество, соответствующий разряд имеет значение 1, в противном случае - 0. В то же время минимальной единицей памяти является один байт, содержащий 8 бит, поэтому компилятор выделил множествам по одному байту, и в результате мощность каждого из них стала равна 8 элементам. Максимальная мощность множества - 256 элементов. Для таких множеств компилятор выделяет по 16 смежных байт. И еще один эксперимент: измените диапазон базового типа на 1..256. Хотя мощность этого типа составляет 256 элементов, при попытке компиляции программы компилятор сообщит об ошибке: Sets may have at most 256 elements (Множества могут иметь не более 256 элементов) т. к. нумерация элементов множества начинается с нуля независимо от объявленной в программе нижней границы. Компилятор разрешает использовать в качестве базового типа целочисленный тип-диапазон с минимальной границей 0 и максимальной 255 или любой перечисляемый тип не более чем с 256 элементами (максимальная мощность перечисляемого типа - 65536 элементов).
Dec Уменьшает значение переменной на заданную величину. Inc Увеличивает значение переменной на заданную величину. Odd Определяет четность аргумента. Ord Возвращает порядковый номер выражения перечислимого типа или код ASCII выражения символьного типа. Pred Возвращает значение, предшествующее аргументу. Succ Возвращает значение, следующее за аргументом.
Сайт рассылки Здесь Так же можете посетить несколько сайтов для заработка в Интернете: |
В избранное | ||