صفحة 3 من 4 الأولىالأولى 1234 الأخيرةالأخيرة
النتائج 31 إلى 45 من 46

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

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

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

    المثال 6 /

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


    ملاحظة / هذا المثال صعب لذلك أنصح بقراءة الحل و الشرح أكثر من مرة لفهم الفكرة التي قمنا بحل السؤال بها .

    فكرة الحل / سنستخدم نظام الترميز ASCII لأنه سيسهل علينا اختبار جميع الرموز و الأحرف و الأرقام و التي من الممكن أن يدخلها المستخدم عن طريق لوحة المفاتيح .. و ذلك عن طريق الآتي :

    1 - سنقوم بتعريف مصفوفة تحتوي على 256 عنصر من النوع int .. و جميع تلك المتغيرات عبارة عن عدادات إحصائية .. بحيث سيقوم العنصر رقم 97 بحساب عدد مرات تكرار الحرف a و العنصر رقم 98 بحساب عدد مرات تكرار الحرف b ... إلخ .. و هكذا .. و بالتالي قمنا بإنشاء مصفوفة تمثل نظام ASCII بنفس الترتيب للرموز من الصفر و حتى 256 .. حيث أن العنصر الأول يمثل الرمز الأول في ذلك النظام و العنصر الثاني يمثل الرمز الثاني ... إلخ .. و هكذا .
    2 – سنقوم باستخدام طريقة إعطاء قيمة حرفية لمتغير عددي من النوع int و التي ذكرناها في فقرة نظام الترميز ASCII .

    الحل /
    كود:
    #include <iostream.h>
    #include <string.h>
    #include <ctype.h>
    
    void main()
    {
        int counter[256] = {0};
        char s[100];
    
        cout << "Enter a Sentence : ";
        cin.getline(s, 100);
    
        for( int i=0; i<strlen(s); i++ )
            if( isalpha(s[i]) )
                counter[ s[i] ]++;
    
        for( i=0; i<256; i++ )
            if( counter[i] != 0 )
                cout << (char)i << " : " << counter[i] << endl;
    }
    ملاحظة / انظر إلى الحل كم هو قصير .. و افرض أننا لم نستخدم نظام الآسكي .. عندها سنحتاج ربما لصفحات لحل هذا السؤال .

    شرح الحل :

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


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

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

    المثال 7 /

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


    ملاحظة 1 / القطر الأساسي جميع عناصره تحقق المعادلة : i = j .. حيث أن ( i ) للسطر و ( j ) للعمود .
    ملاحظة 2 / القطر الثانوي جميع عناصره تحقق المعادلة : i = n – 1 .. حيث أن ( n ) هي حجم المصفوفة المربعة .

    الحل /
    سنستخدم التوابع في هذا الحل :
    كود:
    #include <iostream.h>
    
    const int n = 3;
    
    void read(int a[][n]);
    int sum_primary (int a[][n]);
    int sum_secondary(int a[][n]);
    
    void main()
    {
        int array[n][n];
    
        read(array);
        cout << "Primary = " << sum_primary(array) << endl;
        cout << "Secondary = " << sum_secondary(array) << endl;
    }
    
    void read(int a[n][n])
    {
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<n; j++)
            {
                cout << "array[" << i << ']' << '[' << j << "] = ";
                cin >> a [i][j];
            }
            cout << endl;
        }
    }
    
    int sum_primary(int a[n][n])
    {
        int c=0;
    
        for(int i=0; i<n; i++)
            c += a[i][i];
        
        return c;
    }
    
    int sum_secondary(int a[n][n])
    {
        int c=0;
    
        for(int i=0; i<n; i++)
            c += a[i][n-i-1];
    
        return c;
    }

    شرح الحل :


    1 – في أول البرنامج قمنا بتعريف ثابت من النوع الصحيح ( int ) و الذي سيحدد حجم المصفوفة الثنائية .. و سنستخدمه أيضاً في الحلقات for و التي سنحتاجها لبعض العمليات على تلك المصفوفة .. و لقد افترضنا أن حجم المصفوفة هو 3×3 .

    2 – قمنا بتعريف ثلاثة توابع :
    الأول / read : من أجل إدخال المصفوفة .. و لقد أضفنا عليه بعض عمليات الإخراج من أجل تحسين مظهر البرنامج .. و هذا التابع من النوع void أي أنه لن يعيد قيمة و بالتالي يمكن استدعاءه بذكر اسمه فقط مع المتحولات الوسيطة التي يحتاجها .. و طبعاً يجب أن نرسل له المصفوفة الثنائية لكي يستطيع أن يتعامل معها .
    الثاني / sum_primary : من أجل حساب مجموع القطر الرئيسي .. و قلنا أن القطر الرئيسي يمكن إيجاد عناصره عن طريق المعادلة i=j .. و هذا التابع هو من النوع int أي أنه سيعيد قيمة عددية صحيحة .. و بالتالي لا يمكن استدعاءه بذكر اسمه فقط مع القوسين ! بل يجب ذلك إما ضمن عملية إسناد أو إخراج ( cout ) ... إلخ ( راجع درس التوابع ) .. و أيضاً سيحتاج إلى المصفوفة لكي يستطيع أن يتعامل معها لهذا وضعنا مصفوفة ثنائية من نفس الحجم كمتحول وسيط .
    الثالث / sum_secondary : من أجل حساب مجموع القطر الثانوي .. و بنفس أسلوب التابع السابق و لكن يمكن إيجاد عناصره عن طريق المعادلة التالية : i = n-1 .. و هذا يعني أن دليل أي عنصر من القطر الثانوي يحقق التالي : ( n – i – 1 ) .. و ذلك بعد أن أرسلنا i إلى طرف المعادلة الأيمن .

    3 – في التابع main قمنا بتعريف المصفوفة الأساسية و التي سنجري عليها جميع العمليات المطلوبة .. و قمنا أيضاً باستدعاء التوابع الثلاثة المذكورة سابقاً .

    4 – في التابع sum_primary : قمنا باستقبال المصفوفة array عن طريق المتحول الوسيط الشكلي ( المصفوفة ) :
    كود:
    a[n][n]
    و هي بنفس الحجم 3×3 .. و أيضاً قمنا بتعريف المتغير c و الذي سيحسب لنا مجموع القطر الرئيسي .
    الأن من أجل حساب ذلك المجموع .. ما الذي سنحتاجه ؟ في الحقيقة لن نحتاج إلا لحلقة for واحدة فقط .. و ذلك عن طريق استخدام دليل i الخاص بالحلقة لأننا قلنا أن القطر الرئيسي عناصره يمكن إيجادها عن طريق المعادلة i=j .. و بالتالي ليس هناك ضرورة إلى المتحول j لذلك لن نستخدمة حلقة أخرى .. و بالتالي في داخل الحلقة قمنا بجمع عناصر القطر الرئيسي ذو الدليل :
    كود:
     a[i][i]
    و وضعنا المجموع في المتحول c .. و في النهاية قمنا بإرجاع قيمة c كقيمة معادة لكي يستفيد من هذه القيمة باقي التوابع و منهم بالتأكيد التابع الأساسي main .


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

    5 – في التابع sum_secondary : بنفس أسلوب التابع السابق و لكن ستختلف معادلة البعد الثاني للمصفوفة في داخل الحلقة for .. فأول عنصر في القطر الثانوي سيكون سطره يساوي ( الصفر ) و عموده يساوي ( 2 ) .. و العنصر الثاني سيكون سطره ( 1 ) و عموده ( 1 ) .. و العنصر الثالث سيكون سطره ( 2 ) و عموده ( صفر ) .. و بالتالي ستكون معادلة دليل البعد الثاني :
    (n-i-1) .. و هذا ما وضعناه في أدلة المصفوفة a .. و بالتالي يقوم المتغير c بحساب مجموع ذلك القطر و من ثم سنقوم بإرجاع قيمته إلى التابع main .

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

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

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

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

    :: برنامج تطبيقي ::

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


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


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


    و بعد ذلك ندخل أسماء المواد على الشكل التالي :


    و بعد ذلك ندخل علامات الطلاب في تلك المواد على الشكل التالي :


    و بعد ذلك نحسب معدل ( النسبة المئوية ) لكل طالب على الشكل التالي :


    الحل /
    كود:
    #include <iostream.h>
    #include <ctype.h>
    
    const int number = 3;
    
    void readstudents(char [ ][10]);
    void readmaterials(char [ ][10]);
    void readmarks(char [ ][10], char [ ][10], int [ ][number]);
    void averagestudents(char [ ][10], int [ ][number], float [ ]);
    void printall(char [ ][10], char [ ][10], int [ ][number], float [ ]);
    
    void main()
    {
        char students[number][10];
        char materials[number][10];
        int marks[number][number];
        float average[number];
    
        char choice;
    
        do{
            cout << "S. Read Students\n"
                     << "M. Read Materials\n"
                     << "K. Read Marks\n"
                     << "P. Average Students\n"
                     << "A. Print All\n"
                     << "Q. Quit\n\n"
                     << "Enter your choice : ";
            cin >> choice;
    
            switch( toupper(choice) )
            {
                case 'S' : readstudents(students); cout << "\n------------------------------\n"; break;
                case 'M' : readmaterials(materials); cout << "\n------------------------------\n"; break;
                case 'K' : readmarks(students, materials, marks); cout << "\n------------------------------\n"; break;
                case 'A' : printall(students, materials, marks, average); cout << "\n------------------------------\n"; break;
                case 'P' : averagestudents(students, marks, average); cout << "\n------------------------------\n"; break;
                case 'Q' : return;
    
                default : cout << "Error.. \n\n------------------------------\n\n";
            }
        }while( true );
    }
    
    void readstudents(char students[ ][10])
    {
        cout << endl;
        for( int i=0; i<number; i++ )
        {
            cout << "Name of Student Number " << i << " : ";
            cin >> students[i];
        }
    }
    
    void readmaterials(char materials[ ][10])
    {
        cout << endl;
        for( int i=0; i<number; i++ )
        {
            cout << "Name of Mterial Number " << i << " : ";
            cin >> materials[i];
        }
    }
    
    void readmarks(char students[ ][10], char materials[ ][10], int marks[ ][number])
    {
        cout << endl;
        for( int i=0; i<number; i++ )
        {
            for( int j=0; j<number; j++ )
            {
                cout << "Mark of " << students[i] << " in " << materials[j] << " : ";
                cin >> marks[i][j];
            }
            cout << endl;
        }
    }
    
    void averagestudents(char students[ ][10], int marks[ ][number], float average[ ])
    {
        cout << endl;
    
        for( int i=0; i<number; i++ )
        {
            int sum=0;
    
            for( int j=0; j<number; j++ ) sum+=marks[i][j];
    
            average[i] = (float)sum / (float)number;
        }
    
        for( i=0; i<number; i++ )        
            cout << "Average of Student " << students[i] << " = " << average[i] << endl;
    }
    
    void printall(char students[ ][10], char materials[ ][10], int marks[ ][number], float average[ ])
    {    
        cout << "\n---------------\n\n";
        for( int i=0; i<number; i++ )
        {
            cout << "Student : " << students[i] << endl
                    << "Average = " << average[i] << endl << endl;
            for( int j=0; j<number; j++ )
                cout << "Mark of " << materials[j] << " = " << marks[i][j] << endl;
            cout << "\n---------------\n\n";
        }
    }
    شرح الحل :

    1 – أولاً قمنا بتعريف الثابت number و الذي سنحتاجه من أجل حجم المصفوفات التي سنعرفها .

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

    3 – في التابع الرئيسي main :
    أ ) قمنا بتعريف المصفوفات المطلوبة و هي كالتالي :
    students : مصفوفة ثنائية الأبعاد من النوع char .. و التي ستخزن أسماء الطلاب بحيث يكون كل اسم على سطر مستقل ضمن أسطرها على أن لا يتجاوز اسم الطالب عشرة أحرف .
    materials : مصفوفة ثنائية الأبعاد من النوع char .. لتخزين أسماء المواد ( بنفس أسلوب المصفوفة students ) .
    marks : مصفوفة ثنائية الأبعاد من النوع int .. لتخزين علامات الطلاب بحيث يمثل السطر الأول منها علامات الطالب الأول و السطر الثاني من أجل علامات الطالب الثاني و السطر الثالث من أجل علامات الطالب الثالث .. و يمثل العمود الأول من المصفوفة المادة الأولى .. و العمود الثاني المادة الثانية .. و العمود الثالث المادة الثالثة .. و بالتالي نستطيع بهذه المصفوفة تخزين علامات جميع الطلاب في جميع المواد .
    average : مصفوفة أحادية البعد من النوع float .. من أجل تخزين معدل كل طالب بحيث تمثل القيمة الأولى معدل الطالب الأول .. و القيمة الثانية معدل الطالب الثاني .. و القيمة الثالثة معدل الطالب الثالث .

    ب ) بعد ذلك قمنا بتعريف المتحول choice ذو النوع char .. و الذي سيخزن قيمة الحرف الذي سيدخله المستخدم و الذي يمثل اختصار لتنفيذ العملية المطلوبة منا .

    ج ) استخدمنا التكرار ( do\while ) لأن برنامجنا سينفذ أكثر من أمر من بين الأوامر التي وضعناها حتى يضع المستخدم الحرف ( Q ) الذي سينهي البرنامج .

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

    هـ ) بعد ذلك استخدمنا العبارة switch لكي نضع الاحتمالات التي يمكن أن يحتوي عليها المتغير choice .. و لكن ما معنى الدالة toupper ؟ افرض أن المستخدم أدخل الحرف ( S ) كحرف صغير و الذي يدل على عملية إدخال أسماء الطلاب .. لذلك لن يقوم برنامجنا باستدعاء التابع readstudents لأننا قمنا بوضع في الحالة case الحرف ( S ) كحرف كبير .. لذلك تقوم الدالة toupper بتكبير حجم الحرف .. و بذلك نتعامل مع المتحول choice بالأحرف الكبيرة فقط .

    و ) داخل switch .. قمنا بوضع جميع اختصارات الأوامر الموجودة و التي يمكن للمستخدم أن يقوم بها و في كل حالة قمنا باستدعاء التابع الخاص بالعملية المطلوبة .. مع إضافة سطر بعد استخدام كل عملية .. و إذا أدخل المستخدم الحرف ( Q ) فإن البرنامج سينتهي و ذلك لأننا استخدمنا return و التي تقوم بالخروج من التابع و إنهاء عمله كلياً و بما أننا في التابع main فسينهي البرنامج .. و إذا أدخل المستخدم حرف يختلف عن الأحرف التي وضعناها فسنكتب له رسالة Error و ذلك عن طريق default .

    4 – بالنسبة للتوابع التي كتبناها .. أعتقد أنها سهلة و لا حاجة لشرحها فكل ما هو موجود فيها قد شرحناه سابقاً .. و إذا أراد أحد منكم أن أشرح له أحدها فلا بأس .. و لكن لاحظوا إلى أننا نريد أن نكتب التوابع بالشكل الموجود في الصور تماماً .

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

    :: المكتبة Library2.h ::

    سنقوم بإضافة التابع nword الموجود في المثال الخامس بالإضافة إلى التابعين sum_primary و sum_secondary الموجودين في المثال السابع إلى مكتبتنا الخاصة و سنسميها Library2 .. حيث أن الرقم يمثل كل إضافة جديدة للمكتبة .. المكتبة موجودة في المرفقات .

    ملاحظة 1 / قمنا بإضافة المكتبتين string.h و ctype.h .. لأننا استخدمنا في المكتبة كلاً من :
    - strlen .
    - isalpha .

    ملاحظة 2 / قمنا بتعريف الثابت n في بداية مكتبتنا لأننا سنستخدم هذا الثابت في التابعين sum_primary و التابع sum_secondary .

    ملاحظة 3 / قمنا بتغيير اسم التابع read و الذي وضعناه في المكتبة Library1 إلى mirror_word .

    ملاحظة 4 / لتجربة أحد توابع المكتبة Library2 .. قم بكتابة ملف مصدري ( cpp ) و قم بضم المكتبة إلى هذا الملف عن طريق :
    كود:
    #include "Library2.h"
    مع ملاحظة أن تكون المكتبة بالمكان الذي يوجد فيه ذلك الملف المصدري ( راجع الدرس السابق لفهم طريقة الاستخدام أكثر ) .

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

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

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

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

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

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

    :: السجلات ( Structures ) ::

    :: 1- مقدمة ::

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

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

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

    :: 2- تعريف ::

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

    لاحظ كيف استخدمنا أحد حقول ( متغيرات ) السجل .. فقط وضعنا اسم السجل ثم النقطة ثم اسم الحقل ( المتغير ) .

    :: 3- فائدة استخدام السجلات ::

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

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

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

    :: 4- التصريح عن السجلات ( تعريف السجلات ) ::

    أولاً / تعريف النوع : نقوم بتعريف السجل .. و هذا السجل سيمثل نوع ( Type ) نستطيع به بعد ذلك أن نعرف أي متحول على أنه من هذا النوع .

    قاعدة :
    كود:
    struct Name {
        Type Column;
        Type Column;
        Type Column;
    };
    ملاحظة / انتبه إلى الفاصلة المنقوطة الموجودة في آخر تعريف السجل .

    بحيث تمثل :
    struct : هي الكلمة الخاصة بتعريف السجلات .
    Name : اسم هذا السجل .
    Type : نوع المتغير ( الحقل ) الذي نريد أن نضعه داخل هذا السجل ( int, float, double .. إلخ ) .. أو حتى من الممكن أن نضع نوع لسجل آخر .
    Column : اسم المتغير ( الحقل ) الذي نريد أن نضعه داخل هذا السجل .

    مثال / لسجل يحتوي على بيانات الطالب ( الاسم ، العمر ، الرقم ) :
    كود:
    struct student {
        char name[20];
        int age;
        int number;
    };

    ثانياً / تعريف متحول : نقوم بتعريف متحول ما على أنه من النوع الذي قمنا بتعريفه سابقاً على أنه سجل ( Student على سبيل المثال ) .. و كأنك تقوم بتعريف متحول بشكل طبيعي على أنه من النوع int أو أي نوع آخر .

    مثال / لمتحولات من النوع student :
    كود:
    student s1;
    student s2;
    student student1;

    ملاحظة 1 / سيظهر النوع student باللون الأسود و ليس الأزرق لأننا قلنا سابقاً بأن الكلمات التي تلون باللون الأزرق هي الكلمات المحجوزة فقط مثل ( if .. switch .. int .. float .. break و غيرها ) .

    ملاحظة 2 / يمكننا اختصار العمليتين السابقتين على الشكل التالي :
    كود:
    struct student {
        char name[20];
        int age;
        int number;
    }s;

    :: 5- استخدام قنوات الدخل و الخرج ( cin, cout ) مع السجلات ::

    لقراءة و طباعة ( cin, cout ) حقول السجل لا بد من استخدامها على شكل إفرادي ( كل حقل بشكل منفرد ) ..

    مثال 1 / بفرض أن s هو سجل نوعه student المعرف في الفقرة السابقة :
    كود:
    cin >> s;
    cin >> s.age;
    المثال الأول خاطئ لأننا كما قلنا يجب استخدام كل حقل في السجل على حدة .. بينما المثال الثاني صحيح .

    مثال 2 / سنقوم بكتابة النوع student مرة أخرى لكي نضيف عليه بعض الحقول :
    كود:
    #include <iostream.h>
    
    struct student{
        char name[15];
        char phone[10];
        char address[50];
        int age;
        int number;
    };
    
    void readinfo(student &);
    void printinfo(student &);
    
    void main()
    {
        student s;
        
        readinfo(s);
        cout << "--------------------\n";
        printinfo(s);
    }
    
    void readinfo(student &s)
    {
        cout << "Name : "; cin >> s.name;
        cout << "Phone : "; cin >> s.phone;
        cout << "Address : "; cin >> s.address;
        cout << "Age : "; cin >> s.age;
        cout << "Number : "; cin >> s.number;
    }
    
    void printinfo(student &s)
    {
        cout << "Name : " << s.name << endl;
        cout << "Phone : " << s.phone << endl;
        cout << "Address : " << s.address << endl;
        cout << "Age : " << s.age << endl;
        cout << "Number : " << s.number << endl;
    }
    في هذا المثال قمنا بتعريف النوع student و الذي يحتوي على خمسة حقول .. الاسم و رقم الهاتف و العنوان و العمر و الرقم .. و استخدمنا تابعين .. الأول لقراءة السجل و الثاني لطبعاته على الشاشة .. و لكن لاحظ كيف أننا وضعنا نوع المتحول الوسيط في كلا التابعين من النوع student .. لأن كل تابع سيستقبل السجل المطلوب كمدخلات له و سيقوم بالعمليات المطلوبة .

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

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

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

    :: 6- استخدام عملية الإسناد مع الحقول ذات النوع char ::

    في الحقيقة لا نستطيع وضع عملية الإسناد مع الحقول من ذلك النوع .. انظر المثال التالي :
    كود:
    void main()
    {
        student s;
    
        s.name = "Montada";
    }
    هنا سوف يظهر خطأ ؟!! .. بحيث سيخبرك المترجم بأنه لا يستطيه وضع قيمة حرفية حجمها أقل من حجم الحقل الأساسي name الموجود في السجل student .. هنا لدينا 7 حروف ( مع المحرف الصفري ) و الحقل name يحمل 15 خانة ..

    و لحل هذه المشكلة نستخدم الدالة ( التابع ) strcpy و تلفظ string copy .. و طريقة استخدامها على الشكل التالي :
    كود:
    Strcpy(s.name, "Montada");
    هنا سيتم إسناد قيمة القسم الثاني ( Second Parameter ) إلى القسم الأول ( First Parameter ) .. و بالتالي نستنتج أن القيمة Montada ستخزن في الحقل name .. و نستنتج أيضاً أنه يجب علينا دائماً أن نضع في القسم الثاني متحول ما لكي يتم تخزين القيمة فيه .

    ملاحظة / بالنسبة للحقول ذات الأنواع الأخرى ( int, float, bool .. إلخ ) .. يمكن القيام بعملية الإسناد بشكل طبيعي عليها .

    :: 7- الحقول المركبة ( سجل كحقل من حقول سجل آخر ) ::

    تفرض لو أننا نريد أن نجعل سجل داخل سجل آخر .. فهل هذا ممكن ؟ بالتأكيد نستطيع ذلك .. مثال / سنعرف سجلان جديدان هما : date و address .. و سنضعهما بداخل السجل student :
    كود:
    #include <iostream.h>
    
    struct date{
        int day,month,year;
    };
    
    struct address{
        char street[15];
        char city[15];
    };
    
    struct student{
        char name[15];
        char phone[10];
        date bd;
        address ad;
    };
    
    void readinfo(student &s)
    {
        cout << "Name : "; cin >> s.name;
        cout << "Phone : "; cin >> s.phone;
        cout << "Birthday : "; cin >> s.bd.day
                                   >> s.bd.month
                                   >> s.bd.year;
        cout << "Address : "; cin >> s.ad.street
                                  >> s.ad.city;
    }
    
    void printinfo(student &s)
    {
        cout << "Name : " << s.name << endl;
        cout << "Phone : " << s.phone << endl;
        cout << "Birthday : " << s.bd.day << " "
                              << s.bd.month << " "
                              << s.bd.year << endl;
        cout << "Address : " << s.ad.street << " "
                             << s.ad.city << endl;
    }
    
    void main()
    {
        student s;
    
        readinfo(s);
        cout << "--------------------\n";
        printinfo(s);    
    }
    فقط قمنا في هذا المثال ( كالمثال السابق ) بإدخال البيانات اللازمة عن طريق التابع readinfo و من ثم قمنا بطعاتها على الشاشة عن طريق التابع printinfo .. و المهم في هذا المثال هو كيفية تداخل السجلات بعضها ببعض .

    :: 8- المصفوفات كحقول في السجلات ::

    افرض الأن أننا نريد أن نضع مصفوفة داخل السجل student الذي كتبناه مسبقاً .. انظر المثال التالي / سنضع المصفوفة marks و التي تخزن علامات الطالب في داخل السجل student :
    كود:
    struct student{
        char name[15];
        char phone[10];
        int age;
        int marks[5];
    };
    و سيكون شكل هذا السجل ( student ) على النحو التالي :


    لذلك سنحتاج إلى حلقة for للتعامل مع المصفوفة marks .. انظر إلى كيفية عمل ذلك داخل التابع readinfo :
    كود:
    void readinfo(student &s)
    {
        cout << "Name : "; cin >> s.name;
        cout << "Phone : "; cin >> s.phone;
        cout << "Age : "; cin >> s.age;
    
        cout << "Marks : ";
    
        for(int i=0; i<5; i++)
            cin >> s.marks[i];
    }
    ملاحظة / انظر إلى الحقلين name و phone .. أيضاً هما مصفوفتين و لكنهما لا يحتاجان إلى حلقة for لأنهما من النوع char ( راجع درس المصفوفات لمعرفة السبب ) .

    :: 9- السجل كعنصر في مصفوفة ( مصفوفة سجلات ) ::

    هذه المرة سنقوم بتعريف مصفوفة تحتوي كلها على سجلات من النوع student .. و طريقة عمل ذلك سنعرفها عن طريق المثال التالي / سنقوم بتعريف المصفوفة s و التي ستحتوي على 3 سجلات بحيث يكون لكل طالب سجل خاص فيه :
    كود:
    struct student{
        char name[15];
        char phone[10];
        int age;
        int mark[5];
    };
    
    void main()
    {
        student s[3];
    }
    و سيكون شكل هذه المصفوفة ( s ) على النحو التالي :


    بهذه الطريقة نكون قد عرفنا مصفوفة تحتوي على ثلاثة سجلات .. و لكن كيف يمكننا استخدام تلك السجلات ؟ نستطيع ذلك عن طريق التالي : مثال /
    كود:
    s[2].name = "Amer";
    بهذه الطريقة قمنا بتغيير اسم الطالب الثالث .. و ذلك باستخدام القوسين ( [ ] ) بعد المتحول s و طبعاً مع تحديد الدليل المطلوب .. و بعد ذلك نستخدم الحقل الذي نريده مع ملاحظة استخدام النقطة دائماً لذلك .

    سنكمل مثالنا السابق .. حيث أننا نريد إدخال بيانات ثلاثة طلاب و ليس طالب واحد فقط .. و بعد ذلك نقوم بكتابة تلك المعلومات على الشاشة كالأمثلة السابقة :
    كود:
    #include <iostream.h>
    
    const int m=3;
    
    struct student{
        char name[15];
        char phone[10];
        int age;
        int marks[5];
    };
    
    void readinfo(student &);
    void printinfo(student &);
    
    void main()
    {
        student s[m];
        
        for(int i=0; i<m; i++)
        {
            readinfo(s[i]);
            cout << endl;
        }
    
        cout << "--------------------\n";
    
        for(i=0; i<m; i++)
        {
            printinfo(s[i]);
            cout << endl;
        }
    }
    
    void readinfo(student &s)
    {
        cout << "Name : "; cin >> s.name;
        cout << "Phone : "; cin >> s.phone;
        cout << "Age : "; cin >> s.age;
    
        cout << "Marks : ";
    
        for(int i=0; i<5; i++)
            cin >> s.marks[i];    
    }
    
    void printinfo(student &s)
    {
        cout << "Name : " << s.name << endl;
        cout << "Phone : " << s.phone << endl;
        cout << "Age : " << s.age << endl;
    
        cout << "Marks : ";
    
        for(int i=0; i<5; i++)
            cout << s.marks[i] << " ";    
        
        cout << endl;
    }
    لاحظ التابع main كيف تغير ( بقية المثال كما هو ) .. أولاً عليك معرفة أن التابعين readinfo و printinfo يتعاملون مع سجل واحد فقط .. لذلك قمنا بوضع حلقتين for الأولى وظيفتها أن تقوم في كل دورة باستدعاء التابع readinfo لإدخال بيانات السجلات .. ففي الدورة الأولى سيتم إدخال بيانات السجل الأول و بعد الانتهاء من ذلك يتم الاستدعاء مرة أخرة لأدخال بيانات السجل الثاني و ذلك في الدورة الثانية .. و هكذا إلى أن ندخل بيانات جميع السجلات الموجودة .

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

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

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

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

    :: 10- برنامج تطبيقي ::

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


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

    سنقوم بإدخال أسماء المدن على الشكل التالي :


    و بعد ذلك سنقوم بإدخال درجات الحرارة على الشكل التالي :



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



    و أيضاً يمكننا أن نطبع جميع المعلومات التي أدخلناها على الشكل التالي :


    ملاحظات حول الحل :

    1 – سنستخدم السجلات في هذا البرنامج .

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

    3 – سنضع كل أمر موجود في القائمة على شكل تابع ( دالة ) على الشكل التالي :
    - التابع read_cities : من أجل إدخال أسماء المدن .
    - التابع read_temp : من أجل إدخال درجات الحرارة الخاصة بكل مدينة .
    - التابع search : من أجل البحث عن درجة حرارة مدينة معينة .
    - التابع print : من أجل طباعة جميع المعلومات التي أدخلناها على الشاشة .

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

    5 – سنفترض أننا نريد من المستخدم إدخال ثلاثة مدن فقط مع درجات الحرارة فيها .. لذلك سنحتاج إلى مصفوفة سجلات من النوع City حجمها 3 .. و تعريف هذه المصفوفة على الشكل التالي :
    كود:
    city c[3];
    الحل :
    كود:
    #include <iostream.h>
    #include <ctype.h>
    #include <string.h>
    
    const int m=3;
    
    struct city{
        char name[15];
        int temp;
    };
    
    void read_cities(city [ ]);
    void read_temp(city [ ]);
    void search(city [ ]);
    void print(city [ ]);
    
    void main( )
    {
        city c[m];
        char choice;
    
        do{ 
            cout << "C. Name of Cities\n"
                 << "T. Enter Temeratures\n"
                 << "S. Search For a City\n"
                 << "P. Print All\n"
                 << "Q. Quit\n\n"
                 << "Enter your choice : ";
            cin >> choice;
    
            switch( toupper(choice) )
            {
                case 'C' : read_cities(c); cout << "\n------------------------------\n\n"; break;
                case 'T' : read_temp(c); cout << "\n------------------------------\n\n"; break;
                case 'S' : search(c); cout << "\n------------------------------\n\n"; break;
                case 'P' : print(c); cout << "\n------------------------------\n\n"; break;
                case 'Q' : return;
    
                default : cout << "Error.. \n\n------------------------------\n\n";
            }
        }while( true );
    }
    
    void read_cities(city c[ ])
    {
        for(int i=0; i<m; i++)
        {
            cout << "Name of City " << i << " : ";
            cin >> c[i].name;
        }
    
        for(i=0; i<m; i++)
            for(int j=0; j<strlen(c[i].name); j++)
                c[i].name[j] = toupper(c[i].name[j]);
    }
    void read_temp(city c[ ])
    {
        for(int i=0; i<m; i++)
        {
            cout << "Temperature of City " << c[i].name << " = ";
            cin >> c[i].temp;
        }
    }
    void search(city c[ ])
    {
        char word[15];
    
        cout << "Enter a Name of City : ";
        cin >> word;
    
        for(int i=0; i<strlen(word); i++)
            word[i] = toupper(word[i]);
    
        for(i=0; i<m; i++)
            if( strcmp( c[i].name, word) == 0 )
            {
                cout << "City : " << c[i].name << endl
                          << "Temperature = " << c[i].temp << endl;
                return;
            }
        cout << "There is No City Like This Name...\n";
    }
    void print(city c[ ])
    {
        for(int i=0; i<m; i++)
            cout << "City : " << c[i].name << endl
                      << "Temperature = " << c[i].temp << "\n\n";
    }
    شرح الحل :

    1 – هذا البرنامج يشبه تماماً البرنامج الذي حللناه في درس المصفوفات .. لذلك سوف أشرح الإختلافات فقط و أهمها التابعين search و read_cities .

    2 – في التابع search .. قمنا بتعريف مصفوفة حرفية و هذه المصفوفة ستخزن الكلمة التي سيدخلها المستخدم و التي سيتم البحث بها بين أسماء المدن .. و بعد ذلك طلبنا من المستخدم إدخال تلك الكلمة .. الأن علينا معرفة أننا نريد عمل مقارنة بين هذه الكلمة و بين أسماء المدن المدخلة فإذا وجدنا أن أحد تلك الأسماء يطابق تماماً تلك الكلمة نكون قد وجدنا المدينة المطلوبة .. و لكن يجب أن ننتبه لحجم الأحرف التي أدخلها المستخدم هل هي صغيرة أم كبيرة .. و بالتالي قمنا في التابع search بتكبير حجم أحرف المصفوفة word .. و أيضاً قمنا بنفس الأمر بالنسبة لأسماء المدن و ذلك في التابع read_cities و ذلك لكي يصبح التطابق ممكناً .. و لكن لاحظ أن التابع toupper يتعامل مع حرف واحد فقط لذلك استخدمنا حلقات for لتكبير حجم جميع أحرف المصفوفة المعنية .

    3 – نعود للتابع search .. الأن علينا وضع التعليمات المناسبة لاختبار التطابق .. أولاً علينا معرفة أنه من أجل إجراء عملية مقارنة بين نصين يجب استخدام الدالة ( التابع ) strcmp .. و هذه الدالة تحتاج إلى وسيطين حرفين .. و هذه الدالة ستعيد إحدى ثلاثة قيم :
    الصفر : و يعني تطابق الوسيطين .
    +1 : و يعني اختلاف الوسيطين .. و هو يعني أن الوسيط الأول هو الأكبر ( من حيث الترتيب الأبجدي للأحرف ) .
    -1 : و يعني أيضاً اختلاف الوسيطين .. و لكنه يدل على أن الوسيط الأول هو الأصغر .

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

    و لاحظ أيضاً كيف و ضعنا عملية اختبار التطابق داخل حلقة for و ذلك من أجل اختبار جميع السجلات الموجودة في المصفوفة c و المرسلة من التابع main .

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

    :: المكتبة Library3.h ::

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

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

    ستكون تلك السجلات في المكتبة ( Library3.h ) على النحو التالي :
    كود:
    struct date{
        int day,month,year;
    };
    
    struct address{
        char street[15];
        char city[15];
    };
    
    struct student{
        char name[15];
        char phone[10];
        int age;
        date birhday;
        address ad;
    };
    المكتبة موجودة في المرفقات مع هذا الدرس .

    ملاحظة / بالنسبة لاستخدام السجلات التي وضعناها في المكتبة ( Library3.h ) داخل ملف مصدري .. فبنفس الأسلوب .. فقط نقوم بضم المكتبة إلى مشروعنا عن طريق ( include# ) على الشكل التالي :
    كود:
    #include "Library3.h"
    بشرط وضع تلك المكتبة بداخل المجلد الذي يحتوي على مشروعك .

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

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

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

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

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

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

    لنبدأ بهذه المفاهيم أولاً ثم سندخل على موضوع المؤشرات

    :: إعطاء قيم ابتدائية لحقول السجلات ::

    القيم الابتدائية هنا ليس المقصود بها إعطاء قيم ابتدائية للحقول الموجودة في بنية ( جسم ) تعريف السجل .. بل المعنى هنا هو إعطاء قيم ابتدائية لحقول المتغيرات التي عرفناها على أنها سجلات .. فمن الخطأ إعطاء قيم ابتدائية لحقول السجل في بنية تعريفه .. انظر إلى هذا المثال /
    كود:
    struct date{
        int day=1;
        int month=12;
        int year;
    };
    في هذا المثال سيحدث خطأ قواعدي و هو عدم قبول إعطاء قيم ابتدائية لحقول السجل في بنية تعريفه .. و لكن يمكننا إعطاء قيم ابتداية لحقول المتغيرات التي سنعرفها على أنها من النوع date .. و ذلك على النحو التالي :
    كود:
    date x = {1, 12};
    لاحظ هنا كيف قمنا بذلك .. بنفس أسلوب إعطاء قيم ابتدائية للمصفوفات .. هنا في هذا المثال قمنا بإعطاء حقول السجل x القيم : 1 و 12 على التوالي .. بذلك سيتم إعطاء القيمة ( 1 ) للحقل الأول و هو day و القيمة ( 12 ) للحقل الثاني و هو month .. و تماماً كالمصفوفات سيتم إعطاء باقي الحقول القيمة صفر .. لأننا لم نعط تلك الحقول قيماً ابتدائية و بالتالي سيضع المترجم لها القيمة صفر بشكل تلقائي .

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

    :: مفهوم الـ Scope .. أو ما يسمى بـ ( فترة حياة المتغير ) ::

    و هو المكان الذي سنقوم به بتعريف المتغير هل هو خارج التوابع ( متغير عام ) أم داخل أحد التوابع ( متغير خاص ) أم داخل أحد الكتل الخاصة بالبنى التي درسناها ( كحلقة for أو حتى عبارة if ) ..

    و نحن نريد هنا الحالة الثالثة .. و كمثال على ذلك انظر إلى البرنامج التالي :
    كود:
    int x,y;
    
    cin >> x >> y;
    
    if( y==0 )
        cout << "Error..\n";
    else
    {
        float c;
        c = (float)x / (float)y;
        cout << c << endl;
    }
    هذا المثال هو من أجل إدخال عددين و من ثم حساب ناتج قسمة العدد الأول على الثاني .. و استخدمنا عبارة if من أجل معالجة حالة القسمة على الصفر .. فإذا كان العدد الثاني يساوي صفراً نرفض العملية أما إذا كان العدد الثاني يحتوي على أي رقم غير الصفر فنقوم بالتالي :
    نعرف متغير جديد اسمه c من أجل تخزين ناتج عملية القسمة و من ثم نقوم بعملية الإسناد من أجل تلك العملية و بعد ذلك نكتب الناتج على الشاشة ( من أجل العملية القسرية (float) راجع أمثلة الدرس الثاني ) ..

    ما أريد قوله هنا أن المتغير c الجديد قد عرفناه في داخل البنية else و بالتالي عند انتهاء عمل البنية else يتم تحرير المتغير c من الذاكرة ( يتم إلغاؤه ) .. و بالتالي لا يمكن استخدام هذا المتغير خارج تلك البنية .. بنفس أسلوب المتغيرات الخاصة بالتوابع و التي قلنا عنها أنه لا يمكننا استخدامها خارج كتلة التابع .. و بذلك نصل إلى مفهوم الـ Scope .
    و هذا المفهوم ينطبق أيضاً على جميع البنى و الكتل .. مثل ( for, if, while, switch .. إلخ ) .. و بالتالي نستطيع تلخيص هذه الفقرة على أن المتغيرات التي يتم التصريح عنها داخل أحد الكتل ( البنى ) تتصف بما يلي :
    1 – يتم إنشاؤها عند الدخول إلى تلك الكتلة .
    2 – يتم التخلص منها ( إلغاؤها من الذاكرة ) عند انتهاء عمل تلك الكتلة .
    3 – من الفقرة ( 2 ) نستنتح أنه لا يمكن استخدام هذا المتغير خارج هذه الكتلة .

    ملاحظة / كان باستطاعتنا داخل البنية ( else ) أن لا نعرف متغير جديد بحيث نقوم بكتابة ناتج القسمة على الفور و ذلك على الشكل التالي :
    كود:
    else
        cout << (float)x / (float)y << endl;
    و لكني استخدمت ذلك المتغير ( c ) من أجل شرح الفقرة التي نحن في صددها .

    :: طريقة أخرى لتشغيل تطبيقاتك باستخدام Command Prompt أو ما يسمى MS-DOS Prompt ::

    بكل بساطة تستطيع تشغيل الأمثلة و البرامج التي كتبناها سابقاً عن طريق استخدام Command Prompt .. و هو عبارة عن نافذة شبيهة تماماً بنافذة الدوس و لكنه يعمل على نظام الويندوز 32 بت ، و بالتالي هو ليس الدوس و لكنه يشببه تماماً فتستطيع به أن تستخدم جميع الأوامر التي كنت تستخدمها في الدوس مثل ( dir /p ) .

    ملاحظة 1 / تستطيع تشغيل Command Prompt عن طريق التالي :

    في ويندوز XP : اضغط على ( Start >> Programs >> Accessories >> Command Prompt ) .
    في ويندوز ME : فقط الاسم سيختلف و هو MS-DOS Prompt و هو موجود بنفس المكان السابق في ويندوز XP .

    ملاحظة 2 / البرامج التي نستطيع تشغيلها بواسطة Command Prompt يجب أن تكون امتدادها ( نوع الملف ) من النوع ( exe ) .. و نستطيع عمل هذه الملفات لبرامجنا عن طريق عملية بناء ( Build ) البرنامج .. و بها سيتم صنع ملف خاص لبرنامجك من النوع ( exe ) و سيتم وضعه داخل المجلد الخاص بمشروعك في الملف Debug .

    سنفرض أننا نريد تشغيل البرنامج العملي الموجود في درس السجلات .. لذلك كل ما علينا فعله هو معرفة مكان وجوده في الهارد ديسك .. على سبيل المثال :

    البرنامج اسمه Ex1 .. و هو موجود ضمن المجلد التالي ( E:\My Programs ) .. و بالتالي نستطيع تشغيله عن طريق كتابة اسم البرنامج فقط في داخل المجلد الموجود فيه .. انظر الصورة :


    ملاحظة 3 / تستطيع طبعاً أن تشغل تطبيقاتك ذات الامتداد ( exe ) بالضغط عليها فوراً بواسطة الفأرة .. و لكن لن يتم التوقف عند الانتهاء من التطبيق لرؤية النتائج .. و بذلك أردت توضيح كيفية تشغيل التطبيقات بواسطة Command Prompt .

    ---------- ---------- ---------- الدرس الثامن ---------- ---------- ----------
    اليوم : الثلاثاء ... التاريخ : 26 / 4 / 2005

    :: المؤشرات ( Pointers ) ::

    :: 1- مقدمة ::

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

    إن للمؤشرات العديد من الامكانيات التي تزيد قدرة المبرمج على تحكمه ببرنامجه .. فنذكر على سبيل المثال :
    1 – الحجز الديناميكي و التحرير الديناميكي ( الحجز و التحرير اليدوي للمتغيرات ) .
    2 – التعامل بشكل غير مباشر مع البنى الأخرى عن طريق عناوينها في الذاكرة .. و بالتالي سنستفيد من ميزة الاستدعاء بالمرجع ( Reference ) و التي كنا قد استخدمناها في التوابع ( الدوال ) .
    3 – المؤشر ذو النوع ( void ) و الذي يستطيع أن يحمل قيمة من أي نوع نريده .
    4 – استخدام المؤشرات بدلاً من المصفوفات من أجل تحديد حجم ديناميكي للمصفوفة أثناء زمن التنفيذ .. و هذه الميزة تخالف مفهوم المصفوفة و التي تقوم على أساس تحديد حجمها في مرحلة ما قبل الترجمة ( ترجمة البرنامج بواسطة المترجم Compilor ) .
    5 - المؤشرات على التوابع ( الدوال ) و فائدتها .
    6 – هناك مزايا أخرى فنذكر مثلاً : القوائم المرتبطة و المكدسات و الأشجار و غيرها .. و لكننا لن نتطرق إلى هذه المواضيع .

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

    و لكن قبل الدخول الفعلي بالمؤشرات يجب أن نتحدث حول فكرة مهمة .. و هي أن لكل متغير أربع معلومات يجب علينا معرفتها و هي كالتالي :
    1 – اسم المتغير .
    2 – حجمه ( و يتحدد عن طريق النوع ) .
    3 – قيمته ( نحددها نحن أو المستخدم ) .
    4 – عنوانه في الذاكرة .. و هذا ما سنتعامل معه في هذا الدرس .

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

    في الدروس السابقة تعلمنا كيف نحدد المعلومات الثلاثة الأولى .. فعلى سبيل المثال / إذا قمنا بتعريف متغير x على الشكل التالي :
    كود:
    int x=5;
    نكون قد حددنا التالي :
    1 – اسم المتغير : x .
    2 – حجمه : 4 بايت ( لأن نوعه int ) .
    3 – قيمته : العدد 5 .

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

    قاعدة / المتغيرات تحمل قيماً إما عددية أو حرفية أو منطقية .. أما المؤشرات فتحمل عناوين في الذاكرة فقط .

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

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

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

    :: 2 - تعريف ::

    المؤشر / هو متحول يحتوي على عنوان موقع في الذاكرة .. و هذا العنوان هو عنوان متحول آخر يحتوي على بيانات من نوع معين ( int, float .. إلخ ) .. و بالتالي المؤشر يشير إلى ذلك المتحول بطريقة غير مباشرة .. و بذلك فإن المؤشرات لا تحتوي إلا عناوين في الذاكرة تخص متحولات أخرى .

    :: 3 - التصريح عن المؤشرات ::

    نصرح عن المؤشرات تماماً كالمتغيرات و لكن مع إضافة رمز النجمة ( * ) قبل اسم المتغير .

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

    أمثلة على المؤشرات :
    كود:
    int* ptr;
    float* p1;
    double* p2;

    :: 4 – إعطاء قيم ابتدائية لمؤشر ::

    ينصح دائماً بإعطاء قيم ابتدائية لأي مؤشر يتم تعريفه في البرنامج .. و ذلك لكي لا نحصل على نتائج غير مرغوب بها .. و يمكننا إعطاء ثلاثة قيم ابتدائية لمؤشر و هي كالتالي :
    1 – الصفر : و هو يعني أن المؤشر لا يشير إلى أي قيمة .
    2 – القيمة NULL : و هي تعني أيضاً أن المؤشر لا يشير إلى أي قيمة .. و لكن مع ملاحظة أن NULL هي بالأحرف الكبيرة .
    3 – عنوان متحول آخر : و ذلك عن طريق إستخدام عنوان ذلك المتحول كقيمة ابتدائية للمؤشر .

    أمثلة :
    كود:
    int* ptr1 = 0;
    int* ptr2 = NULL;
    int* ptr3 = &x;

    ملاحظة / في المثال الثالث .. استخدمنا عنوان المتحول x كقيمة ابتدائية للمؤشر ptr3 .. و ذلك عن طريق الرمز ( & ) .. سنتعرف أكثر على معنى هذا الرمز و رموز أخرى في الفقرة التالية .

    :: 5 – الرموز التي سنحتاجها أثناء التعامل مع المؤشرات ::

    هناك رمزين مهمين يجب أن نعرفهما أثناء التعامل مع المؤشرات و هما كالتالي :
    1 – الرمز ( * ) : لقد استخدمنا هذا الرمز سابقاً عند التصريح عن المؤشرات .. الأن سنستخدم هذا الرمز في أماكن أخرى في البرنامج و بالتحديد في عمليات الإسناد و في قنوات الدخل و الخرج ( cin, cout ) .

    و معنى هذا الرمز هو : القيمة التي يشير إليها المؤشر ( اقرأها جيداً ) .

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

    2 – الرمز ( & ) : يستخدم هذا الرمز لتحديد عنوان المتحولات في الذاكرة .. و بالتالي يجب استخدام هذا الرمز عند تحديد قيم المؤشرات لأن المؤشرات كما قلنا تحتوي على عناوين المتحولات في الذاكرة .

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

    مثال :
    كود:
    int x = 5;
    
    int* ptr = &x;
    
    cout << "*ptr = " << *ptr << endl << "x = " << x << "\n\n"
         << "ptr = " << ptr << endl << "&x = " << &x << "\n\n"
         << "&ptr = " << &ptr << "\n\n";

    في هذا المثال .. قمنا بتعريف المتحول x و سنعطيه القيمة 5 كقيمة ابتدائية له .. و بعدها قمنا بتعريف المؤشر ptr و أعطينا عنوان المتحول x كقيمة ابتدائية له .. لاحظ كيف استخدمنا الرمز ( & ) و ذلك لكي نخزن عنوان المتحول x كقيمة في المؤشر ptr .. و بهذه الطريقة أصبح المؤشر ptr يشير إلى المتحول x .. لمزيد من التوضيح انظر الصورة التالية :


    هذه الصورة تبين كيف أن المؤشر ptr يحتوي على عنوان المتغير x .. و لا تخلط بين مفهوم قيمة مؤشر و عنوان المؤشر فكلاهما مختلفان .. حيث أن قيمة مؤشر تحتوي على عنوان متحول آخر و في مثالنا هنا فإن قيمة المؤشر ptr أصبحت تساوي عنوان المتحول x .. و بالتالي فإن المعلومات الخاصة بالمؤشر ptr هي كالتالي :

    1 – اسمه : ptr .
    2 – حجمه : 4 بايت لأن نوعه int .
    3 – قيمته : 101 و هي عنوان المتحول x .
    4 – عنوانه في الذاكرة : 201 .
    5 – القيمة التي يشير إليها : 5 و هي قيمة المتحول x .

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

    ملاحظة / العناوين 101 و 201 هي عناوين مفترضة قمت بوضعها من أجل التوضيح فقط .

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

    في السطر الثاني .. وضعنا قيمة المؤشر ptr و عنوان المتحول x و لاحظ أيضاً كيف أنهما متساويين .. و في السطر الثالث قمنا بوضع عنوان المؤشر ptr و لاحظ كيف أنه يختلف عن البيانات السابقة .. انظر الصورة لمعرفة نتائج هذا المثال :


    :: 6 – استخدام Const مع المؤشرات و مفهوم المؤشر الثابت ::

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

    الأولى / قيمة المؤشر نفسه ( و هي عنوان المتحول الآخر ) .. و هو ما يسمى بالمؤشر الثابت .
    الثانية / القيمة التي يشير إليها المؤشر ( و هي قيمة ذلك المتحول الذي يشير إليه المؤشر ) .. و هو ما يسمى بالمعطيات الثابتة .

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

    1 – تثبيت قيمة المؤشر لكي لا يشير إلى متحول آخر جديد ( مفهوم المؤشر الثابت ) :

    الأن كيف يمكننا تثبيت القيمة الأولى ( قيمة المؤشر ) ؟ نستطيع ذلك عن طريق وضع const بعد رمز النجمة ( * ) و قبل اسم المتغير و ذلك في سطر تعريف ذلك المؤشر .. مثال :
    كود:
    int x=5;
    int* const ptr=&x;
    ملاحظة / يجب وضع قيمة ابتدائية للمؤشر الثابت .
    بهذه الطريقة نكون قد ثبتنا قيمة المؤشر ptr بأن جعلنا هذا المؤشر يشير فقط إلى المتحول x .. و بالتالي لن نستطيع بعد ذلك أن نشير بهذه المؤشر إلى متحول آخر .. فعلى سبيل المثال : لو قمنا بتعريف متغير جديد اسمه ( y ) و حاولنا أن نضع عنوان هذا المتحول كقيمة للمؤشر ptr فسيحدث خطأ :
    كود:
    int y=6;
    ptr=&y;
    2 – تثبيت القيمة التي يشير إليها المؤشر ( مفهوم المعطيات الثابتة ) :

    نستطيع ذلك عن طريق وضع const قبل سطر تعريف المؤشر .. مثال :
    كود:
    int x=5;
    const int* ptr=&x;
    عن طريق هذا المثال لن نستطيع تغيير القيمة ( 5 ) إلى أي قيمة أخرى .. لأننا قمنا بجعل تلك القيمة ثابتة بالنسبة للمؤشر ptr .. الأن سنحاول تغيير تلك القيمة و لكن سيظهر لنا خطأ .. على سبيل المثال : لو كتبنا التالي :
    كود:
    *ptr=6;
    هنا سيحدث خطأ لأننا حاولنا تغيير القيمة التي يشير إليها ptr .. و لكنا لو كتبنا التالي فلن يحدث خطأ :
    كود:
    x=6;
    هنا لن يحدث خطأ !!! لأن x ليس ثابت بل القيمة التي يشير إليها ptr هي الثابتة .. و لو حاولنا طباعة ( ptr* ) فسيكون الناتج يساوي 6 :
    كود:
    cout << *ptr << endl;

    خلاصة / نستنتج من هذه الفقرة أن هناك أربع حالات بالنسبة لاستخدام const مع المؤشرات :

    1 – مؤشر غير ثابت على معطيات غير ثابتة :
    كود:
    int* ptr;
    2 – مؤشر ثابت على معطيات غير ثابتة :
    كود:
    int* const ptr=&x;
    3 – مؤشر غير ثابت على معطيات ثابتة :
    كود:
    const int* ptr;
    4 – مؤشر ثابت على معطيات ثابتة :
    كود:
    const int* const ptr=&x;
    التعديل الأخير تم بواسطة Wolf Sniper ; 27-04-2005 الساعة 02:18 PM
    لا إله إلا الله
    مشروع لتعليم أساسيات لغة الـ ++C :
    -
    موضوع لتعليم الفيجوال بيسك 6 :

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

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

    :: 7 – كيفية التعامل مع المؤشرات ::

    الطريقة الأولى / استخدام عنوان متحول آخر للإشارة إلى محتوياته :

    أخذنا في بداية هذا الدرس كيفية عمل ذلك عن طريق استخدام الرمز ( & ) .. و ذلك كالتالي :
    كود:
    int x=10;
    int* ptr;
    ptr=&x;
    ملاحظة / يجب أن يكون نوع المؤشر مطابق لنوع المتحول الذي سيشير إليه ذلك المؤشر .

    الأن لو أردنا طباعة القيمة ( 10 ) نستطيع ذلك بطريقتين .. إما عن طريق استخدام المتحول x أو عن طريق المؤشر ptr :
    كود:
    cout << x << endl;
    cout << *ptr << endl;
    وقلنا أن الرمز ( * ) يعني : القيمة التي يشير إليه المؤشر .. و بهذه الطريقة نستطيع استخدام المؤشر ptr بدلاً من المتحول x .. مع ملاحظة أنه إذا تغيرت ( *ptr ) فستتغير قيمة المتحول ( x ) و العكس صحيح .. فلو كتبنا التالي :
    كود:
    (*ptr)++; 
    cout << *ptr << endl << x << endl;
    هنا سيتم زيادة القيمة 10 إلى 11 .. و بالتالي تغيرت قيمة x إلى 11 مع أننا استخدمنا المؤشر ptr لذلك .. لاحظ نتائج الطباعة حيث أنه سيتم طباعة العدد 11 مرتين .

    ملاحظة 1 / العملية ( ++ ) لها أولية على الرمز ( * ) لذلك استخدمنا الأقواس لكي نزيد القيمة التي يشير إليها المؤشر واحد و ليس قيمة المؤشر .. فبدون استخدام الأقواس سيتم زيادة قيمة ptr واحد و بذلك فلن يشير هذا المؤشر إلى المتحول x لأنه أصبح لديه قيمة جديدة و هي عنوان جديد .. و بذلك خطورة كبيرة لأننا لا نعرف على ماذا يحتوي ذلك العنوان الجديد في الذاكرة .. و بالتالي سيحدث خطأ منطقي و ليس قواعدي .

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

    الطريقة الثانية / استخدام المؤشر عن طريق الحجز الديناميكي و التحرير الديناميكي ( اليدوي ) و ذلك خلال زمن تنفيذ البرنامج :

    أولاً : الحجز الديناميكي :

    و يتم ذلك على خطوتين و هما كالتالي :
    1 – حجز موقع جديد يتسع لقيمة معينة .. و هذا يعني باختصار : التصريح عن مؤشر جديد يتسع لتخزين القيمة التي نريدها .. و ذلك تماماً كالسابق .. مثال :
    كود:
    int* ptr;
    2 – البحث عن موقع جديد فارغ في الذاكرة و من ثم وضع عنوان ذلك الموقع كقيمة في المؤشر الذي قمنا بتعريفه في الخطوة الأولى .. و ذلك عن طريق التالي :
    كود:
    Pointer = new Types;
    حيث أن :
    Pointer : هو المؤشر الذي قمنا بتعريفه في الخطوة الأولى .
    new : هي كلمة محجوزة ( ستظهر باللون الأزرق ) .. وظيفتها هي إيجاد موقع جديد فارغ في الذاكرة حجمه يطابق حجم النوع الذي سيتم وضعه بعدها ( Types ) .
    Types : أي نوع نريده سواءً أكان أحد الأنواع مسبقة التعريف مثل ( int,float,double .. إلخ ) أو أحد الأنواع المعرفة من قبل المستخدم كالسجلات و الكائنات .

    و في مثالنا هنا نستطيع كتابة التالي :
    كود:
    ptr = new int;
    ملاحظة / يمكن اختصار الخطوتين السابقتين على الشكل التالي :
    كود:
    int ptr = new int;
    و لمزيد من التوضيح .. نفرض أن المؤشر ( ptr ) قد أخذ العنوان ( 320 ) و ذلك في الخطوة الأولى التي تم فيها تعريف ذلك المؤشر .. و لاحظ أيضاً كيف أنه في هذه الخطوة لم يأخذ قيمة بعد ( و هي عنوان موقع آخر في الذاكرة ) و هذا يعني أن المؤشر لا يشير إلى قيمة .. انظر الصورة :


    و في الخطوة الثانية سيتم البحث عن موقع فارغ في الذاكرة ذو حجم معين ( 4 بايت قي مثالنا السابق من أجل النوع int ) .. و سيتم إعطاء عنوان ذلك الموقع للمؤشر المعرف قي الخطوة الأولى و سنفرض أن عنوان ذلك الموقع هو ( 120 ) .. انظر الصورة :


    بهذه الطريقة أصبح المؤشر ( ptr ) يؤشر على موقع جديد عنوانه ( 120 ) .. و لكن لاحظ أن ذلك الموقع لا يحتوي على قيمة !!! إذا كيف نستطيع تخزين قيمة داخل ذلك الموقع ؟ ببساطة .. نستطيع ذلك عن طريق المؤشر ( prr ) و ذلك طبعاً باستخدام الرمز ( * ) .. مثال :
    كود:
    *ptr = 5;
    تماماً كما كنا نفعل في السابق .. و لكن افرض أننا نريد إعطاء قيمة ابتدائية لذلك الموقع .. نستطيع فعل ذلك في الخطوة الثانية عن طريق وضع قوسين بعد النوع و وضع القيمة الابتدائية للموقع الجديد بين هذين القوسين .. مثال : في الخطوة الثانية /
    كود:
    ptr = new int(5);
    هنا أصبح الموقع الجديد الذي يشير إليه المؤشر ( ptr ) يحتوي على القيمة ( 5 ) .. و انتبه إلى أن هذه القيمة هي ليست قيمة المؤشر ( ptr ) !!! بل كما قلنا هي قيمة الموقع الذي يشير إليه ذلك المؤشر .. انظر الصورة ( و ذلك مع افتراض نفس القيم السابقة للعناوين ) :


    للتأكد سوف نقوم بطباعة قيمة المؤشر ( ptr ) و أيضاً القيمة التي يشير إليها المؤشر ( ptr* ) و شاهد كيف أن تلك القيم تختلف و كيف أن ( ptr* ) تساوي ( 5 ) .. و ذلك عن طريق كتابة التالي :
    كود:
    cout << ptr << endl << *ptr << endl;


    ثانياً / التحرير ( الحذف أو الإلغاء ) الديناميكي :

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

    قاعدة / نستطيع القيام بعملية التحرير الديناميكي عن طريق كتابة الأمر ( delete ) و من ثم نضع اسم المؤشر و من ثم نضع الفاصلة المنقوطة :
    كود:
    delete Pointer;
    حيث أن :
    delete : كلمة محجوزة ستظهر باللون الأزرق .. و تستخدم من أجل عملية التحرير الديناميكي .
    Pointer : اسم المؤشر الذي تريد أن تقوم بتحريره من الذاكرة .

    مثال :
    كود:
    delete ptr;

    ملاحظة / يجب عليك استخدام عملية حجز جديدة بعد أي عملية تحرير و ذلك إن أردت استخدام المؤشر مرة أخرى .. إذا نستنتج من ذلك أننا نستطيع أن نقوم بعملية الحجز و التحرير الديناميكي أكثر من مرة و ذلك لمؤشر واحد فقط .. ( تم التصحيح هنا ) .. مثال :
    كود:
    int* p;
    
    p = new int(10);
    cout << *p << endl;
    delete p;
    
    p = new int(20);
    cout << *p << endl;
    delete p;
    هنا سيتم إنشاء موقع جديد يشير إليه المؤشر ( p ) و يحمل القيمة ( 10 ) .. و بعدها سنقوم بإلغاء ذلك الموقع مع ملاحظة أن العملية ( delete p ) لا تقوم بإلغاء المؤشر نفسه كما قلنا بل تلغي الموقع الذي يشير إليه ذلك المؤشر .. و بعدها قمنا بحجز موقع جديد آخر يختلف عنوانه عن العنوان السابق و وضعنا فيه القيمة ( 20 ) ..

    الأن لاحظ كيف أنه بعملية التحرير سيلغى الموقع المحجوز و لن توجد القيمة ( 100 ) في ذلك الموقع .. انظر المثال التالي /
    كود:
    int* p;
    
    p = new int(100);
    delete p;
    
    cout << *p << endl << p << endl;
    و يمكننا تلخيص طريقة عمل الحجز الديناميكي و التحرير الديناميكي عن طريق مثالنا السابق على الشكل التالي :
    كود:
    int* ptr;
    ptr = new int(5);
    
    cout << *ptr << endl;
    (*ptr)--;
    cout << *ptr << endl;
    *ptr = 3;
    cout << *ptr << endl;
    
    cin >> *ptr;
    cout << *ptr << endl;
    
    delete ptr;

    ملاحظة / لاحظ كيف أن هذه الطريقة ( الحجز الديناميكي و التحرير الديناميكي ) لا تحتاج إلى وجود متحول آخر لكي يشير إليه المؤشر كما في الطريقة الأولى .. و ذلك بسبب استخدام الأمر ( new ) الذي يبحث عن موقع فارغ جديد لا يشغله أي متغير آخر .
    التعديل الأخير تم بواسطة Son Of UAE ; 29-04-2005 الساعة 08:46 PM
    لا إله إلا الله
    مشروع لتعليم أساسيات لغة الـ ++C :
    -
    موضوع لتعليم الفيجوال بيسك 6 :

  9. #39
    التسجيل
    22-11-2004
    الدولة
    oman
    المشاركات
    9

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

    السلام عليكم ورحمة الله وبركاته
    احببت موضوعك اخي العزيز.....وسوف اقوم جاهداً لكسب شيئا ولو قليل ...انا طالب في كليات ولجات للعلوم التطبيقية
    الكلية معتمدة من معهد بيرلا بالهند
    انا طالب سنة اولى... العائق الوحيد بالنسبة لي هي اللغة الأنجليزية ,كوني لم استطيع فهم الاستاذ بسبب اللغة و لغات البرمجة مثل:-
    (السي / السي ++)
    حاولت لكن من دون فائدة
    عندي خلفيه في السي لكن سي++ صفر بالمائة يعني كأني طاولة في الصف وكل هذا الكلام غير صحيح لاني حاولت من دون جدوى و
    يشرفني ان اكون احد من طلابك الفاشل الطموح في نفس الوقت .
    al_yahmedi@hotmail.com

  10. #40
    التسجيل
    03-07-2005
    المشاركات
    18

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

    مشكووور على هذا المجهود الرائع......

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

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

    اقتباس المشاركة الأصلية كتبت بواسطة wabdulaziz
    مشكووور على هذا المجهود الرائع......

    ::

    ::

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

    ::

    ::
    ::

    ::



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

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

    من مواضيعي

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

    ::

    ::

  12. #42

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

    مجرد تنبيه !! اكثر الصور لا تعمل

  13. #43
    التسجيل
    05-07-2005
    المشاركات
    10

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

    الصور لاتعمل ارجو ان يتم رفعها من جديد

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

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

    يا إخوان أرجو عدم الرد في هذا الموضوع و ذلك لأننا خصصنا موضوع آخر للردود !!! و ذلك لترتيب الدروس بشكل متتالي و جيد .. ادخلوا هنا و اكتبوا أي سؤال أو أي استفسار تريدون موضوع الردود
    سيتم إبلاغ المشرف لحذف الردود .. و بالنسبة للصور سأحاول إيجاد حل للمشكلة حيث أن الموقع قد أقفل حسابي الذي كنت أرفع الصور عليه .
    لا إله إلا الله
    مشروع لتعليم أساسيات لغة الـ ++C :
    -
    موضوع لتعليم الفيجوال بيسك 6 :

  15. #45
    التسجيل
    08-02-2007
    المشاركات
    1

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

    انا عضوه جديده وبحاجه الى تعلم هذه اللغه لاني طالبه في جامعه علوم الحاسبات واعاني منها جدا واتمنى الرد على سؤالي
    اكتب برنامج لحساب عدد مراتب رقم مدخل
    مع جزيل الشكر

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

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

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