المؤشرات والدوال في ++C
المؤشرات والدوال
علمنا من مقالات سابقة أن المعاملات قد ترحل بين الدوال إما كقيم للمتغيرات المطلوب التعامل معها أو كمعاملات مرجعية , وذلك في حالة أن يكون المطلوب من الدالة المستدعاة أن تغير من قيمة متغير وهو في مكانه في الدالة المستدعية (دون أن تنسخ صورة منه ) في هذه الحالة يمكن أيضا اللجوء لأسلوب المؤشرات .
وسوف نراجع في المثال التالي كيفية استخدام المعاملات المرجعية .
passref.cpp
// passref.cpp
// arguments passed by reference
#include <iostream.h>
#include <conio.h>
void main()
{
void centimize(double&); // prototype
double var = 10.0; // var has value of 10 inches
cout << endl << "var=" << var << " inches";
centimize(var); // change var to centimeters
cout << endl << "var=" << var << " centimeters";
getche();
}
void centimize(double& v)
{
v *= 2.54; // v is the same as var
}
نريد في هذا البرنامج أن نغير من قيمة المتغير var لنحوله من البوصات للسنتيمترات , ومن ثم فقد كان الترحيل بنظام المعاملات المرجعية , باستخدام المؤثر & وفي البرنامج التالي سوف نستخدم أسلوب الترحيل بالمؤشرات للوصول لنفس النتيجة .
passptr.cpp
// passptr.cpp
// arguments passed by pointer
#include <iostream.h>
#include <conio.h>
void main()
{
void centimize(double*); // prototype
double var = 10.0; // var has value of 10 inches
cout << endl << "var=" << var << " inches";
centimize(&var); // change var to centimeters
cout << endl << "var=" << var << " centimeters";
getche();
}
void centimize(double* ptrd)
{
*ptrd *= 2.54; // *ptrd is the same as var
}
عهنا عرفت الدالة الفرعية بأن معاملها يتقبل مؤشرات لنوع double وحين استدعت الدالة الأصلية هذه الدالة , رحل لها عنوان المتغير , &var وليس القيمة المحتواة به , وهو يماثل حالة الترحيل المرجعي من حيث المبدأ .
ولما كانت الدالة الفرعية تتعامل مع المؤشرات فإن تعاملها مع المتغير يكون بأسلوب المخاطبة غير المباشرة , ويبين ذلك من الأمر :
*ptrd *= 2.54:
أي أنه سوف ينفذ علي المتغير ذي العنوان المخزن في المؤشر *ptrd (استخدمت النجمة هنا مرتين , مرة كمؤشر لا مباشرة , ومرة كمؤثر عملية الضرب مع المؤثر =) .
ويبين الشكل أسلوب الترحيل بالمؤشرات .
ترحيل المصفوفات
رأينا في مقالات سابقة كيف تمرر المصفوفات كمعاملات للدوال , وكيفية التعامل مع عناصرها , مستخدمين أسلوب التعبير الخاص بالمصفوفة ] بمعلومية القيم الدليلية indexes [ إلا أن المتبع غالبا هو التعامل مع المصفوفات بأسلوب التعبير الخاص بالمؤشرات ] باعتبار أن اسمها هو عنوان العنصر الأول فيها [ كما سنري في المثال التالي .
passarr.cpp
// passarr.cpp
// array passed by pointer
#include <iostream.h>
#include <conio.h>
const int MAX = 5; // number of array elements
void main()
{
void centimize(double*); // prototype
double varray[MAX] = { 10.0, 43.1, 95.9, 59.7, 87.3 };
centimize(varray); // change elements of varray to cm
for(int j=0; j<MAX; j++) // display new array values
cout << endl << "varray[" << j << "]="
<< varray[j] << " centimeters";
getche();
}
void centimize(double* ptrd)
{
for(int j=0; j<MAX; j++)
*ptrd++ *= 2.54; // ptrd points to elements of varray
}
إن الإعلان عن الدالة الفرعية هنا مماثل له في البرنامج السابق فالمعامل معرف علي أنه لمؤشر من نوع double [] فهي هنا تقابل double* إلا أن صيغة المؤشرات هي الأكثر شيوعا .
ونكرر مرة أخري حيث إن اسم المصفوفة يعبر عن عنوانها فلم نحتج لمؤثر العنوان & ومن ثم كان استدعاء الدالة باسم المصفوفة مجردا من ذلك المؤثر :
Centimize(array);
وفي الدالة الفرعية وضع هذا العنوان في المتغير ptrd ومنه مورست العمليات المطلوبة .
ويبين الشكل طريقة التعامل مع عناصر المصفوفة .
شكل تعامل دالة مع عناصر مصفوفة
ويثور هنا تساؤل متعلق بمسألة الصياغة , كيف نعلم أن التعبير *ptrd++ يزيد قيمة المؤثر وليس المتغير المشار إليه ؟ بمعني أخر , هل يترجم هذا التعبير إلي *(ptr++) أم (*ptr++) ؟ والسبب في وجاهة التساؤل أن كلا المؤثرين : ++ و *(حينما يستخدم مؤشر لا مباشرة ) لهما نفس درجة الأسبقية في التنفيذ .
والقاعدة في حالة تساوي درجة الأسبقية تحدد بطريقة معينة تسمي المصاحبة accciativity , وبموجبها تكون الإجابة بحسب طريقة تنفيذ المترجم للتعبير , هل بدءا من اليمين أم من اليسار . ففي حالتنا هذه تنفذ المؤثرات وحيدة التأثير unary من جهة عملية التزايد تنصب علي المؤثر وليس علي ما يشير إليه .
ترتيب المصفوفات –الترتيب الفقاعي
كمثال أخر لطريقة التعامل مع المصفوفات بأسلوب المؤشرات لنر كيف يمكننا ترتيب عناصر مصفوفة , وسوف نعرض أولا تمهيدي , ترتيب عددين بأسلوب المؤثرات.
ptrorder.cpp
// ptrorder.cpp
// orders two arguments using pointers
#include <iostream.h>
#include <conio.h>
void main()
{
void order(int*, int*); // prototype
int n1=99, n2=11; // one pair ordered, one not
int n3=22, n4=88;
order(&n1, &n2); // order each pair of numbers
order(&n3, &n4);
cout << endl << "n1=" << n1; // print out all numbers
cout << endl << "n2=" << n2;
cout << endl << "n3=" << n3;
cout << endl << "n4=" << n4;
getche();
}
void order(int* numb1, int* numb2) // orders two numbers
{
if(*numb1 > *numb2) // if 1st larger than 2nd,
{
int temp = *numb1; // swap them
*numb1 = *numb2;
*numb2 = temp;
}
}
نعمل الدالة order() كما كانت في البرنامج reforder() عدا أن يحال إليها هو عناوين المتغيرات المطلوب ترتيبها , وأنها تتعامل مع العدد عن طريق مؤشره فعن طريق الرمز *num1 تتعامل الدالة المستدعاة مع المتغير n1 في الدالة الأصلية , ومن خلال *num2 تتعامل مع n2 .
والأن نطور البرنامج ليرتب عناصر مصفوفة
ptrsort.cpp
// ptrsort.cpp
// sorts an array using pointers
#include <iostream.h>
#include <conio.h>
void main()
{
void bsort(int*, int); // prototype
const int N = 10; // array size
// test array
int arr[N] = { 37, 84, 62, 91, 11, 65, 57, 28, 19, 49 };
bsort(arr, N); // sort the array
for(int j=0; j<N; j++) // print out sorted array
cout << arr[j] << " ";
getche();
}
void bsort(int* ptr, int n)
{
void order(int*, int*); // prototype
int j, k; // indexes to array
for(j=0; j<n-1; j++) // outer loop
for(k=j+1; k<n; k++) // inner loop starts at outer
order(ptr+j, ptr+k); // order the pointer contents
}
void order(int* numb1, int* numb2) // orders two numbers
{
if(*numb1 > *numb2) // if 1st larger than 2nd,
{
int temp = *numb1; // swap them
*numb1 = *numb2;
*numb2 = temp;
}
}
في هذا البرنامج يحال كل من مؤشرات عناصر المصفوفة وعدد عناصرها إلي دالة bsort() والتي بدورها تستخدم الدالة order() في عملية الترتيب .
الترتيب الفقاعي
ترتب الدالة bsort() بطريقة تسمي " الترتيب الفقاعي bubble sort . وهذه الطريقة تتميز ببساطتها , إلا أنها بطيئة وإليك طريقة عملها بفرض أن الترتيب تصاعدي ( أنظر الشكلين ) .
في الدورة الأولي تتم مقارنة أول عنصر arr[0] ببقية العناصر واحدا بعد الأخر , وكلما صادف عنصرا أقل منه تبادل معه الوضع . في نهاية هذه الدورة يكون أصغر عنصر قد (طفا) إلي أعلي المصفوفة . في الدورة الثانية يقارن العنصر الثاني مع بقية العناصر , وفي نهايتها يكون ثاني عنصر في القيمة قد احتل المكان الثاني . وتتوالي الدورات حتي نهاية العناصر .
وتحتوي الدالة bort() علي دوارة خارجية تحدد الدورات ودوارة داخلية تحدد العناصر التي تقارن ببعضها البعض عن طريق الدالة order() .
ويبين برنامج ptrsort.cpp قوة المؤشرات فهي تقدم وسيلة فعالة ومنتظمة في التعامل مع عناصر المصفوفات وغيرها من المتغيرات دون حاجة للجوء لأسمائها .
شكل الترتيب الفقاعي
شكل عمل البرنامج ptrsotr.cpp
تعليقات
إرسال تعليق