صفحة 2 من 4 الأولىالأولى 1234 الأخيرةالأخيرة
النتائج 16 إلى 30 من 46

الموضوع: :: مشروع لتعليم أساسيات ++C :: موضوع الدروس

  1. #16
    التسجيل
    11-04-2002
    الدولة
    سوريا
    المشاركات
    760

    مشاركة: :: مشروع لتعليم أساسيات ++C :: موضوع الدروس

    المثال 3 /

    نريد من المستخدم إدخال 10 أعداد صحيحة ( int ) و من ثم نكتب له أكبر عدد و أصغر عدد من بين تلك الأعداد .


    مناقشة الحل :

    1 – نريد ثلاثة متغيرات صحيحة ( int ) :

    A : من أجل الأعداد التي سيدخلها المستخدم .
    Max : من أجل أكبر عدد .
    Min : من أجل أصغر عدد .

    2 – سنستخدم الحلقة for لأننا عرفنا مسبقاً عدد الأعداد التي نريد اختبارها .

    الحل /
    كود:
    #include <iostream.h>
    void main()
    {
        int a, max, min;
    
        cout << "Enter the Number 1 : ";
        cin >> a;
    
        max = a;
        min = a;
    
        for( int i=2; i<=10; i++ )
        {    
            cout << "Enter the Number " << i << " : ";
            cin >> a;
    
            if( a > max ) max = a;
            if( a < min ) min = a;
        }
        
        cout << "Max = " << max << endl;
        cout << "Min = " << min << endl;
    }
    شرح الحل :

    1 – قمنا بتعريف المتغيرات المطلوبة من النوع int .

    2 – سأشرح فكرة السؤال ثم سأخبركم لماذا قمت بذلك :

    أولاً / طلبنا من المستخدم العدد الأول و من ثم جعلنا قيم min و max تساوي ذلك العدد .. و بعد ذلك بقي لدينا 9 أعداد لم يدخلها المستخدم بعد لذلك نضع حلقة تبدأ من 2 و تنتهي بـ 10 .. لأن العدد الأول قد حصلنا عليه ..

    ثانياً / عند إدخال كل عدد من الأعداد سنختبر فيما إذا كان هذا العدد أكبر من max ( افترضنا أن العدد الأول هو الأكبر ) فإذا كان كذلك فهذا يعني أن هذا العدد هو الأكبر و بالتالي نجعل max تساوي ذلك العدد .. و نختبر أيضاً فيما إذا كان هذا العدد أصغر من min ( افترضنا أيضاً أن العدد الأول هو الأصغر ) فإذا كان كذلك فهذا يعني أن هذا العدد هو الأصغر و بالتالي نجعل قيمة min تساوي هذا العدد .

    ثالثاً / نكتب قيمة العدد الأكبر و الأصغر .

    ملاحظة / لا يمكن أن نستخدم else هنا .. لأنه إذا لم يكن a أكبر من max فهذا لا يعني أنه أصغر من min .

    لماذا لم نضع إدخال العدد الأول داخل الحلقة ؟ قلت سابقاً أنه لا يجوز أن نقوم بأي عملية أو اختبار على متحول ما لا يحمل قيمة .. و بالتالي إذا وضعنا إدخال العدد الأول داخل الحلقة for .. فلن نستطيع إعطاء قيمة أولية للمتغيرين max و min .. حاول أن تضع إدخال العدد الأول داخل الحلقة و شاهد الأخطاء التي ستظهر .

    ----------------------------------------

    الأن سنقوم بحل بعض الأمثلة الجميلة المتعلقة برسم أشكال على الشاشة بواسطة الرمز ( * ) .. و سنحاول رسم بعض هذه الأشكال .. و كمثال على ذلك انظر الصورة :



    هذه الأشكال تعتمد بشكل كبير على الحلقات for و بالتالي إذا استطعتم حلها و فهم كيفية رسم هذه الأشكال فستعرفون أنكم قد أتقنتم كل ما تعلمناه حتى الأن حول التكرار .

    ملاحظة / هذه الأشكال وجدتها كأمثلة موجودة في أحد الكتب .. و هو باسم "كيف تبرمج بلغة السي++" للدكتور صلاح الدوه جي .. و هذا الكتاب هو لدار لشعاع الموجودة في سوريا .. و هو أفضل كتاب موجود في سوريا يشرح السي++ بشكل كامل لأنه ليس كتاب بل مرجع كامل .. لذلك من استطاع الحصول عليه فلا يتردد بشرائه .

    المثال 4 /

    نريد رسم الشكل السابق .. لذلك سنستخدم for كما قلنا .. بحيث في السطر الأول يوجد 5 نجوم و الثاني 4 و الثالث 3 و الرابع 2 و الخامس نجمة واحدة فقط .

    مناقشة الحل :
    1 – نحتاج إلى حلقتين for متداخلتين .. لماذا ؟ لأن هناك عملتي تكرار :
    الأولى / من أجل الكتابة على كل 5 أسطر .
    الثانية / من أجل كتابة مجموعة من النجوم على كل سطر .
    2 – سنستخدم متغير التكرار I من أجل السطر ( من أجل التنقل بين الأسطر ) .. و المتغير j من أجل العمود ( المتمثل في كتابة النجوم كما ذكرنا ) .


    الحل /
    كود:
    #include <iostream.h>
    void main()
    {
        for( int i=5; i>=1; i-- )
        {
            for( int j=1; j<=i; j++ )
                cout << "*";
    
            cout << endl;
        }
    }
    شرح الحل :

    1 – قلنا بأن الحلقة الأولى من أجل التنقل بين الأسطر .. حيث أنه في كل دورة سيتم كتابة مجموعة من النجوم .. و بالتالي عرفنا الأن فائدة الحلقة الأولى .. و نحن نحتاج إلى 5 أسطر .. و لكن لماذا وضعنا التكرار بشكل تنازلي من 5 إلى الواحد و ليس العكس ؟ انظر الفقرة التالية .

    2 – الحلقة الثانية من أجل كتابة النجوم .. و لكن كيف يمكننا أن نكتب تلك النجوم ؟ لاحظ العلاقة بين عدد النجوم و السطر .. في التكرار الأول سنكتب 5 نجوم و بالتالي يجب أن تكون القيمة النهائية للحلقة الثانية 5 و لكننا سنجد أن الأمر خاطئ إذا كتبنا 5 لأننا بذلك سنكتب 5 نجوم في كل سطر .. لذلك يجب أن نعتمد في الحلقة الثانية على قيم i المتمثلة في السطر .. هل عرفت الأن لماذا وضعنا i شكل تنازلي .

    ففي الدورة الثانية ( السطر الثاني ) ستكون قيمة i تساوي 4 و بالتالي سنكتب 4 نجوم .. و في الدورة الثالثة ( السطر الثالث ) ستكون i تساوي 3 و بالتالي سنكتب 3 نجوم .. و هكذا .

    لاحظ أيضاً أننا وضعنا الأمر : cout << endl من أجل النزول سطر في كل دورة .

    ----------------------------------------

    المثال 5 /

    الأن نريد رسم هذا الشكل :


    مناقشة الحل :
    لاحظ هذا الشكل فهو يشابه الشكل السابق و لكن أصبح في السطر الأول نجمة واحدة فقط و الثانية نجمتان و هكذا .. لذلك انظر إلى العلاقة بين السطر و عدد النجوم .. نجد أن أنهما متساويان لذلك يجب وضع قيم السطر i بنفس قيم العمود j .

    الحل /
    كود:
    #include <iostream.h>
    void main()
    {
        for( int i=1; i<=5; i++ )
        {
            for( int j=1; j<=i; j++ )
                cout << "*";
    
            cout << endl;
        }
    }
    شرح الحل :

    هذا الحل هو نفس الحل السابق .. و لكن غيرنا قيم i بحيث أصبحت تتزايد من الواحد إلى 5 .. و قلنا سبب ذلك لأننا نريد أن تكون القيمة النهائية لـ j مساوية لـ i بسبب تساوي رقم السطر مع رقم العمود .

    ----------------------------------------
    لا إله إلا الله
    مشروع لتعليم أساسيات لغة الـ ++C :
    -
    موضوع لتعليم الفيجوال بيسك 6 :

  2. #17
    التسجيل
    11-04-2002
    الدولة
    سوريا
    المشاركات
    760

    مشاركة: :: مشروع لتعليم أساسيات ++C :: موضوع الدروس

    المثال 6 /
    نريد رسم مستطيل فارغ بحيث عدد الأسطر و الأعمدة تساوي 10 .. انظر الصورة :


    مناقشة الحل :

    بنفس طريقة المثالين السابقين .. سنضع الحلقة الأولى من أجل التنقل بين الأسطر و الثانية من أجل التنقل بين الأعمدة .. و أيضاً سنستخدم الشرط لمعرفة أماكن رسم النجوم .. انظر الحل :

    الحل /
    كود:
    #include <iostream.h>
    void main()
    {
        for( int i=1; i<=10; i++ )
        {
            for( int j=1; j<=10; j++ ) 
                if( i==1 || i==10 || j==1 || j==10 )
                    cout << "*";
                else
                    cout << " ";
    
            cout << endl;
        }
    }
    شرح الحل :

    بنفس الأسلوب .. و لكننا أضفنا هنا شرط .. سيفيدنا هذا الشرط في تحديد أماكن رسم النجوم .. انظر إليه جيداً ..
    لقد جعلنا النجوم ترسم في السطر الأول و العاشر .. و في العمود الأول و العاشر .. كيف ذلك ؟ قلنا أن في الحلقات المتداخلة ( مثل ما هو مذكور ) ستكون i للسطر و j للعمود .. لذلك اختبرنا فيما إذا كانت I تساوي الواحد أو عشرة .. و ذلك ينطبق على j أيضاً .. فإذا تحقق الشرط نرسم نجمة و إذا لم يتحقق نرسم فراغ ( لأننا نريد المستطيل فارغ ) .


    ملاحظة مهمة / الشرط يتبع الحلقة الثانية الخاصة بـ j و لا تتبع الحلقة الأولى .. و لم نضع أقواس للحلقة الثانية لأنها تحتوي على تعليمة واحد و هي if .. و حيث أن else تتبع if .

    قاعدة / باستطاعتك عن طريق هذا المثال رسم عدة أشكال عن طريق تحديد رقم الأسطر و الأعمدة التي تريد رسم النجوم فيها .. و ذلك بتغيير الشرط فقط .. مثلاً :

    1 – لرسم الحرف N : يكون الشرط ..
    كود:
    if( j==1 || j==10 || i==j )
    نحدد العمود الأول و العاشر .. بالإضافة إلى المكان الذي يتساوى فيه I مع j لأننا نريد رسم مستقيم مائل و يمكن ذلك عندما يكون رقم السطر يساوي رقم العمود .

    ملاحظة / حاول دائماً معرفة العلاقة بين السطر و العمود .. لكي تعرف مكان رسم النجوم .

    2 – لرسم الحرف Z : يكون الشرط ..
    كود:
    if( i==1 || i==10 || i+j==11 )
    نحدد السطر الأول و العاشر .. بالإضافة إلى المكان الذي يكون مجموع السطر مع العمود يساوي 11 .. لاحظ العلاقة بين السطر و العمود :

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

    ----------------------------------------

    :: النافذة Watch ::


    تستخدم هذه النافذة من أجل معرفة القيم التي تأخذها المتغيرات التي قمت بتعريفها في برنامج .. و ذلك أثناء عمل البرنامج .. و بالتالي تستطيع معرفة الأخطاء أو أماكن الضعف في برنامج .

    يمكنك استخدام هذه النافذة من خلال أوامر القائمة Debug و ستظهر تلك النافذة باستخادم أربع أوامر .. سنأخذ نحن أمرين :

    الأول / باستخدام F10 / Step Over /
    من أجل الانتقال سطراً سطراً داخل برنامج أثناء عمله و بالتالي ستعرف كيف يقرأ المترجم برنامجك .. و لكنك بهذا الأمر لن تدخل إلى داخل التوابع عند استدعائها أو إلى الأماكن الأخرى التي ينتقل إليها المترجم .. بل ستكتفي بالتنقل ضمن التابع الذي يوجد به المترجم حالياً .

    الثاني / باستخدام F11 / Step Into /
    نفس الأمر السابق و لكنك ستدخل إلى كل التفاصيل و إلى داخل التوبع أو الأوامر التي وضعتها في برنامجك .. و يمكنك طبعاً استخدام كلا الأمرين معاً .. على حسب المكان الذي تريد الإنتقال إليه .. مثلاً إذا كنت تريد إلى تفاصيل تابع ما فاستخدم F11 عند سطر استدعائه .. أما لو كنت لا تريد ذلك فاستخدم F10 و بالتالي سيتم الإنتقال إلى السطر الذي يلي سطر استدعاء ذلك التابع ( ستتوضح الأمور حينما تجرب تلك الأوامر بنفسك ) .. و سنأخذ التوابع في الدرس القادم إن شاء الله .

    ملاحظة 1 / لن تظهر النافذة Debug أو النافذة Watch إلا حين استخدام أحد الأمرين السابقين .. و ذلك بالضغط فوراً إما على F10 أو F11 .. و ستظهر في الأسفل على اليمين .. أما القائمة Debug فستظهر ضمن القوائم .


    ملاحظة 2 / في النافذة Watch ضع اسم المتغير الذي تريد معرفة قيمه ضمن اللائحة Name و ستظهر قيمه ضمن اللائحة Value .

    ملاحظة 3 / تستطيع الخروج من عملية إزالة الأخطاء Debug بالضغط على F7 و بعد ذلك اضغط Ok .

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


    ---------- ---------- ---------- نهاية الدرس الثالث ---------- ---------- ----------

    حاول حل الأمثلة التي كتبناها اليوم على جهازك .. و في الدرس القادم إن شاء الله سنأخذ التوابع و ستشعر بأنك بدأت بدراسة البرمجة الحقيقة .. بالتوفيق .
    التعديل الأخير تم بواسطة Wolf Sniper ; 14-02-2005 الساعة 12:48 AM
    لا إله إلا الله
    مشروع لتعليم أساسيات لغة الـ ++C :
    -
    موضوع لتعليم الفيجوال بيسك 6 :

  3. #18
    التسجيل
    11-04-2002
    الدولة
    سوريا
    المشاركات
    760

    مشاركة: :: مشروع لتعليم أساسيات ++C :: موضوع الدروس

    السلام عليكم ...

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

    ---------- ---------- ---------- الدرس الرابع ---------- ---------- ----------
    اليوم : الخميس ... التاريخ : 17 / 2 / 2005

    :: البرامج الجزئية ( التوابع ) ::

    :: مقدمة ::

    كان أسلوبنا في حل المسائل السابقة هو أن نضع البرنامج كله ككتلة واحدة .. و في الحقيقة أن البرامج الحديثة اليوم لا تتبع هذا الأسلوب .. بل تأخذ تلك البرامج شكلاً آخر .. هو بتقسيم البرنامج ( مهما كان حجمه ) إلى أجزاء صغيرة تسمى الكائنات .. و لكننا نريد الأن التوابع التي هي جزء من هذه الكائنات .

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

    أمثلة على التوابع :

    1 – تابع لإيجاد متوسط مجموعة من الأعداد .
    2 – تابع لإيجاد العدد الأكبر أو الأصغر من بين مجموعة من الأعداد .
    3 – تابع لحل معادلة من الدرجة الثانية .. بحيث نعطيه البيانات اللازمة فيقوم بالعملية الخاصة به .
    4 – تابع الإظهار ( الكتابة ) على الشاشة .
    5 – تابع من أجل ترتيب مجموعة من الأعداد .. بحيث نعطيه تلك الأعداد فيعيدها لنا مرتبة .
    6 – تابع إيجاد القيمة المطلقة و الجذر التربيعي لعدد ما .

    فائدة التوابع :

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

    2 – سهولة استخدام عملية ( وظيفة ) ما عدد كبير من المرات عن طريق استدعاء ذلك التابع بسطر واحد فقط .. فإذا كنا لا نستخدم التوابع فإننا سنضطر إلى كتابة قوانين تلك العملية في كل مرة نحتاج إلى تلك العملية .

    3 – تستطيع استخدام التوابع التي كتبتها في برامج أخرى .. بحيث تضعها في مكتبة خاصة بك .. و من ثم تستدعيها عن طريق #include .

    خلاصة / إن جميع المكتبات القياسية الموجودة في اللغة مثل :
    1 – math.h .
    2 – string.h .
    3 – ctype.h .

    و الكثير من المكتبات الأخرى هي في الحقيقة مجموعة من التوابع الجاهزة الخاصة بلغة السي++ .. فانظر مثلاً إلى هذه العملية التي أخذناها سابقاً :
    كود:
    sqrt();
    هذه العملية من أجل إيجاد الجذر التربيعي لعدد ما .. و هي تابع من توابع المكتبة math.h بحيث ندخل له عدد ما عن طريق وضعه داخل القوسين و من ثم سيعيد لنا الجذر التربيعي .

    :: تعريف ::

    البرنامج الجزئي ( التابع ) : هو كتلة برمجية مستقلة لها بداية و نهاية و اسم يستدعى به .. و يخصص لمعالجة جزء من مسألة كبرى .

    :: الإعلان عن التوابع ::

    يتم الإعلان عن تابع عن طريق ذكر نوعه ثم اسمه ثم وضع القوسين ( سنأخذ فائدتهما في هذا الدرس ) و بعد ذلك نضع جسم ذلك التابع .
    كود:
    void Name()
    {
        Function body
    }
    void : تمثل أي نوع من الأنواع التي أخذناها .. مثل : int أو float أو ... إلخ .. و لكننا مبدئياً سنستعمل void .
    Name : من أجل اسم التابع .
    Function body : جسم التابع الذي يحتوي على التعليمات الخاصة به .

    ملاحظة / انظر إلى شكل التوابع .. هل عرفت الأن لماذا التابع main كبقية التوابع .. و لكن يختلف عنهم أن المترجم سيبدأ قراءة برنامجك منه .

    مثال / حيث أن a و b متغيرات عددية عامة ( سأشرح معنى عامة و خاصة في هذا الدرس ) من النوع int :
    كود:
    void read_num()
    {
        cin >> a;
        cin >> b;
    }
    :: استدعاء التوابع ::

    يتم استدعاء التوابع ( أي طلب عملها ) بكتابة اسمها فقط متبوعاً بالقوسين و ما بداخل هذين القوسين من متغيرات وسيطة ( إن وجدت ) في أي مكان من برنامجك ( وطبعاً لا تنسى الفاصلة المنقوطة ) .. بشرط أن يكون ذلك الاستدعاء إما بداخل التابع main أو في تابع من التوابع الأخرى .. لأننا نستطيع استدعاء تابع داخل تابع آخر .

    فعلى سبيل المثال : افرض أننا وضعنا تابعين .. الأول لإيجاد مجموع الأعداد و الثاني لحساب المتوسط .. و بالتالي يمكن أن يستفيد التابع الثاني من الأول بأن يستدعيه أثناء عمله ليطلب من ذلك التابع إيجاد مجموع تلك الأعداد .. لأن قاونون المتوسط هو : مجموع الأعداد على عددها .

    مثال / لبرنامج يقوم بطلب إدخال عددين من المستخدم و من ثم يقوم بكتابتهما مرة أخرى على الشاشة .

    ملاحظة / بالنسبة لتعريف المتحولات العامة a و b سيتم شرحمها في الفقرة التالية .
    كود:
    int a, b;
    
    void read_num()
    {
        cin >> a;
        cin >> b;
    }
    
    void print_num()
    {
        cout << a << endl;
        cout << b << endl;
    }
    
    void main()
    {
        read_num();
        print_num();
    }
    ملاحظة مهمة / يجب وضع القوسين دائماً عند استدعاء التابع .. و ذلك للتمييز بين التابع و المتحول .

    :: المتحول العام و الخاص ::

    1 – المتحول العام :

    - يعلن عنه في بداية البرنامج و خارج كل التوابع ( حتى التابع main ) .
    - يحجز مكانه في الذاكرة عند بداية البرنامج .. و يحرر ( يلغى أو يفقد قيمته ) عند انتهاء البرنامج .
    - يمكن استخدامه في أي مكان من برنامجك ( في كل التوابع ) .

    2 – المتحول الخاص :

    - يعلن عنه داخل التابع .
    - يحجز مكانه في الذاكرة عند الدخول إلى التابع ( استدعائه ) .. و يحرر عند انتهاء استدعاء ذلك التابع .
    - يمكن استخدامه فقط داخل التابع الذي تم تعريفه فيه .. و بالتالي لا يمكن استخدامه في التوابع الأخرى أو حتى في التابع main .

    ملاحظة / حاول التقليل من المتحولات العامة قدر الإمكان .. لماذا ؟ لأن هذه المتحولات ستبقى طوال فترة برنامجك ( كما ذكرنا ) و يمكن أن يكون استخدامها لفترة قصيرة فقط .. لذلك سيتم تحميل الذاكرة ببيانات لا نحتاجها .. و نحن نهدف إلى تخزين القيم المهمة فقط في الذاكرة و البقية يجب التخلص منه .

    ملاحظة / يمكن أن يكون هناك متحول خاص يحمل نفس اسم متحول عام آخر .. و لكن إذا قمنا باستخدام ذلك الاسم ( داخل التابع الذي تم تعريف ذلك المتحول الخاص فيه ) فسيعتبر المترجم أنك تقصد المتحول الخاص .. و هذا يقودنا إلى قاعدة .

    قاعدة / المتحول الخاص له أولية في التعامل من المتحول العام عند تشابه أسماء المتغيرات العامة و الخاصة .

    مثال /
    كود:
    int a;
    void r()
    {
        int a;
        cin >> a;
    }
    هنا سيتم إدخال عدد من المستخدم و سيتم تخزينه في المتحول الخاص ( الموجود داخل التابع r ) و ليس في المتحول العام .
    لا إله إلا الله
    مشروع لتعليم أساسيات لغة الـ ++C :
    -
    موضوع لتعليم الفيجوال بيسك 6 :

  4. #19
    التسجيل
    11-04-2002
    الدولة
    سوريا
    المشاركات
    760

    مشاركة: :: مشروع لتعليم أساسيات ++C :: موضوع الدروس

    :: العملية الثنائية ( :: ) ::

    تستخدم هذه العملية من أجل كسر القاعدة السابقة ( أولوية المتحول الخاص عند تشابه الأسماء ) .. و بالتالي عند استخدامها سيتم تجاهل المتحول الخاص و اعتبار المتحول العام هو المقصود .

    يكتب هذا الرمز قبل اسم المتحول .. كالتالي :
    كود:
    ::a
    مثال / نفس المثال السابق مع بعض التعديل :
    كود:
    int a;
    void r()
    {
        int a;
        cin >> ::a;
    }
    في هذا المثال سيتم إدخال عدد صحيح و تخزينه في المتحول العام و ليس الخاص .

    تمرين على المتحولات العامة و الخاصة /
    اكتب قيم المتحولات العامة النهائية و التي ستظهر في نهاية البرنامج .
    كود:
    int a, b, c;
    
    void f1()
    {
        int a,b;
        a = 10; b = 20; c = 30;
    }
    
    void f2()
    {
        int b=0;
        a++; b++; c++;
    }
    
    void main()
    {
        a = 1; b=2; c=3;
        f1();
        f2();
        cout << a << endl << b << endl << c << endl;
    }
    ملاحظة / عند قراءتك لأي برنامج ابدأ دائماً بالتابع main .

    النتائج :
    كود:
    a = 2          b = 2          c = 31
    شرح النتائج :

    1 – أولاً نجد أن هناك ثلاثة متحولات عامة و هي a و b و c .

    2 - نبدأ القراءة من التابع main .. فنجد ثلاثة عمليات إسناد ( إعطاء قيم ) بحيث أخذ a القيمة واحد .. و b أخذ القيمة 2 .. و c أخذ القيمة 3 ..

    3 – بعد ذلك قمنا باستدعاء التابع f1 و بالتالي سينتقل المترجم إلى ذلك التابع لقراءته ( أي أنه سينتقل إلى جسم ذلك التابع ) .. في ذلك التابع : سنجد أن هناك متحولين ( من النوع الخاص ) يحملان نفس الاسم لمتحولين عامين و هما a و b .. و بالتالي سيتم تجاهل المتحولين العامين و استخدام هذه المتحولات الخاصة بهذا التابع .. حيث قام هذا التابع بإعطاء المتحولين a و b قيم معينة و هي متحولات خاصة به .. و لكنه أعطى المتحول العام c قيمة جديدة و هي 30 .. لذلك قيم المتغيرات العامة حتى الأن هي :
    كود:
    a = 1          b = 2          c = 30
    و بهذا يكون المترجم قد نفذ هذا التابع .. و بعد ذلك سيرجع المترجم قراءة باقي برنامجك في التابع main .. أي إلى السطر الذي يلي مكان استدعاء التابع f1 .

    4 – قمنا بعد ذلك باستدعاء التابع الثاني f2 .. و بالتالي سينتقل إليه المترجم لقراءته .. و نجد أن في ذلك التابع متحول خاص باسم b و يحمل نفس اسم المتحول العام .. لذلك سيتم تجاهل المتحول العام b في هذا التابع ..
    نجد بعد ذلك أن هناك ثلاث عمليات إضافة واحد لكل متغير .. و الذي يهمنا هو المتحولين العامين a و c .. لأن b تم تجاهله كما قلنا .. و بالتالي ستصبح القيم كالتالي :
    كود:
    a = 2          b = 2          c = 31
    بعد ذلك سيعود المترجم للسطر الذي يلي سطر استدعاء التابع f2 .

    5 – قمنا بكتابة قيم المتحولات العامة .

    ملاحظة / من أجل زيادة الفهم .. استخدم النافذة Watch من أجل معرفة قيمة المتحولات العامة التي ستأخذها أثناء استدعاء التوابع .. و استخدم F10 للمرور على استدعاء التابع دون الدخول إلى جسمه .. و استخدم F11 للدخول إلى التابع لمعرفة ما قام به من تغييرات على المتحولات ( راجع النافذة Watch في الدرس السابق ) .

    :: المتحولات الوسيطة ( Parameters ) ::

    هل تتذكر الأقواس الموجود بعد اسم التابع ؟ الأن سنعرف فائدتها .. تستخدم هذه الأقواس من أجل القيام بإدخال قيم يحتاجها التابع لكي يقوم بالعمليات المناسبة عليها .. كيف ؟ افرض أنك تريد كتابة تابع يحسب مجموع عددين .. و بالتالي يجب أن تعطيه العددين لكي يجمعه .. و من الخطأ أن نستخدم العددين كمتحولات عامة للأسباب المذكرة سابقاً ( بسبب تحميل الذاكرة ببيانات لا نحتاجها ربما إلا مرة واحدة و حيث أن المتحولات العامة ستبقى حتى نهاية برنامجك .. و بالتالي سيزداد حجم الذاكرة دون فائدة ) .. إذاً يجب أن نستخدم المتحولات الوسيطة من أجل إدخال هذين العددين إلى داخل التابع .

    قاعدة / داخل الأقواس الخاصة بالتابع و التي توضع بعد اسمه مباشرة .. نضع نوع المتغير ثم اسمه كأننا نعرف أي متغير آخر .. و إذا كنت تريد إدخال أكثر من متغير إلى التابع فاستخدم الفاصلة ( , ) من أجل ذلك .. و لكن انتبه لا يجوز أن تضع الأتي :
    كود:
    …(int a,b)
    بل عليك مع كل متغير وسيط أن تضع نوعه قبل اسم ذلك المتغير الوسيط .

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

    ملاحظة 2 / المتحولات المعرفة داخل التابع main هي أيضاً متحولات خاصة بهذا التابع و ليست عامة .. لأن التابع main هو كباقي التوابع ( كما قلنا ) و لكنه يتميز عنهم بأن برنامجك يبدأ التنفيذ عنده أولاً .

    مثال /
    كود:
    void avr(int a, int b)
    {
        int c = ( a+b ) / 2;
        cout << c << endl;
    }
    
    void main()
    {
        int x, y;
        x = 10; y = 20;
        avr( x, y );
    }
    شرح المثال :

    في هذا المثال قمنا بتعريف عددين صحيحين و هما x و y .. و من ثم أعطيناهم قيم معينة .. و بعد ذلك قمنا بإرسال هذين العددين للتابع avr لكي يحسب المتوسط و من ثم يكتب الناتج .

    ملاحظة 1 / المتحولات الوسيطة الموجودة في تعريف التابع .. تسمى بالمتحولات الوسيطة الشكلية .. بينما المتحولات الموجودة بين القوسين عند استدعاء التابع تسمى بالمتحولات الفعلية ( لأنها هي التي ستجرى عليها العمليات ) .
    ملاحظة 2 / لا يشترط تشابه أسماء المتحولات الفعلية مع المتحولات الشكلية .

    قواعد :
    1 – عدد المتحولات الوسيطة الشكلية يجب أن يساوي عدد المتحولات الوسيطة الفعلية .
    2 – أنواع المتحولات الوسيطة الفعلية يجب أن تكون متوافقة بالترتيب مع المتحولات الوسيطة الشكلية .
    التعديل الأخير تم بواسطة Wolf Sniper ; 17-02-2005 الساعة 01:06 AM
    لا إله إلا الله
    مشروع لتعليم أساسيات لغة الـ ++C :
    -
    موضوع لتعليم الفيجوال بيسك 6 :

  5. #20
    التسجيل
    11-04-2002
    الدولة
    سوريا
    المشاركات
    760

    مشاركة: :: مشروع لتعليم أساسيات ++C :: موضوع الدروس

    :: القيمة العائدة ( Return Value ) ::

    تعلمنا حتى الأن كيف نقوم بكتابة التوابع و كيف نقوم بإدخال البيانات اللازمة للتابع لكي يجري عليها العمليات اللازمة .. و لكن افرض أننا نريد أن نرجع ناتج العمليات التي قام بها التابع إلى التابع الأخر الذي قام باستدعائه ؟ نستطيع ذلك عن طريق القيمة العائدة .. و على سبيل المثال :

    في التابع السابق الذي يحسب متوسط عددين .. افرض أننا نريد من نجعل أي تابع آخر ( main مثلاً ) أن يستفيد من ذلك الناتج و يجري عليه عمليات أخرى .. لذلك لا يكفي في التابع avr أن ندخل عددين فقط بل يجب أن نعطي التوابع الأخرى ناتج عملية إيجاد المتوسط .

    قاعدة 1 / هل تتذكر كيف شرحنا طريقة تعريف تابع ( الإعلان عن التوابع ) ؟ قلنا أنه يجب ذكر نوع التابع ثم اسمه ثم المتحولات الوسيطة ضمن قوسين ثم يأتي جسم ذلك التابع .. الأن حان الوقت لتغيير النوع void .. و لكن يجب أولاً توضيح بعض النقاط :

    1 – التوابع التي لا تعيد قيمة : يكون نوعها void .

    2 – التوابع التي تعيد قيمة : يمكن أن يكون نوعها بأي الأنواع المعرفة من قبل اللغة .. مثل : int و float و bool .. أو بأي من نوع من أنوع الكائنات أو السجلات ( سنأخذ ذلك لاحقاً ) .. و المهم في هذا الدرس الأنواع المعرفة مسبقاً .

    قاعدة 2 / يجب استخدام التعليمة return مع التوابع التي تعيد قيمة .. و طريقة استخدامها هي كالتالي :
    كود:
    return Value;
    حيث أن Value ممكن أن تكون أي قيمة أو ممكن أن تكون متحول ما .. و يشترط أن تلك القيمة مناسبة لنوع التابع .

    قاعدة 3 / لا يمكن استدعاء التوابع التي تعيد قيمة بذكر اسمها فقط !!! بل يجب أن توضع إما كإسناد قيمة أو في تعليمات cout أو ... إلخ .. أمثلة :

    بفرض أن لدينا التابع avr الذي يحسب المتوسط و يعيد الناتج .. و بفرض أن c هو متحول ما من النوع float :
    كود:
    c = avr( 2, 3 );
    cout << avr( x, y ) << endl;
    و بهذا تختلف عن التوابع التي من النوع void التي يمكن استدعاؤها يذكر اسمها فقط :

    بفرض أن read تابع من النوع void .. و بالتالي يمكن استدعاؤه كالتالي:
    كود:
    read();
    مثال / إيجاد متوسط عددين :
    كود:
    float avr( float a, float b )
    {
        float c;
        c = ( a+b ) / 2;
        return c;
    }
    
    void main()
    {
        float x, y;
        cin >> x >> y;
        cout << avr( x, y ) << endl;
    }
    و من الممكن أيضاً كتابة التابع avr بشكل آخر .. بحيث لا نستخدم متحول جديد لإرجاع القيمة :
    كود:
    float avr( float a, float b )
    {
        return  ( a+b ) / 2;
    }
    ملاحظة / هل عرفت الأن أن التابع الذي كتبناه ( avr ) يشابه تماماً توابع أخرى موجودة في المكتبات القياسية للغة ؟ انظر كيف استخدمنا التابع avr .. و كيف استخدمنا التابع sqrt الموجود في المكتبة math.h :
    كود:
    cout << sqrt(9) << endl;
    وبذلك تستطيع وضع مكتباتك الخاصة و لكن سنتطرق إلى الطريقة فيما بعد .

    :: أماكن كتابة التوابع ::

    في الأمثلة السابقة قمنا بكتابة التوابع فوق التابع الأساسي main .. و لذلك لكي يتعرف عليها برنامج حين استدعائها .. بينما إذا وضعناها أسفل التابع main فسيحدث خطأ .. لأنك قمت باستدعاء تابع لم يتم التعرف عليه بعد .. و كما قلنا فإن نمط البرمجة يتم سطراً سطراً من أول سطر في برنامجك إلى آخر سطر و لكن التنفيذ يتم من التابع main .. لذلك إذا استخدمت تابع و لم تقم قبل ذلك بتعريفه فسيحدث خطأ .

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

    ملاحظة / إذا وضعت أسماء المتحولات الوسيطة فلا بأس و لكن المترجم سيتجاهلها .

    مثال /
    كود:
    int f1();
    bool f2( int, int);
    void f3(float);
    
    void main()
    {
    }
    
    int f1()
    {
        return 0;
    }
    
    bool f2(int a, int b)
    {
        return true;
    }
    
    void f3(float a)
    {
    }
    ملاحظة 1 / لا يشترط التوافق في ترتيب ذكر رؤوس التوابع مع أجسامها في الأسفل .

    ملاحظة 2 / قلنا أنه لا يشترط وضع أسماء المتحولات الوسيطة .. و لكن يجب ذكرها في أجسام التوابع .

    ملاحظة 3 / بهذه الطريقة إذا كنت قد كتبت عدد كبير من التوابع .. فستجد أن التابع main موجود في الأعلى دائماً و بالتالي ستسهل على نفسك عناء البحث عنه .

    :: الاستدعاء بالقيمة ( by Value ) و الاستدعاء بالمرجع ( by Reference ) ::

    إذا أردنا أن ندخل مجموعة من القيم إلى داخل تابع ما .. كنا نستخدم لذلك المتحولات الوسيطة .. و عند استدعاء التابع كنا نرسل القيم التي نريدها عن طريق المتحولات الوسيطة الفعلية ( راجع فقرة المتحولات الوسيطة ) .. و لكن ما معنى الاستدعاء بالقيمة و الاستدعاء بالمرجع ؟

    الاستدعاء بالقيمة ( by Value ) :

    و هو الوضع الافتراضي لإرسال القيم للتوابع عن طريق المتحولات الوسيطة .. و لكن ما معناها ؟ انظر الاستدعاء التالي للتابع avr و الذي يحسب متوسط عددين ( لا يعيد قيمة ) و بفرض أن x و y هما العددين الذين سنحسب متوسطهما :
    كود:
    void avr(int a, int b)
    {
        int c = ( a+b ) / 2;
        cout << c << endl;
    }
    
    void main()
    {
        int x, y;
        x = 10; y = 20;
        avr( x, y );
    }
    عند الاستدعاء .. سيتم إجراء نسخة عن كل من x و y و إرسالها إلى المتحولات الوسيطة a و b الخاصين بالتابع avr .. و بالتالي لن يتأثر المتغيرين x و y بنتائج عمليات التابع ( أي لن تتغير قيمتهما حتى بعد الانتهاء من التابع ) لأن المترجم قام بنسخ قيم x و y و وضعها في المتحولين الوسيطين a و b .. و بالتالي نستنتج أيضاً أنه سيتم تحميل الذاكرة بقيمتين مشابهتين تماماً لقيم x و y .

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

    الاستدعاء بالمرجع ( by Reference ) :
    يختلف الاستدعاء بالمرجع عن الاستدعاء بالقيمة بأنه هذا الاستدعاء ( بالمرجع ) سيقوم بالتعامل مع عنوان المتحول الوسيط الفعلي في الذاكرة .. و بالتالي إذا حدثت أي تغييرات على المتحول الوسيط الشكلي فإن هذا التغير سيطرأ أيضاً على المتحول الوسيط الفعلي .. أي أن قيمة المتحول الوسيط الشكلي ستعطى للمتحول الوسيط الفعلي . انظروا المثال .

    قاعدة / يمكنك استخدام الاستدعاء بالمرجع عن طريق وضع العلامة ( & ) قبل اسم المتحول الوسيط الشكلي الموجود في تعريف التابع .
    كود:
    void f1( int &a )
    {
    }
    مثال / لتابع ندخل عليه عدد ما فيعطينا العدد الذي يليه :
    كود:
    void num( int &a )
    {
        a++;
    }
    
    void main()
    {
        int x;
        cin >> x;
        num( x );
        cout << x << endl;
    }
    ملاحظة / لاحظ أننا لم نستخدم التوابع التي تعيد قيمة لإخراج الناتج .. بل استخدمنا الاستدعاء بالمرجع .

    شرح المثال :

    في هذا المثال .. سيدخل المستخدم عدد ما ( x ) ثم سنرسل هذا العدد كمرجع إلى التابع num .. و بهذه الطريقة سيتم إخراج الناتج عن طريق ( x ) أيضاً .. و لكن لن يتم هنا إجراء نسخة مطابقة للمتغير x بل سيتم تعديلها فقط عن طريق المتحول الشكلي a .. فإذا تغيرت a تتغير x و كأن a هي اسم آخر لـ x أي يتم التعامل معه و كأنه المتحول x .

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

    و في هذا المثال .. إذا فرضنا أن المستخدم أدخل العدد 5 فسيظهر على الشاشة العدد 6 .. و هكذا .

    قاعدة 1 / من أجل القيم صغيرة الحجم يمكنك إرسال تلك القيم إلى التوابع باستخدام الاستدعاء بالقيمة .. بينما ينصح باستخدام الاستدعاء بالمرجع بالنسبة للقيمة الكبيرة .. و أيضاً من أجل إرجاع أكثر من قيمة ( أي تكون مخرجات التابع أكثر من قيمة ) .

    قاعدة 2 / عليك معرفة متى تستخدم الاستدعاء بالقيمة و الاستدعاء بالمرجع .. حسب وظيفة التابع و على حسب العمليات التي سيقوم .

    ---------- ---------- ---------- نهاية الدرس الرابع ---------- ---------- ----------

    في الدرس القادم إن شاء الله سنأخذ موضوع العودية ( Recursion ) بالإضافة إلى الأمثلة التطبيقية للمعلومات الموجودة في هذا الدرس .. بالتوفيق .
    التعديل الأخير تم بواسطة Wolf Sniper ; 17-02-2005 الساعة 01:56 AM
    لا إله إلا الله
    مشروع لتعليم أساسيات لغة الـ ++C :
    -
    موضوع لتعليم الفيجوال بيسك 6 :

  6. #21
    التسجيل
    11-04-2002
    الدولة
    سوريا
    المشاركات
    760

    مشاركة: :: مشروع لتعليم أساسيات ++C :: موضوع الدروس

    ---------- ---------- ---------- الدرس الخامس ---------- ---------- ----------
    اليوم : الاثنين ... التاريخ : 21 / 2 / 2005

    :: مقدمة ::

    سنأخذ في هذا الدرس إن شاء الله موضوع العودية بالإضافة إلى بعض الأمثلة التطبيقية على التوابع .. و أيضاً سأتطرق إلى موضوع كيفية صناعة مكتبتنا الخاصة ذو الامتداد (h.) و وضع التوابع اللازمة بها .

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

    سم بالله ثم اقرأ بتركيز و تمعن ..

    :: العودية ( الاستدعاء الذاتي ) Recursion ::

    و هي أن يقوم التابع باستدعاء نفسه .. و بالتالي سيستمر الاستدعاء إلى ما لا نهاية حتى يتم تحقيق شرط معين في الاستدعاء رقم n .. و عندها سيعود المترجم لتنفيذ التابع رقم n-1 و بعد ذلك إلى التابع رقم n-2 و هكذا إلى أن يصل المترجم للتابع الأول الذي قام باستدعاء نفسه .

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


    و كمثال على العودية سنستخدم تابع يقوم بإيجاد العاملي لعدد n .. بحيث سندخل هذا العدد إلى التابع و سنعيد الناتج كقيمة معادة :
    كود:
    int fact( int n )
    {
        if( n==0 ) return 1;
        else return ( n * fact( n-1 ) );
    }
    ملاحظة / اجعل هذا المثال كقاعدة لك مبدئياً عند استخدامك للعودية .

    شرح المثال :
    1 – يجب عليك أولاً معرفة الحالة النهائية التي سيقف عندها الاستدعاء المتكرر للتابع .. و في مثالنا حول العاملي سيتم الوقوف عند الوصول إلى ( صفر! ) و النتيجة ستكون هي الواحد ( لأن 0! تسوي واحد ) .. لذلك وضعنا شرط يقوم باختبار العملية السابقة .

    2 - و إذا لم تكن n تساوي الصفر .. فسيتم استدعاء التابع لنفسه .. بحيث سنضع قانون العاملي و الذي يمثل العدد نفسه ضرب العدد ناقص واحد في كل مرة حتى نصل إلى العدد واحد .. و لكن لماذا وضعنا القانون بهذا الشكل :
    كود:
    n * fact( n-1 )
    ملاحظة / يجب عليك معرفة أين ستقوم بعملية العودية ( استدعاء التابع لنفسه ) .. حيث هناك قانون آخر للعودية و هو كالتالي :

    5! = 5 × 4 × 3 × 2 × 1 = 120

    5! = 5 × 4! = 120
    5! = 5 × 4 × 3! = 120
    5! = 5 × 4 × 3 ×2! = 120
    5! = 5 × 4 × 3 × 2 × 1! = 120
    5! = 5 × 4 × 3 × 2 × 1 × 0! = 120

    و بالتالي نستنتج القانون التالي :
    كود:
    n! = n * (n-1)!

    و هذا ما يوضحه الحل الذي وضعناه .

    و انظر إلى آخر حالة كيف أننا وصلنا إلى ( 0! ) .. حيث أننا نعرف مسبقاً أن ( 0! ) تساوي الواحد الصحيح .. لذلك تكون عادة حالة التوقف عن العودية عند الوصول إلى قيمة معروف جوابها مسبقاً ( مثل 0! ) و التي تساوي الواحد .

    سنشرح الفكرة السابقة : بفرض أن المستخدم أدخل العدد 5 فستكون العملية كالتالي ( انظر الشكل ) :


    الاستدعاء الأول :

    في هذا المثال .. سيكون لدينا 6 استدعاءات .. عند الاستدعاء الأول ( الذي قام التابع main باستدعائه ) ستكون قيمة المتحول الشكلي الأول 5 لأن المستخدم أدخل العدد 5 و قد أرسلنا هذا العدد إلى التابع fact .. و بالتالي سيتم اختبار الشرط فيما إذا كان العدد يساوي الصفر ( لأن جواب 0! نعرفه مسبقاً ) .. و لكنه لا يساويه .. لذلك سيستدعي التابع نفسه مرة أخر لحساب ( 5 * 4! ) .. و بالتالي سيتم إرسال القيمة n-1 كمتحول شكلي إلى الاستدعاء الثاني و هذه القيمة تساوي 4 .. و كأننا نطلب من الاستدعاء الثاني أن يحسب لنا ( 4! ) .

    الاستدعاء الثاني :

    كما قلنا فإن قيمة المتحول الشكلي n في هذا الاستدعاء ستكون 4 .. و بالتالي لا تساوي الصفر .. لذلك سيستدعي التابع نفسه مرة أخرى و لكنه سيرسل القيمة n-1 كمتحول شكلي إلى الاستدعاء الثالث بحيث ستكون القيمة 3 .. لأننا قلنا سابقاً بأن : ( n! = n * ( n-1 .. حيث أننا نطلب من الاستدعاء الثالث أن يحسب لنا ( 3! ) .. و هكذا بقية الاستدعاءات .

    الاستدعاء الثالث :

    قيمة المتحول الشكلي
    كود:
     ( n ) = 3
    القيمة المرسلة إلى الاستدعاء الرابع
    كود:
     ( n-1 ) = 2

    الاستدعاء الرابع :

    قيمة المتحول الشكلي
    كود:
     ( n ) = 2
    القيمة المرسلة إلى الاستدعاء الرابع
    كود:
     ( n-1 ) = 1

    الاستدعاء الخامس :

    قيمة المتحول الشكلي
    كود:
     ( n ) = 1
    القيمة المرسلة إلى الاستدعاء الرابع
    كود:
     ( n-1 ) = 0

    الاستدعاء السادس :

    هنا ستكون قيمة ( n ) تساوي الصفر .. و بالتالي وصلنا إلى الحالة النهائية التي لا يوجد حل للعاملي بعدها .. لذلك سيعيد هذا التابع القيمة ( 1 ) لأن الشرط تحقق .. و لكنه لن يعيدها إلى الاستدعاء الأول بل إلى التابع الذي قام باستدعائه و هو الاستدعاء رقم 5 .. لذلك :
    القيمة المعادة للاستدعاء الخامس = 1 .

    في الاستدعاء الخامس :

    الأن في الاستدعاء الخامس نحن موجودين ضمن الشرط في else ( لأننا كما قلنا أن n لا تساوي الصفر ) .. و بالتالي الأن أصبحت قيمة :
    كود:
    n * fact( n-1 )
    معروفة و هي :

    n = 1 .
    fact( n-1 ) = 1 .. ( أوجدناها من الاستدعاء السادس الذي أعاد لنا تلك القيمة ) .

    و الناتج سيكون الواحد .. و بالتالي ( بسبب وجود return ) سيعيد الاستدعاء الخامس ناتج عملية الضرب إلى الاستدعاء ( التابع ) رقم 4 .
    القيمة المعادة للاستدعاء ( التابع ) رقم 4 = 1 .. ( لأن 1 × 1 = 1 ) .


    في الاستدعاء الرابع :

    تماماً كالاستدعاء رقم 5 .. سنكون بداخل else لأن n لا تساوي الصفر .

    n = 2 .
    fact( n-1 ) = 1 .. ( و هي القيمة المعادة من الاستدعاء الخامس ) .

    و الناتج سيكون ( 2 ) .. و بالتالي ( بسبب وجود return ) سيعيد الاستدعاء الرابع ناتج عملية الضرب إلى الاستدعاء ( التابع ) رقم 3 .
    القيمة المعادة للاستدعاء ( التابع ) رقم 3 = 2 .. ( لأن 2 × 1 = 2 ) .

    في الاستدعاء الثالث :

    تماماً كالبقية .. سنكون بداخل else لأن n لا تساوي الصفر .

    n = 3 .
    fact( n-1 ) = 2 .. ( و هي القيمة المعادة من الاستدعاء الرابع ) .

    و الناتج سيكون ( 6 ) .. و بالتالي ( بسبب وجود return ) سيعيد الاستدعاء الثالث ناتج عملية الضرب إلى الاستدعاء ( التابع ) رقم 2 .
    القيمة المعادة للاستدعاء ( التابع ) رقم 2 = 6 .. ( لأن 3 × 2 = 6 ) .


    في الاستدعاء الثاني :

    تماماً كالبقية .. سنكون بداخل else لأن n لا تساوي الصفر .

    n = 4 .
    fact( n-1 ) = 6 .. ( و هي القيمة المعادة من الاستدعاء الثالث ) .

    و الناتج سيكون ( 24 ) .. و بالتالي ( بسبب وجود return ) سيعيد الاستدعاء الثاني ناتج عملية الضرب إلى الاستدعاء ( التابع ) رقم 1 .
    القيمة المعادة للاستدعاء ( التابع ) رقم 1 = 24 .. ( لأن 4 × 6 = 24 ) .

    في الاستدعاء الأول:

    سنكون أيضاً بداخل else لأن n لا تساوي الصفر .

    n = 5 .
    fact( n-1 ) = 24 .. ( و هي القيمة المعادة من الاستدعاء الثاني ) .

    و الناتج سيكون ( 120 ) لأن ( 24 × 5 = 120 ) .. و هكذا نكون قد وصلنا إلى الجواب الذي نريده .. و بالتالي سيعيد هذا التابع تلك القيمة إلى التابع main الذي قام باستدعائه لكي يقوم بكتابة الناتج على الشاشة أو لإجراء أية عمليات أخرى على الناتج .


    خلاصة الحل :

    1 – لاحظ كيف بدأنا بالاستدعاء الأول و انتهينا أيضاً به .
    2 – عند القيام باستدعاء التابع لنفسه .. سيقف المترجم عند السطر الذي قمنا به بالعودية ( استدعاء التابع لنفسه ) .. و سيعود المترجم إلى نفس المكان حين إيجاد القيمة المعادة ( أو عند الانتهاء من التابع الذي قمنا باستدعائه ) و سيكمل ما تبقى من تعليمات موجودة في المكان الذي توقف به ( سنأخذ مثال على ذلك في هذا الدرس ) .

    مساوئ العودية :

    - إن الاستدعاء المتكرر للتوابع سيكون تنفيذه أبطأ من استخدام حلقات التكرار ( for و while ... إلخ ) .. و لكن هناك مسائل لا تحل إلا عن طريق العودية .. مثل عملية البحث في الملفات و التي ينفذها windows و طريقة عملها تشابه تماماً طريقة عمل العودية.. لذلك عليك معرفة متى تستخدم العودية و متى تستخدم حلقات التكرار ( على حسب المسألة ) .
    لا إله إلا الله
    مشروع لتعليم أساسيات لغة الـ ++C :
    -
    موضوع لتعليم الفيجوال بيسك 6 :

  7. #22
    التسجيل
    11-04-2002
    الدولة
    سوريا
    المشاركات
    760

    مشاركة: :: مشروع لتعليم أساسيات ++C :: موضوع الدروس

    :: أمثلة تطبيقية ::

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

    المثال 1 /
    نريد من المستخدم إدخال عددين بحيث يكون الأول هو الأساس و الثاني يكون هو الأس بحيث نوجد ناتج عملية الرفع إلى أس عن طريق العودية .. انظر الصورة :


    الحل /
    كود:
    #include <iostream.h>
    
    int power( int, int );
    
    void main()
    {
        int a,b;
    
        cout << "Enter X : "; cin >> a;
        cout << "Enter Y : "; cin >> b;
    
        cout << "X Power Y = " << power( a, b ) << endl << endl;
    }
    
    int power( int x, int y)
    {
        if( y==0 ) return 1;
        else return ( x * power( x, y-1 ) );
    }
    شرح الحل :

    1 – استخدمنا الطريقة الثانية في كتابة التوابع ( راجع الدرس السابق ) بحيث وضعنا رأس التابع في الأعلى لكي يتعرف عليه المترجم إذا ما استخدمناه في التابع main .. ثم كتبنا جسم ذلك التابع أسفل التابع main .

    2 – عرفنا متغيرين في التابع main : a من أجل الأساس .. و b من أجل الأس .. و بعد ذلك طلبنا من المستخدم إدخال تلك القيم .. و بعد ذلك استدعينا التابع power لحساب الناتج عن طريق إرسال a و b كمتحولات وسيطة .

    3 – في التابع power .. سنستخدم نفس أسلوب المثال السابق .. و لكن هناك اختلافات بسيطة :

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


    .. القوة تعني الأس ..
    4 قوة 4 = 256 .
    4 قوة 4 = 4 × 4 قوة 3 .
    4 قوة 4 = 4 قوة 2 × 4 قوة 2 .
    4 قوة 4 = 4 قوة 3 × 4 .

    4 قوة 4 = 4 قوة 4 × 4 قوة صفر .. و هكذا باقي الأرقام .

    لذلك يمكن أن نستنتج القانون التالي :
    كود:
    x Power y = x * x Power ( y-1)
    و هذا ما وضعناه في حل المثال .. و باقي الشرح يشابه تماماً شرح المثال السابق حول العاملي .. و بالتالي يجب عليك معرفة أين تضع مكان الاستدعاء التابع لنفسه و لاحظ كيف وضعناه نحن في الأمثلة السابقة .

    ----------------------------------------

    المثال 2 /
    نريد الأن أن نطلب من المستخدم إدخال جملة حرفية و من ثم نكتبها معكوسة .. بحيث تنتهي الجملة بإدخال النقطة .. انظر الصورة :


    الحل /
    كود:
    #include <iostream.h>
    
    void read()
    {
        char x;
    
        cin >> x;
    
        if( x != '.' )
        {
            read();
            cout << x;
        }
    }
    
    void main()
    {
        cout << "Enter a Sentence : ";
    
        read();
    
        cout << endl << endl;
    }
    شرح الحل :

    1 – أظن أن التابع main مفهوم .

    2 – في التابع read .. سيمثل كل استدعاء ( تابع ) حرف من الجملة .. و بالتالي سنطلب في كل مرة من المستخدم إدخال حرف جديد طالما أن الحرف السابق ليس النقطة .. و سيكون كل حرف مخزن في المتغير x الخاص بتابع معين .. فكلما أدخل المستخدم حرف جديد لا يساوي النقطة قمنا باستدعاء تابع آخر ليخزن الحرف الجديد في المتحول x الخاص به .

    3 – عند إدخال النقطة في نهاية الجملة .. سيكون آخر تابع يحمل قيمة النقطة و بذلك ينتهي هذا التابع دون كتابة تلك النقطة .. و بالتالي سيكون التابع قبل الأخير يحمل قيمة آخر حرف .. إذا سيتم كتابة هذا الحرف أولاً على السطر و بذلك ينتهي هذا التابع ..
    و تستمر التوابع بعد ذلك بكتابة كل منها قيمة المتغير x ( الحرف ) الذي يحمله .. و بالتالي ستكتب الجملة معكوسة لأن كتابة المتغيرات x الخاصة بالتوابع ستكتب من آخر تابع إلى الأول و نحن نعرف أن الأحرف وضعت بشكل متتالي في التوابع نتيجة إدخال المستخدم في كل مرة حرف واحد .


    و سبب ذلك أن المترجم يقف عند سطر استدعاء التابع و ينتقل إلى ذلك التابع .. و عند الانتهاء من هذا التابع يعود المترجم إلى السطر الذي يلي سطر الاستدعاء الخاص بالتابع .. و هكذا باقي التوابع .. انظر الصورة :


    هذه الصورة تمثل التوابع الثلاثة الأولى لكلمة Montada و بقية التوابع كهذه .. لاحظ كيف تم تسلسل إدخال الأحرف .. و أيضاً في النهاية كيف سيتم كتابة قيمة الحرف x في التابع رقم 3 و بذلك ينتهي عمله ثم سيتم سينتقل المترجم إلى التابع رقم 2 و لكن ليس إلى أوله بل إلى السطر الذي استدعينا فيه التابع رقم 3 .. و عندها سيتم كتابة قيمة x .. و عندها ينتهي عمل التابع رقم 2 .. و بالتالي سيعود المترجم إلى التابع رقم 1 .. و يكتب قيمة x و الذي تمثل أول حرف تم إدخاله و بالتالي سيكتب هذا الأحرف في النهاية .. و هذا يعني أن الجملة ستكتب معكوسة .

    ----------------------------------------

    المثال 3 /
    نريد من المستخدم إدخال عددين صحيحين و من ثم نعطي العدد الأكبر كقيمة عائدة عن طريق التابع bigger .. و إذا كان العددين متساويين فنعيد القيمة صفر .


    الحل /
    كود:
    #include <iostream.h>
    
    int bigger( int, int );
    
    void main()
    {
        int a, b;
    
        cout << "Enter Number 1 : "; cin >> a;
        cout << "Enter Number 2 : "; cin >> b;
    
        cout << "Max = " << bigger( a, b ) << endl;
    }
    
    int bigger( int a, int b )
    {
        if( a>b )
            return a;
        else if ( b>a )
            return b;
        else
            return 0;
    }
    شرح المثال :

    1 – استخدمنا الطريقة الثانية في تعريف التوابع كالمثال الأول .. بحيث عرفنا رأس التابع في بداية الصفحة لكي يتعرف عليه المترجم لأننا نريد استخدامه في التابع main و أيضاً لأننا سنضع هذا التابع ( bigger ) أسفل التابع main .

    2 – في التابع main .. طلبنا من المستخدم إدخال العددين .. و من ثم استدعينا التابع bigger لكي يحسب العدد الأكبر عن طريق إرسال العددين كمتحولات وسيطة .. و من ثم سيعيد لنا العدد الأكبر .. حيث أن العددين بالنسبة للتابع bigger يعتبران المدخلات و العدد الأكبر يعتبر مخرجاته .

    3 – في التابع bigger .. سنحل السؤال كما حللناه في الدروس السابقة .. فإذا كان العدد a أكبر من b فأرجع a كقيمة معادة .. أما إذا كان b أكبر من a فأرجع b .. أما إذا كان العددين متساويين فأرجع الصفر .

    ----------------------------------------
    لا إله إلا الله
    مشروع لتعليم أساسيات لغة الـ ++C :
    -
    موضوع لتعليم الفيجوال بيسك 6 :

  8. #23
    التسجيل
    11-04-2002
    الدولة
    سوريا
    المشاركات
    760

    مشاركة: :: مشروع لتعليم أساسيات ++C :: موضوع الدروس

    المثال 4 /
    نريد من المستخدم إدخال عدد و من ثم نحسب له العاملي لهذا العدد عن طريق التابع fact .. و لكن ليس عن طريق العودية كما حللناه في بداية هذا الدرس بل عن طريق حلقة التكرار for ( نفس الحل الموجود في الدرس الثالث و لكن سنستخدم التوابع للحل ) .


    الحل /
    كود:
    #include <iostream.h>
    
    int fact( int );
    
    void main()
    {
        int a;
    
        cout << "Enter a Number : "; cin >> a;
    
        cout << fact( a ) << endl;
    }
    
    int fact( int x )
    {
        int f=1;
    
        for( int i=1; i<=x; i++ )
            f *= i;
    
        return f;
    }
    شرح المثال :

    1 – أظن أن التابع main مفهوم .

    2 – التابع fact مشروح فكرته في الدرس الثالث .. و لكننا هنا بدلاً من جعل I تتغير قيمتها تنازلياً .. جعلناها هنا تتغير تصاعدياً من الواحد و حتى العدد نفسه الذي أدخله المستخدم و الجواب هو نفسه .. و بعد ذلك سنعيد الناتج كقيمة عائدة إلى التابع main .

    ----------------------------------------

    المثال 5 /
    نريد من المستخدم أن يدخل عدد صحيح و من ثم نكتب له فيما إذا كان هذا العدد من الأعداد الأولية أما لا .. و ذلك عن طريق التابع prime .


    الحل /
    كود:
    #include <iostream.h>
    
    bool prime( int );
    
    void main()
    {
        int a;
    
        cout << "Enter a Number : "; cin >> a;
    
        if( prime(a) ) cout << "Prime" << endl;
        else cout << "not Prime" << endl;
    }
    
    bool prime( int x )
    {
        if( x==2 || x==3 ) return false;
    
        for( int i=2; i<=x-1; i++ )
            if( x%i == 0 ) return false;
    
        return true;
    }
    شرح الحل :

    1 – في التابع main .. قمنا بتعريف العدد الذي سيدخله المستخدم .. بحيث طلبنا منه ذلك .

    2 – بعد ذلك قمنا باختبار فيما إذا كان هذا العدد أولي أم لا عن طريق استدعاء التابع prime مع إرسال العدد كمتحول وسيط لكي يختبر فيما إذا كان ذلك العدد كذلك .. فإذا كان أولي سنكتب جملة Prime أما إن لم يكن أولي فسنكتب له not Prime .. وحيث أننا نعرف بأن القيمة المعادة هي من النوع bool لذلك نستطيع وضع التابع داخل الشرط if .. بحيث سيقوم الإختبار أولاً باستدعاء التابع ثم سيختبر القيمة المعادة هل هي true أم false .. و لاحظ أيضاً كيف أننا لم نضع التالي :
    كود:
    if( prime(a)==true )
    بل اكتفينا باستدعاء التابع .. لأن العبارتين متشابهتين ( اقرأ الملاحظة التالية ) .

    ملاحظة مهمة / من أجل طريقة استخدام التوابع التي تعيد قيمة من النوع bool أو من أجل المتغيرات من ذلك النوع أيضاً و كيفية استخدامها مع الشرط if .. راجع أول الدرس الثالث .. و طريقة استخدام تلك التوابع هي بنفس طريقة استخدام المتغيرات في الشرط .. و لكن في التوابع من ذلك النوع عند استخدامها مع الشرط سيتم استدعاء التابع أولاً ثم سيتم اختبار القيمة المعادة منه .

    3 – في التابع prime .. بنفس الحل الموجود في الدرس الثالث و لكننا سنطوره قليلاً .. حيث أن العددان 2 و 3 ليسا من الأعداد الأولية ( هكذا أتذكر ) .. و طريقة الحل هنا ستعتبر أن تلك الأعداد هي من الأعداد الأولية و هذا ما لا نريده .. لذلك وضعنا الشرط الأول لإختبار قيم x فوراً .. فإذا كانت تساوي إحدى العددين فسيتم إرجاع القيمة false للدلالة على أنهما ليسا من الأعداد الأولية .

    و من ثم في الاختبار الثاني حسبنا عدد القواسم للعدد المدخل .. و لكننا لن نحسب كم هي بالضبط ! فإذا وجدنا واحد فقط فهذا يعني أنه ليس من الأعداد الأولية ( طبعاً لم نضع في الحلقة العدد 1 و العدد نفسه x لأن العدد الأولي دائماً يقبل القسمة على الواحد و على نفسه فليس هناك فائدة من إدخالهم في عملية الحساب ) .. فإذا وجدنا قاسماً واحداً فسنعيد القيمة false أما إذا لم نجد أي قاسم للعدد فهذا يعني أنه أولي و بالتالي سنعيد القيمة true التي تدل على أنه أولي .

    ملاحظة مهمة / الأمر return ينهي عمل التابع .. و بالتالي عند استخدامه سيتم الخروج من التابع فوراً و عن طريقه سيتم إرجاع قيمة ما ( راجع الدرس السابق ) .

    ----------------------------------------
    لا إله إلا الله
    مشروع لتعليم أساسيات لغة الـ ++C :
    -
    موضوع لتعليم الفيجوال بيسك 6 :

  9. #24
    التسجيل
    11-04-2002
    الدولة
    سوريا
    المشاركات
    760

    مشاركة: :: مشروع لتعليم أساسيات ++C :: موضوع الدروس

    :: كيفية صناعة المكتبات الخاصة بنا ذو الامتداد (h.) ::

    تحدثنا سابقاً حول الملفات التي نستطيع كتابة الأوامر فيها .. و لقد كتبنا جميع برامجنا و أمثلتنا على ملفات مصدرية Source File ذو الامتداد (cpp.) .. و لقد تحدثنا عن المكتبات ذو الامتداد (h.) و أنا تحتوي على مجموعة من الأوامر و التوابع و أشياء أخرى معرفة مسبقاً من قبل المستخدم .

    لذلك افرض مثلاً أنك تريد صناعة مجموع من التوابع الجاهزة بحيث لا تعيد كتابتها ثانية في برامجك الأخرى و بالتالي ستوفر على نفسك عناء تلك الكتابة .

    لذلك نستنتج أن هذه المكتبات مفيدة لكي يستخدمها المبرمج في برامج أخرى يقوم بكتابتها هو أو غيره و ستصبح هذه المكتبات كالمكتبات الجاهزة الأخرى مثل : math.h و ctype.h و مكتبات أخرى كثيرة .. و هناك أشخاص يقومون ببيع تلك المكتبات بأسعار خيالية و لكنها ليست على شكل ملفات رأسية (h.) كما سنتعلم الأن !!! حيث أنها عبارة عن مكتبات ربط ديناميكية و هو ما يسمى بملفات dll .. و لكن لا تخف فإن هذه المكتبات ما هي بالنهاية إلا ملفات رأسية (h.) و لذلك يجب أن نعرف الأن مبدئياً كيف يمكن أن نصنع مكتبة جزئية خاصة بنا من ذلك النوع من الملفات .

    ملاحظة / راجع الدرس الأول لمزيد من المعلومات حول الملفات الرأسية (h.) .

    أولاً / على ماذا تحتوي الملفات الرأسية (h.) ؟

    قد تحتوي هذه الملفات على الأنواع التالية :

    1 – توابع .
    2 – كائنات .
    3 – مؤشرات .
    4 – سجلات من النوع struct .
    5 – متغيرات .
    إلخ ...

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

    ثانياً / كيف نصنع ( ننشئ ) تلك الملفات ؟

    تستطيع ذلك بنفس طريقة إضافة ملف مصدري (cpp.) .. إضغط على : File >> New و من ثم ضمن القائمة File اختر Header File .. و اضغط على Ok .. و بهذا تكون قد أنشأت ملف رأسي .

    ثالثاًُ / ما هي طرق كتابة تلك الأنواع من الملفات ؟

    في الحقيقة هناك طريقتين :

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

    الثانية : هي أن نضع رؤوس الأوامر فقط داخل الملف الرأسي ( كما استخدمنا الطريقة الثانية لكتابة التوابع ) و أجسام تلك الأوامر في ملف مصدري (cpp.) .. و هذه الطريقة هي الأفضل و هي الأكثر استخداماً و لكننا لن نتطرق إليها إلا عند الوصول إلى الكائنات .. لأننا سنضطر لذكرها .. لذلك اتركوا هذه الطريقة الأن و لنرجع إلى الطريقة الأولى .

    رابعاً / الأن سنقوم بكتابة مكتبتنا الأولى .. لذلك قم بإنشاء ملف رأسي و سميه باسم Library1 .. و هذا الاسم سيكون اسم تلك المكتبة و التي ستحتوي على التوابع التالية :

    1 – تابع إيجاد قيمة العاملي لعدد ما ( بطريقة التكرار و ليس العودية ) .
    2 – تابع إيجاد القوة لعدد ما ( الأس و الأساس ) .
    3 – تابع إيجاد الجملة المعكوسة للجملة التي أدخلها المستخدم .
    4 – تابع إيجاد أي العددين أكبر .
    5 – تابع معرفة فيما إذا كان العدد أولي أم لا .

    و كل تلك التوابع قمنا بكتابتها في هذا الدرس .. و سنضيف على هذه المكتبة توابع أخرى إن شاء الله في الدروس القادمة .

    خامساً / كتابة المكتبة ( Library1 ) :

    المكتبة ( Library1 )

    كود:
    int fact( int x )
    {
        int f=1;
    
        for( int i=1; i<=x; i++ )
            f *= i;
    
        return f;
    }
    
    int power( int x, int y)
    {
        if( y==0 ) return 1;
        else return ( x * power( x, y-1 ) );
    }
    
    void read()
    {
        char x;
    
        cin >> x;
    
        if( x != '.' )
        {
            read();
            cout << x;
        }
    }
    
    int bigger( int a, int b )
    {
        if( a>b )
            return a;
        else if ( b>a )
            return b;
        else
            return 0;
    }
    
    bool prime( int x )
    {
        if( x==2 || x==3 ) return false;
    
        for( int i=2; i<=x-1; i++ )
            if( x%i == 0 ) return false;
    
        return true;
    }
    قمنا بوضع التوابع كما هي في الملف الرأسي Library1 .. و الحلول موجودة في هذا الدرس .

    سادساً / كيف يمكن أن نضيف المكتبة ( Library1 ) إلى برنامجنا بعد أن كتبناها ؟

    يمكننا ذلك عن طريق الأمر :
    كود:
    #include "Library.h"
    هل عرفت فائدة الأمر ( include ) ؟ تستطيع بهذا الأمر ضم المكتبات التي تريدها إلى مشروعك .. و لكن لماذا وضعنا اسم المكتبة داخل علامتي التنصيص ( " " ) ؟ قلنا سابقاً في الدرس الأول أننا نستخدم علامتي التنصيص في هذا المكان لكي نخبر المترجم بأن المكتبة موجودة ضمن الملف ( المجلد ) الذي يوجد فيه تطبيقك ذو الامتداد exe .

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

    هل عرفت أيضاً ما فائدة الـ Linker ؟ يقوم Linker بربط برنامجك بالمكتبات و الملفات المطلوبة و التي قمت بإضافتها إلى مشروعك ( راجع الدرس الأول ) .

    سابعاً / الاستخدام الفعلي للمكتبة ( Library1 ) :

    سنأخذ الأن مثالاً عن كيفية استخدام هذه المكتبة .. سنجرب استخدام التابع bigger و الذي يقوم بإرجاع قيمة أي العددين أكبر و الذين أرسلناهما إليه عن طريق المتحولات الوسيطة .
    كود:
    #include <iostream.h>
    #include "Library1.h"
    
    void main()
    {
        int a, b;
    
        cout << "Enter Number 1 : "; cin >> a;
        cout << "Enter Number 2 : "; cin >> b;
    
        cout << "Max = " << bigger( a, b ) << endl;
    }
    لاحظ كيف أننا بهذا الشكل إختصرنا على أنفسنا عناء كتابة التابع bigger داخل هذا الملف المصدري .. و حيث أننا أيضاً نستطيع استخدام باقي التوابع المكتوبة في المكتبة ( Library1 ) و كأن تلك التوابع مكتوبة بداخل الملف الأساسي المصدري في هذا المثال .. و كل ذلك نتيجة إضافة تلك المكتبة عن طريق الأمر include و الذي جعل المترجم يقوم بتعريف تلك التوابع و كأنها في برنامجك .

    إذاً تستطيع الأن استخدام هذه المكتبة في أي برنامج تريد كتابته دون الحاجة إلى كتابة تلك التوابع مرة أخرى في ذلك البرنامج .

    ---------- ---------- ---------- نهاية الدرس الخامس ---------- ---------- ----------

    في النهاية .. أتمنى أن يكون هذا الدرس بداية طريقكم لفهم هذه اللغة و بداية لاحترافكم لها .. لأن هذه المواضيع مهمة و إذا لم تفهموها فلا أعتقد أنكم ستفهمون المواضيع القادمة .. لذلك أرجو من الجميع التركيز على كل ما سبق لكي تكون الدروس القادمة مفهومة لكم إن شاء الله .. بالتوفيق .
    لا إله إلا الله
    مشروع لتعليم أساسيات لغة الـ ++C :
    -
    موضوع لتعليم الفيجوال بيسك 6 :

  10. #25
    التسجيل
    01-02-2005
    المشاركات
    0

    Angry مشاركة: :: مشروع لتعليم أساسيات ++C :: موضوع الدروس

    السلام عليكم انا اعشق لغة c++ولكن مررت بوضوع جديد وهو class الذي خلبط علي الشغله وكرهني في لغة c++فيا ليت تقوموم يالشرح الكافي مع الاملثه لو سمحتم (تكفى ياwolf) ولكم الشكر الجزيل:vereymad:

  11. #26
    التسجيل
    03-09-2003
    الدولة
    d=(n_n)=b دار بو خليفة d=(n_n)=b
    المشاركات
    1,096

    مشاركة: :: مشروع لتعليم أساسيات ++C :: موضوع الدروس

    ::

    ::

    ممنوع الرد فب هذا الموضوع

    سيتم ابلاغ المشرف

    ::

    ::
    ::

    ::



    "التوقيع فوق التعديل لوووووول "

    3/12/2005...."and UAE Naruto...is back again"

    من مواضيعي

    "مؤقتاً ماشي مواضيع حلوة "

    ::

    ::

  12. #27
    التسجيل
    11-04-2002
    الدولة
    سوريا
    المشاركات
    760

    مشاركة: :: مشروع لتعليم أساسيات ++C :: موضوع الدروس

    السلام عليكم ...

    :: مقدمة ::

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

    1 – المصفوفات ( Arrays ) .
    2 – السجلات ( Structures ) .
    3 – المؤشرات ( Pointers ) .

    أما الجزء الأخير و هو البرمجة غرضية التوجه ( بجميع مواضيعه ) أعتبره ذو مستوى متقدم .. و بذلك نكون قد قسمنا الدروس إلى ثلاثة مستويات .. و هذا الأمر مهم برأيي .

    ملاحظة / أريد أن أوضح نقطة مهمة .. أن الدروس التي أكتبها هي ليست مني 100% ؟؟! .. بل هي مجهود مشترك بيني و بين من قام بتعليمي تلك اللغة ( لن أذكر اسمه ) و أيضاً المرجع " كيف تبرمج بلغة السي++ " للدكتور صلاح الدوه جي .. و بذلك أكون أنا حلقة وصل بينكم و بينهم .. و لكن هذا لا يعني أني لا أكتب شيئاً في هذا الدروس بل على العكس .. الدروس مكتوبة 100% مني و لكن محتواها من الأمثلة و المعلومات و الشرح مجزء كالآتي :

    1 – مني أنا 50% .. و شرح الأمثلة كلها كتبته بنفسي .. بالإضافة إلى أمثلة كثيرة و أمور أخرى كتبتها بنفسي .
    2 – أستاذي 45% .. حيث أنني وضعت الكثير من التعاريف و الأمثلة و الحلول التي قام هو بإعطائها لي حين تعلمت لغة السي++ منه .
    3 – المرجع 5% .. استفدت قليلاً من المرجع في وضع الدروس من هذا المرجع و هو مهم لذلك أنصح الجميع بالحصول عليه .

    و بذلك أكون قد أخلصت نيتي .. و برّأت ذمتي أمام الله .. و مع ذلك أنا على استعداد تام لأي سؤال يتعلق في تلك المواضيع .. فالحمدلله تلك الأساسيات أتقنها بشكل كبير .. لذلك من أراد اختباري فلا بأس .

    اليوم سنتكلم عن المصفوفات .. و سننتهي منها في هذا الدرس بشكل كامل إن شاء الله .. لذلك درس اليوم أيضاً مهم و سأحاول تبسيط الموضوع قدر الإمكان .. بسم الله نبدأ .

    ---------- ---------- ---------- الدرس السادس ---------- ---------- ----------
    اليوم : السبت ... التاريخ : 26 / 3 / 2005

    :: المصفوفات ( Arrays ) ::

    تخيل أنك تريد مثلاً أن تعرّف ( تنشئ ) مجموعة كبيرة من المتغيرات من النوع الواحد ( int على سبيل المثال ) .. فإذا أردنا مثلاً تعريف 10 متغيرات من ذلك النوع فإننا سنكتب التالي عشر مرات لعشر متغيرات يختلفون في الاسم :
    كود:
    int a;
    و بالتالي هذه الطريقة تضيع وقتنا و تدخلنا في متاهات نحن في غنا عنها .. إذاً نحتاج لتعلم شيء جديد يفيدنا في تنفيذ ذلك الأمر .. و هذا ما توفره لنا المصفوفات .

    إذاً ما الفائدة من المصفوفات ؟ نستفيد منها بالتالي :

    1 – تعريف مجموعة من المتغيرات من النوع الواحد بحيث يكون لتلك المتغيرات اسم مشترك بينها يمثل اسم المصفوفة .. بالإضافة إلى أننا نستطيع الوصول إلى أي من متغيرات المصفوفة عن طريق ما يسمى بالدليل ( Index ) .
    2 – هناك مسائل و أمور لا تحل إلا عن طريق المصفوفات حيث أن لها استخدامات متعددة .
    3 – أنك حينما تتعامل في برنامجك مع اسم المصفوفة فإنك تتعامل مع عنوانها في الذاكرة .. سنستفيد من هذه المعلومة في حل بعض المسائل .

    :: تعريف ::

    المصفوفة / هي معرّف ( Identifier ) مكونة من مجموعة من المتحولات كلها من نوع واحد .. و لتلك المتحولات اسم مشترك و يشار إلى كل قيمة منها ( متحول ) باسم المصفوفة مع الدليل الذي يدل على موقع القيمة ( المتحول ) في المصفوفة و يكون دليل العنصر الأول يساوي الصفر .

    :: التصريح عن المصفوفات ::

    بنفس طريقة تعريف المتحولات العادية و لكن مع إضافة شيء بسيط .. حيث يمكننا تعريف مصفوفة عن طريق تحديد نوع المصفوفة ثم اسمها .. ثم القوسين ( [] ) و نضع داخل القوسين عدد المتغيرات التي نريد وضعها في المصفوفة ( قلت العدد و ليس الدليل ) و بعد ذلك نضع الفاصلة المنقوطة .. و بذلك نكون قد عرّفنا مجموعة من المتغيرات من النوع الواحد .

    مثال /
    كود:
    int a[10];
    float a[5];
    bool a[8];
    ملاحظة 1 / في المثال الأول سيكون دليل العنصر الأول صفر و سيكون دليل العنصر الأخير 9 .
    ملاحظة 2 / هذه المصفوفات تمثل أشعة ( مصفوفة أحادية البعد ) و هذا يعني أن أبعادها كالتالي :
    كود:
    N × 1
    حيث n ممكن أن تكون أي عدد صحيح يمثل عدد أعمدة المصفوفة .. و الواحد طبعاً يمثل وجود سطر واحد فقط في تلك المصفوفة ( لذلك قلنا أن تلك المصفوفات أحادية الأبعاد ) .

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

    :: إعطاء قيم ابتدائية للمصفوفة ::

    أمثلة /
    كود:
    int a[3] = { 10, 20, 30 };
    int a[3] = { 5. 2 };
    int a[3] = { 5 };
    int a[3] = { 0 };
    في المثال الأول أعطينا المصفوفة a ثلاثة قيم قيم للعناصر الثلاثة التي تحتوي عليها و سيتم إسناد القيم بالترتيب .. فالقيمة عشرة سيأخذها العنصر الأول و القيمة عشرين سيأخذها العنصر الثاني و هكذا .. بينما في المثال الثاني أعطينا المصفوفة a قيمتان فقط و الثالثة ستعتبر صفر .. بينما في المثال الثالث أعطينا المصفوفة a قيمة واحدة و البقية ستعتبر أيضاً صفر .. أما في المثال الرابع ستعتبر قيم جميع العناصر أصفار .

    مثال توضيحي :


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

    :: التعامل مع المصفوفة ::

    هناك طريقتين .. الأولى إذا كنا نريد التعامل مع عنصر واحد فقط .. و الثانية إذا كنا نريد التعامل مع جميع عناصر المصفوفة أو مع بعضها .. و الطريقتان متشابهتان :

    1 – التعامل مع عنصر واحد فقط :

    نستطيع ذلك عن طريق استخدام رقم الدليل الخاص بالعنصر المطلوب .. مثال / بفرض لدينا مصفوفة a تحتوي على ثلاثة أعداد حقيقية :
    كود:
    float a[3];
    a[0] = 5.0;
    هنا أعطينا العنصر الأول من المصفوفة العدد 5 .. بالتالي تحتاج دائماً إلى الدليل لاستخدام عناصر المصفوفة في أي عملية مهما كانت ( إخراج على الشاشة أو إسناد أو عملية جمع ... إلخ ) .

    2 – التعامل مع أكثر من عنصر :

    نحتاج لذلك إلى حلقة تكرار for .. حيث أننا سنستخدم متحول التكرار كدليل للمصفوفة في كل دورة .. لأن هذا المتحول ستتغير قيمته في كل مرة .. بحيث نضع القيمة الابتدائية له تساوي الصفر ( لأن دليل أول عنصر يساوي الصفر ) و القيمة النهائية تساوي عدد عناصر المصفوفة ناقص واحد ( لأننا قلنا أن أول أول عنصر سيأخذ الدليل صفر و الأخير سيكون دليله عدد العناصر ناقصاً الواحد ) .. مثال / بفرض أن لدينا مصفوفة a تحتوي على ثلاثة أعداد صحيحة :
    كود:
    int a[3];
    for( int i=0; i<3; i++ )
        a[i] = 5;
    ملاحظة / تخيل أنك استخدمت المصفوفة a السابقة عدداً كبيراً من المرات في برنامجك بواسطة حلقة التكرار for .. بالتالي ستضع القيمة الابتدائية لمتحول التكرار صفر و النهائية 2 .. و لكن افرض أنك قمت بتغيير حجم المصفوفة a فجعلتها تحتوي 5 عناصر بدلاً من 3 ؟ فستحتاج إلى تغيير الأرقام كلها التي وضعتها لمتحول التكرار في الحلقات for .. و لكن هناك حل جيد لهذه المشكلة و هي كالتالي :

    نضع ثابت صحيح في أول البرنامج يمثل حجم المصفوفة a و لنفرض أنه m .. و نضع جميع القيم النهائية لمتحول التكرار في الحلقات for القيمة m .. و بالتالي إذا احتجت إلى تغيير حجم المصفوفة بعد ذلك فستغير رقماً واحداً فقط و هو قيمة m .. مثال :
    كود:
    const int m = 5;
    float a[m];
    
    for( int i=0; i<m; i++ )
        a[i] = 5;
    لا إله إلا الله
    مشروع لتعليم أساسيات لغة الـ ++C :
    -
    موضوع لتعليم الفيجوال بيسك 6 :

  13. #28
    التسجيل
    11-04-2002
    الدولة
    سوريا
    المشاركات
    760

    مشاركة: :: مشروع لتعليم أساسيات ++C :: موضوع الدروس

    :: المصفوفة الحرفية ( char ) ::

    أولاً / فائدة المصفوفة الحرفية :

    للمصفوفة الحرفية عدة مزايا أهمها أننا نستطيع بها تخزين جملة حرفية كاملة بدلاً من حرف واحد بواسطة char ( اسم على سبيل المثال ) .. بالإضافة إلى أن لها مزايا عند استخدامها مع قنوات الدخل و الخرج ( cout, cin ) .

    ثانياً / الفرق بين مفهوم الحرف و المحرف :

    الحرف : نقصد به الحرف أبجدي حصراً .. سواءً كان باللغة العربية أو الأجنبية .
    المحرف : ممكن أن يكون حرف أبجدي أو من الممكن أن يكون رمز أو علامة مثل ( $ ) .

    ثالثاً / التصريح عن المصفوفة الحرفية :

    بنفس طريقة التصريح عن المصفوفات الأخرى .. و ذلك بذكر نوع المصفوفة ( نريد هنا char ) ثم اسمها ثم حجمها و ذلك بوضع عدد عناصرها داخل القوسين ( [ ] ) .. مثال :
    كود:
    char name[20];

    رابعاً / إعطاء قيم ابتدائية للمصفوفة الحرفية :

    الطريقة الأولى :
    كود:
    char name[20] = { 'm','o','n','t','a','d','a','\0' };
    بنفس طريقة إعطاء قيم ابتدائية للمصفوفة العددية .. ولكن القيم هنا هي أحرف و ليست أرقام لذلك وضعنا الحاصرة الأحادية .. و لكن ما فائدة آخر رمز ؟ هذا الرمز يسمى بالمحرف الصفري .. و هو مهم لأنه يدل على نهاية الجملة الحرفية في المصفوفة .. فإذا أردنا مثلاً كتابة تلك الكلمة ( montada ) على الشاشة فإن الحرف الأخير الذي ستتم كتابته هو الحرف ( a ) و لن يتم كتابة باقي قيم المصفوفة ( حيث أن حجمها يساوي 20 أي أنها تتسع لجملة طولها 20 محرف ) .. و ذلك بسبب وجود المحرف الصفري ( سنأخذ مثال على ذلك في الفقرة التالية ) .. و لكن يمكنك في هذه الطريقة ألا تكتب المحرف الصفري لأن المترجم سيضعه بشكل تلقائي بعد آخر حرف وضعته في المصفوفة و لكن يفضل أن تكتبه أنت لكي تفهم فائدته .

    الطريقة الثانية :
    كود:
    char name[20] = "montada";
    هذه الطريقة أبسط و أسرع .. و المترجم هنا أيضاً سيقوم بوضع المحرف الصفري بشكل تلقائي بعد آخر حرف تضعه .. و هذه الطريقة خاصة بالمصفوفة الحرفية فقط دون غيرها .

    خامساً / قنوات الدخل و الخرج في المصفوفة الحرفية :

    1 – قنوات الدخل ( cin ) :
    تستطيع ذلك بواسطة استخدام اسم المصفوفة ( فقط دون القوسين طبعاً و دون استخدام الدليل ) .. و أيضاً دون الحاجة إلى استخدام حلقة for .. و هذه الطريقة أيضاً خاصة بالمصفوفة الحرفية فقط .. مثال / بفرض أن name هي مصفوفة حرفية تحتوي على 20 عنصر :
    كود:
    char name[20];
    cin >> name;

    2 – قنوات الخرج ( cout ) :
    بنفس أسلوب cin .. حيث أننا نستخدم اسم المصفوفة الحرفية فقط ( دون استخدام الدليل ) و دون استخدام حلقة for .. مثال / بفرض أن name مصفوفة حرفية :
    كود:
    char name[20];
    cout << name;
    من الممكن توضيح عمل الطريقة السابقة كالآتي ( للتوضيح فقط ) :
    كود:
    int i=0;
    while( name[i] != '\0' )
    {
        cout << name[i];
        i++;
    }

    سادساً / معرفة عدد المحارف التي تحتوي عليها المصفوفة الحرفية و التي تسبق المحرف الصفري :

    يمكننا ذلك عن طريق استخدام المعامل ( الدالة ) strlen الموجودة في المكتبة string.h .. بحيث نرسل المصفوفة الحرفية ( عن طريق اسمها فقط ) أو السلسلة الحرفية ( جملة حرفية موجودة داخل حاصرات مزدوجة " " ) إلى تلك الدالة كوسائط .. مثال :
    كود:
    #include <string.h>
    #include <iostream.h>
    
    void main()
    {
        cout << strlen("Montada") << endl;
    
        char name[20] = "Montada";
        cout << strlen(name);
    }
    ملاحظة / لن يتم احتساب المحرف الصفري في الدالة strlen و الموجودة في المكتبة string.h .

    سابعاً / تطوير cin من أجل المصفوفة الحرفية :

    قلنا سابقاً أنك إذا أردت أن تطلب من المستخدم إدخال قيم حرفية و ذلك بأن تضعها في مصفوفة من النوع char .. فإنك ستستخدم التالي / بفرض أن name مصفوفة حرفية :
    كود:
    cin >> name;
    و لكن افرض أن المستخدم أدخل جملة و ليس كلمة .. بالتالي سيكون هناك فراغات بين كل كلمة .. و هذه المسألة تعتبر مشكلة ! لماذا ؟ لأن قناة الدخل ( cin ) لن تقرأ إلا الكلمة الأولى فقط .. لأنها سوف تقف عند المسافة فهي مبرمجة على أن تنهي القراءة عند وجود الفراغ .. و لحل هذه المشكلة يمكننا استخدام الآتي :
    كود:
    cin.getline( name, 20 );
    حيث قمنا بإضافة المنهج ( الدالة ) getline إلى cin ( سنتحدث عن المناهج أو التوابع الخاصة عن الحديث حول الكائنات ) .. فبذلك سيتم قراءة السطر ككل دون الانتهاء عند الفراغ كالسابق .. و طبعاً عليناً إرسال المصفوفة الحرفية كوسيط بالإضافة إلى أقصى حد من عدد المحارف التي تريد أن تقف القراءة عندها و يفضل أن تضع حجم المصفوفة كحد أقصى .

    :: المصفوفة ثنائية الأبعاد ::

    أولاً / مقدمة :

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

    ثانياً / التصريح عن المصفوفة ثنائية الأبعاد :

    بنفس أسلوب المصفوفة أحادية البعد .. و لكننا كما قلنا يجب أن نحدد حجم البعد الثاني أيضاً .. بحيث يمثل البعد الأول عدد الأسطر و البعد الثاني عدد الأعمدة .. مثال :
    كود:
    int a[3][3];

    ثالثاً / إعطاء قيم ابتدائية :

    مثال 1 / بفرض أننا نريد تعريف مصفوفة تحتوي على سطرين و عمودين .. بحيث يكون في السطر الأول العددين 1 و 2 .. و في السطر الثاني العددين 3 و 4 :
    كود:
    int a[2][2] = { {1,2} , {3,4} };
    مثال 2 / نريد تعريف مصفوفة تحتوية على سطرين و ثلاثة أعمدة و لكن قيمها كلها أصفار :
    كود:
    int a[2][3] = { 0 };
    بنفس الأسلوب .. حيث أننا إذا أعطينا قيم ابتدائية لجزء من المصفوفة فالباقي ستعتبر قيمه كلها أصفار .

    مثال توضيحي :


    رابعاً / المصفوفة ثنائية الأبعاد الحرفية :

    تتمتع بنفس مزايا المصفوفة الحرفية أحادية البعد ما عدا ميزة الإدخال ( cin ) و الإخراج ( cout ) .. حيث أننا نحتاج إلى حلقتين for بدلاً من واحدة .. الحلقة الأولى من أجل عدد الأسطر و الثانية من أجل عدد الأعمدة .

    مثال 1 / بفرض أننا نريد تعريف مصفوفة تحتوي على خمسة أسماء بحيث يمثل كل سطر اسم ما .. و طلبنا من المستخدم إدخال تلك الأسماء فسيكون الحل كالتالي :
    كود:
    char name[5][20];
    
    for( int i=0; i<5; i++ )
        for( int j=0; j<20; j++ )
            cin >> name[i][j];
    مثال 2 / أو من ممكن أن نستخدم الشكل التالي :
    كود:
    char name[5][20];
    
    for( int i=0; i<5; i++ )
            cin >> name[i];
    يمكننا إعطاء قيم ابتدائية للمصفوفة السابقة على الشكل التالي :
    كود:
    char name[5][20] = { {"montada"} , {'m','o','n','t','a','d','a','\0'} };
    حيث أنني وضعت اسمين فقط بطريقتين مختلفتين .. و هما صحيحتين على ألا تنسى في الطريقة الثانية المحرف الصفري كما قلنا سابقاُ .. و باقي الأسطر ستحتوي على الفراغ .. فإذا حاولنا كتابة المصفوفة السابقة على الشاشة فإننا سنكتب التالي :
    كود:
    for( int i=0; i<5; i++ )
    {
        for( int j=0; j<20; j++ )
            cout >> name[i][j];
        cout << endl;
    }
    استخدمنا cout << endl لكي نضع كل جملة على سطر .
    لا إله إلا الله
    مشروع لتعليم أساسيات لغة الـ ++C :
    -
    موضوع لتعليم الفيجوال بيسك 6 :

  14. #29
    التسجيل
    11-04-2002
    الدولة
    سوريا
    المشاركات
    760

    مشاركة: :: مشروع لتعليم أساسيات ++C :: موضوع الدروس

    :: استخدام المصفوفات في التوابع كمتحولات وسيطة ::

    كما قلنا في موضوع التوابع .. أنه يمكننا وضع أي شيء نريده كمتحولات وسيطة شكلية ( متغيرات عددية أو حرفية أو مؤشرات أو مصفوفات أو كائنات ... إلخ ) .. و نحن نريد هنا المصفوفات فكيف يمكننا ذلك ؟
    الحل بسيط .. فقط نقوم بوضع نوع المصفوفة ثم اسمها ثم حجمها .. مثال :
    كود:
    void f1( int s[20] )
    {
    }
    و يمكننا أيضاً عدم وضع حجم المصفوفة .. مثال /
    كود:
    void f1( int s[ ] )
    {
    }
    هذا إن كنا نستخدم الطريقة الأولى في كتابة التوابع .. أما لو استخدمنا الطريقة الثانية و هي كالتالي / بوضع رأس التابع فوق التابع الأساسي main و جسم التابع يكون أسفله :
    كود:
    void f1( int [ ] );

    فإننا لن نحتاج إلى وضع حجم المصفوفة أو حتى اسمها في رأس التابع و لكن يجب وضع القوسين( [ ] ) ( ليس كالسابق كالمتغيرات العادية حيث كنا نكتفي بنوع المتغير فقط ) .. هذا الأمر فقط في المصفوفة أحادية البعد .. أما في المصفوفة ثنائية البعد فيجب تحديد حجم البعد الثاني فقط متجاهلين حجم البعد الأول .

    مثال :
    كود:
    void f1( int [ ][20] );
    أو حتى عند كتابة التابع نفسه :
    كود:
    void f2( int s[ ][20] )
    {
    }

    قاعدة 1 / عند تعريف المصفوفات ( سواءً الأحادية أم الثنائية البعد ) كمتحولات وسيطة في التوابع :

    1 - فإننا لن نحتاج إلى وضع اسم المصفوفة إذا ما عرفنا رأس التابع و لكن نحتاج إلى وضع حجم البعد الثاني لمصفوفة إذا كانت ثنائية الأبعاد .. و الأقواس ( [ ] ) طبعاً ضرورية .
    2 – و لكننا نحتاج إلى وضع اسم المصفوفة عند وضع جسم التابع مع إمكانية تجاهل حجم البعد الأول فقط للمصفوفة ( قلت الحجم و ليس القوسين ( [ ] ) ) كما في الفقرة الأولى .

    و لكن كيف يمكننا إرسال إحدى قيم ( عناصر ) المصفوفة ؟ أو حتى كيف يمكننا إرسال المصفوفة كلها إلى إحدى التوابع كمتحولات وسيطة فعلية ؟ ( راجع درس التوابع لمعرفة معنى متحولات وسيطة فعلية و شكلية ) ..

    قاعدة 2 / يمكننا إرسال أحد عناصر المصفوفة عن طريق وضع اسم المصفوفة مع الدليل الخاص بذلك العنصر .. مثال / بفرض أن f1 تابع لا يعيد قيمة يحتاج إلى عدد صحيح كمدخلات له .. و s هي مصفوفة من النوع int :
    كود:
    f1( s[0] );

    قاعدة 3 / أما إذا كنا نريد إرسال المصفوفة كلها إلى تابع ما .. فنضع اسم المصفوفة فقط دون حجمها و بدون الأقواس ( [ ] ) .. مثال / هنا التابع f1 يحتاج إلى مصفوفة كمدخلات و ليس إلى قيمة واحدة كالمثال السابق :
    كود:
    f1( s );

    قاعدة 4 / إن نوع الاستدعاء في المصفوفات هو استدعاء بالمرجع ( حصراً ) و لا يمكن استدعاؤها بالقيمة ( هذا إن كنا أرسلنا المصفوفة كلها كمتحول فعلي إلى تابع آخر ) ..
    و بالتالي إذا قمت بتغيير إحدى قيم المصفوفة في أحد التوابع فإن تلك القيمة ستتغير في المصفوفة الأصلية لأنها تستدعى بالمرجع بشكل تلقائي .. حيث أن اسمها يمثل عنوان أول عنصر ( لها ) في الذاكرة .. و قلنا سابقاً بأن معنى المرجعية ( Reference ) هو التعامل مع عنوان المتغير في الذاكرة .. و بالتالي يمكن أن يكون هناك أكثر من متغير يحملون نفس الموقع في الذاكرة ( متغير فعلي واحد مع عدة متغيرات شكلية ) .. فإذا تغير أحدهم يتغير الآخر ( راجع موضوع الاستدعاء بالقيمة و بالمرجع في درس التوابع ) .

    قاعدة 5 / إن الاستدعاء لأحد قيم المصفوفة من الممكن أن يكون إما بالقيمة أو بالمرجع .. مثال لاستدعاء إحدى قيم المصفوفة بالقيمة / لبرنامج يقوم بطلب جملة من المستخدم و من ثم يكتب له أول حرف من هذه الجملة ( للتوضيح فقط ) :
    كود:
    #include <iostream.h>
    
    void f1( char s )
    {
        cout << s << endl;
    } 
    
    void main()
    {
        char name[20];
    
        cin >> name;
    
        f1(name[0]); 
    }

    ملاحظة / إذا كنا نريد في المثال السابق أن نستدعي القيمة :
    كود:
    name[0]
    بالمرجع فما علينا إلا أن نضيف الرمز ( & ) قبل المتحول الشكلي ( s ) الموجود في التابع ( f1 ) .


    :: نظام الترميز ASCII ::

    1 – مقدمة :

    لماذا نتعلم نظام الآسكي ؟ في الحقيقة هذا النظام مفيد جداً لحل بعض المسائل .. فهذا النظام قام على أساس وضع لكل رقم أو حرف أو رمز ... إلخ رقماً معين ( شفرة ) يشير إلى ذلك المحرف أو الرقم .. بحيث يحتوي هذا النظام على 256 رقماً يشيرون إلى تلك الرموز و الأحرف و الأرقام .

    2 – محتويات النظام ASCII :

    يحتوي هذا النظام كما قلنا على عدة رموز ( سنأخذ المهمة فقط ):

    أ ) من 0 إلى 32 : عبارة عن مجموعة من الأوامر و الأحـداث .. كحدث الضغط على زر Enter أو Space .
    ب ) من 48 إلى 57 : الأرقام من الصفر و حتى التسعة .
    ج ) من 65 إلى 90 : الأحرف الإنجليزية الكبيرة مثل : ( A, B, C ) .
    د ) من 97 إلى 122 : الأحرف الإنجليزية الصغيرة مثل : ( a, b, c ) .

    3 - استخدام نظام الترميز ASCII في السي++ :

    يمكننا ذلك عن طريق المتغيرات من النوع int و char .. كيف ؟ و ذلك إما بوضع محرف في متغير عددي صحيح ( int ) أو بوضع عدد صحيح في متغير حرفي ( char ) .. انظر المثال التالي :
    كود:
    int a = 'a';
    
    char b = 98;
    في المثال الأول أعطينا المتغير العددي a قيمة حرفية من النوع char .. و بالتالي سيقوم المترجم بإيجاد الرمز الذي يشير إلى الحرف الصغير a في النظام ASCII .. و سوف يضع القيمة ( 97 ) في المتحول a .. لذلك إذا حاولت كتابة قيمة المتحول a على الشاشة فسوف يظهر الرقم 97 كما قلنا .

    في المثال الثاني عكسنا العملية .. فقمنا بوضع قيمة عددية في متغير حرفي .. لذلك سيقوم المترجم بإيجاد القيمة التي يشير إليها الرمز 98 و هي الحرف b الصغير .. لذلك قم بكتابة قيمة كل من المتغيرين a و b على الشاشة ( cout ) و شاهد النتائج .

    4 – الاستفادة من نظام الترميز ASCII :

    سوف نأخذ مثال يوضح فائدة استخدام هذا النظام و أنه يبسط الحل أكثر .. فهناك مسائل إذا استخدمت فيها هذا النظام فسختصر على نفسك سطوراً كثيرة من الأوامر و سنرى ذلك في المثال العملي السابع .

    ----------------------------------------
    لا إله إلا الله
    مشروع لتعليم أساسيات لغة الـ ++C :
    -
    موضوع لتعليم الفيجوال بيسك 6 :

  15. #30
    التسجيل
    11-04-2002
    الدولة
    سوريا
    المشاركات
    760

    مشاركة: :: مشروع لتعليم أساسيات ++C :: موضوع الدروس

    :: أمثلة تطبيقية ::

    سأقوم بوضع أمثلة تتدرج من السهل إلى الأصعب .. مع وضع برنامج تطبيقي في النهاية يشمل موضوع المصفوفات بشكل كامل .

    ملاحظة / جميع هذه الأمثلة من أستاذي ( جزاه الله خيراً ) .. و ها أنا أضعها بين يديكم .

    المثال 1 /

    نريد من المستخدم أن يدخل خمسة أعداد صحيحة و من ثم نكتب له مجموع تلك الأعداد :


    الحل /
    كود:
    #include <iostream.h>
    
    void main()
    {
        int sum = 0;
        int a[5];
        
        for( int i=0; i<5; i++ )
        {
            cout << "a[" << i << "] : ";
            cin >> a[i];
            sum += a[i];
        }
    
        cout << "Sum = " << sum << endl;
    }
    شرح الحل :

    1 – قمنا بتعريف المتغير sum و الذي سيقوم بحساب مجموع الأعداد المدخلة .. و وضعنا له قيمة ابتدائية تساوي الصفر ( قلنا سابقاً فائدة ذلك ) .
    2 – عرفنا مصفوفة أعداد صحيحة لكي يدخل المستخدم الأعداد فيها .. و هي تحتوي على خمسة مواقع في الذاكرة من النوع int .
    3 – بعد ذلك قمنا بترتيب عملية الإدخال لكي يعرف المستخدم أي الأعداد سيدخل .. و ذلك عن طريق عملية الإخراج الأولى .. حيث أن المستخدم في كل عملية إدخال جديدة ( في كل دورة ) سيتم تخزين العدد المدخل في مكانه المناسب في المصفوفة a .. و بعدها أيضاً سيتم إضافة ذلك العدد إلى sum في كل مرة لكي يحتوي sum في النهاية على مجموع تلك الأعداد .
    4 – في النهاية قمنا بكتابة ناتج عملية الجمع بواسطة المتحول sum .

    ----------------------------------------

    المثال 2 /

    نريد من المستخدم أن يدخل عدداً من الحروف و من ثم نكتب له كل حرف قام بكتابته على سطر :



    الحل /
    كود:
    #include <iostream.h>
    #include <string.h>
    
    void main()
    {
        char s[100];
    
        cin >> s;
    
        for( int i=0; i<strlen(s); i++ )
            cout << s[i] << endl;
    }
    شرح الحل :

    1 – قمنا بتعريف مصفوفة حرفية تتسع لمائة محرف و سميناها s ( المائة عدد افترضناها تستطيع وضع ما شئت ) .
    2 – طلبنا من المستخدم إدخال تلك الأحرف .
    3 – في الحلقة for .. قمنا بجعل الدليل i يبدأ من القيمة صفر ( لأن دليل أول عنصر في المصفوفة يساوي الصفر ) و ينتهي في آخر دورة بعدد محارف تلك المصفوفة و التي تسبق المحرف الصفري ( راجع فقرة معرفة عدد المحارف ) .. و لكن لماذا فعلنا هذا ؟ لأننا نريد كتابة الأحرف التي أدخلها المستخدم فقط و لا نريد جميع عناصر المصفوفة حيث أن المستخدم قد يدخل عدداً من الأحرف تقل عن المائة .
    4 – في داخل الحلقة for قمنا في كل دورة بكتابة عنصر المصفوفة ذي الدليل i و انتقلنا إلى سطر جديد .. و هذه العملية ستتكرر على حسب عدد المحارف التي أدخلها المستخدم .

    ----------------------------------------

    المثال 3 /

    نريد من المستخدم إدخال جملة و من ثم نكتب له كل كلمة أدخلها على سطر :



    الحل /
    كود:
    #include <iostream.h>
    #include <string.h>
    
    void main()
    {
        char s[100];
    
        cin.getline( s, 100 );
    
        for( int i=0; i<strlen(s); i++ )
            if( s[i] == ' ' )
                cout << endl;
            else
                cout << s[i];
    
        cout << endl;
    }
    شرح الحل :

    1 – بنفس أسلوب المثال السابق و لكن مع بعض التطوير .. حيث قمنا باستخدام المنهج ( الدالة ) getline لكي نخزن في المصفوفة s جميع الكلمات و ليس الكلمة الأولى فقط حيث أننا سنقرأ أول مائة محرف ( قلنا أنه يفضل وضع حجم المصفوفة و الذي سنرسله مع المصفوفة s كوسائط إلى المنهج getline ) .. و حيث أن cin العادية ستقوم بقراءة أول كلمة فقط حيث أنها تنتهي القراءة عند الفراغ ( راجع فقرة تطوير قناة الدخل cin ) .
    2 – في الحلقة for .. سنختبر في كل دورة عناصر المصفوفة عنصراً عنصراً .. فإذا كان هذا العنصر ( المحرف ) يساوي الفراغ فسنقوم بالنزول إلى سطر جديد .. أما إذا لم يكن كذلك فسنكتب له المحرف و بهذه الطريقة سيتم وضع كل كلمة على سطر و هذا هو المطلوب .
    3 – في النهاية قمنا فقط بالنزول إلى سطر جديد من أجل ترتيب البرنامج .

    ----------------------------------------

    المثال 4 /

    نريد من المستخدم إدخال 10 أعداد صحيحة و من ثم نكتب له أكبر عدد و أصغر عدد و ذلك باستخدام المصفوفات :



    الحل /
    كود:
    #include <iostream.h>
    
    void main()
    {
        int a[10], max, min;
    
        cout << "a[0] = ";
        cin >> a[0];
    
        max = a[0];
        min = a[0];
    
        for( int i=1; i<10; i++ )
        {
            cout << "a[" << i << "] = ";
            cin >> a[i];
    
            if( a[i] > max ) max = a[i];
            if( a[i] < min ) min = a[i];
        }
    
        cout << "Max = " << max << endl
                 << "Min = " << min << endl;
    }
    شرح الحل :

    هذا المثال يطابق تماماً المثال الثالث الموجود في الدرس الثالث .. لذلك لن أشرحه لكي لا أقوم بتكرار شيء موجود .

    ----------------------------------------

    المثال 5 /

    نريد من المستخدم إدخال جملة و من ثم نحسب له عدد كلمات تلك الجملة .. على أنه من الممكن أن يكون بين كل كلمتين أكثر من فراغ أو حتى من الممكن أن يكون هناك رمز ما مثل ( $ ) أو حتى رقم .. لذلك من الخطأ حساب عدد الفراغات .. و لكن ما الحل ؟

    فكرة الحل / يمكننا حساب عدد كلمات جملة ما إذا عرفنا متى تنتهي الكلمة .. تنتهي الكلمة إذا وجدنا حرف في المصفوفة بعده فراغ و بالتالي يكون ذلك الحرف هو آخر حرف في الكلمة .. عندها يكون عندنا متحول يحسب لنا عدد الكلمات فنزيده واحد و هكذا ..


    الحل /
    سنستخدم التوابع في هذا الحل :
    كود:
    #include <iostream.h>
    #include <string.h>
    #include <ctype.h>
    
    int nword(char s[ ]);
    
    void main()
    {    
        char s[100];
    
        cout << "Enter The Sentence : ";
        cin.getline(s, 100);
    
        cout << nword (s) << endl;
    }
    
    int nword(char s[100])
    {
        int c=0;
    
        for( int i=0; i<strlen(s); i++ )
            if( isalpha(s[i]) && !isalpha(s[i+1]) )
                c++;
    
        return c;
    }
    شرح الحل :

    1 – ما فائدة التابع ( الدالة ) isalpha ؟ هذا التابع يقوم باختبار فيما إذا كانت القيمة المرسلة إليه حرفاً أبجدياً أم لا .. و هذا ما نحتاجه في سؤالنا هذا .. و هذا التابع موجود في المكتبة ctype.h .
    2 – أظن أن التابع main مفهوم .. فقط قمنا بتعريف مصفوفة حرفية و من ثم طلبنا من المستخدم إدخال جملة و بعد ذلك استدعينا التابع nword و الذي سيحسب لنا عدد الكلمات للجملة المدخلة من قبل المستخدم .
    3 – في التابع nword .. قمنا بأخذ المصفوفة من التابع main و أجرينا عليها الاختبار الذي ذكرناه في فكرة الحل .. قمنا باختبار جميع قيم المصفوفة فإذا وجدنا أن إحدى القيم هي حرف أبجدي و كان المحرف الذي يليه ليس حرفاً أبجدياً ( استخدمنا علامة النفي ! ) بالتالي يكون لدينا كلمة جديدة و عندها نزيد العداد ( c ) واحد .. و هكذا إلى نهاية المصفوفة .. و في النهاية سيقوم التابع nword بإرجاع قيمة عدد الكلمات إلى التابع main .

    ----------------------------------------
    التعديل الأخير تم بواسطة Wolf Sniper ; 25-03-2005 الساعة 10:41 PM
    لا إله إلا الله
    مشروع لتعليم أساسيات لغة الـ ++C :
    -
    موضوع لتعليم الفيجوال بيسك 6 :

صفحة 2 من 4 الأولىالأولى 1234 الأخيرةالأخيرة

ضوابط المشاركة

  • لا تستطيع إضافة مواضيع جديدة
  • لا تستطيع الرد على المواضيع
  • لا تستطيع إرفاق ملفات
  • لا تستطيع تعديل مشاركاتك
  •