ترحيل المعاملات بالإشارة في ++C

ترحيل المعاملات بالإشارة

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

ويعمل أسلوب ترحيل المعاملات بالإشارة refeance argument ] يمكن أن تسمي أيضا ترحيل مرجعي [ بأسلوب مغاير ؛ فبدلا من إرسال قيمة المتغير المراد معالجته , نرسل لها ما يشير إليه لديها , وتتعامل الدالة المستدعية مع هذا المتغير مباشرة وهو في مكانه من الدالة المستدعيه . بمعني أن معاملات الدالة المستدعاة لا تختزن القيم المرحلة لها من الدالة المستدعية , بل تختزن فقط إشارة (أو مرجع) reference للمتغير المراد التعامل معه , ( الواقع أن ما يرحل بين الدالتين هو عنوان المتغير في الذاكرة , وبذلك تستبدل الدالة المستدعاة عليه , ولكن هذا التفصيل ليس له أهمية في فهم فكرة هذا الأسلوب ) , ويميز المعامل الذي يرسل بالإشارة للمتغير وليس بقيمته بعلامة & تلحق باسمه .

وإليك برنامج يطبق هذا الأسلوب :

ref.cpp


 


// ref.cpp


// demonstrates passing by reference


#include <iostream.h>


#include <conio.h>


 


void main()


   {


   void intfrac(float, float&, float&);      // prototype


   float number, intpart, fracpart;          // float variables


   do


      {


      cout << "\nEnter a real number: ";     // number from user


      cin >> number;


      intfrac(number, intpart, fracpart);    // find int and frac


      cout << "Integer part is " << intpart  // print them


       << ", fraction part is " << fracpart;


      } while( number != 0 );                // exit loop on 0


   getche();


   }


 


// intfrac()


// finds integer and fractional parts of real number


void intfrac(float n, float& intp, float& fracp)


   {


   intp = float( long(n) );  // convert to long, back to float


   fracp = n - intp;         // subtract integer part


   }




تطلب الدالة الأصلية من المستخدم أن يدخل رقما حقيقيا (عدد صحيح وكسر عشري ) , وتقوم الدالة الفرعية بفصل الجزئين . ثم تعيدها للدالة الأصلية التي تتولي إخراجهما علي الشاشة , مثلا :



Enter a real number: 99.44



Integer part is 99, fractional part is 0.44



وتقوم الدالة الفرعية بفصل الجزء الصحيح بتحويل العدد بأكمله من نوع float إلي نوعين long int عن طريق الأمر :



(راجع التحويل القسري , الفصل الثالث ) ويتسبب هذا التحويل للنوع في أن يحذف الجزء الكسري ( متغيرات الأعداد الصحيحة لا تخزن إلا الجزء الصحيح ) , ثم يعاد الناتج ( الجزء الصحيح ) إلي سيرته الأولي (كعدد كسري ) بتحويل قسري أخر , مخزنا في المتغير intp :



Intp = float ( long(n) );



] لاحظ تحويل قسريين في عبارة واحد [ .



أما الجزء الكسري والمخزن في المتغير fracp فهو ببساطة العدد الأصلي n مطروحا منه الجزء الصحيح intp .



تلاحظ أن معاملي الدالة الفرعية الخاصين بالجزء الصحيح والجزء الكسري معرفين كمعاملات بالإشارة , أما المعامل الأول , الخاص بالعدد الرحل نفسه , فقد عرف علي أنه معامل عادي (معامل (مراحل) بالقيمة value argument ) .



والأهمية العملية لهذا الأسلوب هو إمكانية أن الدالة المستدعاة يمكنها التعامل المباشر مع متغيرات الدالة المستدعية من جهة . ولك أن تدرك ميزة ذلك إذا كان البيان المراحل هيكل بيانات ضخم , مثل هيكل معقد أو مصفوفة كبيرة , وما يسببه نسخ هذه البيانات من إهدار لمسافة التخزين بالذاكرة .



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



C   3



شكل الإستدعاء غير المباشر



] في الشكل يعبر المؤلف عن أسماء المتغيرات في الدالة المستدعاة بأنها aliases أي أسماء مستعارة [ .



مثال أخر :



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






reforder.cpp


 


// reforder.cpp


// orders two arguments passed by reference


#include <iostream.h>


#include <conio.h>


 


void main()


   {


   void order(int&, int&);       // prototype


 


   int ras1=99, ras2=11;             // this pair not ordered


   int ras3=22, ras4=88;             // this pair ordered


 


   order(ras1, ras2);                // order each pair of numbers


   order(ras3, ras4);


 


   cout << endl << "ras1=" << ras1;  // print out all numbers


   cout << endl << "ras2=" << ras2;


   cout << endl << "ras3=" << ras3;


   cout << endl << "ras4=" << ras4;


   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;


      }


   }




إمرار الهياكل بالإشارة



نقدم لك المثال التالي لبيان كيفية التعامل معه الهياكل بالأسلوب غير المباشر . وتقوم الدالة الفرعية بضرب الأبعاد في معامل معين , يمكن النظر إليه كمقياس رسم .






referst.cpp


 


// referst.cpp


// demonstrates passing structure by reference


// UCS Laboratories


#include <iostream.h>


#include <conio.h>


 


struct Distance                       // English distance


   {


   int feet;


   float inches;


   };


 


void scale( Distance&, float );       // function


void engldisp( Distance );            // declarations


 


void main()


   {


   Distance ucs1 = { 12, 6.5 };         // initialize ucs1 and ucs2


   Distance ucs2 = { 10, 5.5 };


 


   cout << "\nucs1 = "; engldisp(ucs1);   // display old ucs1 and ucs2


   cout << "\nucs2 = "; engldisp(ucs2);


 


   scale(ucs1, 0.5);                    // scale ucs1 and ucs2


   scale(ucs2, 0.25);


 


   cout << "\nucs1 = "; engldisp(ucs1);   // display new ucs1 and ucs2


   cout << "\nucs2 = "; engldisp(ucs2);


   getche();


   }


 


// scale()


// scales value of type Distance by factor


void scale( Distance& ucs, float factor)


   {


   float inches = (ucs.feet*12 + ucs.inches) * factor;


   ucs.feet = inches / 12;


   ucs.inches = inches - ucs.feet * 12;


   }


 


// engldisp()


// display structure of type Distance in feet and inches


void engldisp( Distance ucs )   // parameter ucs of type Distance


   {


   cout << ucs.feet << "\'-" << ucs.inches << "\"";


   }




وقد طبعت الدالة مرتين , مرة لتخفيض أبعاد الأول بمقدار النصف , ومرة لتخفيض أبعاد الهيكل الثاني بمقدار الربع . وتلاحظ أن هذه العمليات جرت علي كل من d1,d2 مباشرة , فإذا لم تعد أي شئ , بل مارست وظيفتها علي الهيكلين وهما بمقرهما في الدالة الأصلية .



ملاحظات حول إمرار الإشارة



هذا الأسلوب متبع في كل من لغتي البيزك والباسكال , ولكنه غير متبع في السي التقليدية فهي تتبع أسلوب المؤشرات عوضا عنه .

التسميات: