المؤشرات والدوال في ++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 (استخدمت النجمة هنا مرتين , مرة كمؤشر لا مباشرة , ومرة كمؤثر عملية الضرب مع المؤثر =) .



ويبين الشكل أسلوب الترحيل بالمؤشرات .



Object-Oriented Programming in C   _Page_0471_Image_0001



ترحيل المصفوفات



رأينا في مقالات سابقة كيف تمرر المصفوفات كمعاملات للدوال , وكيفية التعامل مع عناصرها , مستخدمين أسلوب التعبير الخاص بالمصفوفة ] بمعلومية القيم الدليلية 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 ومنه مورست العمليات المطلوبة .



ويبين الشكل طريقة التعامل مع عناصر المصفوفة .



Object-Oriented Programming in C   _Page_0473_Image_0001



شكل تعامل دالة مع عناصر مصفوفة



ويثور هنا تساؤل متعلق بمسألة الصياغة , كيف نعلم أن التعبير *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 قوة المؤشرات فهي تقدم وسيلة فعالة ومنتظمة في التعامل مع عناصر المصفوفات وغيرها من المتغيرات دون حاجة للجوء لأسمائها .



Object-Oriented Programming in C   _Page_0477_Image_0001



شكل الترتيب الفقاعي



Object-Oriented Programming in C   _Page_0478_Image_0001



شكل عمل البرنامج ptrsotr.cpp

التسميات: