Logo

ماركو كانتو:
أساسي باسكال

الفصل 8
الذاكرة

ملاحظة من المؤلّف: سيغطّي هذا الفصل موضوع مناولة الذاكرة. مناقشا مناطق الذاكرة المختلفة، كما يعرّف المصفوفات الحيوية. مؤقتا فقط الجانب الأخير هو المتوفر.

المصفوفات الحيوية في دلفي 4

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

ادخلت دلفي 4 طريقة تنفيذ بسيطة جدا للمصفوفات الحيوية، انتظمت بعد نوع الجمل الطويلة الحيوية التي كنت قد ناقشتها منذ قليل. مثلها مثل الجمل الطويلة، المصفوفات الحيوية يتم تخصيصها و تعدادا اشاراتها آنيا، لكنها تخلو من تقنية النسخ عند الكتابة copy-on-write. هذا لا يشكّل مشكلة كبيرة، حيث يمكنك نزع تخصيص deallocate المصفوفة بجعل متغيرها خاليا nil.

يمكنك الآن ببساطة تعريف مصفوفة بدون الحاجة لتحديد عدد عناصرها ثم تقوم بتخصيصها بحجم معين باستخدام إجراء SetLength. يمكن إستخدام نفس الإجراء لتغيير حجم المصفوفة دون أن تفقد محتوياتها. توجد أيضا إجرائيات أخري لها علاقة بالجمل، مثل وظيفة Copy ، و التي يمكنك استخدامها مع المصفوفات.

فيما يلي مقطع من توليف بسيط، يبرز حقيقة انك يجب أن تقوم بتعريف و تخصيص الذاكرة الخاصة بالمصفوفة قبل أن تبدأ باستعمالها:

procedure TForm1.Button1Click(Sender: TObject);
var
  Array1: array of Integer;
begin
  Array1 [1] := 100; // error
  SetLength (Array1, 100);
  Array1 [99] := 100; // OK
  ...
end;

حالما تحدد فقط عدد العناصر في المصفوفة، يبدأ فهرس المصفوفة دائما من صفر. المصفوفات عامة في باسكال معروفة بإمكانية أن يكون حدّها الأدني غير الصفر و أن تكون فهارسها ليست أعداد صحيحة، خاصيّتان لا تدعمهما المصفوفات الحيوية. لمعرفة حالة المصفوفة الحيوية، يمكنك استخدام وظائف Length و High و Low، كما في أي مصفوفة أخرى. بالنسبة للمصفوفات الحيوية وظيفة Low ترجع دائما قيمة 0، و High ترجع دائما الطول ناقص 1. هذا يعني أنه بالنسبة للمصفوفة الفارغة فإن High ترجع -1(الأمر الذي عندما تتأمل فيه، تجده رقما غريبا، فهو أقل من ذلك المرتجع من Low).

شكل 8.1: نموذج مثال DynArr

بعد هذا التقديم القصير يمكنني أن أريك مثالا بسيطا يدعى DynArr و يظهر في الشكل 8.1. هو بالتأكيد بسيط لأنه لا يوجد أمرا معقّدا فيما يخصّ المصفوفات الحيوية. أنا سأستخدم المثال أيضا لعرض بعض الأخطاء التي قد يقع فيها المبرمجون. يعرّف البرنامج مصفوفتين جامعتين global و يقوم بتمهيد الأول في مناول حدث OnCreate :

var
  Array1, Array2: array of Integer;

procedure TForm1.FormCreate(Sender: TObject);
begin
  // allocate
  SetLength (Array1, 100);
end;

هذا يجعل كل القيم صفرا. توليف التمهيد هذا يجعل من الممكن البدء حالا بقراءة و كتابة قيم المصفوفة، دون أي خوف من أخطاء الذاكرة. (طبعا، بافتراض أنك لن تحاول الوصول إلى عناصر خارج الحدّ العلوي للمصفوفة.) و من أجل تمهيد أفضل، لدى البرنامج زرا يقوم بالكتابة داخل كل خلية في المصفوفة:

procedure TForm1.btnFillClick(Sender: TObject);
var
  I: Integer;
begin
  for I := Low (Array1) to High (Array1) do
    Array1 [I] := I;
end;

زرّ Grow يسمح لك بتعديل حجم المصفوفة بدون أن تفقد محتوياتها. يمكنك إختبار هذا باستعمال قيمة زرّ Get بعد الضغط على زرّ Grow:

procedure TForm1.btnGrowClick(Sender: TObject);
begin
  // grow keeping existing values
  SetLength (Array1, 200);
end;

procedure TForm1.btnGetClick(Sender: TObject);
begin
  // extract
  Caption := IntToStr (Array1 [99]);
end;

التوليف الوحيد المعقّد قليلا هو في حدث OnClick لزرّ Alias. البرنامج ينسخ مصفوفة داخل الأخرى بواسطة العامل :=، منشئا بكفاءة رديفا Alias (متغير جديد يشير إلى نفس المصفوفة في الذاكرة). عند هذه النقطة، على أي حال، إذا قمت بتعديل أحد المصفوفتين، ستتأثر الأخرى كذلك، بالنظر إلى أن كلتاهما تشيران إلى نفس منطقة الذاكرة:

procedure TForm1.btnAliasClick(Sender: TObject);
begin
  // alias
  Array2 := Array1;
  // change one (both change)
  Array2 [99] := 1000;
  // show the other
  Caption := IntToStr (Array1 [99]);

مسار btnAliasClick يقوم بعمليتين أخريين. الأولى هي إختبار تساوي على المصفوفتين. هذه العملية لا تختبر العناصر الفعلية للبنيتين و لكن تختبر مناطق الذاكرة التي تشير لها المصفوفتان، فتتفحّص إذا ما كانا المتغيران هما رديفان لنفس المصفوفة في الذاكرة.

procedure TForm1.btnAliasClick(Sender: TObject);
begin
  ...
  if Array1 = Array2 then
    Beep;
  // truncate first array
  Array1 := Copy (Array2, 0, 10);
end;

العملية الثانية هي إستدعاء لوظيفة Copy، و التي ليست فقط تنقل البيانات من مصفوفة للأخرى، لكنها أيضا تستبدل بالمصفوفة الأولى المصفوفة الجديدة التي تم إنشاؤها بواسطة الوظيفة. التأثير هو أن متغير Array1 الآن يشير إلى مصفوفة تحوي 11 عنصرا، لذا فإن الضفط على زرّ Get value أو زرّ Set value سوف يولّد خطأ ذاكرة و يبرز اعتراضا exception (إلا إذا كنت قد أوقفت خيار تفحّص المدى range-checking، في هذه الحالة يظلّ الخطأ لكن الاعتراض لا يتم اظهاره). توليف زرّ Fill يستمر في العمل جيدا حتى بعد هذا التغيير، حيث أن تحديد عناصر المصفوفة التي سيتم تعديلها يتم بمعرفة حدّيها الحاليين.

ملخّص

يغطّي هذا الفصل مؤقتا المصفوفات الحيوية، و هو بالتأكيد عنصرا مهمّا في إدارة الذاكرة، لكنه جزءا فقط من كامل الصورة. المزيد من الموضوعات ستتبع لاحقا.

بنية الذاكرة التي تم وصفها في هذا الفصل هي من صميم برمجة ويندوز، الموضوع الذي سأتولى تقديمه في الفصل التالي (بدون الخوض في كامل موضوع استخدام VCL، مع ذلك).

الفصل التالي: برمجة ويندوز

حقوق النسخ محفوظة لماركو كانتو؛ وينتش ايطاليا © Copyright Marco Cantù, Wintech Italia Srl 1995-2000
حقوق الترجمة: خالد الشقروني ، 2000