PDA

عرض كامل الموضوع : المؤشرات في دلفي



GeeK
29-08-2003, 08:02 PM
بسم الله الرحمن الرحيم

السلام عليكم ورحمة الله وبركاته

يعد المؤشر من الأدوات القوية في البرمجة وفي نفس الوقت خطيرة إن لم نحسن استخدامها ... دلفي (باسكال ) من اللغات التي تدعم المؤشر بقوة وفي نفس الوقت لا يجبر المبرمج التعامل مع المؤشرات مباشرة بكثر.. وتقوم بالكثير من الاعمال خلف الكواليس ... وقد لا تستعمل المؤشرات في دلفي كثيرا إلا أن فهمها سوف يساعدك حتما في وقت من الأوقات ..(ملاحظة : نتكلم عن المؤشرات بالتركيز علي استخدامها في دلفي/باسكل ونذكر لكل مصطلح عربي مقابله الانجليزي حتي يساعد في القراءات المستقبلية )

المؤشر Pointer ما هو؟

المؤشر Pointer نوع بيانات متغير 4 بايتات مثل الأنواع الاخري Integer,String إلا أنه يحوي أو يشير إلي موقع في الذاكرة، أو إلي موقع متغير آخر في الذاكرة..

ولأنك أثناء البرمجة جل ما تستخدمه من أهداف والمتغيرات يتم حفظها في مكان ما في الذاكرة فهذا يعني أنك باستخدام المؤشرات تستيطع الوصول إليها بسهولة ...

مثلا لو لديك مؤشر يشير إلي اول بايت للذاكرة وسيطية Buffer يحتفظ بصورة ما، فباستخدام ذلك المؤشر تستطيع الوصول إلي كل الحجرات ( البايتات ) لتلك الذاكرة الوسيطية ..

أو لديك مؤشر إلي هدف object ما، فباستخدام ذلك المؤشر تستطيع الوصول إلي ذلك الهدف والتعامل به، أو تستطيع أن تجعل ذلك المؤشر يشير إلي هدف آخر الخ ( أغلب الأهداف Object يتعامل معها دلفي من خلال المؤشر ولكن بشكل غير ظاهر ) ...

ولان الموضوع معقد سوف نسهب في الكلام والامثلة حتي تستطيع ان تاخذ فكرة كاملة عن عمل المؤشر ...

كيف أمكن الوصول إلي المتغيرات بالمؤشر؟؟علينا أن نطلع علي توضع المتغيرات أو البيانات في الذاكرة ،،

فلنقل أن لدينا متغير من نوع Integer وموقعه في الذاكرة كما تري
لعناوين [1][2]....[7][8][9][A]

الحجرات [?][?]....[3][2][4][5]......[?][?][?][?]

|

أول الموقع للمتغير يبدأ من هنا

كما تري لدينا اربع حجرات( اربع بايتات) عندنا نربط المتغير بمؤشرنا سوف يشير إلي أول حجرة وعنوانها 7 ( في الحقيقة العناوين أكبر بكثير ولكن هنا لمجرد التوضيح ) ولذلك أمكن الوصول، واذا فهمت هذه النقطة جيدا، سوف يساعدك كثيرا في التحويل من مختلف الأنواع ... ليس فقط من المتحول إلي المؤشر او العكس بل من نوع متغير إلي نوع آخر ( مع بعض القيود ) ... الأهم إذن أن تفهم آلية توضع البيانات بأحجامه المختلفة في الذاكرة ...

كيف الإعلان عنه؟

يتم الإعلان عن المؤشر المتغير Pointer Variable باستخدام العلامة ^ caret، مثلا

Var MyPointer : ^Integer

هنا أعلنا عن مؤشر يشير إلي متغير من نوع العدد الصحيح ...

ولكن الطريق الأمثل هو أن نعلن عن نوع مؤشر Pointer Type لنوع آخر ثم نستخدم متغيرا لذلك المؤشر مثلا :

[code:1:]
Type PInteger = ^Integer

Var MyPint : Pinteger ..

[/code:1:]
كيف نستخدمه؟

قلنا أن المؤشر يشير إلي موقع في الذاكرة ( فيه متغير او هدف الخ ) وبه نستطيع الوصول إلي الذاكرة او المتغير ونستطيع ان نتعامل معه ، مثلا إذا أعلنت عن مؤشر، يمكن تخصيص عنوان متغير له باستخدام المعامل @ :
[code:1:]
Var MyInt : Integer;
MyPtr : ^Integer;
Begin
MyInt:=20;
MyPtr:=@MyInt; // Myptr:=Address Myint=20;
MyPtr^:=22; // Myptr:=Address of '22', also Myint=22 ;-)
Myint:=Myptr^; //=22;
MyInt:=Integer(Myptr); // Myint = Address location not value 25;
end;
[/code:1:]
اولا أعلنا عن متغير لنوع Integer، ثم أعلنا عن مؤشر إلي النوع نفسه فأصبح لدينا متغير ومؤشر ، ثم أعطينا المتغير العدد 20، ثم جعلنا المؤشر يشير إلي عنوان المتغير MyInt في الذاكرة وذلك باستخدام العلامة @ التي تعني "في العنوان" ..

والآن MyPtr يشير إلي موقع المتغير MyInt وبذلك نستطيع الوصول إلي العدد 20 باستخدام السطر MyPtr^ ، وهنا العلامة ^ بعد الاسم تعني محتوي العنوان الموقع للذكرة وليس العنوان لموقع الذاكرة ... او محتوي المتغير المحفوظ في ذلك العنوان من الذاكرة الخ ،ولكن السطر الآخير المتغير Myint يحتوي علي العنوان الذي يشير إليه المؤشر من الذاكرة وليس القيمة المحفوظة (22) هنا .. ولكن عكس أيام البرمجة لبيئة دوس، في بيئة ويندوز لم يعد مهما كثيرا الحصول علي عناوين الذاكرة ( مع بعض الاستثناءات )...

والسطر MyPtr^:=22 تعني ضع 22 في عنوان الذاكرة أو المتغير التي يشير إليها المؤشر MyPtr ..وهذا يسمي DeReferencing. فعندما نتعامل مع المحتوي للذاكرة التي يشير إليها المؤشر يجب ان نقوم ب DeReference واستخدام ^ .

وبذلك نري اننا بالمؤشر : نستطيع ان نقرأ من، ونكتب إلي عنوان الذاكرةونستطيع أن نقرأ من ونكتب إلي محتوي عنوان الذاكرة / المتغير المحفوظ في تلك الذاكرة ...

بدلا من الإشارة إلى موقع ذاكرة موجود، يمكن للمؤشر أن يشير إلى مساحة جديدة في الذاكرة يتم تخصيصها بصورة حيّة بواسطة الإجرائية New. في هذه الحالة، و عند إنتهاء حاجتك للمؤشر، عليك أيضا أن تتخلص من الذاكرة التي قمت بحجزها، بإستدعاء الوظيفة Dispose
[code:1:]
var
P: ^Integer;
begin
// initialization
New (P);
// operations
P^ := 20;
ShowMessage (IntToStr (P^));
Dispose (P);
end
[/code:1:]
ملاحظة : من المثال السابق يمكن ان نعرف، اذا لم نقم بحجز ذاكرة للمؤشر، يمكننا ان نجعله يشير إلي موقع في الذاكرة ( متغير ما) ولاداع لحجز ذاكرة ...

معقد صح؟ سوف يصبح سهلة إن شاء الله...

ونأخذ مثالا آخر :
[code:1:]
type
TSomething = record
SomeString: String;
SomeInt: Integer;
end;

PSomething = ^TSomething; //نعلن عن مؤشر إلي السجل
....
....
var
Object1, Object2: TSomething;
ObjPointer: PSomething; // نعلن عن متغير من نوع مؤشر إلي السجل
begin
ObjPointer := @Object1; // نمرر إلي المؤشر عنوان المتغير
ObjPointer^.SomeString := 'Hello'; //نستخدم المؤشر بدلا عن المتغير
ObjPointer^.SomeInt := 25;

ObjPointer := @Object2; // نستخدم نفس المؤشر ليشير إلي متغير سجل آخر
ObjPointer^.SomeString := 'Second'; // نستخدم المؤشر
if ObjPointer^.SomeInt = Object2.SomeInt //نستخدم المؤشر والمتغير للمقارنة
then ShowMessage('القيمتان متساويتان');
end;
[/code:1:]
سؤال ما الفرق بين السطرين الآتيتين ؟
[code:1:]
Var
Inttr : ^integer;
.....
IntPtr := 25; // ما الفرق بين هذا السطر والسطر التالي؟
IntPtr^ := 25;
[/code:1:]
المؤشر العام Untyped Pointer : المؤشر العام او المؤشر غير محدد النوع الذي لم يحدد النوع الذي يشير إليه بل مجرد تم الإعلان عنه بعبارة مثل Myptr : Pointer ... وتسمي Generic Pointer ولانه مؤشر عام غير محدد النوع لا يمكنك استخدام ^ قبل أن تقوم بتحويل النوع TypeCast ... مثلا
[code:1:]
procedure UseMe(const p: Pointer);
var
theInt: ^Integer;
begin
theInt := p; // قمنا بتحويل نوع المؤشر العام إلي مؤشر إلي عدد صحيح
ShowMessage('The number is ' + IntToStr(theInt^));
end;

procedure Test;
var
someInt: Integer;
begin
someInt := 21;
UseMe(@SomeInt);// @ مررنا عنوان المتغير في الذاكرة
end;
[/code:1:]
إذا استخدمت مؤشرات بلا نوع يجب أن تستخدم GetMem بدلا من New. إجرائية GetMem مطلوبة في كل مرة يكون فيها حجم متغير الذاكرة المطلوب حجزه غير محدد. يستخدم المؤشر العام بكثرة في Linked List ... المؤشر المشير إلي لاشي Nil Pointer ... هذا النوع من المؤشر يشير إلي لا شيء Nil، اي يعتبر مؤشرا فارغا، يمكنك استخدام Nil للاختبار عما اذا كان المؤشر يشير إلي موقع صحيح أم لا حتي تتجنب حدوث اخطاء مثل General page fault، او Access violation مثل
[code:1:]
if Myptr = nil then ShowMessage('Pointer is nil');
// او
if Assigned(Myptr) then ShowMessage('Pointer is not nil');
[/code:1:]
ستخدام المؤشر كوسيط parameter إلي الدالة والإجراء ...
أيضا يمكنك استخدام المؤشر أو العلامة Var في دوال مكتبات Windows
[code:1:]
Procedure Myvar(var myit : integer);
//او
Procedure Myptr(myptr:pinteger );// Pinteger مؤشر إلي نوع عدد صحيح
//الاستدعاء
var myint : integer;
Myvar(@myint);
Myptr(@myint);
[/code:1:]
سؤال، اذا كان لدينا مؤشرين ل Integer فلنقل MyInPtr,MyintPtr1 ، ماذا تعني السطرين التالية : MyIntPtr:=MyIntPtr1;
MyIntPtr^:=MyIntPtr1^;

إلي هنا انتهي هذا الجزء ...
إلي اللقاء في الجزء الآخر حيث نتكلم عن استخدام أنواع مؤشرات مثل Pchar والتعمل مع String والمؤشر إلي نوع إجرائي الخ
المراجع :
Marco Canto Essential Pascal
Delphi 6 Developers guide
Mastering Delphi 6
Borland's Object Pascal Reference
www.elistrg.org delphi Mailing list مناقشات دارت في
مواقع في الانترنت و ومقالات من google usenet

GeeK (http://geek.securityway.net/articles/pointerz.html)

البرق
29-08-2003, 10:21 PM
بسم الله الرحمن الرحيم وبعد

أولا مجهود رائع أخي العزيز Geek و أحب ان استفسر لقد قلت



المؤشر Pointer نوع بيانات متغير 4 بايتات مثل الأنواع الاخري Integer,String إلا أنه يحوي أو يشير إلي موقع في الذاكرة، أو إلي موقع متغير آخر في الذاكرة..

ولأنك أثناء البرمجة جل ما تستخدمه من أهداف والمتغيرات يتم حفظها في مكان ما في الذاكرة فهذا يعني أنك باستخدام المؤشرات تستيطع الوصول إليها بسهولة ...

مثلا لو لديك مؤشر يشير إلي اول بايت للذاكرة وسيطية Buffer يحتفظ بصورة ما، فباستخدام ذلك المؤشر تستطيع الوصول إلي كل الحجرات ( البايتات ) لتلك الذاكرة الوسيطية ..

أو لديك مؤشر إلي هدف object ما، فباستخدام ذلك المؤشر تستطيع الوصول إلي ذلك الهدف والتعامل به، أو تستطيع أن تجعل ذلك المؤشر يشير إلي هدف آخر الخ ( أغلب الأهداف Object يتعامل معها دلفي من خلال المؤشر ولكن بشكل غير ظاهر ) ...


سؤالي أخي العزيز ما الفائدة من هذا كله ؟
هل هناك فائدة اخرى غير فائدة السرعة في الوصول الى المعلومة؟

و شكراً

السلطان
19-09-2003, 10:42 AM
GeeK,

مشكور اخوي على موضوعك المميز. .

تحياتي

ابوفيصل
21-09-2003, 10:24 AM
الأخوان ... GeeK والبرق

الله يزيدكم من علمه ... ما شاء الله عليكم


تقبلوا تحياتي ،،،