قراءة موجّهة · الملاحظات

الأسبوع 1: لغة C

اقرأ للفهم، ثم دوّن المصطلحات الإنجليزية التي تحتاج إلى تذكّرها أثناء التطبيق.

جلسة دراسة مقترحة: حتى 55 دقيقةWeek 1
محاور هذه الخطوة
  • C
  • المترجم
  • الأنواع
  • الدوال

ترجمة آلية تحت المراجعة

الأسبوع 1: لغة C

المصدر الأصلي على CS50

المحاضرة الأولى (Lecture 1)

مرحبًا! (Welcome!)

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

الشيفرة المصدرية (Source Code)

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

    flowchart LR
        in["الشيفرة المصدرية"] --> BOX[" compiler "]
        BOX --> out["شيفرة الآلة"]
    
  • اليوم، بالإضافة إلى تعلم كيفية البرمجة، ستتعلم كيفية كتابة تعليمات برمجية جيدة.

Visual Studio Code لـ CS50 (Visual Studio Code for CS50)

  • محرر النصوص المستخدم في هذه الدورة هو Visual Studio Code، المعروف أيضًا باسم VS Code، يشار إليه بمودة باسم cs50.dev، والذي يمكن الوصول إليه عبر نفس عنوان URL.
  • أحد أهم الأسباب التي تجعلنا نستخدم VS Code هو أنه يحتوي على جميع البرامج المطلوبة للدورة التدريبية المحملة مسبقًا عليه. تم تصميم هذه الدورة والتعليمات الواردة هنا مع وضع VS Code في الاعتبار.
  • يعد تثبيت البرنامج الضروري للدورة يدويًا على جهاز الكمبيوتر الخاص بك بمثابة صداع مرهق. من الأفضل دائمًا استخدام VS Code للمهام في هذه الدورة.
  • يمكنك فتح VS Code على cs50.dev.
  • يمكن تقسيم IDE إلى عدد من المناطق:

    IDE لاحظ أن هناك مستكشف الملفات على الجانب الأيسر حيث يمكنك العثور على ملفاتك. علاوة على ذلك، لاحظ أن هناك منطقة في المنتصف تسمى أ محرر النصوص حيث يمكنك تعديل برنامجك. وأخيراً هناك أ command line interface، والمعروف باسم أ سطر الأوامر, سطر الأوامر، أو النافذة الطرفية، حيث يمكننا إرسال الأوامر إلى الكمبيوتر في السحابة.

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

مرحبًا بالعالم (Hello World)

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

    code hello.c
    
    make hello
    
    ./hello
    
    

    الأمر الأول، يقوم code hello.c بإنشاء ملف ويسمح لنا بكتابة تعليمات لهذا البرنامج. الأمر الثاني، make hello, يجمع الملف من تعليماتنا في لغة C ويقوم بإنشاء ملف قابل للتنفيذ يسمى hello. الأمر الأخير، ./hello، يقوم بتشغيل البرنامج المسمى hello.

  • يمكننا إنشاء برنامجك الأول بلغة C عن طريق الكتابة code hello.c في النافذة الطرفية. لاحظ أننا قمنا عمدا بتصغير اسم الملف بالكامل وقمنا بتضمين ملف امتداد .c . ثم، في محرر النصوص الذي يظهر، اكتب الشيفرة كما يلي:

    // A program that says hello to the world
    
    #include <stdio.h>
    
    int main(void)
    {
        printf("hello, world\n");
    }
    

    لاحظ أن كل حرف أعلاه يخدم غرضًا ما. إذا قمت بكتابته بشكل غير صحيح، فلن يعمل البرنامج. printf هي دالة يمكنها إخراج سطر من النص. لاحظ موضع علامات الاقتباس والفاصلة المنقوطة. علاوة على ذلك، لاحظ أن \n يقوم بإنشاء سطر جديد بعد الكلمات hello, world.

  • بالنقر مرة أخرى في النافذة الطرفية، يمكنك تجميع الشيفرة الخاصة بك عن طريق التنفيذ make hello. لاحظ أننا نغفل .c. make هي أداة بناء ستقوم بتجميع ملفاتنا hello.c وتحويله إلى برنامج يسمى hello. إذا لم ينتج عن تنفيذ هذا الأمر أي أخطاء، فيمكنك المتابعة. إذا لم يكن الأمر كذلك، فتحقق جيدًا من الرمز الخاص بك للتأكد من مطابقته لما ورد أعلاه.
  • الآن، اكتب ./hello وسيقوم برنامجك بتنفيذ القول hello, world.
  • الآن، افتح مستكشف الملفات الموجود على اليسار. ستلاحظ أن هناك الآن ملفًا يسمى hello.c وملف آخر يسمى يحتوي hello. hello.c على الشيفرة المصدرية الخاص بك والذي يمكن قراءته بواسطة البشر والمترجم. hello هو ملف قابل للتنفيذ يحتوي على رمز الجهاز الذي يمكن للكمبيوتر تشغيله مباشرة.

من الصفر إلى لغة C (From Scratch to C)

  • في سكراتش، استخدمنا say حظر لعرض أي نص على الشاشة. في الواقع، في لغة C، لدينا وظيفة تسمى printf يفعل هذا بالضبط.
  • لاحظ أن الشيفرة الخاص بنا يستدعي هذه الوظيفة بالفعل:

    printf("hello, world\n");
    

    لاحظ أنه يتم استدعاء الدالة printf. الوسيطة التي تم تمريرها إلى printf هي hello, world\n محاط بعلامات اقتباس مزدوجة. يتم إغلاق بيان الشيفرة بـ a ;.

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

    // \n is missing
    
    #include <stdio.h>
    
    int main(void)
    {
        printf("hello, world");
    }
    

    لاحظ \n اختفى الآن.

  • في نافذتك الطرفية، قم بتشغيل make hello. لأنك قمت بتغيير البرنامج الخاص بك، يجب عليك إعادة ترجمة البرنامج الخاص بك.
  • الكتابة ./hello في نافذة المحطة، كيف تغير برنامجك؟ هذا \ الحرف يسمى حرف الهروب يخبر المترجم بذلك \n هي تعليمات خاصة لإنشاء فاصل أسطر.
  • هناك أحرف هروب أخرى يمكنك استخدامها:

    \n  create a new line
    \r  return to the start of a line
    \"  print a double quote
    \'  print a single quote
    \\  print a backslash
    
  • قم باستعادة البرنامج الخاص بك إلى ما يلي:

    // A program that says hello to the world
    
    #include <stdio.h>
    
    int main(void)
    {
        printf("hello, world\n");
    }
    

    لاحظ الفاصلة المنقوطة و تمت استعادة \n .

ملفات الرأس وصفحات الدليل CS50 (Header Files and CS50 Manual Pages)

  • العبارة في بداية الشيفرة #include <stdio.h> هو أمر خاص جدًا يخبر المترجم أنك تريد استخدام إمكانيات مكتبة stdio.h، أ ملف الرأس. يتيح لك هذا، من بين العديد من الأشياء الأخرى، الاستفادة من printf . لاحظ أنه لم يتم استدعاؤه استوديو: إنه كذلك مكتبة stdio.h.
  • A عبارة عن مجموعة من الشيفرة التي أنشأها شخص ما. المكتبات عبارة عن مجموعات من الشيفرة والوظائف المكتوبة مسبقًا والتي كتبها الآخرون في الماضي والتي يمكننا استخدامها في الشيفرة الخاصة بنا.
  • يمكنك القراءة عن جميع إمكانيات هذه المكتبة على صفحات الدليل. توفر الصفحات اليدوية وسيلة يمكن من خلالها فهم ما تفعله الأوامر المختلفة وكيفية عملها بشكل أفضل.
  • اتضح أن CS50 لديه مكتبة خاصة به تسمى cs50.h. هناك العديد من الوظائف التي تم تضمينها والتي توفر عجلات التدريب أثناء البدء في لغة C:

    get_char
    get_double
    get_float
    get_int
    get_long
    get_string
    
  • تم تثبيت هذه المكتبات مسبقًا لك على cs50.dev. إذا كنت تحاول استخدام هذه المكتبات على جهاز الكمبيوتر الخاص بك، فمن المحتمل أن تضطر إلى تثبيتها. لهذا السبب يجب عليك استخدام cs50.dev في هذه الدورة، حيث أنها تحتوي على كافة البرامج الضرورية المثبتة لك.
  • دعنا نستخدم هذه المكتبة في برنامجك.

مرحبًا، أنت (Hello, You)

  • تذكر أنه في برنامج Scratch كان لدينا القدرة على سؤال المستخدم، "ما اسمك؟" وقل "مرحبًا" مع هذا الاسم الملحق به.
  • في لغة C، يمكننا أن نفعل الشيء نفسه. قم بتعديل الشيفرة الخاص بك كما يلي:

    // get_string and printf with incorrect placeholder
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        string answer = get_string("What's your name? ");
        printf("hello, answer\n");
    }
    

    ال يتم استخدام الدالة get_string للحصول على سلسلة من المستخدم. ثم المتغير يتم تمرير answer إلى وظيفة printf .

  • قيد التشغيل make hello مرة أخرى في نافذة الوحدة الطرفية، لاحظ ظهور العديد من الأخطاء.
  • بالنظر إلى الأخطاء، لم يتم التعرف على string و get_string بواسطة المترجم. نحن بحاجة إلى تزويد المترجم بهذه التعريفات عن طريق إضافة مكتبة تسمى cs50.h. ونلاحظ ذلك أيضاً answer لم يتم توفيره كما أردنا. قم بتعديل الشيفرة الخاص بك كما يلي:

    // get_string and printf with %s
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        string answer = get_string("What's your name? ");
        printf("hello, %s\n", answer);
    }
    

    ال يتم استخدام الدالة get_string للحصول على سلسلة من المستخدم. ثم المتغير تم تمرير answer إلى وظيفة printf . %s يخبر وظيفة printf لتحضير نفسها لتلقي أ string.

  • الآن، قيد التشغيل make hello مرة أخرى في نافذة الوحدة الطرفية، يمكنك تشغيل البرنامج الخاص بك عن طريق الكتابة ./hello. يطلب البرنامج الآن اسمك ثم يقول مرحبًا مع إرفاق اسمك، كما هو مقصود.
  • answer هو مكان احتجاز خاص نسميه أ متغير تم الاتصال بـ. answer من النوع string ويمكنه الاحتفاظ بأي سلسلة بداخله. هناك الكثير أنواع البيانات، مثل int, bool, char، وغيرها الكثير.
  • %s هو عنصر نائب يسمى a كود التنسيق الذي يخبر وظيفة printf للتحضير لتلقي أ string. answer هو يتم تمرير string إلى %s.

لينكس (Linux)

  • لقد تم استخدام واجهة سطر الأوامر (CLI) من أجل make وقم بتشغيل برنامجنا.
  • غالبًا ما تكون واجهة سطر الأوامر (CLI) أكثر فائدة من واجهة المستخدم الرسومية (GUI) لتنفيذ الأوامر والعمل مع ملفاتنا.
  • في نافذة الوحدة الطرفية، واجهة سطر الأوامر (CLI)، تتضمن بعض الأوامر الشائعة التي قد نستخدمها ما يلي:
    • cd، لتغيير دليلنا الحالي (المجلد)
    • cpلنسخ الملفات والأدلة
    • ls، لإدراج الملفات في الدليل
    • mkdir، لإنشاء دليل
    • mvلنقل (إعادة تسمية) الملفات والأدلة
    • rmلإزالة (حذف) الملفات
    • rmdirلإزالة (حذف) الدلائل
  • الأكثر استخدامًا هو ls والذي سيسرد جميع الملفات الموجودة في الدليل الحالي. المضي قدما واكتب ls في النافذة الطرفية واضغط enter. سترى جميع الملفات الموجودة في المجلد الحالي.

الشروط (Conditionals)

  • من العناصر الأساسية الأخرى التي استخدمتها في برنامج Scratch هي الشرطية. على سبيل المثال، قد ترغب في القيام بشيء واحد إذا كانت x أكبر من y. علاوة على ذلك، قد ترغب في القيام بشيء آخر إذا لم يتم استيفاء هذا الشرط.
  • نحن نلقي نظرة على بعض الأمثلة من سكراتش.
  • في لغة C، يمكنك مقارنة قيمتين كما يلي:

    // Conditionals that are mutually exclusive
    
    if (x < y)
    {
        printf("x is less than y\n");
    }
    else
    {
        printf("x is not less than y\n");
    }
    

    لاحظ كيف إذا x < y، تحدث نتيجة واحدة. إذا x لا يقل عن y، ثم تحدث نتيجة أخرى.

  • وبالمثل، يمكننا التخطيط لثلاث نتائج محتملة:

    // Conditional that isn't necessary
    
    if (x < y)
    {
        printf("x is less than y\n");
    }
    else if (x > y)
    {
        printf("x is greater than y\n");
    }
    else if (x == y)
    {
        printf("x is equal to y\n");
    }
    

    لاحظ أنه ليست كل هذه الأسطر من الشيفرة مطلوبة. كيف يمكننا التخلص من الحساب غير الضروري أعلاه؟

  • ربما خمنت أنه يمكننا تحسين هذا الشيفرة على النحو التالي:

    // Compare integers
    
    if (x < y)
    {
        printf("x is less than y\n");
    }
    else if (x > y)
    {
        printf("x is greater than y\n");
    }
    else
    {
        printf("x is equal to y\n");
    }
    

    لاحظ كيف تم استبدال العبارة النهائية بـ else.

الأنواع (Types)

  • هناك العديد من أنواع البيانات المتوفرة في لغة C:

    bool
    char
    float
    int
    long
    string
    ...
    

رموز التنسيق (Format Codes)

  • في وقت سابق، ربما تتذكر أننا استخدمنا عنصرًا نائبًا %s لسلسلة في printf. يُسمى هذا العنصر النائب أ كود التنسيق.
  • printf يسمح بالعديد من رموز التنسيق. فيما يلي قائمة غير شاملة بتلك التي يمكنك استخدامها في هذه الدورة:

    %c
    %f
    %i
    %li
    %s
    

    %c يستخدم لـ char (الحرف) المتغيرات. %f يستخدم لـ float متغيرات (الفاصلة العائمة). %i يستخدم لـ int أو متغيرات صحيحة. %li يستخدم لـ long متغيرات عددية. %s يستخدم لـ string المتغيرات. يمكنك معرفة المزيد عن هذا على صفحات الدليل.

  • سنستخدم العديد من أنواع البيانات المتوفرة في لغة C طوال هذه الدورة.

المتغيرات (Variables)

  • في لغة C، يمكنك تعيين قيمة لـ int أو عدد صحيح كما يلي:

    int counter = 0;
    

    لاحظ كيف يتم استدعاء المتغير counter من النوع int تم تعيين القيمة 0.

  • يمكن أيضًا برمجة لغة C لإضافة واحد إليها counter على النحو التالي: تتم إضافة

    counter = counter + 1;
    

    لاحظ كيف 1 إلى قيمة counter.

  • يمكن أيضًا تمثيل ذلك على النحو التالي:

    counter += 1;
    
  • يمكن تبسيط ذلك بشكل أكبر من أجل:

    counter++;
    

    لاحظ كيف يتم استخدام ++ لإضافة 1.

  • يمكنك أيضًا طرح واحد من counter على النحو التالي:

    counter--;
    

    لاحظ كيف، في بناء الجملة هذا، تمت إزالة 1 من قيمة counter.

compare.c

  • باستخدام هذه المعرفة الجديدة حول كيفية تعيين قيم للمتغيرات، يمكنك برمجة العبارة الشرطية الأولى.
  • في النافذة الطرفية، اكتب code compare.c واكتب الشيفرة كما يلي:

    // Conditional, Boolean expression, relational operator
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        // Prompt user for integers
        int x = get_int("What's x? ");
        int y = get_int("What's y? ");
    
        // Compare integers
        if (x < y)
        {
            printf("x is less than y\n");
        }
    }
    

    لاحظ أننا قمنا بإنشاء متغيرين، و int أو تم استدعاء عدد صحيح x واتصل آخر y. يتم ملء قيم هذه باستخدام وظيفة get_int .

  • يمكنك تشغيل الشيفرة الخاصة بك عن طريق التنفيذ make compare في النافذة الطرفية، متبوعًا بـ ./compare. إذا تلقيت أي رسائل خطأ، فتحقق من الرمز الخاص بك بحثًا عن الأخطاء.
  • يمكننا تحسين برنامجك عن طريق البرمجة كما يلي:

    // Conditionals
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        // Prompt user for integers
        int x = get_int("What's x? ");
        int y = get_int("What's y? ");
    
        // Compare integers
        if (x < y)
        {
            printf("x is less than y\n");
        }
        else if (x > y)
        {
            printf("x is greater than y\n");
        }
        else
        {
            printf("x is equal to y\n");
        }
    }
    

    لاحظ أنه تم الآن احتساب كافة النتائج المحتملة.

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

agree.c

  • بالنظر إلى نوع بيانات آخر يسمى أ char، يمكننا بدء برنامج جديد عن طريق الكتابة code agree.c في النافذة الطرفية.
  • حيث أ string عبارة عن سلسلة من الأحرف، أ char هو حرف واحد.
  • في محرر النصوص، اكتب الشيفرة كما يلي:

    // Comparing against lowercase char
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        // Prompt user to agree
        char c = get_char("Do you agree? ");
    
        // Check whether agreed
        if (c == 'y')
        {
            printf("Agreed.\n");
        }
        else if (c == 'n')
        {
            printf("Not agreed.\n");
        }
    }
    

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

  • يمكنك اختبار الشيفرة الخاص بك عن طريق الكتابة make agree في النافذة الطرفية، متبوعًا بـ ./agree.
  • يمكننا أيضًا السماح بإدخال الأحرف الكبيرة والصغيرة:

    // Comparing against lowercase and uppercase char
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        // Prompt user to agree
        char c = get_char("Do you agree? ");
    
        // Check whether agreed
        if (c == 'y')
        {
            printf("Agreed.\n");
        }
        else if (c == 'Y')
        {
            printf("Agreed.\n");
        }
        else
        {
            printf("Not agreed.\n");
        }
    }
    

    لاحظ أنه يتم تقديم خيارات إضافية. ومع ذلك، هذه ليست تعليمات برمجية فعالة.

  • يمكننا تحسين هذا الشيفرة كما يلي:

    // Logical operators
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        // Prompt user to agree
        char c = get_char("Do you agree? ");
    
        // Check whether agreed
        if (c == 'Y' || c == 'y')
        {
            printf("Agreed.\n");
        }
        else
        {
            printf("Not agreed.\n");
        }
    }
    

    لاحظ ذلك تم الآن إنشاء || يعني بشكل فعال أو.

الحلقات وmeow.c (Loops and meow.c)

  • يمكننا أيضًا استخدام كتلة بناء الحلقة من Scratch في برامج C الخاصة بنا.
  • في نافذتك الطرفية، اكتب code meow.c واكتب الشيفرة كما يلي:

    // Opportunity for better design
    
    #include <stdio.h>
    
    int main(void)
    {
        printf("meow\n");
        printf("meow\n");
        printf("meow\n");
    }
    

    لاحظ أن هذا يتم على النحو المنشود ولكن لديه فرصة لتصميم أفضل. يتكرر الشيفرة مرارا وتكرارا.

  • يمكننا تحسين برنامجنا عن طريق تعديل الشيفرة الخاصة بك على النحو التالي:

    // Better design
    
    #include <stdio.h>
    
    int main(void)
    {
        int i = 3;
        while (i > 0)
        {
            printf("meow\n");
            i--;
        }
    }
    

    لاحظ أننا قمنا بإنشاء int i وقم بتعيين القيمة له 3. ومن ثم نقوم بإنشاء أ while حلقة سيتم تشغيلها طالما i > 0. وبعد ذلك، يتم تشغيل الحلقة. في كل مرة يتم طرح 1 من i باستخدام i-- .

  • وبالمثل، يمكننا تنفيذ عد الأنواع عن طريق تعديل الشيفرة الخاص بنا على النحو التالي:

    // Print values of i
    
    #include <stdio.h>
    
    int main(void)
    {
        int i = 1;
        while (i <= 3)
        {
            printf("meow\n");
            i++;
        }
    }
    

    لاحظ كيف عدادنا i يبدأ في 1. في كل مرة يتم تشغيل الحلقة، سيتم زيادة العداد بمقدار 1. بمجرد أن يصبح العداد أكبر من 3، سوف يقوم بإيقاف الحلقة.

  • بشكل عام، في علوم الحاسوب، نقوم بالعد من الصفر. من الأفضل مراجعة الشيفرة الخاص بك على النحو التالي:

    // Better design
    
    #include <stdio.h>
    
    int main(void)
    {
        int i = 0;
        while (i < 3)
        {
            printf("meow\n");
            i++;
        }
    }
    

    لاحظ أننا الآن نعد من الصفر.

  • هناك أداة أخرى في صندوق الأدوات الخاص بنا للتكرار وهي for حلقة.
  • يمكنك تحسين تصميم موقعنا بشكل أكبر meow.c برنامج باستخدام for حلقة. قم بتعديل الشيفرة الخاص بك كما يلي: تتضمن حلقة

    // Better design
    
    #include <stdio.h>
    
    int main(void)
    {
        for (int i = 0; i < 3; i++)
        {
            printf("meow\n");
        }
    }
    

    لاحظ أن يتم استخدام كتلة for ثلاث وسائط. الحجة الأولى int i = 0 يبدأ العداد عند الصفر. الحجة الثانية i < 3 هو الشرط الذي يتم التحقق منه. وأخيرا الحجة i++ يخبر الحلقة بأن تزيد بمقدار واحد في كل مرة يتم فيها تشغيل الحلقة.

  • يمكننا أيضًا التكرار إلى الأبد باستخدام الشيفرة التالي:

    // Infinite loop
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        while (true)
        {
            printf("meow\n");
        }
    }
    

    لاحظ ذلك تم الآن إنشاء true سيكون هو الحال دائمًا. لذلك، سيتم تشغيل الشيفرة دائمًا. سوف تفقد السيطرة على نافذتك الطرفية عن طريق تشغيل هذا الرمز. يمكنك الخروج من حلقة لا نهائية عن طريق الضرب control-C على لوحة المفاتيح (يرسل هذا إشارة SIGINT لإنهاء البرنامج).

  • يمكننا أن نطلب من المستخدم عدد مرات المواء عن طريق تعديل الشيفرة كالتالي: يتم تعريف

    // Prompts user for n.
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        int n = get_int("What's n? ");
    
        for (int i = 0; i < n; i++)
        {
            printf("meow\n");
        }
    }
    

    لاحظ ذلك تم الآن إنشاء n من قبل المستخدم. ثم هناك n مواء.

  • ماذا يحدث إذا أدخل المستخدم شيئًا أقل من الصفر؟ قم بتعديل الشيفرة الخاص بك كما يلي:

    // Prompts user again if need be. (Poor design.)
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        int n = get_int("What's n? ");
        if (n < 0)
        {
            n = get_int("What's n? ");
        }
    
        for (int i = 0; i < n; i++)
        {
            printf("meow\n");
        }
    }
    

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

  • قم بتحسين الشيفرة الخاص بك كما يلي: سيتم تشغيل حلقة

    // Uses a loop with continue/break.
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        int n;
        while (true)
        {
            n = get_int("What's n? ");
            if (n < 0)
            {
                continue;
            }
            else
            {
                break;
            }
        }
    
        for (int i = 0; i < n; i++)
        {
            printf("meow\n");
        }
    }
    

    لاحظ أن يتم استخدام كتلة while إلى الأبد حتى n أكبر من أو يساوي الصفر.

  • ومع ذلك، يمكن تحسين هذا الشيفرة بشكل أكبر:

    // Uses a loop with just break.
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        int n;
        while (true)
        {
            n = get_int("What's n? ");
            if (n >= 0)
            {
                break;
            }
        }
    
        for (int i = 0; i < n; i++)
        {
            printf("meow\n");
        }
    }
    

    لاحظ كيف تم تبسيط الشيفرة السابقة لدينا، وإزالة الأسطر غير الضرورية من الشيفرة.

  • يشبه أ حلقة while ، يمكننا تنفيذ هذا الشيفرة باستخدام ملف حلقة do-while : سيتم تشغيل

    // Uses a do-while loop instead.
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        int n;
        do
        {
            n = get_int("What's n? ");
        }
        while (n < 0);
    
        for (int i = 0; i < n; i++)
        {
            printf("meow\n");
        }
    }
    

    لاحظ أن يتم استخدام كتلة do دائمًا مرة واحدة على الأقل. سيتم تكرار هذا الجزء من الشيفرة أثناء ذلك n أقل من الصفر.

  • قد ترى العين الناقدة أننا نستطيع ذلك مجردة بعيدا الجزء من الشيفرة الذي يتموء.

الوظائف (Functions)

  • بينما سنقدم المزيد من الإرشادات لاحقًا، يمكنك إنشاء وظيفتك الخاصة داخل لغة C كما يلي:

    void meow(void)
    {
        printf("meow\n");
    }
    

    الأولي void يعني أن الدالة لا تُرجع أية قيم. ال (void) يعني أنه لم يتم توفير أي قيم للوظيفة.

  • يمكن استخدام هذه الوظيفة في الوظيفة الرئيسية كما يلي:

    // Abstraction
    
    #include <stdio.h>
    
    void meow(void);
    
    int main(void)
    {
        for (int i = 0; i < 3; i++)
        {
            meow();
        }
    }
    
    // Meow once
    void meow(void)
    {
        printf("meow\n");
    }
    

    لاحظ كيف يتم استدعاء الدالة meow باستخدام الدالة تعليمات meow() . وهذا ممكن لأن يتم تعريف وظيفة meow في الجزء السفلي من الشيفرة، و النموذج الأولي من الوظيفة متوفرة في الجزء العلوي من الشيفرة كـ void meow(void).

  • الخاص بك يمكن تعديل وظيفة meow بشكل أكبر لقبول الإدخال:

    // Abstraction with parameterization
    
    #include <stdio.h>
    
    void meow(int n);
    
    int main(void)
    {
        meow(3);
    }
    
    // Meow some number of times
    void meow(int n)
    {
        for (int i = 0; i < n; i++)
        {
            printf("meow\n");
        }
    }
    

    لاحظ أن النموذج الأولي قد تغير إلى void meow(int n) لإظهار ذلك meow يقبل int كمدخل له.

  • عند العمل مع المتغيرات والوظائف، من المهم فهم نطاق للمتغير. خذ بعين الاعتبار الشيفرة التالي: تم تعريف

    // Demonstrates scope
    
    #include <stdio.h>
    
    void meow(int n);
    
    int main(void)
    {
        int n = 3;
        meow(n);
    }
    
    // Meow some number of times
    void meow(int n)
    {
        for (int i = 0; i < n; i++)
        {
            printf("meow\n");
        }
    }
    

    لاحظ كيف n في وظيفة main . وبسبب ذلك، n يقع فقط في نطاق وظيفة main . الطريقة الوحيدة التي وظيفة meow قادرة على الاستخدام n هي تلك النسخة يتم تمرير n إلى وظيفة meow . meow لا يستخدم النسخة الأصلية n من وظيفة main . بدلا من ذلك، فإنه يستخدم نسخته الخاصة من n.

  • مع بعض التعديلات على الشيفرة الخاص بنا، يمكننا الحصول على مدخلات المستخدم: يتم استخدام

    // User input
    
    #include <cs50.h>
    #include <stdio.h>
    
    void meow(int n);
    
    int main(void)
    {
        int n;
        do
        {
            n = get_int("Number: ");
        }
        while (n < 1);
        meow(n);
    }
    
    // Meow some number of times
    void meow(int n)
    {
        for (int i = 0; i < n; i++)
        {
            printf("meow\n");
        }
    }
    

    لاحظ ذلك تم الآن إنشاء get_int للحصول على رقم من المستخدم. n إلى meow.

  • يمكننا أيضًا إجراء اختبار للتأكد من صحة المدخلات التي نحصل عليها من المستخدم:

    // Return value
    
    #include <cs50.h>
    #include <stdio.h>
    
    int get_positive_int(void);
    void meow(int n);
    
    int main(void)
    {
        int n = get_positive_int();
        meow(n);
    }
    
    // Get number of meows
    int get_positive_int(void)
    {
        int n;
        do
        {
            n = get_int("Number: ");
        }
        while (n < 1);
        return n;
    }
    
    // Meow some number of times
    void meow(int n)
    {
        for (int i = 0; i < n; i++)
        {
            printf("meow\n");
        }
    }
    

    لاحظ أنه تم استدعاء دالة جديدة get_positive_int يطلب من المستخدم عددًا صحيحًا أثناء n < 1. بعد الحصول على عدد صحيح موجب، سيتم تنفيذ هذه الوظيفة return n العودة إلى وظيفة main .

الصحة، التصميم، الأسلوب (Correctness, Design, Style)

  • يمكن تقييم الشيفرة على ثلاثة محاور.
  • أولاً، الصحة يشير إلى "هل يعمل الشيفرة على النحو المنشود؟" يمكنك التحقق من صحة الشيفرة الخاصة بك مع check50.
  • ثانيا، التصميم يشير إلى "ما مدى جودة تصميم الشيفرة؟" يمكنك تقييم تصميم الشيفرة الخاصة بك باستخدام design50.
  • أخيرًا، النمط يشير إلى "ما مدى جمالية الشيفرة واتساقه؟" يمكنك تقييم نمط الشيفرة الخاصة بك باستخدام style50.

ماريو (Mario)

  • لقد ركز كل ما ناقشناه اليوم على العناصر الأساسية المختلفة لعملك كعالم كمبيوتر ناشئ.
  • سيساعدك ما يلي في التوجه نحو العمل على حل مشكلة محددة لهذا الفصل بشكل عام: كيف يمكن للمرء التعامل مع مشكلة متعلقة بعلوم الحاسوب؟
  • تخيل أننا أردنا محاكاة الصورة المرئية للعبة Super Mario Bros. بالنظر إلى مجموعات الأسئلة الأربعة الموضحة في الصورة، كيف يمكننا إنشاء كود يمثل هذه الكتل الأفقية الأربع تقريبًا؟

    Mario Question Marks

  • في النافذة الطرفية، اكتب code mario.c والشيفرة كما يلي:

    // Prints a row of 4 question marks
    
    #include <stdio.h>
    
    int main(void)
    {
        printf("????\n");
    }
    

    لاحظ أنه تمت طباعة أربع علامات استفهام.

  • باستخدام الحلقة، يمكننا طباعة علامات الاستفهام بشكل أكثر كفاءة:

    // Prints a row of 4 question marks with a loop
    
    #include <stdio.h>
    
    int main(void)
    {
        for (int i = 0; i < 4; i++)
        {
            printf("?");
        }
        printf("\n");
    }
    

    لاحظ كيف تتم طباعة أربع علامات استفهام هنا باستخدام حلقة.

  • وبالمثل، يمكننا تطبيق نفس المنطق لإنشاء ثلاث كتل رأسية.

    Mario Blocks

  • لتحقيق ذلك، قم بتعديل الشيفرة الخاص بك كما يلي:

    // Prints a column of 3 bricks with a loop
    
    #include <stdio.h>
    
    int main(void)
    {
        for (int i = 0; i < 3; i++)
        {
            printf("#\n");
        }
    }
    

    لاحظ كيف تتم طباعة ثلاثة قوالب عمودية باستخدام حلقة.

  • ماذا لو أردنا دمج هذه الأفكار لإنشاء مجموعة من الكتل ثلاثية في ثلاثة؟

    Mario Grid

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

    // Prints a 3-by-3 grid of bricks with nested loops
    
    #include <stdio.h>
    
    int main(void)
    {
        for (int i = 0; i < 3; i++)
        {
            for (int j = 0; j < 3; j++)
            {
                printf("#");
            }
            printf("\n");
        }
    }
    

    لاحظ وجود حلقة واحدة داخل حلقة أخرى. تحدد الحلقة الأولى الصف الرأسي الذي تتم طباعته. لكل صف، تتم طباعة ثلاثة أعمدة. بعد كل صف، تتم طباعة سطر جديد.

  • ماذا لو أردنا التأكد من عدد الكتل ثابت، أي أنه غير قابل للتغيير؟ قم بتعديل الشيفرة الخاص بك كما يلي:

    // Prints a 3-by-3 grid of bricks with nested loops using a constant
    
    #include <stdio.h>
    
    int main(void)
    {
        const int n = 3;
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                printf("#");
            }
            printf("\n");
        }
    }
    

    لاحظ كيف n أصبح الآن ثابتًا. لا يمكن تغييره أبدا.

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

    // Helper function
    
    #include <stdio.h>
    
    void print_row(int width);
      
    int main(void)
    {
        const int n = 3;
        for (int i = 0; i < n; i++)
        {
            print_row(n);
        }
    }
    
    void print_row(int width)
    {
        for (int i = 0; i < width; i++)
        {
            printf("#");
        }
        printf("\n");
    }
    

    لاحظ كيف تتم طباعة صف من خلال وظيفة جديدة.

المشغلين (Operators)

  • المشغلين يشير إلى العمليات الرياضية التي يدعمها المترجم الخاص بك. في لغة C، تتضمن هذه العوامل الرياضية ما يلي:

    • + للإضافة
    • - للطرح
    • * للضرب
    • / للتقسيم
    • % للباقي
  • سنستخدم كل هذه العوامل في هذه الدورة.
  • دعونا ننفذ الآلة الحاسبة الخاصة بنا عن طريق الكتابة code calculator.c في المحطة وتعديل الشيفرة الخاص بنا على النحو التالي:

    // Addition with int
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        // Prompt user for x
        int x = get_int("What's x? ");
    
        // Prompt user for y
        int y = get_int("What's y? ");
    
        // Add numbers
        int z = x + y;
    
        // Perform addition
        printf("%i\n", z);
    }
    

    لاحظ كيف قمنا بإنشاء متغير ثالث z لتخزين مجموع x و y، ثم اطبع النتيجة باستخدام %i (محدد التنسيق للأعداد الصحيحة).

  • يمكننا كتابة تعليمات برمجية أكثر كفاءة على النحو التالي:

    // Addition with int, without third variable
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        // Prompt user for x
        int x = get_int("What's x? ");
    
        // Prompt user for y
        int y = get_int("What's y? ");
    
        // Perform addition
        printf("%i\n", x + y);
    }
    

    لاحظ أننا قمنا بإلغاء الحاجة إلى المتغير الثالث z عن طريق إجراء الإضافة مباشرة داخل بيان printf ، مما يجعل الشيفرة الخاص بنا أكثر إيجازًا.

  • يمكننا استخدام الضرب:

    // Doubles a number
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        // Prompt user for x
        int x = get_int("What's x? ");
    
        // Double it
        printf("%i\n", x * 2);
    }
    

    لاحظ كيف نستخدم عامل الضرب * لمضاعفة قيمة الإدخال، مما يوضح عملية حسابية أخرى بخلاف الجمع.

  • الأعداد الصحيحة في C يمكن أن يعد عاليًا جدًا. خذ بعين الاعتبار ما يلي:

    // Overflow 
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        int dollars = 1;
        while (true)
        {
            char c = get_char("Here's $%i. Double it and give to next person? ", dollars);
            if (c == 'y')
            {
                dollars *= 2;
            }
            else
            {
                break;
            }
        }
        printf("Here's $%i.\n", dollars);
    }
    

    لاحظ كيف يقوم البرنامج بمضاعفة المبلغ بالدولار بشكل متكرر. في النهاية، سيتجاوز العدد الصحيح الحد الأقصى لقيمته و"التجاوز"، ويلتف حول رقم سالب أو صفر.

  • يحدث تجاوز عدد صحيح عندما تنتج عملية حسابية قيمة تتجاوز الحد الأقصى لسعة التخزين لنوع البيانات، مما يتسبب في التفاف القيمة بشكل غير متوقع.
  • أحد التحديات التي تواجه لغة C هو أنه على الرغم من أنها توفر لك تحكمًا هائلاً في كيفية استخدام الذاكرة، إلا أنه يجب على المبرمجين أن يكونوا على دراية تامة بالمخاطر المحتملة لإدارة الذاكرة.
  • تشير الأنواع إلى البيانات المحتملة التي يمكن تخزينها في متغير. على سبيل المثال، أ تم تصميم char لاستيعاب حرف واحد مثل a أو 2.
  • الأنواع مهمة جدًا لأن كل نوع له حدود معينة. على سبيل المثال، بسبب الحدود في الذاكرة، أعلى قيمة موقعة int عادةً ما يكون 2147483647، في حين أنه غير موقع int يمكن أن تصل 4294967295. إذا حاولت حساب int أعلى من الحد الأقصى، وهو تجاوز العدد الصحيح سيؤدي إلى تخزين قيمة غير صحيحة في هذا المتغير.
  • يحدد عدد البتات نطاق القيم التي يمكننا تمثيلها.
  • يمكن أن يكون لهذا تأثيرات كارثية في العالم الحقيقي.
  • يمكننا حل هذه المشكلة باستخدام ملف long نوع المتغير:

    // long
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        long dollars = 1;
        while (true)
        {
            char c = get_char("Here's $%li. Double it and give to next person? ", dollars);
            if (c == 'y')
            {
                dollars *= 2;
            }
            else
            {
                break;
            }
        }
        printf("Here's $%li.\n", dollars);
    }
    

    لاحظ أننا تغيرنا من int إلى long والاستخدام %li بدلاً من %i في سلاسل التنسيق الخاصة بنا. أ يمكن لـ long تخزين قيم أكبر بكثير من int، يؤخر (ولكن لا يزيل) مشكلة الفائض.

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

    // Division with ints, demonstrating truncation
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        // Prompt user for x
        int x = get_int("What's x? ");
    
        // Prompt user for y
        int y = get_int("What's y? ");
    
        // Divide x by y
        printf("%i\n", x / y);
    }
    

    لاحظ أنه عند قسمة عددين صحيحين، تقوم لغة C بإجراء قسمة عدد صحيح واقتطاع (تجاهل) أي جزء عشري. على سبيل المثال، 7/2 سيعطي 3، وليس 3.5.

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

  • وبالمثل، يمكننا ذلك cast عدد صحيح ليكون أ float. خذ بعين الاعتبار ما يلي:

    // Casting
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        // Prompt user for x
        int x = get_int("What's x? ");
    
        // Prompt user for y
        int y = get_int("What's y? ");
    
        // Divide x by y
        printf("%f\n", (float) x / y);
    }
    

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

  • يمكننا استخدامه floatطوال الوقت:

    // Floats
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        // Prompt user for x
        float x = get_float("What's x? ");
    
        // Prompt user for y
        float y = get_float("What's y? ");
    
        // Divide x by y
        printf("%.50f\n", x / y);
    }
    

    لاحظ أننا نستخدم get_float للإدخال و %.50f لعرض ما يصل إلى 50 منزلة عشرية، مما يكشف عن حدود دقة الفاصلة العائمة حيث قد تظهر النتيجة أرقامًا غير متوقعة بسبب قيود التمثيل الثنائي.

التلخيص (Summing Up)

في هذا الدرس، تعلمت كيفية تطبيق الكتل البرمجية الإنشائية التي تعلمتها في Scratch على لغة البرمجة C. تعلمت…

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

نراكم في المرة القادمة!

أنهيت قراءة الملاحظات؟

يمكنك قراءة الدورة كاملة دون حساب. يصبح حفظ التقدم متاحًا بعد تفعيل الحساب، ويبقى محفوظًا بعد انتهاء العضوية.