[MUSIC PLAYING]
[VIDEO PLAYBACK]
- --نحن نعلم؟
- أنه في الساعة 9:15، كان راي سانتويا عند جهاز الصراف الآلي.
- إذن السؤال هو، ماذا كان يفعل في الساعة 9:16؟
- إطلاق عيار ناري 9 ملليمتر على شيء ما.
ربما رأى القناص.
- أو كان يعمل معه.
- حسنًا.
ارجع للصورة السابقة.
- ماذا ترى؟
- قرّب وجهه.
في وضع ملء الشاشة.
- نظّارته.
- هناك انعكاس.
- هذا فريق نيوفيتاس للبيسبول.
هذا هو شعارهم.
- وهو يتحدث إلى الشخص الذي يرتدي تلك السترة.
- قد يكون لدينا شاهد.
- لعمليتي إطلاق النار.
[END PLAYBACK]
ديفيد مالان: هذه هي دورة CS50، وهذه هي المحاضرة الثالثة، وتلك
ليست الكيفية التي تعمل بها علوم الكمبيوتر.
وبالفعل، بحلول نهاية اليوم، سنوضح
بالضبط ما هو صحيح، وما هو غير صحيح في ذلك،
وآمل أن أجعلكم تفكرون قليلاً في أي وقت تشاهدون فيه التلفاز أو الأفلام فيما بعد
وتلاحظوا هذه الأشياء الصغيرة التي يبدو أن الكثير من المؤلفين
يعدونها من المسلمات.
إذن تذكرون آخر مرة، ألقينا فيها نظرة على ماهية التحويل البرمجي
في الواقع.
وتذكرون أنه كان عبارة عن بضعة أشياء، هذه الخطوات الأربع من المعالجة المسبقة
والتحويل البرمجي والتجميع والربط،
إذن عندما تبدأ بتعليمة المصدر البرمجية الخاصة بها،
التي قد تبدو مثل هذه التعليمة البرمجية التي كتبناها في الماضي،
يتعين عليك أولاً أن تعالجها مسبقًا، وكانت الخطوة الأولى في المعالجة المسبقة
هي تحويل جميع تعليمات المعالج تلك--
أي شيء يبدأ بعلامة تجزئة في البداية-- إلى ما يعادله.
إذن فتح الملفات ونسخ ولصق المحتويات بفعالية
هناك لكي تعرف تلك البرامج وأداة التحويل البرمجي ما هو get_string
وما هي printf.
كانت الخطوة التالية التي جاءت بعد ذلك في الواقع
هي التحويل البرمجي، حيث إن التحويل البرمجي من الناحية الفنية يعني أخذ تعليمة مصدر
برمجية، بمجرد أن تتم معالجتها مسبقًا، وطباعة وإنشاء
هذه الأشياء التي تبدو مشفرة للغاية والتي تسمّى تعليمة التجميع البرمجية.
وتعليمات التجميع البرمجية تلك أو تعليمات التجميع هي ما تستوعبه وحدة المعالجة المركزية--
عقل جهاز الكمبيوتر لديك-- في الواقع،
على الرغم من أن الكمبيوتر من الناحية الفنية يستوعبها فقط في شكل
الأصفار والواحدات.
ولذا عندما تقوم بـ "تجميع-- الخطوة الثالثة--
تعليمة التجميع البرمجية تلك، فأنت في الواقع تُخرِج تلك الأصفار والواحدات.
ولكن حتى أبسط البرامج التي نطالب المستخدم فيها فقط بسلسلة
ثم نطبع اسمه والذي ما زال يتضمّن بضعة ملفات أخرى.
لم يكن هناك فقط cs50.h وstdio.h في الأعلى،
فهناك على الأرجح في مكان ما في نظام الكمبيوتر ملفات باسم cs50.c،
وفي حالة stdio، وprintf.c، التي تكون التعليمة البرمجية فعليًا فيها
لهاتين الدالتين، ويجب تحويل هاتين الدالتين برمجيًا إلى
أصفار وواحدات، ثم نحتاج إلى ربط كل شيء معًا،
ودمج تلك الأصفار والواحدات وبذلك يصل جهاز الكمبيوتر إلى التعليمة البرمجية الخاصة بك
وإلى تعليمة printf البرمجية وإلى تعليمة مكتبة cs50 البرمجية وهكذا.
ولكن يمكننا إنهاء كل ذلك فقط بشكل عام في أداة وصف التحويل البرمجي.
إذن هذه إحدى المسائل التي تناولناها الأسبوع الماضي.
وقدمنا أيضًا، الأسبوع الماضي والذي قبله، بضع أدوات.
وتوجد احتمالات، بأنكم تواجهون إحباطات كثيرة من المحتمل أنها موجودة بالفعل
مع "p-sets" لأنكم حققتم إنجازات
ولديكم شعور بالرضا.
وهذا أمر طبيعي، وتؤكد البقية أن المقاييس ستميل في النهاية أكثر
صوب السعادة وبعيدًا عن الحزن، لكننا سنقدم
لكم في الواقع المزيد من الأدوات اليوم مقارنة بما سبق للعثور على
المشاكل أو أوجه القصور في التعليمة البرمجية لديك.
help50، أتذكرون، يساعد في أي معالجة؟
عندما تفكر بديهيًا في استخدام help50؟
عندما ترى رسائل خطأ على الشاشة.
شيء لا تفهمه هذا هو نتيجة خطأ من المحتمل أنك قمت
به ولكنك لا تدرك تمامًا ما الذي يخبرك به جهاز الكمبيوتر،
قم بتشغيل help50، ثم نفس ذلك الأمر ونحن، طاقم العمل،
باستخدام تعليمتنا البرمجية سنحاول فهم الرسالة من أجلك
وتزويدك بالملاحظات.
يقوم style50 بذلك بالضبط.
يساعدك في رؤية ما هي الفراغات التي يجب أن توجد بالضبط، وما التي لا يجب
أن توجد هناك باستخدام ترميز باللونين الأحمر والأخضر-- إنه يساعدك فقط في تزيين
تعليمتك البرمجية بحيث يمكنك قراءتها بشكل أفضل أنت والأشخاص الآخرون أيضًا.
ثم printf، وهو نوع من الأدوات الفظة إلى حد ما الموجودة في مربع الأدوات لديك،
هذا يساعدك ليس فقط في رؤية الرسائل التي تريد أن تراها،
ولكن أيضًا في رؤية قيم المتغيرات.
يمكنك طباعة الأعداد الصحيحة والسلاسل، أيًا كان ما تريد،
ثم يمكنك حذف تلك الأسطر من printf
بمجرد أن تكون واثقًا من عمل برنامجك.
لكن يصبح هذا الأمر مملاً قليلاً، وبصراحة، حيث تصبح برامجنا أكبر،
سنرغب في وجود أدوات أكثر قدرة منها مثل طباعة الأشياء يدويًا،
وإعادة التحويل البرمجي، وإعادة التشغيل، فتصبح مملة بشكل أسرع.
وليس الهدف من البرمجة أن تشعر بالملل، بل أن تكون مُمكنًا،
وهذا ما نرمي إلى القيام به اليوم عبر هذا.
إذن CS50 IDE هو إصدار أروع مما
كنت تستخدمه المسمّى CS50 Sandbox، وبدوره، CS50 Lab.
الآن تذكّروا أن كلا الأداتين، Sandbox وLab،
لديها نافذة طرفية حيث يمكنك كتابة الأوامر،
لديهما محرر تعليمات برمجية حيث يمكنك كتابة تعليماتك البرمجية في الواقع،
ثم لديهما متصفح ملفات مع الرموز وحيث
يمكنك بالفعل رؤية الملفات والمجلدات الخاصة بك.
لذلك يتضح أن CS50 IDE هي أداة أخرى للوهلة الأولى
وهي مشابهة جدًا، وعلى الرغم من أنه تم وضعها بشكل مختلف قليلاً،
لكنها تحتوي على العديد من الميزات مثل Sandbox وLab، ولكن يوجد المزيد.
الميزات الأخرى التي تساعدك في الواقع في حل المشاكل في تعليماتك البرمجية
والتعاون مع الآخرين لإنهاء مشروع بحلول موعده النهائي إذا كنت ترغب في ذلك.
إذن سنرى هل هذا هو CS50 IDE.
إنه يأتي مع ما يسمّى بالوضع الليلي إذًا
يمكنك جعل كل شيء داكن أكثر على شاشتك، خاصةً إذا كان إعداد p
ليلاً، ودعونا في الواقع نلقي نظرة فيما بعد
على ما يمكنكم القيام به باستخدام هذه الأداة.
عندما تسجّل دخولك إلى هذه الأداة للمرة الأولى في مجموعة المشاكل التالية،
سترى أن الواجهة تكون كما كانت عليه من قبل تقريبًا.
تبدو الألوان مختلفة قليلاً، وأحجام الخطوط مختلفة قليلاً،
ولكن في الجزء السفلي بشكل افتراضي، لديك ما تسمّى بالنافذة الطرفية،
وبدلاً من علامة الدولار الآن، سترى
مساحة عمل أكثر تفصيلاً، ولكن المزيد حول ذلك بعد قليل.
هنا في الأعلى لديك فقط نافذة محرر التعليمات البرمجية،
لا يحدث شيء هناك حقًا.
ثم لدينا الميزة المضافة Ceiling Cat
في أعلى الزاوية اليمنى.
كما سنرى بعض الميزات الأخرى أثناء النقاش.
لذا دعونا في الواقع نكتب برنامج في CS50 IDE، وهو، لنكن واضحين،
مجرد بيئة برمجية قائمة على الويب ويتيح لك أيضًا
الوصول إلى الخادم القائم على السحابة الخاص بك.
كما يقوم بتشغيل نظام التشغيل Ubuntu Linux، والذي يعد من أشهر أنظمة التشغيل
وهو ليس macOS ولا Windows.
ولكن على عكس بيئة أداة تحديد الوصول حيث لا تقوم حتى بتسجيل الدخول
وتفقد ملفاتك في نهاية المطاف، لأنه قد
تعرف من أين يتم فقد ملفات تعريف الارتباط الخاصة بك أو عندما يحدث خطأ ما،
يحفظ IDE كل شيء.
وستقوم بتسجيل الدخول باستخدام حسابك، وأيًا كان ما
وضعته هناك الأسبوع الماضي سيكون هناك هذا الأسبوع والأسبوع المقبل
وما بعدها.
إذن دعوني أمضي قُدمًا إلى قائمة الملف، ملف جديد، أو يمكنني فقط النقر فوق رمز علامة الجمع الصغير
في أعلى الزاوية اليمنى، ودعوني أمضي قدمًا وأضغط بشكل استباقي
على Control-S أو Command-S أو أنتقل إلى الملف، وحفظ--
يجب أن تجد الواجهة مشابهة تمامًا لأي برنامج Mac أو جهاز كمبيوتر شخصي--
ودعوني أمضي قدمًا وأحفظ هذا الملف على النحو التالي.
سأسمي هذا hello.c.
ومن الهام ذِكر ملحق الملف،
وإلا فإن IDE، مثل Sandbox وLab،
لن يعرف ما هو نوع البرنامج الذي تكتبه.
ثم دعوني أمضي قدمًا وأكتب فقط أبسط برامجي.
إذن دعوني أمضي قدمًا وأقوم بكتابة include stdio.h, int main void.
دعوني أمضي قدمًا وأفتح الأقواس المتعرجة، printf--
مرحبًا، بالعالم، خط مائل عكسي n، وفاصلة منقوطة.
إذن ستلاحظوا أن تقريبًا كل شيء هو نفسه.
الألوان مختلفة قليلاً، ربما،
قد ترون بعض ميزات مساعدة
مختلفة أثناء كتابة التعليمات البرمجية، ولكن النتيجة النهائية هي نفسها.
وترميز اللون الذي حصلت عليه للتو مجانًا لأنه يساعد
في جذب انتباهك إلى أجزاء مختلفة من التعليمات البرمجية.
دعوني أمضي قُدمًا الآن و--
أوه لاحظوا هذا.
يوجد اختلاف واحد.
هو أن IDE أداة أكثر قدرةً، إلا أنها، تعد أداة يدوية أكثر
ولا تقوم فقط بحفظ التعليمات البرمجية من أجلك تلقائيًا.
هذا الأمر جيد كما كان بأداة تحديد الوصول، لدرجة أنك لن تقوم أبدًا
بالضغط على Command-S أو Control-S في الواقع--
وإذا كان الأمر كذلك، فأنت لست بحاجة للقيام بذلك، حيث سيقوم IDE
بحفظ الأشياء عندما تريد ذلك بحيث لن يحدث شيء
بطريقة سحرية بعد الآن.
لذا الأمر الذي يتعين عليّ القيام به هو الرجوع للأعلى هنا، الملف، حفظ، أو Command-S
أو Control-S، سترى نقطة خضراء صغيرة
لفترة وجيزة، والآن إلى المحث الخاص بي.
سأمضي قدمًا الآن وأكتب الأمر المألوف الخاص بي، make hello، Enter،
وسترون تقريبًا أمر العميل نفسه الذي يبدو مشفرًا
كما كان من قبل لأنه تم تكوين IDE تمامًا مثل أداة تحديد الوصول.
وإذا أردت المضي قدمًا وقمت بتشغيل هذا الآن، كيف يمكنني تشغيل هذا البرنامج؟
فحص سريع؟
./hello، إنه بالضبط الأمر نفسه كما كان من قبل.
./hello، وهناك يكون لدينا، مرحبًا، العالم.
القصة الطويلة جدًا باختصار، واجهة المستخدم حتى الآن مختلفة قليلاً،
لكن من الناحية الوظيفية هي نفسها.
سنبدأ الآن في رؤية بعض ميزات إضافية.
إذن ما هي تلك الميزات؟
ودعونا نقدم بعض القدرات الجديدة التي كانت في الواقع
ممكنة في أداة تحديد الوصول، ولم نقم بتقديمهم بالفعل في ذلك الوقت.
إذا قمت بالنقر فوق رمز هذا المجلد في أعلى اليسار، سترون كل الملفات والمجلدات الخاصة بي.
واليوم لهذه المحاضرة قمت بإعداد أمثلة متعددة مسبقًا
وهي موجودة بالفعل على موقع الويب الخاص بالدورة، وسنلقي نظرة على بعضها،
وسنشير إلى بعض منها على موقع الويب،
ولكن هذه ليست سوى ملفات ومجلدات مألوفة.
ويمكنكم رؤية أن كل شيء في حسابي
على ما يبدو أنه في شيء ما يسمّى مساحة العمل، وهي
مجرد مجلد، أو اسم، أو دليل.
ها هو دليل Sc3 الخاص بي، والذي يأتي، مجددًا
من موقع الويب لمحاضرة اليوم، المحاضرة الثالثة.
ومن ثم ها هو الملف الذي حولته فقط برمجيًا في البرنامج والملف
الذي كتبته، hello.c.
كما ستلاحظون أنه يوجد رمز غريب هنا، tilde،
حيث قد لا تسنح لكم فرصة لكتابته في كثير من الأحيان باللغة الإنجليزية،
ولكن باللغة الإسبانية، وبلغات أخرى قد تستخدمون هذا الحرف.
هذا في الواقع تدوين شديد الاقتضاب لما يطلق عليه دليلك الرئيسي.
في هذه البيئة، CS50 IDE، يوجد لديك الدليل الرئيسي الخاص بك، والذي
يقصد به مجلد ملفاتك والمجلدات الأخرى التي قمت بإنشائها،
والموجودة لديك، والشيء الموجود في كل مرة تقوم فيها بتسجيل الدخول-- ولن
تفقد المحتويات الموجودة فيه.
إذن هذا يعني أنه في دليلك الرئيسي، والمعروف أيضًا باسم tilde،
يوجد مجلد يسمى مساحة العمل الذي أعمل فيه حاليًا.
وهذا مجلد واحد فقط حيث سيتم كل عملي فيه،
نظرًا لوجود عدد كبير جدًا من المجلدات والملفات الأخرى في بيئة السحابة هذه،
تمامًا كما هو الحال في جهاز Mac و جهاز الكمبيوتر الشخصي لديك، فنحن فقط لا
نهتم بماهيتهم عمومًا.
ولكن لاحظوا ما يمكننا القيام به في هذه النافذة الطرفية إلى جانب التحويل البرمجي
وتشغيل التعليمات البرمجية.
هناك أوامر أخرى.
على سبيل المثال، هذا النص باللون الأزرق هنا، وعلى نحو مشابه لمتصفح الملفات في الأعلى،
يشير الآن ليس فقط إلى أن هذا هو المحث الخاص بي لكل علامة دولار،
ولكن أيضًا في دليل مساحة العمل الخاصة بدليلي الرئيسي.
لذا هذا يعني أنه يمكنني الانتقال إلى أي مكان آخر على الرغم من أنني لم
أحدد إلى أين أريد أن أنتقل بعد.
وفي الواقع، يمكنني القيام بهذا. يرمز ls إلى القائمة،
إنه مجرد تدوين شديد الاقتضاب لذلك.
والآن أرى نسخة نصية من شجرة ملفاتي، إذا جاز التعبير.
إذن سترى هنا، أن sc3 هو مجلد، ويمكنك
القول بقدر كافٍ لأن هناك خط مائل في نهايته.
hello.c هو بالطبع الملف الذي كتبته منذ لحظات.
ثم مرحبًا باللون الأخضر هو برنامجي الذي قمت بتحويله برمجيًا، والنجمة
أو رمز النجمة يوجد فقط--
وليس اسم الملف، إنه يوضح ليّ فقط
وبشكل مرئي أن هذا قابل للتنفيذ.
هذا برنامج يمكنني تشغيله فقط حتى أعرف ما تم تحويله برمجيًا
وما قد تكون تعليمات المصدر البرمجية.
إذن عندما تقوم بتشغيل ./hello، فإن السبب وراء عمل هذا طوال هذا الوقت
هو لأنه يوجد ملف باسم مرحبًا، في نقطة، مجلدك الحالي،
وعندما تضغط على Enter، فأنت تقوم بتشغيل هذا البرنامج هناك.
لذا إذا رجعتم بعد اليوم إلى CS50 Sandbox أو CS50 Lab وكتبتم ls،
سترون بالضبط الشيء نفسه كما كان بواسطة رمز المجلد
الصغير في تلك البرامج أيضًا.
ولكن لنفترض أنني أرغب في الانتقال إلى الدليل.
في macOS أو Windows أو حتى IDE، يمكنني، بالطبع،
الانتقال إلى رمز الملف، ثم في المثلث الصغير
هنا، والذي قد يبدو بديهيًا، فتقوموا بالنقر فوقه فقط
ويمكنكم رؤية ما يحدث في الداخل، ليس الأمر مفاجئًا.
لكن كيف تقوم بذلك نصيًا؟
في محث الأوامر، حسنًا الأمر ليس بتلك الصعوبة.
تحتاج فقط إلى تغيير الدليل الخاص بك.
إذن إذا كتبت cd مسافة sc3، Enter، يبدو أن لا شيء يحدث تمامًا حتى الآن
باستثناء أن المحث قد تغيّر.
ها هي الإشارة إلى أن-- هذا هو المحث الخاص بي، ولكن ترون على يساره
باللون الأزرق أنني الآن في مجلد مساحة العمل الخاص بدليلي الرئيسي،
في مجلد sc3 الخاص بي هناك.
إذن هو مجرد إصدار قائم على نص من GUIs، واجهات المستخدم الرسومية
والتي بالتأكيد نعدها جميعًا
من المسلمات في عالم macOS و Windows حتى الآن.
حسنًا، لنفترض أنني انتهيت إلى حد ما من برنامج مرحبًا الخاص بي
وأرغب في حذفه.
حسنًا في IDE، كما في أداة تحديد الوصول، يمكنك بالفعل الانتقال هنا ويمكنك
النقر فوقه، ثم يمكنك عادةً النقر بزر الماوس الأيمن أو النقر فوق زر التحكم
وستحصل على قائمة كاملة من الخيارات الأخرى، أحد هذه الخيارات هو حذف--
ولا تتردد في إصلاح مثل هذا في بيئتك الخاصة.
ولكن ماذا عن سطر الأوامر؟
إذا قمت بالتكبير بالأسفل هنا ورغبت في إزالة مرحبًا، ولن تكتب
إزالة لأن هذا يبدو أكثر إطنابًا قليلاً
ومنذ عقود قرر البشر أن أمر الكتابة ممل جدًا،
دعونا نسمّي هذا الأمر rm--
للإزالة-- مرحبًا، سترون محث مشفر إلى حد ما.
rm-- إزالة الملف المنتظم "مرحبًا؟"
هذا أمر غامض أكثر مما يجب أن يكون عليه، لكنه يسأل فقط،
هل أنت متأكد من أنك تريد حذف "مرحبًا؟"
ثم إنه فقط في انتظارك.
وهنا يمكنك كتابة y أو نعم أو أحيانًا أوامر أخرى أيضًا،
الآن لقد أكدت أن نواياي كانت نعم.
إذا كتبت ls مجددًا، أنا-- عذرًا، في المجلد الخاطىء.
إذا كتبت ls مجددًا بعد كتابة مرحبًا--
لا-- بعد كتابة مرحبًا وls ، الآن سأرى
شيئين اثنين فقط sc3 وhello.c.
ماذا لو أردت إنشاء مجلد؟
حسنًا لاحظوا ذلك.
إذا كتبت في الجزء السفلي هنا، make directory--
mkdir-- test فقط لإنشاء مجلد اختبار، وأنا
على وشك الضغط على Enter، ولكن شاهدوا في أعلى الزاوية اليسرى
حيث لديّ هذه المجلدات والملفات الأخرى حاليًا، وعندما أضغط على Enter،
الآن لديّ مجلد اختبار.
إذن هذه الأشياء متطابقة.
واحدة تكون رسومية، والأخرى سطر أوامر، وتوجد أوامر أخرى
إذا قررت أنني لا أرغب في ذلك.
rmdir هو إزالة الدليل، ثم يختفي فقط
لأنه فارغ وبالتالي آمن.
أي أسئلة لاحقًا على أي من تلك الأوامر
أو فقط التخطيط العام لما ننظر إليه؟
حسنًا، لذلك لا تتوقفوا أمام أي من تلك الأوامر،
ومجموعة المشاكل وما بعدها والتي ستذكركم دائمًا
بأنواع الميزات هذه.
فالنقطة التي نحن بصدد مناقشتها الآن هي فقط أننا في بيئة جديدة إلى حد ما،
ولكنها ما تزال كما هي في الأساس، ولديها القدرات نفسها.
إذن ما هي الأدوات الأخرى التي نظرنا إليها؟
إذن ربما سمعتم شائعات عن أداة تسمى check50، وفي الحقيقة،
هذه أداة يستخدمها طاقم العمل في تقييم مجموعة المشاكل 1 ومجموعة المشاكل
2 لتقييم مدى صحتهما حتى لا يتعين علينا كتابة
./mario أو ./caesar مرارًا وتكرارًا لاختبار التعليمات البرمجية الخاصة بالطلاب.
ولكن بداية من هذا الأسبوع، أنتم، أيضًا، يمكنكم الوصول إلى البرنامج نفسه.
check50 هو أمر من طاقم العمل يتحقق من صحة التعليمات البرمجية الخاصة بك
تمامًا مثلما يفحص style50 أسلوب التعليمات البرمجية لديك.
وفي الواقع، إذا انتقلت مجددًا إلى IDE،
دعونا نحاول استخدام هذا للمرة الأولى عن طريق جعل الإصدار نفسه من مرحبًا
والذي من المحتمل أنك قمت به في مجموعة مشاكلك الأولى.
لذا إذا مضيت قدمًا وقمت بتضمين ليس فقط stdio، ولكن cs50.h أيضًا،
ومضيت قدمًا وحصلت على سلسلة من المستخدم
باستخدام get_string، وقمت بطلب الاسم منه، ثم مضيت قدمًا
وطبعت ليس فقط مرحبًا، بالعالم، ولكن أيضًا مرحبًا، النسبة المئوية s فاصلة اسم،
أعتقد أن هذا هو البرنامج نفسه الذي من المحتمل أنكم قمتم
بكتابته بأنفسكم، أو بديلاً عنه تقريبًا.
إذن إذا مضيت قدمًا الآن واختبرت ذلك بنفسي--
make hello, Enter، يبدو هذا الأمر على ما يرام، ./hello.
سأمضي قدمًا واكتب فيه اسمي، وها هو ذا، مرحبًا، ديفيد.
الآن لنفترض أنكم تشعرون بالرضا، وواثقين
للغاية أن تعليماتكم البرمجية صحيحة،
والأهم من ذلك، أنكم اختبرتم التعليمات البرمجية بأنفسكم.
لا يكفي الاعتماد على الأداة الخاصة بنا وحدها
من أجل اختبار التعليمات البرمجية لأنها، أيضًا، قد لا تكون شاملة.
إذن بمجرد أنك قد جربت بعض المدخلات، ليس فقط ديفيد، ولكن ربما
اسم فيرونيكا أيضًا، يبدو أنه يعمل.
اسم براين كذلك، يبدو أنه يعمل.
لا يوجد اسم على الإطلاق، لا يبدو أنه يعمل، ربما؟
ولكن يتعين علينا أن ننظر مجددًا إلى مجموعة المشاكل
لمعرفة ما إذا كانت هذه مشكلة بالفعل.
دعوني أمضي قُدمًا وأشغّل check50.
تتوقع check50 وحدة كتلة خاصة، إذا جاز التعبير.
مجرد معّرف فريد للمشكلة التي ترغب في التحقق منها.
وستعرف هذا فقط من خلال قراءة مجموعة
المشاكل أو الوثائق عبر الإنترنت.
تذكرتُ للتو فقط أنه الأمر الذي كان طاقم العمل يستخدمونه
لتصحيح وتقييم مرحبًا هو فقط cs50/2018/fall/hello.
والخط المائل هو لتمييز تلك الكلمات بشكل مرئي،
هذا ليس مجلدًا أو ملفات أو أي شيء من هذا القبيل في حسابك الخاص.
إذن سأقوم بتشغيل check50 cs50/2018/fall/hello في الدليل
نفسه حيث يوجد hello.c بداخله.
Enter.
ستمضي قُدمًا وتتصل بـ GitHub، وهو الطرف الخلفي،
تذكّروا، أننا نستخدمه لتخزين التعليمات البرمجية.
تتم المصادقة الخاصة بي الآن، وهذا يعني ما هو اسم المستخدم وكلمة المرور لديك؟
سأمضي قُدمًا وأستخدم إحدى حسابات الاختبار الخاصة بي.
والآن تطلب مني كلمة المرور الخاصة بي،
وسأمضي قُدمًا وأكتب ذلك فيها.
ستلاحظون أنكم تشاهدون نجوم مثلما ترون النقاط في موقع ويب
فقط وبذلك لا يختلق شخص النظر إلى ما تكتبه.
الآن سأمضي قُدمًا وأراقب التقدم.
إنه يستعد، دعوني أمضي قُدمًا وأقوم بالتكبير.
نقطة-نقطة-نقطة.
إنه يفحص التعليمات البرمجية الخاصة بي، ويستعد لتقديمها،
الآن يتم تحميلها إلى GitHub.com، وبمجرد وجودها على الخوادم،
ستقوم بإبلاغ خادم CS50، هنا بأنه يتم تقديم كذا وكذا،
سأمضي قدمًا وأشغّل بعض الاختبارات الآلية عليه،
وبالتالي سيتحقق من صحة ذلك، ونأمل أننا على وشك رؤية بعض
الوجوه الخضراء الباسمة والسعيدة، وها هي ذا، نعم،
إنه يبدو مثل أمر check50 ذاك لهذه المشكلة--
أو وحدة الكتلة، إذا جاز التعبير--
تم التحقق من وجود hello.c، لأنه إذا نسيت كتابة الملف
أو إذا أخطأت في تسميته، فلن يعمل أي شيء.
لقد تحققنا من أنه تم تحويله برمجيًا بنجاح،
إذن، سيكون أيضًا، وجهًا أخضرًا سعيدًا.
من الواضح أنه تم فحصه إذًا--
ماذا لو كتبنا في فيرونيكا؟
هل نرى مرحبًا، فيرونيكا؟
على ما يبدو نعم.
ماذا لو كتبنا في كلمة أخرى، براين؟
نعم، من الواضح أننا قولنا مرحبًا، براين.
وهكذا مع احتمالية عالية، أننا سننتهي،
بناء على تلك الاختبارات الأربعة، إلى أن تعليماتك البرمجية صحيحة، في الواقع،
على الأقل فيما يتعلق بتلك المدخلات.
وغالبًا ما توجد تفاصيل أكثر عبر عنوان URL في الأسفل
حيث يمكنك في الواقع رؤية ملاحظات أكثر وبشكل بياني
في تعليماتك البرمجية.
بالطبع، للمرة الأولى، الثانية، الثالثة من المحتمل أنك تقوم بتشغيل هذا الأمر،
وقد لا تشاهد بعض الوجوه السعيدة الخضراء،
أو قد تشاهد بعض الوجوه التعيسة الحمراء أو بعض الوجوه المسطحة الصفراء،
وهي فقط تعني أنه لا يمكننا تشغيل عمليات الفحص لوجود شيء آخر
خاطئ.
لكن مع مرور الوقت، سيساعدك هذا على الشعور براحة أكثر والثقة بأن
تعليماتك البرمجية صحيحة قبل أن تستخدم submit50 وتقوم بتقديمها.
بالمضي قدمًا في ذلك ستشعروا بالتحسن أو بالإحباط قليلاً
لمعرفة ذلك مسبقًا-- انتظروا لحظة، أنا على وشك تقديم هذا ولكن كلا،
إنه ليس صحيحًا بعد.
إذن لابد أن تدركوا أنه سلاح ذو حدين.
أي أسئلة حول check50 أو أي من هذه الأوامر حتى الآن؟
أي شيء على الإطلاق؟
لا؟
حسنًا.
لذلك دعونا نلقي نظرة على الأداة النهائية والأكثر قدرة
وهي الآن متوفرة لكم في بيئة IDE.
تم إنشاؤها في CS50 IDE، والتي تشير إلى بيئة التنمية
المتكاملة، والتي لا تخص CS50-- هذا مصطلح شائع في الصناعة
للأدوات التي تُسهّل كتابة التعليمات البرمجية،
اتضح أن هناك ميزة أخرى بالإضافة إلى القطة في الأعلى هنا.
تحديدًا، التي بها، يمكنك مشاركة مساحة العمل الخاصة بك
مع زملاء التدريس ومساعدي الدورة
لذا ربما يمكنهم مساعدتك في الوقت الحقيقي مثل Google Docs، وحتى في إجراء الدردشة
معك في الوقت الحقيقي.
ولكنه يوفر لك أيضًا ما يسمّى بمصحح الأخطاء.
مصحح الأخطاء، كما يُوحي اسمه، يزيل الأخطاء--
أو بالأحرى، يساعدك في إزالة الأخطاء من تعليماتك البرمجية
عن طريق السماح لك بالوصول إلى printf--
وطباعة الأعداد الصحيحة والسلاسل وأي
شي جيد يحدث في برنامجك، إنه يجعل الأمر تلقائيًا
نوعًا ما ويجعله عملية مملة جدًا بالنسبة لك.
ويتيح لك الانتقال من خلال تعليماتك البرمجية إلى سطر واحد
في آنً واحد على وتيرتك المريحة الخاصة
وترى على طول الطريق جميع قيم المتغيرات الخاصة بك في هذا البرنامج.
لتنشيط مصحح الأخطاء هذا، سأمضي قدمًا وأفعل ما يلي.
سأقوم بالتحويل البرمجي لتعليماتي البرمجية كما جرت العادة مع make hello.
يجب أن يتم تحويلها برمجيًا وإلا قد أرغب في
استخدام help50 وأكتشف لماذا لا يتم تحويلها برمجيًا،
ولكن يبدو أنه تم تحويلها برمجيًا.
والآن سأمضي قدمًا وأقوم بتشغيل debug50، مسافة، ثم
اسم البرنامج الذي أردت تصحيحه من الأخطاء.
واسم البرنامج الذي أردت تصحيحه من الأخطاء في الوقت الحالي
هو ملف الدليل الحالي وهو ما يسمّى مرحبًا.
دعونا نفترض أنه قد يوجد شيء خاطئ به.
في المرة الأولى التي قمت بتشغيل هذا الأمر، رغم أن، debug50
لن يكون راضيًا عني لأنه سيقول،
يبدو أنك لم تقم بتعيين أي نقاط توقف.
قم بتعيين نقطة توقف واحدة على الأقل بالنقر فوق يسار رقم السطر
ثم أعد تشغيل debug50.
حسنًا ما هي نقطة التوقف؟
حسنً هل يقترح الاسم نوعًا ما، أنه يتيح
لك إلغاء أو إيقاف تشغيل التعليمات البرمجية لديك مؤقتًا في أي سطر من سطورك.
وطوال هذا الوقت للأسابيع القليلة الماضية،
تم ترقيم تعليماتك البرمجية في كل سطر تلقائيًا.
وهذا مفيد لأن أكثر سطر مثير للاهتمام في هذا البرنامج،
بمجرد أن يبدأ بالفعل، ليس تلك الاشياء في الأعلى،
ليس int main void، أليس كذلك؟
هذا كله نسخ ولصق من البرامج السابقة.
إنه حقًا السطر السادس هنا حيث لديّ بالفعل بعض المنطق لنفسي.
إذًا في CS50 IDE، ما يمكنك القيام به الآن هو
النقر فوق يسار أحد أرقام الأسطر هذه،
ضوء أحمر صغير مثل علامة التوقف سيظهر قائلاً،
ألغاء أو إيقاف برنامجي مؤقتًا على هذا السطر حتى
يمكنني التدقيق في تعليماتي البرمجية الفعلية.
لا يمكن أن يقوم Sandbox وLab بذلك.
الآن سأمضي قدمًا وأعيد تشغيل debug50 بنفس الطريقة بالضبط، وأضغط على
Enter، ولكن الآن لدي نقطة توقف واحدة.
وسترى على الجانب الأيمن قائمة رائعة منبثقة للتو
بواسطة القطة التي توفر لي مجموعة من الميزات.
وللوهلة الأولى، وبصراحة، إنها زاخمة قليلاً
لأنه يوجد الكثير الذي يحدث هنا، ولكنكم ستلاحظون أولاً،
والأهم من ذلك، أن هناك بعض الإشارات لمتغير الاسم الخاص بي.
لا أفهم تماما 0x0 أو أي شيء، ولكني أفهم سلسلة.
إذًا ما يدركه برنامج debug50 هو أوه، على هذا السطر وأدناه،
لديك متغير باسم اسم.
لا يبدو أن لديه قيمة بعد.
يتضح أن، 0x0، يعني فقط فارغ أو خالي أو صفر.
ولكن هذا أمر جيد، لأنه الآن، عندما أقوم بتنفيذ هذا السطر بالفعل،
آمل أن يأخذ اسم ديفيد أو فيرونيكا أو براين.
دعونا نرى ما سيحدث.
لاحظوا أنه مظلل باللون الأصفر، السطر 6، وهذا يعني أنه
لم يقم بتنفيذ سطر التعليمات البرمجية هذا.
لقد توقفت تعليماتي البرمجية مؤقتًا عند تلك النقطة لأنني قمت بتعيين نقطة الإيقاف تلك.
ثم لاحظوا وجود ما يشبه مُشغّل الموسيقى في الأعلى، وتوجد بعض الرموز.
سيقول زر التشغيل فقط، آه، شغّل برنامجي،
شغّله على طول الطريق حتى النهاية، مثل سكراتش نوعًا ما مع العلم الأخضر.
لكن هذا أكثر قدرةً.
يمكنك تجاوز هذا السطر، وبالتالي تنفيذه مرة واحدة فقط.
إذا كانت دالة، فيمكنك الدخول إلى هذا السطر
والنظر في الحقيقة داخل دالة تستخدمها، مثل get_string،
أو يمكنك الخروج إلى دالة أخرى، ولكن المزيد عن ذلك مرة أخرى.
إذن ما سأفعله هو هذا.
والزر الذي سأقوم بالنقر فوقه كثيرًا عند محاولة فهم
كيفية عمل برنامجي هو هذا--
Step Over.
إذن هو الرمز الثاني من اليسار، بجوار المثلث.
إذن بمجرد أن قمت بالنقر فوق هذا، شاهدوا ما الذي سيحدث،
على الرغم من أنها صغيرة بعض الشيء، على الجانب الأيمن لمتغير اسمي.
لاحظوا أنه تتم مطالبتي بكتابة اسمي لأن البرنامج
ما يزال قيد التشغيل في النافذة الطرفية، ولكن عندما أنقر فوق Enter الآن،
وأقدم اسمي، سترون تلقائيًا على الجانب الأيمن
أن متغير الاسم هذا له قيمة الآن، اقتباس-إنهاء الاقتباس،
"ديفيد" لنوع سلسلة.
يوجد 0x1083010 هذا-- المزيد عن ذلك لاحقًا، فقط مُشفر قليلاً،
ولكن لا يتعين عليّ استخدام printf الآن، يمكنني رؤية ما يحدث بالفعل.
الآن يمكنك رؤية أنه تم تظليل السطر 7،
لأنني قمت بتعيين نقطة توقف فوقه، إذًا أنا الآن في السطر الثاني
لأنني قمت بالدخول فيه.
دعوني أمضي قدمًا وانقر فوق التالي مجددًا، وسترون
ذلك في النافذة الطرفية، تم تنفيذ مرحبًا، ديفيد للتو.
والآن إذا واصلت الاستمرار، سيمضي قدمًا ويعمل للنهاية
ويغلق مصحح الأخطاء.
إذن ليس كل ذلك مفيدًا لهذا البرنامج لأن بصراحة، أنا
متأكد للغاية من أن هذا صحيح، ولكن تلك هي قدرة debug50 ومصحح الأخطاء
بشكل عمومي أكبر حيث يتيح لك، سواء كنت معتادًا بشكل كبير أو صغير،
على التنقل خلال تعليماتك البرمجية في وتيرتك الخاصة تمامًا مثلما قد يقول رفيق التدريس أو مساعد في دورة تدريبية، حسنًا،
ما الذي يفعله هذا السطر؟
لا يتعين عليك اللجوء إلى printf، يمكنك الانتقال فقط بطريقة منهجية
من خلال تعليماتك البرمجية وإيجاد هذا الخطأ اللعين الذي يزعجك
لدقائق أو حتى ساعات.
إذن من الآن فصاعدًا، في أي وقت لديك خطأ في تعليماتك البرمجية التي تتحول برمجيًا
ولكن هذا غير صحيح من الناحية المنطقية-- الهرم في ماريو ليس صحيحًا تمامًا،
تشفيرك لقيصر ليس صحيحًا تمامًا، أو أي شيء آخر،
يجب أن تكون غريزتك الأولى الآن، دعوني أحوله برمجيًا، وأشغّل debug50 عليه،
والمضي خلال التعليمات البرمجية فقط، وتعيين نقطة توقف أينما أريد،
لذا تركز على عدد قليل من السطور، وليس على الأمر برمته--
مثلما فعلت للتو--
وانظروا إذا كان يمكنكم تبين الأمر بشكل منطقي عندما لا تكون القيمة ما كنت تتوقعه،
ثم أوه--
امضي قدمًا وانقر فقط على استئناف، إصلاح الخطأ، وأعد المحاولة.
هذه الأداة الفعّالة.
أي أسئلة؟
أجل؟
ما هو؟
الجمهور: ما الذي يبدو عليه عندما يكون هناك خطأ؟
ديفيد ج. مالان: ما الذي يبدو عليه عندما يكون هناك خطأ؟
إذن لن يجد مصحح الأخطاء الأخطاء ولن يقوم بإظهارها، في حد ذاته.
سيتيح لك مشاهدة ما السطر الذي يتم تنفيذه،
سيتيح لك مشاهدة ما هي المخرجات،
سيتيح لك أخذ المدخل، ولكن كل ما
سيقوم به على الجانب الأيمن هذا هو مجرد عرض
قيم الأشياء على طول الطريق لك.
الأمر متروك لك لتستنتج من تلك المعلومات ما
الذي يسبب الخلل، تمامًا كما كنتم تستخدمون printf في الأسابيع الماضية
لمعرفة ما الذي يجري في برنامجكم.
أي أسئلة أخرى؟
دعوني أقوم بحفظ هذا أيضًا.
من السهل جدا أن تتعودوا على الأمر، وخصوصًا عندما توجد أشياء كثيرة
جديدة على مدار الأسابيع القليلة الماضية من قول، آه،
هذا شيء آخر لنتعلمه.
هذا بدون بذل الكثير من الجهد نوع من الأدوات التي إذا
قضيت بضع دقائق اضافية هذا الأسبوع والأسبوع القادم فقط في استخدامها،
ستشعر بالراحة في استخدامها، وستوفر
عليك ساعات محتملة على المدى الطويل،
لأنه طوال الوقت الذي كنت تقضيه في محاولة
إصلاح الأخطاء لديك أو نشر الأسئلة عبر الإنترنت
ومحاولة فهم الأشياء بشكل يدوي، هذه أداة
إذا استثمرت تلك الدقائق مسبقًا في استخدامها
ستساعدك على فهم كل شيء يجري داخل برنامجك،
وستوفر لك قطعًا خلال الأسابيع المقبلة المزيد والمزيد من الوقت.
حسنًا، أي أسئلة؟ نعم؟
الجمهور: إذن لديكم تكرار حلقي من نوع for loop والذي تم تشغيله لعدد [INAUDIBLE] من المرات،
[INAUDIBLE] قام بفصل عبارات break ومن ثم لا يتعين عليكم [INAUDIBLE]..
ديفيد ج. مالان: آه،سؤال جيد.
إذا كان لديك شيء يشبه تكرارًا حلقيًا من نوع for loop أو من نوع while loop، شيء
يحدث كثيرًا، فهل يمكنكم تعيين نقطة توقف بطريقة
تتوقف فقط بحيث لا يتعين عليكم المرور من خلالها 100 مرة
فقط لرؤية تلك القيمة؟
إجابة مختصرة، نعم.
ودعوني أؤجل قليلاً فقط من هذه الميزات إلى موارد القسم والموارد عبر الإنترنت،
إلا ميزة واحدة، أنكم يمكنكم مشاهدة القيم بالفعل،
ويمكنك الحصول على ما يطلق عليه تعبير watch expression.
يمكنك أن تقول أرني هذه القيمة إذا كانت x أكبر من 50 فقط
أو شيئًا من هذا القبيل.
أو يمكنك أن تضيف بنفسك فقط بعض الأسطر من التعليمة البرمجية.
يمكنكم إضافة دالة، إذا كانت x تساوي-تساوي 50، ثم طباعة شيء ما،
ويمكنك تعيين نقطة توقف على ذلك السطر الجديد، إذا كان سطرًا مؤقتًا،
فتكون هناك إذن طريقتان للقيام بذلك.
سؤال جيد نتوقعه.
نعم؟
هناك في الخلف.
الجمهور: إذا قمتَ بتشغيل debug50، ألستَ تقوم
بإضافة وسيطة أخرى مع [INAUDIBLE] في طريقتك الأساسية في السطر 4؟
ديفيد ج. مالان: سؤال جيد حقًا.
إذا كنتَ تقوم بتشغيل debug50، ألستَ تقوم
بإضافة وسيطة أخرى-- argv-- في مناقشتنا الأسبوع الماضي لوسيطات
سطر الأوامر؟
إجابة مختصرة، لا، لأن debug50 يقوم بالتصحيح لذلك،
لذا لا داعي للقلق بشأن ذلك.
إنها لن تستبق الأشياء عدديًا.
فكرة جيدة حقًا.
أي أسئلة أخرى؟
حسنًا، مع هذا القول، دعونا الآن نخرج بعض عجلات التدريب.
إذن السبب الوحيد لشرائي لعجلات التدريب هذه منذ سنوات
هو جعل هذه النقطة الدراماتيكية جدًا للوقت الحالي لإخراج عجلات التدريب هي
هذا اليوم.
حسنًا، ماذا يعني هذا؟
الأمر يستحق زيارة إلى الهدف.
إذن ماذا يعني هذا؟
خلال الأسابيع القليلة الماضية، كنا نستخدم مجموعة كاملة
من الدوالّ من مكتبة CS50.
وكان من المفترض أن كل هذه الدوالّ كانت مصممة فقط لتجعلها سهلة جدًا، فلنقل نسبيًا،
في الأسابيع القليلة الأولى للحصول على إدخال من المستخدم.
لأنه يتبين، كما سنرى اليوم،
أنها في الواقع تمثل نوعًا ما أمرًا مزعجًا للحصول على إدخال من المستخدمين في C،
وبصراحة، حتى في موثوقية اللغات الأخرى.
لأنكم ستتذكرون أن دالة get_string ودالة get_int وجميع هذه الدوال
تأخذ على عاتقها إعادة مطالبة المستخدم إذا لم يمنحك فعليًا
عددًا صحيحًا int أو إذا لم يمنحك قيمة كسرية float
أو إذا لم يمنحك حرفًا char حسب توقعك، فستقوم هذه الدوال بإعادة المطالبة،
أو تستخدم تكرارًا حلقيًا من النوع while loop أو من النوع do-while loop أو ما شابه،
إذن يوجد فقط كثير من اكتشافات الأخطاء المدمجة في هذه الدوال.
ولكن، الأكثر أهمية-- والأكثر تضليلاً،
هي آخر واحدة في هذه القائمة.
تذكّروا أننا طرحنا منذ أسبوعين من الآن فكرة السلسلة.
وما هو المعنى الحرفي لكلمة سلسلة؟
مصفوفة من الأحرف، عظيم.
إنها تسلسل من الأحرف، ونحن تعلمنا الأسبوع الماضي أنه يمكن تنفيذ
تسلسل في مصفوفة، وهي مجرد مجموعة في الذاكرة
مصفوفة على التوالي.
إذن فالسلسلة، رغم ذلك، ليست تمامًا كأي من أنواع البيانات الأخرى تلك.
يتضح أنها ليست تمامًا كعدد صحيح int أو حرف char أو حتى قيمة منطقية bool أو قيمة كسرية float،
ويمكننا أن نبدأ في رؤية ذلك الآن على النحو التالي.
سأمضي قدمًا و أنتقل إلى IDE اليوم--
وسنمضي من الآن فصاعدًا فقط لبدء استخدام IDE،
ولكن لا مانع من استمرارك في استخدام أداة تحديد الوصول للبرامج السريعة والسيئة،
ولكن لأي شيء تريد البقاء حوله،
ينبغي أن تخبرك غريزتك الآن بفتح IDE الخاص بك.
وسأمضي قدمًا وأُنشئ ملفًا جديدًا،
وسأسميه compare0.c من مثالي الأول الخاص بمقارنة الأشياء.
وسأمضي قدمًا وأقوم بتنشيط برنامج قصير نسبيًا
ستأملون أن يعمل على الفور.
إذن سأمضي قدمًا وأقوم بتضمين cs50.h المألوف.
سأقوم بتضمين stdio.h.
سأمضي قدمًا وأجعل int main لاغيًا.
سأمضي قدمًا وهنا--
دعوني أنشىء متغيرًا يسمى i باستخدام get_int من المستخدم،
ومطالبته فقط بالمتغير i.
دعوني أمضي قدمًا وأطالب المستخدم بدالة get_int أخرى.
سنسميها j ونحصل على تلك الدالة منه.
ثم دعونا فقط نقارن هذه الأشياء.
لذلك إذا كان i يساوي-يساوي j، إذن فلنمضِ قدمًا ونقوم بطباعة
باستخدام printf نفسه وسطر جديد.
ثم أمضي قدمًا وأطبع المقابل، والذي يكون مختلفًا.
وبالتالي فإن المكان الوحيد الذي أظن أني أفسدتّه، ربما،
يكون إذا فعلت هذا، وهو معقول نوعًا ما إذا كنتَ
في طريقك لمعرفة ما تعنيه علامة يساوي.
ولكن مجدّدًا، في التعليمة البرمجية، نحن نحتاج عادةً إلى علامتي يساوي
لأن ذلك يقارن بين قيمتين.
لذلك لم أقع في ذلك الخطأ، ولديّ شعور جيد جدًا حول هذا الأمر.
دعوني أحفظه باستخدام الأمر Command-S أو Control-S أو عن طريق File،
Save؛ وأنتقل إلى مطالبتي وأشغّل make compare0.
عظيم، كل شيء تم تحويله برمجيًا.
ودعوني أمضي وأشغّل compare0، إدخال، وسأقوم بكتابة 50،
وسأكتب 50، ويبدو أنها هي نفسها.
دعوني أمضي قدمًا ونفعل ذلك مجدّدًا، ودعونا نكتب 42 و13،
وهما مختلفان.
وعلى الأرجح ينبغي عليّ اختبار المزيد قليلاً، ربما بعض القيم السالبة، وربما بعض
الأصفار، والقيم الموجبة وما شابه، ولكن لديّ
شعور جيد للغاية حول مدى صحة هذه التعليمة البرمجية.
حسنًا.
إذن دعونا نغير هذا البرنامج قليلاً.
دعوني أمضي قدمًا وأنشىء ملفًا آخر، والذي
يمكنني القيام به بواسطة علامة زائد الخضراء الصغيرة أو عن طريق قائمة File، New File.
سأمضي قدمًا وأحفظ هذا كـ compare1.c.
وفي الوقت الحالي سأمضي قدمًا وأقوم فقط بلصق تلك التعليمة البرمجية
المنسوخة من قبل، لكنني سأجري بعض التغييرات الآن.
سأمضي قدمًا وأعيد تسمية أنماط بياناتي وأعيد كتابتها كسلاسل.
إذن يمنحني سلسلة تسمى s، وستطالب المستخدم
من أجل ذلك باستخدام get_string، ثم سأمضي
قدمًا وأغير هذا الواحد إلى السلسلة t،
وسأمضي قُدمًا وأحصل على get_string.
أنا، بالطبع، بحاجة الآن لمقارنة s وt، وليس i وj.
و s هو اسم متغير مشترك لسلسلة. وتأتي t فقط بعد s،
فهي لذلك معقولة للغاية أيضًا، لكن ينبغي عليّ بالطبع تحديث ذلك أيضًا.
ولذلك أعتقد أن كل شيء الآن هو متشابه منطقيًا.
غيرتُ للتو أنواع بياناتي وأسماء المتغيرات لديّ.
لذا قمتُ بحفظ هذا.
دعوني أمضي قدمًا، وأشغّل make compare1.
عظيم، كل شيء صحيح.
دعوني أمضي قدمًا، وأشغّل ./compare1.
دعوني أمضي قدمًا وأكتب Brian وVeronica.
وبالطبع، فهما مختلفان.
والآن دعوني أمضي قدمًا وأكتب David، ثم دعوني أكتب Da vid مجدّدًا،
وهذان بالطبع مختلفان؟
ها.
ربما لأنني ضغطت على شريط المسافة أو شيء ما.
إذن دعونا نجرب Erin.
اسمها أقصر قليلاً.
امم.
حسنًا، دعونا نجرب-- أوه، ما اسمها؟
TJ.
حسنًا، اسم أكثر قصرًا، رائع.
TJ، لا يمكن أن يكون خاطئًا.
مختلفان.
أعني، ما الذي يحدث؟
دعونا فقط نقول i،i.
مختلفان؟
إذن أين هو الخطأ المنطقي في هذا البرنامج؟
ما هذا الذي يجري؟
أجل، ما رأيكم؟
الجمهور: هل يقارن قيم أعداد صحيحة؟
ديفيد ج. مالان: هل يقارن قيم أعداد صحيحة؟
حسنًا من الممكن.
أعني، حتى الآن عندما استخدمنا يساوي-يساوي
فعلى الأرجح استخدمناها غالبًا لمقارنة أعداد صحيحة،
إذن ربما أسأتُ استخدامه، بالتأكيد.
أي أفكار أخرى؟
الجمهور: [INAUDIBLE]
ديفيد مالان: أوه، هذه كلمة كبيرة سنصل إليها بعد قليل.
لكنها صحيحة، صحيحة-- ولكن لأسباب متشابهة جدًا.
إذن، شيء ما يحدث منطقيًا يتعلق بالمقارنة،
لأنني أستخدم يساوي-يساوي، ولكن ربما أستخدمه لأنواع البيانات الخاطئة؟
أعني، أنه من الواضح أنه مقسم لسلاسل.
فلماذا يحدث ذلك فعليًا؟
حسنًا، يتبين أن السلاسل غير موجودة بالفعل.
إذن فالسلسلة التي نعرفها هي مجرد تسلسل من الأحرف
أو مصفوفة من الأحرف لا تعتبر نوع بيانات فعليًا.
بينما يعتبر int، وfloat، وdouble، وlong ،وbool، وأكثر من ذلك
هي أنواع بيانات فعلية.
فالسلسلة هى كذبة بيضاء نوعًا ما كنا
نقولها لبضعة أسابيع ويتم تنفيذها فقط في مكتبة CS50.
الآن كلمة سلسلة هي شائعة للغاية في البرمجة.
تقريبًا كل مبرمج بالخارج سيعرف ما الذي تعنيه عندما تقول سلسلة.
إنها ليست كلمة CS50، ولكن استخدامنا لها في لغة C هو خاص بـ CS50.
لأنه في ذلك الملف المسمّى cs50.h، بالإضافة إلى
إعلان دوالّ مثل get_string وget_int وget_float
ومجموعة من الأشياء الأخرى، لدينا أيضًا سطر خاص يقول،
إنشاء نوع بيانات يسمى سلسلة.
ولكن ما الذي يفعله حقيقةً أو ما الذي يعنيه حقيقةً؟
حسنًا دعونا نمضي قدمًا ونفكر فيما قد يحدث بالتفصيل
هنا.
لذلك إذا مضيتُ قدمًا ورسمتُ البرنامج الذي قمنا
بتشغيله للتو، فيحصل ذلك البرنامج compare1 على سلسلة s من المستخدم،
ثم يحصل على سلسلة t من المستخدم، ومن ثم يقارن بينهما.
إذن نعرف من الأسبوع الماضي ما هي السلسلة، إنها مجرد مصفوفة.
إذن، عندما أشغّل ذلك السطر الأول من التعليمة البرمجية وأحصل على سلسلة من المستخدم--
على سبيل المثال، Brian، فسأمضي قدمًا وأرى B-R-I-A-N،
والتي نعرف من الأسبوع الماضي أنها تعتبر بالفعل مصفوفة في الذاكرة قد تبدو
مثل هذه من الناحية التصويرية-- وهذه، أيضًا، كذبة بيضاء نوعًا ما
فهناك شيء آخر.
الجمهور: الفارغ.
ديفيد مالان: نعم، الحرف الفارغ، إذا جاز التعبير، و ul،
التي نكتبها عادةً فقط باستخدام /0، وهو عبارة عن كل أصفار البت تحديدًا.
ويتضح، ربما تذكرون من مصحح الأخطاء في وقت سابق، أنكم رأيتم هذا--
تلك هي الطريقة الأكثر تشفيرًا للتعبير عن الحرف الفارغ،
/0.
فقط برامج مختلفة تعرضها بطرق مختلفة.
إذن عندما أحصل على دالة get_string وأكتب Brian، فهذا هو ما يتم تخصيصه في الذاكرة.
وعندما أكتب Veronica، يمكنني رؤية سلسلة V-E-R-O-N-I-C-A.
سأحصل على تلك السلسلة مباشرة على نحو استباقي.
/0.
تلك، أيضًا، هي مجموعة في الذاكرة، سأرسمها على هذا النحو.
1، 2، وأقسّم هذه الأحرف إلى أحرف أو وحدات بايت كفواصل.
وتذكرون من آخر مرة أن وحدات البايت هذه تأتي تحديدًا من الذاكرة الموجودة لديّ،
وأن تلك الذاكرة لديها فقط مجموعة من وحدات البايت بداخلها، ربما الملايين أو حتى
المليارات في الوقت الحالي.
إذن بصراحة، إذا كانت لديك فقط تلك الأشياء العديدة،
يمكن لأي إنسان أو جهاز كمبيوتر بالتأكيد ترقيمها.
على هذا النحو بايت 1، 2، 3، 4.
إذن دعونا نفترض فقط بغرض النقاش
خارج سياق المكونات المادية بجهاز الكمبيوتر الخاص بي،
أن Brian انتهى للتو عند الموقع 100، والموقع 101، و102، 103، 104، 105.
إذن هذا هو البايت رقم 100 في جهاز الكمبيوتر الخاص بي،
وهذا هو البايت رقم 105 في جهاز الكمبيوتر الخاص بي، وBrian
يستخدم تلك الأحرف العديدة في المجمل.
Veronica، انتهى بها المطاف في مكان آخر.
ربما انتهى بها المطاف لأبعد من ذلك فقط لأنه في الموقع 900، 901، 902، 903،
904، 905، 906-- ذاكرة أكبر كثيرًا، 907، و908--
ولكن يمكنكك الرؤية بشكل مرئي أكثر الآن أن طول اسم Brian--
strlen "طول السلسلة" الخاص بـ Brian ما هو؟
الجمهور: [INAUDIBLE]
ديفيد مالان: أسمع خمسة وأسمع ستة.
طول اسم Brian--
Brian، كم يبلغ طول اسمك؟
الجمهور: خمسة.
ديفيد مالان: حسنًا، إنه بالتأكيد خمسة أحرف، هذا
هو طول اسم Brian، ولكن يتعين عليك
أن تقدّر ذلك في الكمبيوتر، فاسم Brian بطول خمسة أحرف في الواقع
يستهلك ست وحدات بايت.
إذن كلتا الإجابتين صحيحتان نوعًا ما، ولكن طول السلسلة من الآن فصاعدًا
سيكون دائمًا هو عدد الأحرف الفعلية.
مقدار المساحة الذي تستهلكه هو ذلك الطول زائد 1 للحرف الفارغ.
إذن يمكنك بالفعل أن ترى لماذا يستهلك اسم Brian ست وحدات بايت في هذه الصورة
بدلاً من الطول الفعلي فقط، والذي هو خمسة.
لذلك عندما تطلق اسم get_string الآن، وعندما تطلق اسم
get_string وget_string أخرى--
Brian وVeronica على الترتيب، فما الذي تتم إعادة تسليمه بالفعل مجددًا؟
قبل أسبوعين، ظهرت Erin وقامت نوعًا ما بإعادة
تسليم سلسلة إليّ، اسم طالب من الجمهور.
اعتقدنا أن اسم الطالب موجود على تلك الورقة.
لكن الأمر ليس كذلك.
يتضح أنه عندما تقوم دالة بإرجاع قيمة،
فيمكنها فقط إلى حد كبير إرجاع 1 بايت أو ربما 2 أو 4 بايت.
ولا يمكنها إرجاع عدد مهول من وحدات البايت، مثل ستة لـ Brian أو 1، 2، 3،
4، 5، 6، 7، 8، 9-- لا يمكنها إرجاع 9 وحدات بايت لـ Veronica.
وإذا قمتَ حتى بكتابة فقرة كاملة أو صفحة من نص،
فلا يمكنها إرجاع كل ذلك النص، ويمكنها فقط إرجاع قيمة واحدة.
إذن، وفقًا لغريزتك فيما سبق، ما الذي
ربما يتم إرجاعه بواسطة get_string عندما يكتب الشخص
اسمًا مثل Brian أو Veronica؟
الجمهور: [INAUDIBLE]
ديفيد مالان: موقع الذاكرة.
في الحقيقة، عدد صحيح، أو كما تسمّونها، مؤشر،
وسنقوم بتقديمه بطريقة رسمية بدرجة أكبر في غضون لحظات.
لذلك عندما تقوم السلسلة get_string بإرجاع "Brian،" علامة اقتباس-إنهاء الاقتباس،
فإنها في الواقع لا تقوم بإرجاع B-R-I-A-N /0، إنما تقوم فقط بإرجاع 100.
وعندما تقوم السلسلة get_string بإرجاع Veronica، فإنها لا تقوم بإرجاع اسمها،
وإنما تقوم بإرجاع 900.
وبذلك إذا أدركت ذلك الآن، فعندما تقوم
بالسلسلة s يساوي يساوي t، فما هو السؤال الأكثر اعتيادًا الذي تسأله فعليًا؟
نعم.
موقع الذاكرة وموقع الذاكرة-- هل 100 تساوي 900؟
وبالطبع لا.
وذلك هو سبب أن اسم Brian، واسم Veronica،
واسمي، واسم TJ-- كل كلمة كتبتها كانت بالطبع مختلفة،
نظرًا لأن كل مُدخل كان ينتهي في موقع مختلف في الذاكرة.
وحتى لو قمتُ بكتابة الكلمة نفسها مثل David مرتين، فكلمة David الأولى كانت تنتقل إلى هنا،
وكلمة David الأخرى انتقلت إلى مكان آخر، فكانتا ينتهي بهما المطاف
في موقعين مختلفين في الذاكرة.
ربما 100، ربما 900، ربما مكان آخر،
لكن انتهى بهما المطاف في موقعين مختلفين في الذاكرة.
إذن فالدالة يساوي-يساوي تقارن بين القيم، ولكن تبًّا
لها إذا لم تكن تقارن القيم الخاطئة.
أجل؟
الحضور: حسنًا ماذا لو استخدمت دالة char*s؟
ديفيد مالان: آه، إذن سنعود لتلك النقطة.
دعوني أعود لتلك النقطة بعد لحظات.
في الواقع أن الدالة char* مرتبطة بطريقة معقدة.
المزيد حول ذلك في غضون لحظات.
أجل؟
الحضور: إذا قمت بإضافة عددين صحيحين في الذاكرة--
ديفيد مالان: آها؟
الحضور: ألن يكونا في مكانين مختلفين في الذاكرة؟
إذن ستقوم بالإرجاع--
لذلك تحتاج إلى قيمة مختلفة.
ديفيد مالان: حسنًا، سؤال جيد حقًا.
إذن انتظروا دقيقة، هذا المنطق نفسه أنني أقوم بإرجاع عنوان شيء ما
ينطبق بالتأكيد على الأعداد الصحيحة كذلك أو قيم النقطة الحرة floating كذلك؟
لأنني إذا كتبتُ الرقم 50 مثلما
فعلتُ سابقًا، ذلك، أيضًا، في مكان ما في الذاكرة-- مثل مربع في الذاكرة،
وذلك، أيضًا، يحتوي على عنوان في مكان ما في الذاكرة،
ولكن يتضح، وذلك لأسباب أشرتم إليها للتو، في الواقع،
أنه يتم إرجاع الأعداد الصحيحة ints كقيمها.
ويتم إرجاع الأحرف Chars كقيمها.
ويتم إرجاع القيم المنطقية Bools كقيمها.
ويتم إرجاع القيم الحرة Floats كقيمها.
بينما تكون السلاسل مختلفة.
حيث يتم إرجاع السلاسل Strings حسب عناوينها.
وتلك العناوين، كما يتضح، سيتم تسميتها في نهاية المطاف
أحرف char*، والتي سنراها في غضون لحظات.
إذن، كيف نمضي ثم نصلح هذا بشكل جذري؟
تقريبًا حتى لو لم تكن لديك فكرة عن كيفية تشفير هذا بعد، فبديهيًا،
إذا كنت فعلاً أرغب في حذف--
إذا كنت فعلاً أرغب في مقارنة--
معذرةً.
حسنًا.
إذا أردت المضي قدمًا ومقارنة تساوي Brian وVeronica،
فما الذي أرغب في فعله بديهيًا؟
فلا يمكنني مجرد مقارنة عنوانهما.
ماذا عليّ أن أفعل؟
أعزل الأحرف ثم ماذا أفعل بها؟
الجمهور: [INAUDIBLE]
ديفيد مالان: جيد.
أجل، بديهيات جيدة.
استخدم تكرارًا حلقيًا من النوع for loop، استخدم تكرارًا حلقيًا من النوع while loop-- أي نوع من بنية التكرار الحلقي.
وبديهيًا، قارن الأحرف الأولى،
واذا كانت مختلفة، فمن ثم نعرف أننا لا يتعين علينا المضي إلى أبعد من ذلك.
B ليست V، لذلك بالتأكيد فهذان الاسمان مختلفان.
لكن ماذا عن الحالة الموجودة لديّ؟
إذا كانت الحالة هي David وDavid، وكنت ستقارن أول حرفين.
D وD متشابهان.
قارن التاليين، A وA متشابهان.
V وV، ثم I وI، ثم D وD، ثم ما الذي سأضغط عليه أخيرًا؟
حرف فارغ.
وهل يجب عليّ المتابعة إلى ما بعد الحرف الفارغ؟
لا.
إذن هذا هو جمال ذلك التصميم الرائع البسيط للسلسلة.
بقدر ما يتم تحديد السلاسل حسب عنوان بدايتها، فإن وحدة البايت فقط
التي تبدأ منها، ستظل بحاجة لمعرفة
طولها، لأنه بطريقة تشغيل خلاف ذلك فأين تبدأ الكلمة الواحدة وأين تنتهي
وأين تبدأ كلمة أخرى؟
وبالتالي فإن القرار البسيط الذي اتخذناه الأسبوع الماضي-- كما فعل الناس منذ عقود--
لإنهاء جميع السلاسل باستخدام /0 أو كل الأصفار هو خدعة سهلة للغاية،
بحيث إذا قلت لكم أن Brian يبدأ عند 100،
فيمكنكم استنتاج أين انتهى؟
عند رقم البايت 105 أو 104، إذا شئت، أيًا كانت الطريقة التي ترغب في التفكير بها،
لأن كل ما عليك القيام به في الزمن الخطي،
إذا أردت، يسارًا أو يمينًا، هو التحقق-- /0، /0-- آه!
/0، الآن أعرف مدى طول اسم Brian.
لذلك دعونا نفكر للحظة في هذا البرنامج المسمى string length "طول السلسلة".
كيف يعمل طول السلسلة strlen فعليًا؟
عندما تنتقل إلى طول السلسلة strlen، متغير يحتوي على سلسلة، مثل Brian،
ما الذي يحتمل أن يفعله طول السلسلة strlen؟
الجمهور: [INAUDIBLE]
ديفيد مالان: بالضبط.
إنه ينظر إلى عنوان ذلك الحرف الفارغ
وطرح عنوان البداية وعنوان النهاية،
واستنتاج الفرق وإرجاع
تلك القيمة فعليًا ناقص 1 من العد الإجمالي.
وعلى نحو ميكانيكي بدرجة أكبر، سترون في غضون لحظات،
أنه ربما يفعل بالضبط الشيء نفسه الذي فعلتُه،
وهو، هل هذا /0؟
هل هذا /0؟
هل هذا، هل هذا، هل هذا؟
سألتُ هذا السؤال خمس مرات قبل أن أرى /0.
strlen هى مجرد دالة كتبها بعض الأشخاص منذ سنوات
والتي يُحتمل أن يكون بها تكرار حلقي بسيط من النوع for loop وشرط if،
ومن ثم فهذا كل شيء.
لأن هذا الشخص أدرك حتى من قبل
أن نُشغّل كيفية تنفيذ السلاسل فعليًا.
أي أسئلة أخرى؟
حسنًا، إذن دعونا ننفّذ هذا بالفعل.
دعوني أمضي قدمًا إلى المحرر الموجود لديّ هنا، وأقدم مثالاً آخر هنا
سأسميه compare2.
سأمضي قدمًا وأقوم بتضمين cs50.h وأقوم بتضمين stdio.h،
ثم سأجعل int main لاغيًا، وسأقوم
الآن بالتقاط التعليمة البرمجية الخاصة بي بسرعة مما سبق حيث حصلت على سلاسل
وقمت بمقارنتها، ولكن يتعين عليّ تثبيت تلك المقارنة.
إذن، ها هي التعليمة البرمجية الخاصة بي مما سبق.
سأقوم بتنفيذها بالطريقة الصحيحة.
سأسستدعي دالة اسمها compare_strings تمر في s وt.
لأنه كما اقترحتم، نحتاج لتنفيذ دالة منطقية ما.
لا يتعين علينا تمريرها إلى دالة، ولكن يمكننا ذلك.
يمكننا فقط إجراء تكرار حلقي من النوع for loop هنا، ولكنني
سأمضي قدمًا وأنفذ سلاسل compare_strings على النحو التالي.
إذا رغبت في كتابة دالة تقوم بإرجاع إجابة نعم/ لا، فما نوع البيانات
التي يجب أن ترجعها؟
تعبير منطقي bool.
إذن لا يتعين علينا بالضرورة فعل ذلك بعد، لكن
يمكنك إرجاع قيمة منطقية bool مثلما يمكنك ذلك مع عدد صحيح int أو حرف char أو أي شيء آخر.
سأسمي هذه الدالة باسم compare_strings.
ستستوعب سلسلة تسمّى a وسلسلة أخرى تسمى b،
لكن يمكنني تسميتهما بأي اسم أريده.
والآن ما هو أسهل شيء يمكن التحقق منه؟
إذا قمت بتمرير سلسلتين، a وb، أو Brian وVeronica،
فما هو أسهل سؤال يمكنك طرحه وقوله تحديدًا على الفور، كلا،
هل هذان مختلفان؟
طول السلسلة، أليس كذلك؟
كما لو أن B-R-I-A-N ليس بنفس طول اسم Veronica،
فلسنا بحاجة للقيام بأي دالة منطقية على الإطلاق أبعد من ذلك،
يمكننا فقط الخروج وقول خطأ.
إذن دعوني أفعل ذلك.
إذا كان طول السلسلة strlen لـ a لا يساوي طول السلسلة strlen لـ b، أتعرفون ماذا إذن؟
دعونا نمضي قدمًا ونقوم بإرجاع خطأ وإخراجه هنا.
حسنًا، ولكن الآن، إذا تجاوزنا تلك البوابة، إذا جاز التعبير،
ذلك التحقق، ذلك السؤال، ذلك التعبير المنطقي،
فيتعين عليّ الآن مقارنة الأشياء حرفًا بحرف بحرف.
حتى يمكنني القيام بذلك بعدة طرق، لكني أحب اقتراح تكرار حلقي من النوع for loop.
إذن بالنسبة للعدد الصحيح int i يصل إلى 0، حيث n تشير إلى الفعالية-- ففي الواقع،
دعونا ننفّذ i أقل من طول السلسلة--
فهل ينبغي عليّ تنفيذ طول السلسلة a أم b؟
لا يهم، أليس كذلك؟
إذن دعونا نختار a.
وبصراحة، لو كنتُ ذكيًا في وقت مبكر،
لتمكنتُّ من تخزين القيمة في متغير ثم إعادة استخدامها،
لكننا سنواصل المضي قدمًا الآن.
ثم i زائد-زائد، ولكن أتذكر من آخر مرة-- أن هذا صحيح،
لكنه ليس تصميمًا جيدًا.
لماذا؟
نعم، أتابع تسمية طول السلسلة strlen مرارًا وتكرارًا، لأنه لو تذكرون، في التكرار الحلقي من النوع for loop،
يتم التحقق من هذا الشرط مرارًا
وتكرارًا-- فأنتم تضيعون وقتكم.
لذا دعوني أمضي قُدمًا وأقوم بهذا بالفعل.
n أو أي متغير يساوي طول السلسلة strlen لـ a، ثم نقارن فقط i مع n،
لأن الآن i تزيد، ولكن n لا تتغير أبداً.
إذن دعوني أمضي قدمًا وأنفّذ هذا للتكرار الحلقي.
إذن لو-- ماذا لو أن الحرف صاحب الترتيب رقم i لـ a لا يساوي الحرف صاحب الترتيب رقم i
لـ b، يمكنني أن أستنتج على الفور
كلا، لا يمكن أن تكون هاتان السلسلتان متشابهتين، لأن حرفًا ما، مثل حرف B،
ليس متشابهًا كحرف آخر، مثل حرف V، أو أي حرف نقوم
بمقارنته بالفعل.
ومن ثم أعتقد أن هذا كل شيء.
إذا تجاوزتُ تحديات الأسئلة هذه--
فهل أطوال السلاسل لديك مختلفة؟
هل الأحرف لديك مختلفة؟
وأنا ما زلتُ لم أقل خطأ، فما الذي ينبغي عليّ إرجاعه افتراضيًا؟
نعم.
كما لو أنك قمت بكل هذا من خلال جميع تلك الأسئلة وسيكون كل شيء على ما يرام،
إذًا يجب أن يعادل د-ي-ف-ي-د بالفعل د-ي-ف-ي-د أو أيًا كان ما كتبه
المستخدم بالفعل.
الآن أنا لم أنتهِ تمامًا بعد.
عندما انتهيتُ من تنفيذ دالة أو دالة مساعدة
كهذه، لأنها تساعدني في القيام بعملي،
ماذا يجب أن أضيفه أيضًا إلى الملف؟
أوه؟
الجمهور: لديّ سؤال منطقي.
ديفيد مالان: بالتأكيد.
الجمهور: في جهاز الكمبيوتر، ألا يمكنك فقط كتابة David بحرف D كبير
ثم david بحرف d صغير، وستقوم بتشغيل [INAUDIBLE]،
فلن يتزامنوا لأن أول حرف ليس
نفس الحرف.
ديفيد مالان: صحيح.
لذلك هذه ميزة، وليست خطأ في الوقت الراهن.
برنامجي في هذه اللحظة حساس لحالة الأحرف.
إذا كتبتُ DAVID وجميع الأحرف كبيرة، فهذا يعني أنها سلسلة مختلفة
أزعم في الوقت الحالي أن david مكتوبة كلها بأحرف صغيرة.
إذا كنت تريد تجاوز حالة الأحرف الكبيرة والأحرف الصغيرة،
فيجب عليك جعل الأمر منطقيًا أكثر.
ولكن في الوقت الراهن هذا هو قرار التصميم الذي أريده.
حسنًا.
ما الذي أحتاج إلى إضافته إلى الملف أيضًا؟
نعم ، النموذج الأولي في الأعلى.
يمكنك النسخ واللصق حرفيًا-- هذه هي المرة الوحيدة التي يكون فيها النسخ واللصق
الشيء الصحيح للقيام به--
في الأعلى، ثم فاصلة منقوطة-- لا تقم بتنفيذ ذلك مجددًا.
لكنني أحتاج إلى ملف رأس واحد آخر.
أنا أستخدم دالة ليست في cs50.h أو في stdio.h.
طول السلسلة؟
أين كان طول السلسلة؟
نعم، string.h.
لذا أنا بحاجة فقط إلى هذا، بما في ذلك string.h، حفظ.
الآن أعتقد أن هذا صحيح.
سنرى ما إذا كنت أتذكر الكلمة الآن.
ولكن اعلموا أنه إذا كنتم تكتبون هذه التعليمات البرمجية بأنفسكم،
كهذه، فلن تكون كتابة برنامج في ساعات العمل
أو في المنزل في غرفة نومكم وإنهاء ذلك بشكل صحيح من المرة الأولى أمرًا طبيعيًا.
لقد وصلت إلى هذا بعد 20 عامًا من القيام بذلك، لذلك اعلموا أن هذا سيحدث--
ولدي أيضا ورقة غش هنا--
سيحدث أن نقوم بهذا بطريقة صحيحة غالبًا،
ولكن لابد أن تدركوا أنها لن تكون مسألة مشتركة.
لذا لنضع ذلك في الاعتبار، لنرى
إذا كان يجب عليّ الآن تناول كل ذلك مجددًا. make compare2.
حسنًا-- بكل سرور.
لقد أثمرت تلك 20 عامًا.
لذا الآن سأمضي قدمًا وأقوم بكتابة ./compare2.
دعونا نكتب في براين، دعونا نكتب في فيرونيكا.
آمل أن هؤلاء في الواقع ما يزالون مختلفين.
الآن دعونا نجرب، ديفيد وديفيد.
بكل سرور!
إنهم نفس الشيء.
والنقطة التي أشرت إليها، ديفيد مكتوبة بالأحرف الكبيرة وديفيد بالأحرف الصغيرة،
مختلفين، لكن هذا ما أتوقعه الآن.
أي أسئلة على compare2؟
أجل؟
الجمهور: [INAUDIBLE]
ديفيد مالان: حسنًا.
الجمهور: [INAUDIBLE] سلسلة في البرنامج وبشكل عام.
ديفيد مالان: حسنًا.
الجمهور: هل ما يزال هذا يعمل [INAUDIBLE]
ديفيد مالان: إذا كنت تشير إلى التعليمات البرمجية الصلبة للسلاسل؟
إجابة مختصرة، نعم، التي ما تزال تعمل.
إذا كنت لأي سبب لم تقم بذلك واستخدمت get_string،
لكن يمكنك كتابة ديفيد، وهنا، على سبيل المثال، ديفيد، هذا من شأنه أن يعمل أيضًا.
ومهما كان خطأك، إذا تمكنت من إعادة إنشاؤه، فقط أخبرنا بذلك.
الجمهور: يبدو أنه مثل سلسلة يمكن أن تزيد
لمجموعة كانت [INAUDIBLE] فقط؟
ولديها مشاكل في [INAUDIBLE].
ديفيد مالان: يجب أن أراه لأتأكد، وسأكون سعيدًا بالتحدّث بعد ذلك.
حسنًا، دعونا نرى إذا لم نتمكّن الآن من تنظيف هذا
قليلاً فقط على النحو التالي.
دعوني أمضي قدمًا وأكتشف ماذا يحدث في الواقع.
لذا بالفعل، لا يوجد شيء مثل سلسلة.
وبالفعل، كما أشرت سابقًا منذ قليل،
إنه يحدث في الواقع تحت اسم مختلف.
السلسلة هي مجرد مرادف لما يسمى بـ char*.
الآن ما الذي يعنيه ذلك؟
إذن char هو نفسه كما كان دائمًا.
إنه حرف واحد.
قد تعني النجمة في برنامج مكتوب في C بالطبع الضرب،
لقد رأينا ذلك.
إن هذا استخدام آخر للنجمة.
عندما تراها بعد نوع من البيانات مثل char،
هذا يعني أن نوع البيانات في السؤال ليس مجرد char،
إنه عنوان char.
لذا فإن النجمة تعني فقط عنوان نوع البيانات أيًا كان إلى اليسار،
وهذا هو، كما أشرتم سابقًا، ما
سنبدأ في تسميته مؤشر.
يُعد المؤشر، لجميع النوايا والأغراض، عنوانًا.
إنه مجرد كلمة طنانة لوصف عنوان.
يشير نوع البيانات هذا هنا، char*، إلى أنني أريد متغيرًا لا يُخزِّن char،
إنه يُخزِّن عنوان char.
الرقم 100، الرقم 900.
لكن هذا العنوان سنسميه فقط مؤشر.
متغير المؤشر هو متغير يُخزِّن عنوان شيء ما.
Char أو أنواع البيانات الأخرى أيضًا.
لذلك مع أخذ ذلك في الاعتبار، دعوني في الواقع وبسرعة أقوم بإنشاء compare3.c، ولصق هذا
وحفظه كـ compare3.c، ودعوني أخرِج، إذا صح التعبير،
عجلات التدريب تلك.
يتضح أنه عندما تحصل على سلسلة باستخدام get_string،
فهو لا يعيد سلسلة، في حد ذاته، لأن مجددًا،
هذه الكلمة غير موجودة في C، فهو يقوم فعليًا بإرجاع char*.
وعندما أكتبها هنا مجددًا وأقوم بإرجاع سلسلة أخرى، فإنه يعيد
char* أيضًا.
الآن من الناحية الفنية، يمكن أن توجد مسافات حول النجمة.
يكتب بعض الناس شيء كهذا، ولكن نوعًا ما الطريقة الصحيحة للقيام بذلك
أو الطريقة الافتراضية يجب أن تكون فقط بوضع النجمة بجانب الاسم المتغير
للتوضيح.
لذلك يجب أن أجري بعض التغييرات الأخرى.
يجب أن يتغير هذا أيضًا، لأنه لا يوجد المزيد من السلاسل اليوم.
سأقوم بتغيير هذا إلى char*؛ ومن ثم أحتاج أيضًا إلى تغييره هنا،
char*؛ ثم هنا، char*؛ وهذا هو في الواقع.
وبصراحة، السبب الوحيد وراء عدم تقديمنا هذا منذ أسبوعين تقريبًا
هو لأنه يبدو مشفرًا فقط.
لا أحد يريد أن يبرمج في أول مرة يلمس فيها لوحة المفاتيح
ويكتب تعليمة برمجية ويرى char* ويقلق حول ما يعنيه ذلك،
إنها مجرد سلسلة من الناحية النظرية.
لكن التغيير الوحيد الذي أحتاج إلى إجرائه من الناحية الفنية لإخراج عجلات التدريب تلك
هو فقط تغيير جميع إشارات السلسلة كأنواع البيانات إلى char*.
وهذا يعني ذلك فقط أتعلمون ماذا-- a؟
نعم إنها سلسلة، لكن من الناحية الفنية إنها عنوان السلسلة.
أو بشكل أكثر دقة، هي عنوان أول بايت من السلسلة،
مثل 100 لبراين أو 900 لفيرونيكا، وأنا لن
أخبرك أين تنتهي السلسلة لأنك، كمبرمج،
يمكنك اكتشاف ذلك عن طريق كتابة strlen أو باستخدام التكرار الحلقي فقط
واكتشاف مكان خط مائل عكسي 0 في الواقع.
إذن هذه معلومات كافية لتمريرها.
لذا إذا مضيت قدمًا الآن وقمت بتحويل هذا برمجيًا، make compare3،
ومن ثم مضيت قدمًا وقمت بكتابة ./compare3، لنمضي قدمًا ونكتب في براين
وفيرونيكا، وهم في الواقع مختلفان.
الآن دعوني أمضي قدمًا واكتب في ديفيد وديفيد، وهما في الواقع الشيء نفسه.
لذا عجلات التدريب متوقفة، لا يوجد أي شيء كسلسلة،
من الآن فصاعدًا إنها char*.
دعونا نمضي قدما ونأخذ استراحة سريعة هنا لمدة خمس دقائق،
وسنعود ونتعمق في الأمر أكثر.
حسنًا.
لقد عدنا، دعونا نمضي قدمًا ونقوم بتبسيط هذا الآن،
كما تعودنا.
إنها نوعًا ما مجموعة من التعليمات البرمجية، ولكن أعتقد
أنه يمكننا أن نجعل هذا أكثر إحكامًا قليلاً.
ولكن بدلاً من كتابة هذا يدويًا،
دعوني أمضي قدمًا وأفتح فقط إحدى الأمثلة التي تم إعدادها مسبقًا
من اليوم، والتي توجد جميعها على موقع الويب الخاص بالدورة، باسم compare4.
وسترون في compare4، هذا هو الأمر.
ليس لدي سوى دالة رئيسية هذه المرة.
لقد تخلصت من دالة compare_strings الخاصة بي لأنه أتعلمون ماذا؟
يبدو أنني سأستخدم شيئًا بدلاً من ذلك.
ما هي الدالة التي من الواضح أنني قمت بنشرها؟
نعم، S-T-R-C-M-P، أو قد ينطقها شخص ما،
فقط str compare أو strcmp.
لذا هذا، مثل strlen، كما تسمى أيضًا بشكل مختصر،
وهي مجرد دالة يتم الإعلان عنها بالفعل
في إحدى مكتباتنا المألوفة في الأعلى، string.h،
ويتضح أنه إذا نظرنا في صفحة man، إذا جاز التعبير،
عن طريق كتابة man strcmp، أو إذا انتقلت إلى مرجع CS50 وقمت بالفعل
بالنظر إلى وصف أقل ارتياحًا للدالة هناك،
هذه مجرد دالة هدفها الوحيد في الحياة
هو مقارنة السلاسل من أجلك.
ولكن الأمر مختلف قليلاً في السلوك لأنه
أفضل قليلاً من الذي كتبته للتو.
دعوني أقوم بتكبير هذا، وسترون أن السطر 14 هنا، أنا
لا أعامله تمامًا بنفس الطريقة.
منطقى مختلف نوعًا ما.
ما الذي أتحقق منه فعليًا في تعبيري المنطقي هذه المرة؟
الجمهور: [INAUDIBLE]
ديفيد مالان: نعم، وهو غريب بعض الشيء.
أنا أتحقق وبشكل صريح-- إذا كانت قيمة إرجاع strcmp يساوي-يساوي 0.
كما قلت للتو، إذا كان compare_strings s فاصلة
t، لأنني كنت أتوقع مجددًا قيمة منطقية-- صحيح أم خطأ. ويعمل strcmp، غريب نوعًا ما،
بالطريقة المعاكسة.
يتضح أن strcmp لا يعيد صحيح وخطأ.
إذا قمت بقراءة وثائقه، فهو يعيد 0 إذا كانت السلاسل متساوية،
لكن بشكل سهل للغاية، فهو يعيد قيمة موجبة
إذا كان من المفترض أن تأتي s قبل t، ويعيد قيمة سالبة
إذا كان من المفترض أن تأتي s بعد t أبجديًا.
لذا يتضح أنه يمكنكم استخدام strcmp ليس فقط للمقارنة من أجل التعادل،
ولكن أيضًا لعدم التعادل--
أقل من أو يساوي--
أقل من أو أكبر من، إذا جاز التعبير، أبجديًا،
أو في ترتيب ASCII، إذا جاز التعبير.
سيقارن حرف بحرف في الواقع بقيم ASCII،
وسيُؤكد على أن B تأتي بعد A،
وC بعد B، وهكذا.
لذا يمكنك بالفعل استخدام strcmp إلى حد ما كقاموس،
أو لترتيب جهات الاتصال في جهاز iPhone الخاص بك أو هاتف Android الخاص بك.
لذا القصة الطويلة باختصار، هذه دالة يمكننا استخدامها،
لسنا مضطرين لإعادة ابتكار هذه العجلة، وبالتالي، ليس لدينا المزيد من التعليمات البرمجية
حتى بعد هذا.
علينا فقط استخدامه بشكل صحيح، وتوجد، هذه الوثائق
فهي بمثابة صديق لك.
لذا إذا قمت بتشغيل هذا البرنامج، فسيعمل بنفس الطريقة بالضبط،
لذا دعوني أمضي قدمًا وأقوم بتوضيح بعض الأخطاء.
يتضح أنه طوال هذا الوقت، كنت كسولاً قليلاً ولم أفحص أخطائي--
فحص الأخطاء.
توجد مجموعة كاملة من الأشياء التي يمكن أن تسوء في الأسبوع الأول من دورة CS50
ولم نتناولها، لأنها من شأنها فقط
أن تنقد تعليماتنا البرمجية، وتجعلها أطول وأقل إثارة للاهتمام ومتعة في الكتابة
ومفهومة بشكل أقل نوعًا ما.
ولكن اليوم، الآن نعرف ماذا يحدث في الواقع،
يمكننا أن نبدأ في طرح بعض الأسئلة الإضافية
ونجعل تعليماتنا البرمجية أقوى وأكثر متانة لذلك
لن يسوء شيء في الواقع.
يتضح أن، إذا قرأت وثائق get_string في صفحة man
أو في مرجع CS50، يتضح أن get_string
يعيد سلسلة-- أوه، ليس تمامًا.
إنه يعيد عنوان السلسلة.
أوه، ليس تمامًا.
إنه يعيد عنوان أول بايت من السلسلة، من الناحية الفنية.
ولكن إذا حدث خطأ ما، فسيعيد حرفًا خاصًا باسم فارغ.
وينبغي عدم الخلط مع NUL، فهو يعيد عنوان خاص باسم فارغ--
اليد اليسرى لا تشبه اليد اليمنى منذ عقود.
لذا فارغ، ف-ا-ر-غ، تعني فقط العنوان 0، الذي لا يجب أن يوجد في أي شيء على الإطلاق.
إنه مجرد وهم، عنوان غير صالح.
طالما يعيد get_string عنوان السلسلة في الذاكرة،
مثل 100 لبراين أو 900 لفيرونيكا، إذا حدثت
مشكلة في get_string وحدث خطأ بجهاز الكمبيوتر،
إذا أعاد 0، على وجه التحديد 0، المعروف أيضًا بهذا الاسم.
فارغ-- ف-ا-ر-غ، ثم يمكنك اكتشاف حدوث خطأ ما.
إذن للقيام بذلك، سيكون الأمر مملاً قليلاً،
لكنه مع ذلك الشيء الصحيح الذي ينبغي عمله،
أحتاج أن أكون أكثر دفاعية قليلاً.
إذا كان s يساوي-يساوي فارغ، وإلا يُعرف باسم 0، وإلا يُعرف أيضًا باسم 0x0،
ولكنني سأكتبه بطريقة تقليدية كهذا،
سأمضي قدمًا وأعيد 1 كتعليمات الخروج البرمجية الخاصة بي.
إذا كان t يساوي-يساوي فارغ، فسأمضي قدمًا وأُعيد 1 كتعليمات الخروج البرمجية الخاصة بي،
أو يمكنني إعادة 2 أو 3--
أنا فقط بحاجة إلى إعادة القيمة للإشارة إلى الكمبيوتر
أن هناك خطأ ما، لكن بشكل افتراضي سنعيد
فقط 1 كلما حدث خطأ، لكن إذا سار كل شيء بشكل جيد،
سأمضي قدمًا وأُعيد 0.
لذا لنتذكر مجددًا من الأسبوع الماضي، ولم نقضي الكثير من الوقت
في هذا--
يمكن أن يعيد main القيم بنفسه.
بشكل افتراضي، منذ الأسبوع 1، إذا لم تقم بإعادة أي شيء،
يعيد main بشكل تلقائي وسري 0 لك لأن 0 أمرٌ جيد.
السبب وراء 0 هو وجود 0 واحد فقط في العالم، بالتأكيد،
ولكن يوجد عدد لا حصر له على اليسار
وهناك عدد لا حصر له على اليمين، سالب وموجب.
هذا عظيم، لأنه كما جربت في الأسابيع القليلة الماضية،
بدا الامر وكأن هناك عدد لا حصر له من الأشياء التي يمكن أن تسوء عندما
تكتب حتى أقصر البرامج.
وهذا يعني أن لدينا الكثير من الأرقام التي يمكننا تعيينها لتعليمات الخطأ البرمجية،
إذا جاز التعبير.
الآن لا يهمني حقًا ما هي تعليمات الخطأ البرمجية،
لذا سأتبنى التقليد البشري في هذه اللحظة--
إذا حدث أي خطأ، فسيُعيد أي شيء آخر بخلاف 0.
ولذا سأقوم بإعادة 1 هنا، ولكن إذا حدث أي خطأ، سأعيد 0.
المغزى هنا أنه بإضافة هذه السطور الثلاثة هنا وهذه السطور الثلاثة
هنا، سأتجنب ما يُسمّى
بخطأ تجزئة أو segfault. هل واجه أحدكم
هذا الخطأ المشفر؟
حسنًا.
إذن، عدد لا بأس به منكم، وإذا لم يكن لديكم أي فكرة عما يعنيه ذلك،
ولكن بدءًا من اليوم سيكون أكثر قليلاً، وخلال الأسابيع القادمة،
ستستوعبون ذلك أكثر.
يشير خطأ التجزئة إلى أنك تطرقت إلى الذاكرة التي لا يجب أن تملكها.
أو حدث خطأ ما ولم تكتشفه.
إنه عبارة شاملة للمشاكل المتعلقة بالذاكرة.
هذا يساعد على تجنب تلك الأنواع من الأخطاء.
إنها ليست الطريقة الوحيدة، ولكنها إحدى هذه الطرق.
لذا لنبدأ اليوم مع برامج مجموعة المشاكل وأي شيء
تكتبه في الدورة، ستحتاج دائمًا إلى التفكير بشأنه،
حتى لو رجعت وأضفته في وقت لاحق، هل يمكن أن يسوء هذا؟
هل يمكن أن يسوء هذا؟
وأضف فقط بعض ماذا لو إضافية وماذا لو أخرى
وتعامل مع هذه المواقف بحيث لا يحدث خطأ ببرنامجك
أو segfault أو تفاجئ الشخص الذي يستخدمه بالفعل.
حسنًا، دعونا نلقي نظرة على مثال أخير،
لأنه وبصراحة هذا ممل بعض الشيء.
سأمضي قدمًا وأفتح--
ويمكن العثور على هذا الملف في compare5.c.
دعوني أمضي قدمًا وأحفظ هذا حتى نحصل عليه-- compare5.c.
سأطرح مثال مقارنة أخير.
سأقوم بحفظ هذا كـ compare6.c.
تبين أن البشر مثل موجزاتهم.
وفارغ، لأنه من الناحية الفنية العنوان 0،
يمكنك في الواقع أن تكون أكثر ذكاءً.
إذا لم يكن s وإذا لم يكن t طريقة كافية للتعبير عن تلك الأشياء نفسها.
والسبب هو ما الذي تفعله إشارة التنبيه؟
علامة التعجب في التعليمة البرمجية إذا كنتم تذكرون؟
إنها تعكس شيئًا ما.
لذا فكما لو أن هذا يقول، إذا كانت s ليست 0، كما هو معروف، إذا كانت s ليست فارغة، أو بالأحرى--
إذن-- أنا في حيرة الآن.
نعم.
فلو أني، كما قلتُ للتو، إذا كانت s، فهي إذن عنوان صالح
ويجب أن أمضي قدمًا في عملي.
ولكن إذا لم تكن s أو إذا كانت s فارغة، فأنا أريد
المضي قدمًا وإرجاع 1 لأنه يوجد خطأ، وتوقف هنا أيضًا.
لذا في أي وقت تتحقق فيه مما إذا كان هناك شيء ما يساوي قيمة فارغة،
فيمكنك جعلها أكثر إيجازًا وذلك فقط بقول إذا لم تكن s؛ إذن فهي فارغة،
فيتم إرجاع 1.
إذا كانت فارغة، فيتم إرجاع 1.
إنها مجرد اختزال لبناء الجملة.
أُف!
كان علي التفكير بشأن هذا الواحد.
أي أسئلة؟
الجمهور: لماذا سيقوم [INAUDIBLE] بتخزين بعض [INAUDIBLE]
ديفيد مالان: صحيح.
أنت تخزن عنوانًا، ولكن إذا كان ذلك العنوان 0.
فرضًا إذا لم يكن 0، فإن 0 كأنها خطأ، لذلك فقول ليس خطأ تعني صحيح،
وبالتالي فإن لها تأثير عكس الدالة المنطقية.
هذا كل شيء.
في أي وقت تستخدم فيه إشارة تنبيه أو علامة تعجب، فإنها تغيّر 0 إلى non-0 "ليس صفرًا"--
الجمهور: [INAUDIBLE]، ولكن حتى--
لو أني لا أفهم لماذا يقوم [INAUDIBLE] بتضمين أنه [INAUDIBLE]..
ديفيد مالان: حتى تتمكن من التفكير فيه بهذه الطريقة.
إذا كانت s-- كان لدينا هذا سابقًا.
إذا كانت s تساوي تساوي قيمة فارغة كأن نقول إذا كانت s تساوي 0 حرفيًا.
ويمكنك نوعًا ما التفكير في ذلك بشكل رسمي كما
لو كانت s ليس بها مؤشر صالح--
0 ليست نقطة صالحة أو أنها ليست عنوانًا صالحًا حسب التعريف.
100 صالحة، 900 صالحة، 0 غير صالحة فقط حسب التقليد البشري.
إذن فهذا كقول، إذا كانت s ليست لها قيمة، فهي صالحة.
إذن لنقل باختصار، إذا لم تكن s،
وهو مجرد اختصار لذلك فهي طريقة أخرى للتفكير في الأمر.
حسنًا، دعونا نلقي نظرة على برنامج مختلف آخر،
لكن هذا يكشف عن النوع نفسه من المشكلات على النحو التالي.
سأمضي قدمًا وأفتح مثالاً يُسمّى
copy0، هدفه في الحياة لحسن الحظ هو نسخ سلسلة.
لذلك لاحظ أنه في برنامجي هنا، والذي
كتبتُه مقدّمًا، أحصل على سلسلة من المستخدم في السطر 11،
وأقوم بتخزينها في سلسلة تسمى s.
يمكنني تغيير هذا إلى char* الآن، ولكننا نعرف ما هو.
وسأمضي قدمًا وأنسخ عنوان السلسلة من s إلى t.
ثم سأقول، إذا كان طول t أكبر من 0،
فمن ثم يتم المضي قدمًا والقيام فقط بجعل الحرف الأول كبيرًا.
لذا فهو مبهم قليلاً، لكنك ربما فعلت
شيئًا من هذا القبيل مع Caesar ومع التعامل الأخير
مع السلسلة.
هذا لمجرد التأكد، هل لدي حرف واحد على الأقل؟
وإذا كان الأمر كذلك، فإن الحرف الأول هو t قوس 0، كما تذكرون.
toupper دالة في ctype.h من الأسبوع الماضي
تقوم فقط بجعل هذا الحرف كبيرًا.
إذن هذا السطر من التعليمة البرمجية، 19، يقوم فقط بجعل الحرف الأول كبيرًا
في t، وهذا كل شيء.
ثم في النهاية نقوم فقط بطباعة ما يمثله s وطباعة ما يمثله t.
هذا كل شيء.
إذن هذا البرنامج ينسخ فقط s إلى t، ويجعل أحرف t كبيرة، وهذا كل شيء.
إذن دعوني أمضي قدمًا وأقوم بتنفيذ make copy0.
هذا في التعليمة البرمجية الخاصة بنا من هذا اليوم.
لذلك سأقوم بتنفيذ cd sc3، لأنني كتبتُه بالفعل في هذا الدليل.
make copy0.
سار الأمر على ما يرام. ./copy0.
دعوني أمضي قدمًا وأكتب tj مرة أخرى بالحروف الصغيرة.
إدخال.
ها.
TJ ،TJ-- كلاهما أصبحا كبيرين.
حسنًا، ربما إنه مجرد شيء غريب مع الأحرف الأولى.
لذلك دعونا فقط نجرّب Veronica، كلها أحرف صغيرة.
ها، هذا بالتأكيد حرف كبير.
دعونا نوضح فارقًا أكثر وضوحًا، Brian حيث
إن حرف B سيبدو مختلفًا حقًا.
ومع ذلك أنا فقط أقوم بجعل حرف t كبيرًا.
حسنًا دعونا نفكر ماذا يحدث هنا فعلاً.
في هذه الحالة، عندما أحصل على سلسلة من المستخدم، s وt، وأقوم بالكتابة،
على سبيل المثال، brian بالأحرف الصغيرة كلها، /0، وهذه، بالطبع،
مجرد مصفوفة غير ظاهرة.
وهذه تستهلك حوالي ست وحدات بايت هنا.
وعندما أقوم بالتخزين في s، فإن s عبارة عن سلسلة.
لذلك أتعرفون ما الأمر؟
نحن لم نفعل هذا من قبل.
دعوني في الواقع أقوم بإنشاء متغير، جزء من الذاكرة لـ s وأسمّيه s.
و لنفترض أن Brian موجود تحديدًا حيثما كان من قبل--
100، و101، و102، و103، و104، و105.
لذا إذا كانت s تساوي get_string وكانت get_string تقوم بإرجاع Brian،
فما الذي أكتبه في المربع المُسمى s؟
نعم، فقط 100، أليس كذلك؟
هذا كل ما سيحدث طوال هذا الوقت
حتى رغم أننا لم نتحدث عنه في هذا المستوى.
وفي الواقع، يتضح أنه-- يمكن استخدام المؤشر فعليًا بشكل تصويري.
إذا كنت تفضل فعلاً أن تفكر حول مؤشر كعنوان
أو نوعًا ما كخريطة تقودك إلى مكانٍ ما، فبطريقة أخرى يمكن لأي شخص
عادةً أن يرسم مؤشرًا-- لأنه بصراحة،
من يهتم حقًا أن Brian موجود في العنوان 100؟
فمثل ذلك يعد طريقة ذات مستوى منخفض للغاية، من مستوى برامج الأسبوع 0.
إنه يشير فقط هناك.
لذلك s هو مؤشر إلى ذلك الجزء من الذاكرة.
يصادف أن يكون 100، أيًا كان، والسهم هو الطريقة التي ستقوم
بها حرفيًا للإشارة إلى جزء الذاكرة إذا كنت ترسم هذا على بعض الملاحظات.
لذا، فذلك، أيضًا، صحيح.
لذلك تنشأ المشكلة هنا مع ذلك السطر من التعليمة البرمجية.
عندما أحاول فعليًا نسخ s وتخزينها في t، فكّر ماذا سيحدث.
الجانب الأيمن هو فقط قيمة s، والتي تصادف أن تكون 100.
الجانب الأيسر هو مجرد قول، يا كمبيوتر، أعطني
متغيرًا آخر، أول سلسلة، وسمّها t.
إذن فذلك كقول، يا، كمبيوتر، أعطني جزءًا آخر من الذاكرة،
وسمّه t، ثم قم بتخزين s فيه.
لكن ماذا يعني تخزين s؟
حسنًا ما قيمة s في هذه المرحلة؟
إنها المؤشر إلى Brian، أو إنها من الناحية التقنية--
سأكتب كليهما فقط من أجل الشمولية-- هي حرفيًا العدد 100.
لذا إذا جعلتَ t تساوي s، فهذا كقول وضع 100 هناك أيضًا،
وبشكلٍ تصويري كقول هذا.
عند هذه النقطة من القصة، عندما أنسخ s إلى t،
قام الكمبيوتر بنقلي حرفيًا.
إنه قام بنسخ s في t، ولكن ما هي s؟
إنها العنوان فقط.
إنها ليست B-R-I-A-N خط مائل عكسي 0، إنها مجرد العنوان.
لذا عندما أقول بعد ذلك، t قوس 0 يحصل على toupper--
لذلك دعونا ننظر إلى هذا السطر من التعليمة البرمجية.
هذا السطر من التعليمة البرمجية هنا الذي تم تظليله،
عندما أقول انتقل إلى الحرف صاحب الترتيب رقم 0 في t وقم بتخزين
نسخة الأحرف الكبيرة من نفس ذلك الحرف، فما عليك سوى اتباع الأسهم.
إذا سبق لك أن لعبت السلم والثعبان وأنت طفل،
فأنت نوعًا ما كنت تتبع الأسهم، لترى أين ينتهي بك المطاف.
t قوس 0 هو هذا الموقع هنا، لأنه مرةً أخرى،
إذا كان هذا جزءًا من الذاكرة، فإنه حسب الأسبوع الماضي يعتبر مصفوفة،
لذا يمكنك التفكير في هذا أيضًا كقوس 0، هذا قوس 1،
هذا قوس 2، وهكذا دواليك.
لذلك إنها مجرد مصفوفة.
لذا يكون t قوس 0 عبارة عن حرف b صغير، ودالة toupper لحرف b صغير،
بالتأكيد، تغيِّر حرف b الصغير هذا إلى حرف B كبير. لكن الآن
كلاً من s وt لا يزالان يشيران إلى نفس الجزء من الذاكرة،
لذا بالتأكيد فإن كلاً من s وt سيمثلان Brian بأحرفٍ كبيرة،
أو TJ أيضًا في المثال الأول لديّ.
أي أسئلة على الجزء الذي انتهينا منه للتو ولماذا حدث ذلك؟
حسنًا، إذن بديهيًا ما هو الإصلاح؟
ألا يهم ما إذا لم تكن لديك فكرة عن كيفية تشفيره،
مثل ما يتعين علينا القيام به في الأساس لنسخ سلسلة، وليس
نسخ عنوان؟
الجمهور: [INAUDIBLE]
ديفيد مالان: إنشاء أي جديد؟
الجمهور: في الأساس إنشاء [INAUDIBLE]..
ديفيد مالان: أجل.
إنشاء نفس السلسلة في جزء جديد من الذاكرة.
ما أحتاج القيام به فعلاً هو تخصيص أو إعطاء
جزء من حجم أكبر من الذاكرة لنفسي بحجم كبير مثل Brian،
تتضمن /0 الخاصة به.
ثم من الناحية المنطقية أحتاج لنسخ كل حرف في ذلك الجزء.
لذا إذا رجعت إلى الأصل لديّ عندما كان حرف b صغيرًا،
فأنا بحاجة لعمل نسخة منطقيًا باستخدام تكرار حلقي من نوع for loop أو من نوع while loop
أو أيًا كان ما تفضله--
B-R-I-A-N خط مائل عكسي 0، لذلك فعندما أنسخ السلسلة ثم أقوم بتخزينها في t،
فإنها لا تقوم بنسخ s حرفيًا.
ودعونا نفترض أنه ينتهي عند الموقع 300 فقط بشكل تعسفي--
ليكوّن فقط أرقامًا سهلة.
والآن t تخزن 300، وتشير إلى هنا.
لذلك عندما أقوم بتنفيذ هذا السطر في هذا الإصدار من القصة، فإن t قوس 0
تحصل على toupper، فما الذي أفعله في الواقع؟
أقوم باتباع سهم مختلف هذه المرة
لأنني أعطيت نفسي جزءًا مختلفًا من الذاكرة، بجعل Brian هذا بأحرف كبيرة،
وبالتالي يتم إصلاح الخطأ لحسن الحظ، وإن كان لفظيًا فقط.
إذن كيف نقوم بفعل ذلك في التعليمة البرمجية؟
نحن بحاجة للقيام بذلك تحديدًا.
نحن بحاجة لإعطاء أنفسنا حيزًا أكثر في الذاكرة،
لذلك دعونا نقدم ميزة واحدة أخرى للغة C. في دالة copy1.c،
نرى الحل لهذه المشكلة.
لاحظ بأعلى أقوم بأشياء بمستوى أدنى قليلاً-- عذرًا، مفاجأة.
لاحظ في هذا الإصدار من التعليمة البرمجية، copy1.c،
لقد بدأت تقريبًا نفس الشيء، ولكن فقط لنكون واضحين تمامًا،
أستخدم فقط char*.
لا أريد أي سحر، لذلك لا توجد أي سلسلة،
لا توجد أي عجلات تدريب هنا.
ولكن منطقيًا هذا نفسه بالضبط كما في السابق--
إضافةً إلى التحقق من الخطأ.
هذا السطر جديد.
ويبدو غير تقليدي بعض الشيء، لكن دعونا نرى ماذا يحدث.
وسطر التعليمات البرمجية هذا هنا، ماذا أفعل؟
الجانب الأيسر، إنه الأقصر، لنبدأ بالأسهل.
Char* t، فقط بمصطلحات غير المتخصصين، ماذا يعني هذا التعبير؟ char*؟
مرحبًا جهاز الكمبيوتر، يفعل ماذا؟
ما هذا؟
الجمهور: [INAUDIBLE]
ديفيد مالان: ليس تمامًا.
صياغة مختلفة.
مرحبًا جهاز الكمبيوتر، أعطني--
ليس تمامًا.
كونوا أكثر دقة؟
الجمهور: مصفوفة؟
ديفيد مالان: ليست مصفوفة تمامًا، فقط هذا الجزء.
لذا دعوني أقوم بإخفاء كل هذا.
إذا لم تكن النجمة هناك--
لا يمكنني فعل هذا بشكل جيد.
لذا هذا-- نعم؟
الجمهور: حرف [INAUDIBLE]؟
ديفيد مالان: جيد، سأتناول ذلك.
لذا مرحبًا جهاز الكمبيوتر، أعطني مؤشر إلى حرف.
أو حتى مستوى منخفض أكثر، مرحبًا جهاز الكمبيوتر، أعطني
جزء من الذاكرة حيث يمكنني تخزين عنوان الحرف به.
أعني، إنه شيء ممل.
ارسم مربع على الشاشة، وسمّيه s-- أو بالأحرى،
t، لكن فقط أعطني مسافة لمؤشر، كما قلت.
لذا هذا كل ما يفعله.
إنه يرسم مربعًا على الشاشة ويطلق عليه t، وهو فارغ حاليًا.
الآن دعونا ننظر إلى الجزء المرعب على الجانب الأيمن.
malloc، دالة جديدة اليوم.
تشير إلى تخصيصات الذاكرة.
إنها تبدو مشفرة، ولكنها تعني أعطني جزء من الذاكرة.
إنها تقول بالضبط ما تقوله من الناحية الوظيفية.
ثم تحتاج فقط إلى إجابة سؤال واحد--
حسنًا، كم من الذاكرة تريد؟
ما عدد وحدات البايت التي تريدها؟
والآن من المحتمل أن تبدو الرياضيات، على الرغم من أنها مشفرة للوهلة الأولى، منطقية.
احصل على طول سلسلة s، وقم بإضافة 1، ثم اضربه
في حجم الحرف.
ونحن لم نرَ هذا من قبل. sizeof حرفيًا يفعل ذلك.
إنه يخبرك عدد وحدات البايت في char.
ويصادف أن يكون 1، وفي الواقع، إنه محدّد.
لذا إذا قمنا بتبسيط هذا في لغة C، فإن char دائمًا 1 بايت،
لذا هذا يعادل فقط الضرب في 1.
ومن الواضح رياضيًا أن هذا مضيعةً للوقت،
لذا يمكننا أن نضم هذا حتى يكون أكثر بساطة.
أريد فقط أن أكون دقيقًا.
حتى الآن، مرحبًا جهاز الكمبيوتر، خصّص لي هذا العدد الكبير من وحدات البايت من الذاكرة.
لماذا هو زائد 1؟
الجمهور: أنت بحاجة إلى الحرف الفارغ.
ديفيد مالان: أحتاج إلى هذا الحرف الفارغ.
براين هو 1، 2، 3، 4، 5 كما قال، ولكن أحتاج السادس لحرفه الفارغ،
وأنا اعلم فقط أنه سيكون هناك.
إذن في هذه المرحلة من القصة، ما الذي حدث؟
كل ما يفعله malloc هو إعطائي هذا المربع من الذاكرة
والذي يحتوي على مساحة بها عدد كبير من وحدات البايت في اسم براين.
ولكن لم تتم تعبئتها حتى الآن.
الآن أنا بحاجة إلى ملء وحدات البايت تلك منطقيًا مع اسم براين الفعلي.
لذا إذا قمنا بالتمرير إلى أسفل إلى التكرار الحلقي الخاص بي،
يمكننا بالفعل نسخ السلسلة إلى تلك المساحة.
وهو طويل بعض الشيء، التعبير، لكن لا شيء جديدٌ هنا.
قم بتهيئة i إلى 0، n إلى طول s، i أقل من أو يساوي n--
سنعود إلى ذلك، i++.
لذا هو مجرد معيار جيد للتكرار الحلقي.
ثم انسخ الحرف i من s إلى الحرف i من t.
الشيء الوحيد الذي يجعلني عصبيًا قليلاً بصراحة هو هذا الشيء هنا.
كما أشعر في كل مرة نقوم بفعل شيء أقل من أو يساوي،
نقوم بإنشاء خطأ مثل الأسبوع الماضي.
لكن هذا صحيح، لماذا؟
لماذا أريد أن أنتقل إلى الأعلى وخلال طول هذا؟
الجمهور: هو الحرف الفارغ الذي يضيف--
ديفيد مالان: بالضبط.
بسبب الحرف الفارغ.
أنا في الواقع لا أريد أن أتوقف عند strlen من s، حتى أتمكن من تغيير هذا.
إذا كنت مرتاحًا أكثر باستخدام أقل من، لأن عقلك فقط
لا يستوعب لماذا قمنا بهذا في المقام الأول، هذا جيد،
نحن بحاجة فقط للقيام بذلك بدلاً من ذلك.
لذا هذا رياضيًا-- إذا انتقلت إلى strlen زائد 1، نفس الشيء
حيث لا تقوم بتلك العملية الحسابية ولكن الانتقال خطوة أخرى فقط إلى الأمام.
فقط أيًا كان ما تريد التفكير فيه على أنه أمرٌ جيد.
وبالكيفية التي تريد التفكير بها على أنه أمرٌ جيد.
حسنًا، وأخيرًا، فقط فحص سريع، هل طول
t على الأقل حرف واحد أو أكثر؟
لأنه بخلاف ذلك لا يوجد شيء ليتم بتكبيره، وإذا كان الأمر كذلك،
أمضي قدمًا وقم بذلك.
لذا إذا قمت الآن بتشغيل هذا المثال، make-- عذرًا، دعوني أحفظه.
make copy1، الذي تم تحويله برمجيًا.
./copy1، الآن دعونا نكتب tj، tj بأحرف صغيرة،
ولكن الآن تم تكبير t.
ودعونا نمضي قدمًا ونكتب اسم براين بأحرف صغيرة، وتم تكبير
واحد منهم الآن.
لذا هل يبدو هذا منطقيًا ماذا يحدث؟
حسنًا.
إذن إلى أين قد يؤدي هذا الأمر؟
حسنًا يتضح أنه-- دعوني أفتح مثال واحد أخير هنا،
لأنه بصراحة، هذا الأمر ممل بشكل لا يصدق،
ولا أحد يريد نسخ سلاسل إذا كنت
مضطرًا إلى المرور خلال كل هذا العمل.
يتضح أن نسخة التخزين هذه موجودة.
لذلك في حالة الشك، تحققوا من صفحة man.
في حالة الشك، تحققوا من مرجع CS50.
هل توجد الدالة في مكان ما مرتبط
ببعض الكلمات الرئيسية التي تتذكرونها؟
مثل نسخ سلسلة، لنرى إذا استرجعت شيئًا ما.
وبالفعل، كان لدينا strlen، وكان لدينا أيضًا strcmp، والآن أصبح لدينا strcpy،
وإذا قرأت الوثائق، فإن هذا يتم عكسه بشكل متعمد هكذا.
الوجهة هي هذا المتغير، المصدر أو سلسلة المنشأ
هي هذه السلسلة، وتُنسخ من نهاية إلى أخرى،
ومن ثم أنا لست بحاجة إلى هذا من أجل التكرار الحلقي.
إنه فقط يوفر لي بعض الأسطر من التعليمات البرمجية.
حسنًا.
لذا دعونا نأخذ تفصيلاً آخر هنا.
أوه، وستلاحظوا، في الواقع، دعوني أقم بإصلاح واحد هنا، إصلاح واحد هنا.
يتضح أن ما أقوم به هنا يُعد كسلاً بعض الشيء.
يتضح أن malloc لديه مقابل له.
لذلك في أي وقت تقوم بتخصيص ذاكرة، من الناحية الفنية
يجب عليك أيضًا تحرير تلك الذاكرة.
وتتيح لك C أن تطلب من الكمبيوتر الحصول على أكبر قدر من الذاكرة كما تريد،
ولكن إذا لم تعيدها، هل قمت بتجربة ذلك من قبل على جهاز Mac أو جهاز الكمبيوتر الشخصي الخاص بك،
بعد تشغيل الكمبيوتر لبعض الوقت
أو استخدام بعض البرامج الجديدة أو الضخمة مثل المتصفح،
فأنه يصبح أبطأ وأبطأ وأبطأ؟
وفي أسوأ الحالات، فأنه يتجمد فقط أو يتوقف أو شيء من هذا القبيل؟
من المحتمل جدًا أن هذا البرنامج ببساطة-- قد تم إنشاؤه بواسطة الإنسان،
بالطبع--
فقط لديه تسرب في الذاكرة.
لذا كتب بعض الأشخاص سطر واحد أو أكثر من التعليمات البرمجية التي تستخدم malloc
أو ما يعادلها في لغة أخرى والتي تعمل على تخصيص الذاكرة
لمُدخل المستخدم فقط.
تقوم بزيارة صفحة ويب واحدة، صفحتي ويب،
والتي تتطلب ذاكرة أيًا كان البرنامج.
وإذا لم يطلب الشخص عكس التخصيص-- إعادة التخصيص،
والمعروف أنه مجاني، فلن تستعيد الذاكرة مجددًا
إلى نظام التشغيل.
لذلك يصبح أبطأ وأبطأ لأنه يُتم تشغيله مع حدوث انخفاض في
الذاكرة بشكل متكرر، وقد يتعين نقل بعد الأشياء
لإفراغ مساحة لأشياء، وهذا ما يسمى بتسرب في الذاكرة.
وفي الواقع، في هذا البرنامج، يجب أن أحسّن هذا قليلاً بالفعل.
إذا عدت إلى هذا الإصدار هنا والسطر 18، تذكرون،
أنني قمت بتخصيص هذه الذاكرة فقط لصنع النسخة الخاصة بي،
الشيء الأخير الذي يجب فعلاً أن أقوم به في هذا البرنامج
هو هذا السطر هنا-- free.
ليس عليك إخبار الكمبيوتر كم بايت تريد تحريره،
إنه سيتذكر هذا من أجلك طالما تمر فقط على المؤشر--
المتغير الذي يُخزِّن عنوان جزء من الذاكرة
الذي قمت بتخصيصه.
حسنًا.
لذا دعونا الآن نرى لماذا نستخدم get_string،
حيث أنها ليست فقط نوعًا من تبسيط التعليمات البرمجية،
إنها أيضًا تعمل على حل بعض المشاكل السهلة جدًا.
ها هو برنامج يُسمى scanf0--
النص المنسق الممسوح ضوئيًا، دالة أخرى غامضة،
لكنها مباشرة للغاية.
يُفتح هذا البرنامج ببساطة من المستخدم باستخدام scanf.
حتى الآن وعلى مدار الثلاثة أسابيع الماضية، كنتم تستخدمون get_int.
لذا هذا بديل لـ get_int الذي
بدأتم استخدامه منذ بضعة أسابيع.
أعطني int تُدعى x، اطبع x نقطتين أيًا يكن--
إنه مجرد مُحث للمستخدم.
scanf %i, &x;، أيًا كان هذا، ثم اطبع قيمة x باستخدام %i.
إذًا ما الذي يحدث هنا؟
الآن يمكننا أن نبدأ في إدراك ما يدور حول get_int
في الواقع.
هذا هو get_int بفعالية.
إذا نظرت في الواقع إلى تعليمات المصدر البرمجية لـ get_int، ستجدها أفضل قليلاً.
ولكن في الأساس، ما يفعله get_int أنه يعلن عن متغير يُسمى x،
ولا يضع أي شيء هناك، لأن هذا
من المفترض أن تفعله أنت، كإنسان.
ثم يطلب منك أيًا كانت السلسلة التي تمررها إلى get_int،
لذا هما أول سطرين.
وهذا هو المظهر الغريب الوحيد.
يُعد Scanf هو مقابل printf.
ما زلتم تستخدمون سلسلة منسقة-- %s، أو %i، أو%f أو أيًا كان،
ولكن لن تقوموا بإخراج هذا، ستقومون بإدخال هذا من
لوحة المفاتيح.
و %x هو مقابل--
هو الرمز الخاص في C الذي يقول، امضي قدمًا واحصل لي على عنوان x.
لذا لا تقم بالتمرير في x، أعطني عنوان x.
الآن، لماذا هذا؟
سنرى، ولكن هذه هي الطريقة حيث يمكنك إخبار الكمبيوتر،
لقد قمت بعمل متغير لك يُسمى x، وهو يوجد هنا.
إنها خريطة كنز تقودك إلى x، اذهب لتضع قيمة هنا من أجلي.
وبالتالي فإن النتيجة النهائية هي أننا، في الواقع، نحصل على int.
إذا قمت بإجراء scanf0، ثم ./scanf0، سأكتب في 42، حسنًا؟
إنه ليس برنامجًا مثيرًا للاهتمام، إنه يعيد لي فقط ما حصلت عليه،
لكن هذا حرفيًا كل ما يقوم به get_int، بالطبع،
إذا قمت بعد ذلك بطباعة القيمة.
لذلك إذا أشرت إلى أن هذا صحيح، فإن هذه هي كيفية حصولك على عدد صحيح من المستخدم،
ولكن بصراحة، السبب في أننا لم نقم بهذا في الأسبوع 1 من هذه الدورة هو،
يا إلهي، لقد فقدنا المتعة في الحصول على رقم بسيط من المستخدم
باستخدام هذه السطور من التعليمات البرمجية ومَن يعرف
ما هو هذا الرمز-- نحن لا نريد منك أن تفكر في ذلك،
نحن نريد فقط أن تحصل على عدد صحيح.
لكن اليوم هذه العجلات التدريبية متوقفة، ولكننا
سنكتشف المشكلة بأسرع ما يمكن.
دعونا نحاول نفس الشيء مع سلسلة.
إذا كنت سأقوم بذلك، قد تعتقدوا أن النتيجة هي ذاتها.
أو دعونا نفعل ذلك فقط كـ char *.
لكن سيكون هناك تغييرًا واحدًا.
إذا مضيت قدمًا وأعطيت نفسي مساحة للحصول على عنوان الحرف،
أنا لست بحاجة إلى استخدام علامة العطف الآن، لأن scanf
بحاجة إلى إخباره أين يوجد جزء الذاكرة،
ولكنه بالفعل عنوان، لذا لست بحاجة إلى علامة العطف هنا.
تذكرون في وقت سابق، أعلنت عن int x، والتي كانت فقط مجرد عدد صحيح.
يحصل %x على عنوان العدد الصحيح هذا.
هنا، أنا أقول من البداية، أحصل لي على عنوان char.
لا أحتاج إلى علامة العطف لأنني بالفعل لدي عنوان char
من خلال تعريف رمز النجمة هذا.
إذن ما الذي يحدث هنا؟
دعوني أرى الآن.
إذا قمت بتشغيل scanf1، ماذا يحدث؟
لذا make scanf1 و--
أوه، لنرى.
لقد حصلت على تحذير.
لم تتم تهيئة المتغير s عند استخدامه هنا.
حسنًا، هذا جيد.
يريدني أن أقوم بتهيئته لأن هذا خطأ شائع جدًا.
الذين أشاروا منكم إلى أخطاء التجزئة
في وقت سابق قد واجهوا شيئًا مماثلاً لهذا.
لذا تم سحق هذا الخطأ.
دعوني أمضي قُدمًا وأشغّل scanf1.
حسنًا، ها قد بدأنا، TJ.
امم.
هذا ليس اسمك، لكن حسنًا.
لم يتعطل على الأقل، إنها مجرد أمور غريبة بعض الشيء.
ديفيد.
فارغ، حسنًا، هذا غريب قليلاً.
لذلك دعونا نمضي قدمًا ونقوم بذلك مجددًا.
دعونا نكتب اسم طويل جدًا.
Enter.
اللعنة، هذا لم ينجح.
لذا دعونا نجرب اسمًا أطول.
أنا اضغط على لصق كثيرًا.
حسنًا-- اللعنة.
مراتٍ كثيرة.
لم يتم العثور على الأمر، هذا بالتأكيد ليس أمرًا.
مدهش، حسنًا.
حسنا هذا مثيرٌ للاهتمام.
أوه، ها هو.
فارغ، نفس الشيء.
حسنًا، ما الذي يحدث بالفعل؟
حسنًا فارغ، وهو جميع الأحرف الصغيرة هنا، وهي
من الأشياء الجمالية، حسنًا هذا لا يعمل.
إنه لا يعمل.
حسنًا ماذا أفعل في الواقع؟
في السطر الأول من التعليمات البرمجية، عندما أقول أعطني s لتكون char*،
والمعروفة كسلسلة بخلاف ذلك، كل ما تقوم به هو تخصيص هذا.
وهو من الناحية الفنية حجم المؤشر.
مؤشر، ونحن لم نذكر هذا سابقًا، ولكن الآن يمكننا ذلك.
يتضح أنه 64 وحدة بايت أو 8 وحدات بايت.
8 وحدات بايت هي 1 بايت، وبالتالي فإن المؤشر عن طريق التعريف على العديد من أجهزة الكمبيوتر هذه
الأيام-- معظم أجهزة Macs، معظم أجهزة الكمبيوتر الشخصي الخاصة بك، Sandbox، Lab--
هو 64 وحدة بايت.
لذا هذا يعني أن هناك 64 وحدة بايت هنا، ولكننا قمنا بتهيئتها لتصبح فارغ،
بحيث يعني فقط أن هناك أصفار 64 هنا، نقطة-نقطة-نقطة.
ولكن عندما أحصل على سلسلة باستخدام scanf، ما
أخبر به الكمبيوتر أن يقوم به مع هذا السطر من التعليمات البرمجية هنا،
لاحظوا أن، مرحبًا جهاز الكمبيوتر، انتقل إلى هذا العنوان وضَع سلسلة هناك.
لذا ما الذي يحدث بالفعل؟
يتضح أنه لا توجد مساحة كافية للكتابة في TJ.
لا توجد مساحة كافية--
هذه كذبة بيضاء، لأننا يمكن أن نجعلك ملائمًا في 64 وحدة بايت،
ولكن لا توجد مساحة كافية لكتابة جملة طويلة أو فقرة من نص
قمت بكتابتها، أليس ذلك صحيحًا؟
ما الذي لم نقم به؟
لم نخصص أي مساحة هنا.
كل ما خصصنا له مساحة كان العنوان.
وهكذا كل مرة أستخدم scanf فيها أقول، احصل لي على سلسلة وضَعها هنا،
لا يوجد مكان لوضعها.
وبالتالي فإن القيمة تقول فقط بشكل دفاعي، لا، تعني لا،
لا يمكن تخزين هذا في أي مكان لك.
لذا أنا بالفعل بحاجة إلى أن أكون ذكيًا قليلاً بشأن هذا الأمر.
أنا بالفعل بحاجة للحصول على بعض المساحة حتى أتمكن بالفعل من تخزين شيء
في المكان الصحيح.
لنقم بذلك.
دعوني أمضي قدمًا وأقوم بإنشاء برنامج جديد.
سأمضي قدمًا وأسمي هذا scanf2.
نحن بحاجة إلى تعليمة برمجية سرية لتذكرني بذلك.
أوه، اسم ملف خاطئ.
إذن سأمضي قدمًا وأقوم بإنشاء ملف باسم scanf2.
scanf2.c.
وسأعيد بسرعة إنشاء هذا stdio.h، int main void،
وبالأسفل هنا سأمضي قدمًا و--أتعلمون؟
بدلاً من سلسلة s، والتي أعرف اليوم أنها ستصبح char* s،
ما هي هذه السلسلة في الواقع؟
حسنًا قلتَ ذلك في وقتٍ سابق.
ما هي هذه السلسلة؟
إنها مصفوفة من الأحرف.
دعوني آخذكم لها بالمعنى الحرفي.
فقط أعطني مصفوفة، لنَقُل، من خمسة أحرف.
D-A-V-I-D، أو واحد أكثر، هذا جيد، كافية تمامًا لـ /0.
دعوني فقط أُنشئ سلسلة-- ذات مستوى منخفض للغاية،
لكن هذه المرة أعطي لنفسي جزء الذاكرة.
أنا لا أريد فقط عنوان حرف،
بل أريد الأحرف الفعلية نفسها.
اسمحوا لي بالمضي قدمًا ومطالبة الشخص المستخدم بالسلسلة s الخاصة به،
تمامًا كما سبق.
ثم دعوني أستدعي scanf وأحصل على سلسلة من المستخدم باستخدام %s ثم أمررها
في s.
وإليكم حيلة بسيطة.
يتضح أنه نظرًا لأن السلسلة هي مجرد مصفوفة في الواقع،
لكن السلسلة أيضًا مجرد مؤشر، يمكنك في الواقع معاملة
مصفوفة كما لو أنها مؤشر--
عنوان.
وحتى بالرغم من أن هذه مصفوفة char*، فهذا جيد.
هذا هو المعادل في هذا السياق لكونها مجرد عنوان لسلسلة.
ولأن السلاسل هي مصفوفات، فيمكن التعامل مع المصفوفات كمؤشرات حتى الآن.
ثم دعوني أمضي قدمًا وأقوم فقط بطباعة أي شيء كتبه الشخص المستخدم.
S هو بالفعل هذا.
مرّر في s;، حفظ.
أجل؟
الجمهور: إذن هل [INAUDIBLE] بمثابة char*؟
ديفيد مالان: عند هذه النقطة سيكون تنفيذ char* أمرًا متكررًا،
لأنني حرفيًا أريد لهذه القصة ستة أحرف.
أريد مساحةً، بدلاً من ذلك، لستة أحرف.
إذن هذا نوعًا ما يمثل برامج الأسبوع الثاني إلى الآن، وليست هناك مؤشرات متضمنة.
لكن مجددًا، مجرد عرض المعادل لهذه الأفكار في الوقت الحالي.
لذلك إذا انتقلتُ الآن إلى هذه، وهذه موجودة في الدليل الآخر لديّ في الوقت الحالي،
make scanf2، إدخال، ./scanf2، s ستكتب--
سأكتب اسمي، وأنا أعلم أنني يمكنني إصلاح ذلك، لقد عدنا إلى العمل.
إنها تعمل الآن تقريبًا لأنني لم أُنشئ فقط العنوان لسلسلة،
إنما قمتُ بإنشاء المساحة للسلسلة.
لكن دعوني أنتقل إلى خطوة خطرة قليلاً--
ديفيد مالان؟
حسنًا، لقد تطور ذلك نوعًا ما، حسنًا.
ديفيد مالان أو اسم ما آخر طويل حقًا؟
حسنًا، تطور ذلك أيضًا.
دعوني أمضي قُدمًا وأشغّله مجددًا.
دعوني أحاول مع تلك السلسلة الطويلة مجددًا، ونرى ما سيحدث.
أعلم أن هذا لم ينجح بشكل كامل في المرة السابقة.
حسنًا، تمت.
أوه، حسنًا.
إذن أنا الآن عضو في نادي هؤلاء الأشخاص أصحاب الأخطاء في التجزئة من بينكم.
إذن دعونا نفهم ما يجري هنا.
خطأ التجزئة الذي ارتكبتُه منذ دقيقة كان عبارة عن
لمس جزء، قطعة من الذاكرة لا تخصك.
هل حدث لذلك فقط؟
حسنًا باستخدام هذا البرنامج البسيط، قلتُ لجهاز الكمبيوتر، يا كمبيوتر،
أعطني حيزًا لستة أحرف، أعطني ست وحدات بايت.
مع سطر scanf ، أقول لجهاز الكمبيوتر، ضع إدخال المستخدم
التالي في ذلك الموقع، في تلك المصفوفة من الأحرف.
D-A-V-I-D خط مائل عكسي 0 هو خيار مناسب.
David Malan لم تكن خيارًا مناسبًا حقًا، ولكن لم يبدُ أن هذا أمر مهم.
كما لم يتسبب اسم David Malan أو اسم ما آخر طويل حقًا، في تعطل الكمبيوتر.
ولكن هذا لأنه عادةً من دون علمنا، عندما تطلب ست وحدات بايت،
يقوم الكمبيوتر بترتيبها نوعًا ما-- فيعطيك بضع عناصر إضافية.
ليس من الآمن استخدامها، لكنها تعطيك ما يكفي
لكيلا تشاهد بالضرورة مشكلة مثل خطأ التجزئة.
لكنها عادةً ما تخصص فقط عددًا قليلاً من وحدات البايت الإضافية،
لذلك إذا تابعت لصق سطور طويلة، طويلة، طويلة من النص،
فأنت في نهاية المطاف لن تتجاوز وحدات البايت الست فقط،
إنما أيضًا ستكون بعيدًا تمامًا عن وحدات المساحة الخاصة--
وحدات البايت السرية التي استعدتها والتي يجب ألا تستخدمها على أي حال،
وفي هذه النقطة سيرضخ الكمبيوتر ويقول،
أنت تلمس الذاكرة لا ينبغي عليك، وهو ما يعرف أيضًا باسم،
خطأ التجزئة.
الجمهور: [INAUDIBLE] إذا منحك الكمبيوتر
قليلاً من وحدات البايت الإضافية، فلماذا لا يقوم بطباعة أي من البرامج الأخرى؟
بعد أن قلتَ [INAUDIBLE] فإنه قد قام تحديدًا بطباعة اسم David.
ديفيد مالان: سؤال جيد حقًا.
إذن حتى رغم أني أتلقى وحدات البايت الإضافية نوعًا ما هذه،
لماذا لا أراها بعد D-A-V-I-D؟
فأنا على الأرجح محظوظ.
خلاصة القول، عندما تقوم بتشغيل برنامج لأول مرة،
فإنه تتم تهيئة مساحة كبيرة من الذاكرة التي يحق لبرنامجك الوصول إليها بشكل افتراضي
للأصفار.
0 هو نفسه خط مائل عكسي 0، ولذلك فأنا محظوظ.
فعندما يكون لديّ D-A-V-I-D ثم مساحة إضافية في تلك المصفوفة،
فتتم تهيئة الكثير منها بالفعل كأصفار،
ويتم إنهاء السلسلة بشكل سري بالنسبة لي.
أو الإجابة الأفضل هي، إنه سلوك غير محدد.
مثلما أنك يحظر عليك لمس ذاكرة ليست خاصة بك.
ما يحدث بعد ذلك سيكون مخاطرتك وحدك.
ولكن ذلك تخمين لسبب حدوث ذلك.
حسنًا، إذن ما هي الميزة الأساسية عمّا تقدّمه لنا
دالة get_int؟
طوال هذا الوقت كانت دالة get_int في الواقع تتعامل مع
كل هذا الصداع نيابةً عنا.
أعني بصراحة، رغم أنني سئمت التفكير، والحديث
حول الكيفية التي حصلتَ بها للتو على سلسلة لعينة من المستخدم،
لأنك بحاجة لأن تعرف جيدًا كم وحدة بايت تحتاجها؟
وماذا لو كانت الأنواع البشرية في وحدة بايت زيادةً عما كنت تتوقع؟
إذن تحتاج إلى تبديل صف والحصول على مزيد من الذاكرة.
get_string يريحنا من كل هذا الصداع.
وهذا لا يعني أنك في حاجة لاستخدامه إلى الأبد،
إنها بالفعل عجلات تدريب، ولكن
فقط لأنك عندما تستخدم C أو العديد من لغات البرمجة،
سيقوم جهاز الكمبيوتر فقط بفعل ما تقوله للقيام به.
وتبين أنه حتى طلب المُدخلات من المستخدم،
إذا كنت لا تعرف كم عدد الأحرف الذي سيقوم أو التي
ستقوم بكتابتها من get-go، فعليك أن تتعامل مع الأمر.
وما تحت الغطاء-- وأنت مرحب بك لتلقي نظرة على تعليمات المصدر
البرمجية لمكتبة CS50، والتي سأقوم بنشرها على الصفحة الرئيسية لاحقًا اليوم،
اتضح أن الطريقة التي نقوم بـ get_string بها تحقق تقدمًا صغيرًا.
نحن حرفيًا نحصل على حرف واحد في كل مرة
من المستخدم، شيء كبناء الطريق بينما نمشي فيه.
وإذا لم يكن لدينا مسافة كافية، نطلب من جهاز الكمبيوتر،
أن يمنحني بعض وحدات البايت الإضافية حتى أتمكن من الحصول على المزيد منها،
ونحصل على حرف واحد في كل مرة حتى
يمكننا التعامل مع المستخدم عند كتابة مدخلات أكثر مما نتوقع بطريقة ما
بشكل متعمد أو عن طريق الخطأ.
لذلك دعونا نحدد سياقات كل هذا إذن.
تذكّروا أننا كنا نرسم هذه الصور في الأسبوعين الماضيين.
دعونا فقط نجعل هذا الأمر واضحًا جدًا لما يحدث.
هذه وحدة الذاكرة في جهاز كمبيوتر.
إنها مجرد لوحة خضراء، وهي صغيرة وتم تكبيرها هنا،
إنها بسهولة تمثل ما بداخل جهاز Mac أو جهاز الكمبيوتر الشخصي أو سطح المكتب لديك،
على الرغم من أنه يمكن أن يختلف حجمها.
إحدى هذه الرقاقات السوداء هي الذاكرة الفعلية أو وحدات البايت
التي كنا نشير إليها.
وإذا قمنا بتكبير ذلك، تذكّروا أنني اقترحت الأسبوع الماضي
أنه يمكنكم فقط التفكير في هذا وكأنه شبكة، مصفوفة.
ولا يتعين أن يكون مستطيلاً، هذا مجرد تصوير فني،
لكن كل هذه المربعات يمثل، كما زعمنا، وحدة بايت.
ويمكن أن يكون كل من وحدات البايت هذه مُعالجًا بطريقة ما برقم.
وهذا الرقم هو موقعه فقط، والمعروف بطريقة أخرى باسم عنوان.
يمكننا بالفعل رؤية هذا على النحو التالي.
حسنًا، دعوني أمضي قدمًا وأفتح هذا المثال هنا.
أو في الواقع، كما تعلمون، دعونا فقط نكتب هذا من سكراتش.
دعوني أكتب برنامجًا يسمّى addresses.c.
وسيستخدم هذا أصدقائنا القدامى، ومكتبة CS50 وstdio.h وint
main void.
دعوني أمضي قُدمًا وأفعل هذا.
وسأمضي قُدمًا وأحصل على سلسلة--
أتعلمون ماذا؟
لا مزيد من السلاسل. char* من المستخدم، get_string، اطلب من المستخدم s.
ونحصل على سلسلة أخرى، والمعروفة أيضًا.
بـ char*، get_string، أطلق عليها t من المستخدم.
ومن ثم، لا أرغب في طباعة السلاسل، التي اعتدت على فعلها هكذا،
طباعة s.
أنا أرغب في طباعة المؤشر حيث يكون s، هو العنوان.
سيظهر %p للمؤشر الذي لن يقوم بطباعة السلسلة في موقع الذاكرة هذا،
سيقوم بطباعة موقع الذاكرة الفعلية لك الخاص بـ s.
ويمكنني فعل الشيء نفسه هنا، %p، خط مائل عكسي 0، وألصق في t.
وفقط لكي أعرف ما الفرق بينهما، دعوني أبدأ فيه
مع نص ما-- s نقطتين وt نقطتين.
دعوني أمضي قدمًا الآن في الأسفل هنا، وأقوم بكتابة make addresses.
أوه، لقد أخطأت، نسيت فاصلة منقوطة.
دعوني أفعل هذا مجددًا.
make addresses.
وأتخلص من هذا.
تم تحويل هذا برمجيًا حسنًا، ./addresses، وها نحن ذا.
دعونا نكتب في-- دعونا نكتب براين وفيرونيكا كما فعلنا من قبل.
Enter.
وهذا غريب إلى حد ما، ولكن يتضح أن IDE في أجهزة Mac
وأجهزة الكمبيوتر الشخصي لديك تحتوي على ذاكرة كبيرة.
إذن هذا هو العنوان.
إنه ليس صغيرًا مثل 100، وليس صغيرًا مثل 900.
إنه في الواقع كبير.
إنه 2331010 مع تلك 0x الغريبة.
إذن يتضح أن، هذا مجرد تقليد إنساني.
في الأسبوع 0 تحدثنا عن الأرقام العشرية وجميعنا
على دراية بالأرقام العشرية منذ الصغر، 10 أرقام من 0 إلى 9.
وتحدثنا قليلاً حول نظام الأصفار والآحاد الثنائي.
يتضح أنه يوجد عدد لا حصر له من الأنظمة الأساسية--
إن رقم عشري/dec، نظام ثنائي/bi هما اثنين فقط من تلك الأعداد اللانهائية من الاحتمالات.
يتضح أنه يوجد واحد آخر وهو شائع للغاية باسم السداسيّ العشريّ.
ويعني السداسيّ 16 في هذه الحالة.
إذن فإن base-16 يحتوي بالفعل على 16 حرفًا في الأحرف الأبجدية الخاصة به.
0، 1، 2، 3، 4، 5، 6، 7، 8، 9، a، b، c، d، e، f.
إذن تبين أن الأنظمة الأساسية التي تحتاج إلى حساب أكثر من 10 أحرف
بدأت للتو في استخدام الأحرف الأبجدية عن طريق التقليد.
قرر البشر هذا فقط.
إذن نحن نحصل فقط على أرقام في هذه الحالة،
ولكن إذا كانت هذه العناوين أكبر من ذلك، فقد
نرى بالفعل بعض الأحرف الأبجدية بين a وf هناك.
وبصراحة لا أعرف ما هو هذا عنوان،
ولكن Google عادةً ما يجيد التعامل مع تلك الأمور،
لذا دعوني أفتح نافذة متصفح أخرى.
إذن Google يصبح صديقك عندما يتعلق الأمر بهذه الأمور،
أو أي عدد من الحاسبات.
0x2331010 بأرقام عشرية من فضلك.
وقد ترجم Google ذلك.
لذا براين، أنا-- نوعًا ما أسفل وحدة بايت في وقت سابق.
هو ليس في موقع عنوان 0، هو في الحقيقة
إنما في 36 مليون بايت داخل جهاز الكمبيوتر الخاص بي
في الوقت الحالي، الموقع 36,900,880.
إذن، عنوان أعلى بقليل من 100.
ثم فيرونيكا، إذا كنا حقًا نريد أن نجعل الأمور معقدة هنا،
يمكننا القول "بالأرقام العشرية،" ولندع Google يترجم هذا لنا.
إنها في الموقع 36,900,944.
لماذا؟
مَن يهتم؟
يدير جهاز الكمبيوتر كل هذا لنا، ولكن عندما يقوم get_string باستخدام malloc،
هذه حرفيًا هي الأرقام التي تم إرجاعها حيث يمكننا قول أنه،
يمكنك استخدام هذا الجزء من الذاكرة.
ولماذا استخدم البشر السداسيّ العشريّ؟
كما لو أنها طريقة أكثر تعقيدًا قليلاً لقول 0x2331050، ثم 36900944--
مثلما قمت بحفظ بعض الأرقام، لذلك فهي تقليدية فقط.
هذا كل شيء، لا يوجد سحر هناك.
لكن، تذكرون في وقت سابق.
هل تتذكرون عندما قمت بفتح مصحح الأخطاء في وقت سابق،
رأيتم بجانب قيمة متغير اسمي التي كانت مشفرة 0x0؟
ثم كانت هناك قيمة أخرى لا أتذكرها--
0X-وشيء ما؟
كان ذلك مجرد عنوان رقمي لاسمي في السداسيّ العشريّ.
و0x0 هو العنوان الفني المُستخدم من قبل فارغ.
أجل؟
الجمهور: لقد قلت أن العنوان المطبوع كان [INAUDIBLE] x للمتغير s
و--
ديفيد مالان: عذرًا، هل يمكنك قول ذلك مجددًا؟
الجمهور: لقد قلت أن العنوان المطبوع على الشاشة كان x،
ولكن x هو [INAUDIBLE]
ديفيد مالان: آه، كان علي أن أوضح ذلك.
0x، قرر البشر منذ سنوات في أي وقت تشاهد أي شيء
مع 0x، هذا يعني أن أيًا كان يأتي بعد ذلك هو سداسيّ عشريّ.
التقليد فقط.
كما أنه من الشائع أيضًا إذا كان يبدأ بـ 0، فهو ثُماني، وهو base-8.
إذا رأيت حرف b صغير في النهاية، فهذا يعني أنه ثنائي.
إذن توصل البشر إلى الرموز
كنوع من توصيل هذا إلى القرّاء، هذا كل شيء.
ليس جزءًا من القيمة.
إذن يتضح أنه بالفعل يمكننا القيام بهذه الحسابات بأنفسنا.
ولا نريد حقًا أن تسوء الأمور لهذا
لأنها بشكل خاص ليست مهارة مفيدة
في الحياة، لتكون قادرًا على التحويل إلى أنظمة أساسية متنوعة،
ولكن دعونا فقط نقدم مثالاً واحدًا حتى نراه.
فقط لتوضيح أن أنه لا يوجد سحر هنا،
إنها مجرد طريقة مختلفة للتفكير حول الأرقام مقابل المرحلة الابتدائية.
إذن إذا رجعنا بذاكرتنا إلى اليوم الذي كان لدينا فيه ثلاثة أرقام عشرية--
255، و216، ثم 255 آخر، إذا رجعنا إلى الأسبوع 0،
يمكننا الانتقال خلال حسابات التحويل لذلك إلى النظام الثنائي.
وحتى إذا كان ذلك يستغرق وقتًا، فهذا هو معادل النظام الثنائي.
وبصراحة، الرقم الأول والأخير سهلين نوعًا ما.
255 هو قيمة خاصة نوعًا ما لأنه مع 8 وحدات بت، كلهم
1، وهذا ما يمنحك 255.
إذن فإن الشيء الوحيد الصعب هو هذا في الواقع.
لكن مَن يهتم بالرياضيات اليوم.
ونحن نعلم منذ أسابيع أننا يمكننا القيام بذلك إذا حاولنا حقًا.
ولكن لاحظوا أن وحدات البايت هي 8 وحدات بايت، وبالطبع، ثمانية هي زوج من أربعة،
إذا صح التعبير.
حسنًا الشيء الجيد حقًا في السداسيّ العشريّ هو أنه يبدأ في 0
وينتهي في f.
وهذا 0، 1، 2، 3، 4، 5، 6، 7، 8، 9--
انتظروا-- نعم، هذا هو 10.
حسنًا.
ثم a، b، c، d، e، f.
لقد عددت 16 إصبعًا في المجمل، وبالتالي، السداسيّ العشريّ.
الشيء الجيد في base-16 هو كم عدد وحدات البايت التي أحتاجها للعدّ من 0
إلى--
واحد، اثنان، ثلاثة، أربعة--
15؟
4 فقط، أليس كذلك؟
لذلك إذا كان لديّ جميع وحدات بايت 0، هذا 0.
وكان لديّ 1 4 وحدة بايت، وهو--
لنرى.
هذا هو 8 زائد 4 زائد 2 زائد 1 يعطيني 15.
إذن خلاصة القول، السداسيّ العشريّ مناسب للغاية لأن 0 خلال f
يقترن بصورة رائعة بسيطة بـ 4 وحدات بايت.
إذن، إنها طريقة لطيفة للتفكير حول العالم وليس بوحدات 8
ولكن في 4 بدلاً من ذلك.
إذن كل ما فعلته هنا أنني أخذت قيمي وقمت فقط
بإضافة مسافة للتوضيح
أن 8 وحدات بايت مثل زوج من 4 وحدات بايت.
يتضح الآن أن 1 1 1 1 هو f للأسباب التي ذكرتها سابقًا.
كل الآحاد تكون f، والمعروف أيضًا بـ 15.
جميع الآحاد مجددًا تكون f، والمعروف أيضًا بـ 15.
إذا قمنا بعملية حسابية، فإن 1 1 0 1 هو d، و1 0 0 0 هو 8، ومن ثم جميع الآحاد هي f وf.
إذن خلاصة القول، توجد طريقة للتحويل من الأرقام العشرية
إلى النظام الثنائي، إلى السداسيّ العشريّ، إلى أي عدد من الأنظمة الأساسية الأخرى.
كل ذلك فقط يتلخص في ما هي الأرقام التي تهتم بها.
والطريقة التي تكتب بها هذا، لسؤالك في وقت سابق،
بالتقليد الإنساني.
ليس فقط FFDAFF، ولكن 0xFF0xD80xFF لسبب فقط.
ثم يتضح إلى المستخدم ماذا يكون ذلك.
إذن بعض المرح الآن.
أنا آسف لفعلي هذا بكم، ولكن آمل من كل ذلك أن تفهموا
هذه الصورة الهزلية ​​الشهيرة.
حسنًا، مرحبًا بكم في هذا النادي الخاص بالأشخاص الذين يفهمون أشياء من هذا القبيل.
لذا دعونا الآن نعثر على مشكلة واحدة أخيرة فقط،
وسنتناولها في المنزل عن طريق وضعها في السياق
مجالاً مثيرًا جدًا من الطب الشرعي حيث جميع تلك العناصر الأساسية
ستدخل حيز التشغيل.
لكن دعونا نبدأ أولاً بمشكلة.
لنفترض أنني أريد تنفيذ دالة هنا تسمّى swap غرضها في الحياة
هو فقط المبادلة بين قيمتين، a وb.
أريد فقط تبديل الصف.
دعونا أولاً نقوم بذلك كوجبة خفيفة في منتصف المحاضرة على الأقل
شخص واحد.
هل يمكن أن يتطوّع شخص ما ويأتي إلى هنا--
حسنًا، كان ذلك سريعًا.
متطوعة، تعالي.
ما اسمك؟
كيلي، حسنًا.
شكرًا لتطوعك بشكل مفاجئ.
كيلي، ديفيد، سررت بلقائك.
حسنًا، مهمة بسيطة جدًا في متناول اليد.
لدي هنا كوبين فارغين، ولدينا بعض من عصير البرتقال.
حسنًا، ضَعي هذا هنا.
ولدينا بعض الحليب هنا.
هذا يجب أن تظهر، ألوان مختلفة للغاية.
حسنًا، أود فقط أن تقومي، كيلي، إذا أمكنك ذلك، بتبديل تلك القيمتين.
البرتقال إلى حليب، والحليب إلى برتقال، من فضلك.
هذا غش، حسنًا؟
لا، أعني حرفيًا الأكواب.
لقد وضعتهما في الكوب الخاطئ، وأفضل أن يكون الحليب
في الكوب الآخر وعصير البرتقال في الكوب الآخر، أنا آسف.
الجمهور: أسكبي من الزجاجة.
ديفيد مالان: لا، هذا غير متاح لك، حسنًا؟
[LAUGHTER]
حسنًا، إذًا أنت تعانين.
لماذا تعانين؟
كيلي: لأنني سأقوم بمزجهما.
ومن ثم لن يكون الأمر ذاته.
ديفيد مالان: حسنًا.
لذا من الواضح أنني أعني، هذه مسألة خاسرة نوعًا ما.
أنه لا يمكنك القيام بذلك بالفعل.
فما الذي قد يجعل هذا الأمر أسهل بالنسبة لك بجانب وضعهم مجددًا
في الزجاجات؟
كيلي: وجود كوب آخر.
ديفيد مالان: أجل.
لذا فأنتي بحاجة إلى ما يكون بمثابة مساحة تخزين مؤقتة لهذا.
أنتم تعرفوا، دعوني--
تارا، هل يمكننا العثور على بعض الأكواب الإضافية هنا؟
آه، هذا سيجعل الأمر أسهل.
حسنًا، إذا حصلتي على مساحة تخزين مؤقتة--
ها هو-- هل يمكنك حل هذه المشكلة الآن من فضلك؟
آه، جميل جدًا.
القليل من التلوث، لكن لا بأس.
لكن أحتاج إلى إعادة ذلك الكوب المؤقت إلى تارا.
أجل، حسنًا.
شكرًا لكِ.
حسنًا، صفقوا بحرارة رجاءً لكيلي هنا.
[APPLAUSE]
حسنًا، ها نحن ذا.
أعتقد أنك لا تريدين حليب دافئ، لكن ما رأيك في عصير البرتقال؟
حسنًا.
شكرًا جزيلاً لكِ.
حسنًا، ما هو المغزى هنا؟
هذا سهل جدًا.
مثل عندما تكون لديك مساحة تخزين
مؤقتة-- متغير، إذا صح التعبير، على سبيل المثال لا توجد مشكلة لتبديل القيمتين.
لذا دعوني أمضي قدمًا وأقوم بذلك على النحو التالي.
سأمضي قدمًا وسأقوم فقط بتنفيذ دالة التبديل هذه
وأرى بالضبط ما قامت كيلي فقط بتنفيذه في النهاية.
إذا كان الهدف هو تبديل a وb، فلا يمكنني فقط القيام بتبديل صف كامل،
كما يبدو.
أحتاج إلى وضع إحدى هذه القيم، مثل الحليب، في كوب آخر،
ومن ثم تبديلها، ثم تبديلها.
لذا هي تتطلب ثلاث خطوات، وليست خطوة واحدة.
حسنًا، إذًا يمكنني تسمية هذا كوب أو متغير إضافي
لقد قدّمت لنا تارا أي شيء نريده-- tmp.
إذن سأقوم فقط بوضع a في tmp.
ثم سأقوم بوضع b في a، لأن a فارغة الآن.
ثم سأقوم بوضع tmp في b، ومن ثم لن أهتم حقًا
بما سيحدث لـ tmp-- في الواقع، إنه ما زال في مكانه هنا،
لكن تمت المهمة الآن.
لذا دعوني أمضي قدمًا وأري هل هذا البرنامج قيد التشغيل، لأن من الواضح يجب
أن يكون هذا مباشرًا للغاية.
لذلك دعوني أمضي قدمًا وأشغّل البرنامج
في سياق دالة رئيسية حيث يمكننا تشغيلها بالفعل.
في هذه التعليمة البرمجية، سأقوم بشرحها على النحو التالي.
ها هي دالتي الرئيسية.
سأقوم بتسمية متغير x، وأعطيه 1، وسأقوم بتسمية متغير y،
وأعطيه 2، وسأمضي قدمًا وسأقوم بالطباعة فقط من أجل فحص السلامة السريع--
x هو هذا، y هو ذاك.
ومن ثم سأقوم بتسمية دالة المبادلة البسيطة الرائعة هذه، x، y.
ومن ثم سأقوم بطباعة نفس الشيء بالضبط-- x هو هذا، y هو ذاك،
لذا يمكنني رؤية ذلك في تلك المتغيرات--
يمكنني أيضًا استخدام debug50، لكن من المفترض أن يكون هذا حلاً كاملاً،
أريد أنا أراه على الشاشة.
ها هي المبادلة.
لقد نسخت ولصقت هذا من قبل.
يبدو أن هذا لا يتطلب التفكير، فهو مباشر للغاية،
دعوني أنتقل إلى دليلي، وأقوم بتحويل هذا البرنامج برمجيًا، وهو، مفسد قليلاً،
noswap هو اسمه.
./noswap.
أووف.
دعوني أقوم بالتكبير.
كلا، هذا ليس ما كنت أعنيه، أليس كذلك؟
أنا أعني بالفعل أن يصبح الحليب عصير برتقال، ويصبح عصير البرتقال حليبًا،
أو x تصبح y، وy تصبح x، لا يبدو أن هذا يعمل.
ومجددًا، السحر الوحيد هو أن هذا الشيء يدعو إلى المبادلة.
حسنًا، ربما قد يعمل فقط لبعض الوقت.
إذن لا، كلا-- حسنًا.
الآن حان وقت مصحح الأخطاء.
أنا لا أفهم ما الذي يجري في برنامجي،
لا يضيء printf بالفعل هنا.
لذا دعوني أمضي قدمًا وأشغّل debug50 ./noswap.
تم فتح لوحات تصحيح الأخطاء جانبًا،
لكن تمهلوا، أحتاج إلى نقطة توقف.
سأقوم ببدء نقطة توقف في الأعلى، السطر الأول الذي أهتم به.
أنا حقًا لا أهتم بالأشياء الموجودة في الجزء العلوي.
الآن سأمضي قدمًا وأعيد تشغيل debug50 ./noswap، أليس كذلك؟
أرى الآن هنا، أنه تم تمييز أول السطر 9.
لاحظوا في الجهة اليمنى، قد تكون هذه هي الإجابة بمثال
على سؤالك السابق.
x وy بشكل مناسب، لكن فقط لأننا انتقلنا إلى 0--
لم أقم بذلك، أنا لا يجب بالضرورة أن أثق في هذا في جميع السياقات،
ولكن هذا هو سبب امتلاكهم قيم.
فهم بخلاف ذلك يتم تعريفهم بقيم ضئيلة، لكنني محظوظ مع الأصفار هنا.
دعوني أمضي قدمًا وأنتقل إلى هذا السطر، وإذا كنتم ترون ذلك، وإن كان صغيرًا،
في الجانب الأيمن، فيجب أن يأخذ x القيمة 1.
وإذا انتقلت إلى سطر واحد آخر، فيجب أن يأخذ y القيمة 2.
حسنًا، إذن أنا واثق تمامًا أن البرنامج حتى الآن صحيح.
سأمضي قدمًا وسأنتقل إلى printf.
ولاحظوا في النافذة الطرفية الزرقاء، أنا أرى خرجًا واحدًا.
الآن تصبح الأشياء مثيرة للاهتمام.
إذا تابعت في الانتقال عبر السطور، فسينهي ذلك التشغيل فقط
وهذا ليس كافيًا.
لذا لاحظوا هذه المرة سأقوم بالتمرير فوق الرمز الثالث هذا، Step Into.
الآن يمكنني نوعًا ما النزول في جحر الأرنب،
إذا جاز التعبير، والدخول إلى دالة المبادلة، ولاحظوا،
أن مصحح الأخطاء ينتقل إلى الدالة الأخرى تلك.
إذن هنا الآن، تغيّر السياق.
متغيراتي المحلية الآن هي a، وb، وtmp، وهذا حقًا أمر غريب.
A هو 1، b هو 2، كما هو متوقع، لأنني تجاوزت x، y.
وفي سياق هذه الدالة أطلق عليهما a، b فقط.
لكن لماذا tmp 32,767؟
فقط لأنه لا يمكن الوثوق به، إنه قيمة ضئيلة.
إذا أعطيت نفسك قيمة مؤقتة، مَن يدري ما الذي يوجد هناك؟
نحن محظوظون، وليس لدى تارا أي شيء في هذا الكوب،
ولكن قد يكون له قيمة ضئيلة، ربما يحتوي على بعض المياه الغازية،
ومن ثم كان يجب علينا استبدال تلك القيمة بطريقة ما.
حتى نكون واضحين، عندما تعلن عن متغيرات في برنامج،
كثيرًا ما تحتوي على قيم ضئيلة، مجرد قيم وهمية--
فالأصفار والآحاد الموجودة هناك أسفل الغطاء في هذه الشريحة،
ولكن لم تقم بتعيينها بنفسك.
ولكن لا بأس، لأنني صراحةً في إعداد السطر التالي هذا حيث tmp يعادل a.
لذا لا يهم ما كانت قيمته الغريبة الأصلية، إذا قمت بالنقر على التالي،
tmp الآن يعادل 1، ويُعرف أيضًا بـ
a.
لاحظوا الآن أن a ستصبح b إذا نظرتم إلى الجانب الأيمن.
الآن يبدو أن لدي a يعادل 2، b يعادل 2، والذي يعد أمرًا مزعجًا قليلاً ولكنه ليس بهذا السوء،
لأن لدي المتغير المنفصل هذا tmp، لذا ما زال لدي متغير في الجوار.
حتى الآن b على وشك أن تصبح 1، وقمت بتنفيذ تبديل صف.
حسنًا، عند هذه النقطة في القصة، السطر 22، تبدو التعليمة البرمجية صحيحة.
b أصبحت a، وa أصبحت b، ويتم تبديل القيم--
ويؤكد لي مصحح الأخطاء هذا ذلك بشكل مرئي.
إذن الآن، دعونا نقوم بخطوة و--
اللعنة.
لقد فقدته.
ما الذي يجري؟
بشكل حدسي؟
حتى ولو لم تروه أو تقوموا به من قبل، من الواضح إنه خطأ.
ما هو هذا الخطأ؟
ماذا يجب أن يحدث؟
أجل؟
الجمهور: [INAUDIBLE] [INAUDIBLE] قيمة جديدة
ليس لها نفس عنوان القيمة الأولى؟
ديفيد مالان: أجل.
ما يبدو أنه سيحدث هنا هو أجل، أنك تمر في x وy
وتطلق على ذلك a وb، لكن يبدو أن a وb هي نسخ من x وy.
وأنني أقوم بتبديل a وb بشكل صحيح وناجح جدًا،
ولكن نظرًا لأنها نُسخ، فليس لها تأثير على x و y الأصليين.
لذا فإن استعارتنا هنا للعصير ليست ملائمة تمامًا
لأنني لم أمرر نُسخ كيلي من عصير البرتقال و الحليب،
لقد ناولتُها عصير البرتقال والحليب الفعليين وتمكنت هي من تغيير القيم.
ولكن في سياق لغة C والتعليمة البرمجية، عندما تقوم بتمرير الوسيطات إلى دالة،
فإنك تمرر نُسخًا من تلك الوسيطات إلى الدالة.
إذن بديهيًا، ما هو الحل؟
لا يمكننا بالتأكيد المرور من دالة واحدة لنُسخ أخرى من القيم إذا كنا
نتوقع مبادلة الدالة، أو المعروفة أيضًا باسم
كيلي، لإجراء بعض التغييرات المفيدة لنا.
ما الذي يجب علينا تمريره إلى الدالة أو إلى كيلي بدلاً من ذلك؟
عناوين تلك القيم، أليس كذلك؟
أخبرتُها بمكان الحليب وعصير البرتقال.
لم أعطها نُسخًا منها، قلت لها، ها هو الحليب،
ها هو عصير البرتقال، استبدلي هذين.
في هذا الإصدار من التعليمة البرمجية، لقد قلتُ للتو،
ها هي نسخة من x، وها هي نسخة من y، يمكنك تسميتهما a وb-- نعم.
نحتاج الآن لاستخدام حرف العطف أو شيء من هذا القبيل للتمرير في خريطة،
إن شئت.
خريطة الكنز لتلك القيم وبالتالي المبادلة يمكنها تغيير القيم الأصلية.
والطريقة التي نفعل بها هذا تبدو غريبة قليلاً،
لكن كل ما سيتوجب علينا القيام به هو إجراء إضافة بسيطة هنا
والتي تبدو كما يلي.
يجب أن تبدو مثل هذا بدلاً من ذلك.
إذن هذا هو الإصدار المعطّل.
أو معطل بافتقاده للتأثير الذي نقصده حتى بالرغم من عمله.
هذا هو ما نحتاج للقيام به بدلاً من ذلك، وهو آخر جزء
من التعبير الجديد بالرموز في الوقت الحالي.
لقد رأينا نجمة في مكانين مختلفين
من قبل، ونحن الآن نستخدمها في سياق نهائي واحد.
عند تحديدك لنجمة هنا وهنا في الوسيطات إلى دالة، فتلك
تحديدًا هي الطريقة التي تقول بها للكمبيوتر، أنا
لا أتوقع عددًا صحيحًا int، بل عنوان عدد صحيح int.
أنا لا أتوقع عددًا صحيحًا int هنا، بل عنوان عدد صحيح int.
إذن مؤشران اثنان، وعنوانان اثنان لأعداد صحيحة.
بالأسفل هنا، tmp هي ما زالت مجرد عدد صحيح int.
لستُ بحاجة لإمعان التفكير في tmp، إنه مجرد كوب فارغ.
أعطني عددًا صحيحًا باسم tmp من الأسبوع الأول.
لكن، ما الذي أريد تخزينه في tmp؟
كل من a وb في هذا الإصدار عبارة عن عنوانين.
هل أريد أن أتذكر العنوان a والعنوان b؟
لا، بل أريد أن أتذكر حجم عصير البرتقال، حجم الحليب،
أريد أن أتذكر 1 و2، لا أهتم لمكان وجودهما في الذاكرة.
لذا فإن النجمة في هذا السياق، عندما لا يوجد ذكر لنوع البيانات،
فهناك فقط نجمة واسم متغير.
ذلك المتغير هو مؤشر وليس عملية ضرب،
فلا توجد هنا عمليات رياضية.
تلك النجمة هي عامل تشغيل الإسناد المؤشري الذي يقول، انتقل إلى هذا العنوان
واحصل على القيمة هناك.
لذلك فإذا كان هذا العنوان a في الموقع، فلا أعرف، 100 مثلما كان Brian،
وكان هذا العنوان b في الموقع 900 مثلما كانت Veronica،
فإن *a تعني الانتقال إلى وحدة البايت صاحبة الترتيب رقم 100 في الذاكرة وحصولي على تلك القيمة، وهي 1.
هذا يعني، بالأسفل هنا، الانتقال إلى العنوان b، وحصولي على تلك القيمة في العنوان 900،
وهي 2.
والمضي قدمًا وتخرين 1 في tmp.
امضِ قدمًا وانتقل إلى ذلك العنوان وضعه أيًا ما كان
في عنوان b-- إذن احصل على ذلك العنوان وضَعه هناك-- واحصل على ذلك العنوان،
واحصل على القيمة، وضَعها هناك في ذلك العنوان عن طريق الإسناد المؤشري.
وأخيرًا، انتقل إلى b في الذاكرة، كتلك الموجودة هناك، وضَع قيمة tmp هناك.
لذا في حين أن علامة العطف في المثال السابق لدينا تعني،
أخبرني ما هو عنوان متغير ما، والنجمة هي العكس.
عندما يكون لديك عنوان، فإنها تقول، انتقل إلى ذلك العنوان.
اتبع خريطة الكنز، حيث تحدد X المكان في ذلك الموقع في الذاكرة،
واحصل عليه عند قيمته.
إذن ما هو التأثير الصافي هنا؟
إذا قمتُ الآن بالفعل بفتح ليس هذا المثال، بل swap.c--
أداة التعطيل، فستعمل هذه الأداة بالفعل.
إذا قمتُ الآن بفتح swap.c، فسنرى الآن ما يلي بدلاً من ذلك.
التعليمة البرمجية هي نفسها تقريبًا، باستثناء أني قمتُ بلصقها
في هذا الإصدار الأخضر الجديد للدالة.
ولاحظوا هنا، أنه حدث تغيير لهذا.
لماذا أكتب الآن &x و$y بدلاً من x وy فقط؟
الجمهور: [INAUDIBLE] عنوان [INAUDIBLE] دوال [INAUDIBLE]..
ديفيد مالان: بالضبط.
دالة المبادلة الآن، يُتوقّع للإصدار المحسن الجديد
عنوانان-- نجمتان.
كل نجمة، كما هي معروفة أيضًا باسم المؤشرات، ليست مجرد قيم.
لذا فهذا يعني أنني أعرف أن x وy هما بالفعل عددان صحيحان من الأسبوع الأول.
والآن أحتاج لعنوان x وعنوان y
بحيث يمكن لهذه المبادلة اتباع خرائط الكنز هذه،
إذا جاز التعبير، والانتقال إلى تلك العناوين.
لذا الآن، عندما أقوم بتشغيل هذا البرنامج، فهذا يشبه إلى حد كبير الاستعارة المستخدمة مع كيلي
عندما أخبرتها بمكان الحليب وعصير البرتقال.
الآن قم بالمبادلة وانتقل إلى تلك المواقع على النحو التالي. make swap.
دعوني أمضي قدمًا ثم أنفّذ ./swap، إدخال--
آه!
يبدو أنها تعمل الآن.
يمكننا مشاهدة ذلك حتى باستخدام مصحح الأخطاء.
على الرغم من أنه لايبدو هناك خطأ، فلا يزال بإمكاني استخدام debug50
لرؤية برنامجي وفهمه، إذا لم يكن واضحًا - أوه،
ما زلت بحاجة إلى نقطة توقف.
دعونا نعين نقطة توقف مثلما سبق.
دعونا نعيد تشغيل debug50.
ستفتح لي اللوحة اليمنى بشكل تلقائي.
ودعونا نمضي قدمًا ونرى، ما إذا كنتُ بدأت الانتقال بالتدرج عبر هذا،
الآن أرى أن x تساوي 1، y تساوي 2، فتقوم الدالة printf بالطباعة بنفس القدر على الشاشة.
الآن سأمضي قدمًا وأنتقل بالتدرج إلى المبادلة،
لاحظوا الآن، إنها تبدو غريبة قليلاً،
لأن a الآن عبارة عن عنوان وb عبارة عن عنوان،
ولكن لا يزال tmp عددًا صحيحًا int بقيمة مهملة، ولكن يمكنني إصلاح ذلك.
الآن tmp يساوي 1، لكن لاحظوا، أن قيم a وb لا تتغير،
ولكن ما هو التغيير الواضح حسب التعليمة البرمجية؟
لذا لاحظوا، أن هذا غريب ومشفر.
a هي قيمة 0x هذه.
هذا عنوان سداسي عشري كبير، مثل هذا الذي توجد فيه a في الذاكرة.
ولكن أتدرون ما الأمر؟
إذا قمتُ بالنقر فوق المثلث الصغير، فيمكنني نوعًا ما متابعة ذلك المؤشر
والانتقال إليه.
يكون مصحح الأخطاء ذكيًا كذلك.
لذا *a، تنتقل إلى a تساوي 2؛ و*b في هذه اللحظة هي 2، لكن إذا تابعتُ،
فأنا الآن قمتُ بتبديل مفاجئ، ويمكنكم أن تروا أن هذه القيم قد تغيرت.
ومرة أخرى، نحن لا نهتم بماهية هذه العناوين،
لا يهمني ما هي العناوين الفعلية.
أنا أهتم بأن تمنحني الدوال هذه، لأنني الآن عندما
أرجع هنا في الطباعة، فإن القيم الآن بالفعل
قد تغيرت كما كنت أتوقع هذا طوال الوقت.
حسنًا.
كان ذلك معقدًا ، لكن نأمل أن يكون موضحًا لسبب عملها الآن حتى رغم أننا قمنا
بجعل هذه التعليمة البرمجية تبدو أكثر تشفيرًا.
إن لم يكن ذلك، فأي أسئلة مرحب بها.
أجل؟
الجمهور: هل هذا من المكان الذي [INAUDIBLE] فيه
ديفيد مالان: آه.
الجمهور: [INAUDIBLE] النجمة [INAUDIBLE] مؤشرات؟
ديفيد مالان: سؤال جيد.
هل نحن حقًا بحاجة لعلامات العطف هذه لأن لدينا بالفعل
هذه النجوم هنا؟
إجابة مختصرة، نعم، لأجل التماثل.
هذا يخبر الدالة ما الذي يمكن توقعه خلال العملية؛
وهذا ما يخبر جهاز الكمبيوتر ما الذي سيرسله بالفعل.
إذن ما هي المدخلات الفعلية لهذه الدالة؟
يجب أن تكون متناسقة.
أجل؟
الجمهور: تقوم قيمة [INAUDIBLE] بتبديل العناوين.
ديفيد مالان: نحن نقوم بتبديل ما هو موجود في العناوين.
الجمهور: إذن ماذا لو قمت بتغيير عنوان [INAUDIBLE]
ديفيد مالان: حسنًا.
الجمهور: هل سنقوم بتبديل العناوين بالقول أن 2 موجودة في 200 و1
موجود في [INAUDIBLE] والتي من الممكن أن تتغير.
ديفيد مالان: إجابة مختصرة، لا يمكنك للسبب التالي.
من الناحية الفنية: عندما نقوم بكتابة %x و%y، فسيتم تحويلهما إلى عنوان
x، وعنوان y.
فالتبديل من الناحية الفنية هو الحصول على نُسخ من شيء ما، ولم تتغير C.
لكن تحصل C الآن على نُسخ من عنوان
x، ونسخ من عنوان y ويطلق عليهما a وb.
بالتأكيد، يمكنك تبديل العناوين، ولكن للأسباب نفسها كما في السابق،
فلن يكون لها أي تأثير أساسي.
الاختلاف هنا هو لأنني أقوم بالتمرير في الخريطة، إذا جاز التعبير،
إلى x وy، عنوانيهما.
ومرة أخرى، فإن العنوان مثل--
أعتقد أننا الآن في 45 Quincy Street--
Cambridge، Massachusetts 02138، USA.
هذا يميز المبنى بشكل فريد.
تحدد هذه الأرقام السداسية العشرية الخاصة بـ 0x المواقع في الذاكرة بشكل فريد.
إذن هذا مثل القول الآن، أعطني عنوان x، أعطني عنوان y،
وأنا أقوم من الناحية الفنية بالتمرير في نُسخ هذه العناوين، ولكن لا يهم،
لأن القيام بتدوين النجمة الأن، فأنا أقول الانتقال إلى تلك العناوين
وتبديل الشخص الموجود بشكل مادي في هذا المبنى بشخص آخر.
حسنًا.
لذا دعونا فقط نضع هذا الآن في سياق ما الذي
يشتمل عليه أيضًا جهاز الكمبيوتر مجرد أنكم
شاهدتم بعض التسميات حول ذاكرة جهاز الكمبيوتر هذا.
إذن هذه هي الشريحة التي تحتوي على شبكة موضوع فوقها
فقط لتوصيل وجود وحدات بايت هنا، ويمكننا ترقيمها.
ولكن دعونا نفكر بشأن هذا الآن بطريقة تجريدية أكثر،
ودعوني أكشف أنه سيتضح أن جهاز الكمبيوتر يعامل
وحدات البايت المختلفة، المربعات المختلفة بطرق مختلفة فقط عن طريق التقليد.
من الواضح أنه في ذاكرة جهاز الكمبيوتر الخاص بك--
وهذا كله مجرد تمثيل فني--
في الجزء العلوي من شريحة الذاكرة تلك، إذا جاز التعبير،
وهو ما يسمى بنص برنامجك.
هذه طريقة رائعة وغير واضحة لقول
الأصفار والأحاد التي تم تحويلها برمجيًا إلى تعليماتك البرمجية.
نص البرنامج هو التعليمات البرمجية التي كتبتها في النظام الثنائي،
حيث تم تحميلها من الذاكرة.
لذا في macOS وWindows، تقوم بالنقر المزدوج على رمز،
فيتم تحميل هذا البرنامج في الذاكرة كما قلت الأسبوع الماضي.
يتم تحميله حرفيًا في الجزء العلوي من ذاكرة الكمبيوتر الخاص بك من الناحية النظرية.
ماذا أيضًا؟
حسنًا، الكومة هي الاسم الوهمي الذي يتم منحه لجزء الذاكرة الذي تأتي
الذاكرة منه عندما تقوم باستدعاء malloc.
لذا عندما قمتُ باستدعاء malloc في وقت سابق للحصول على مساحة كبيرة لبعض الأحرف،
فإنها كانت تأتي فقط من هذه المنطقة المفتوحة الكبيرة التي تسمى الكومة.
وهذا ما يستخدمه get_string والوظائف الأخرى كذلك.
حسنًا، من الواضح أن سبب المشكلة التي واجهناها للتو
هو أن الجزء السفلي من الذاكرة والذي يسمى المكدس.
والمكدس هو مساحة الذاكرة التي تستخدمها الوظائف عندما يتم استدعاؤها.
وهذا بالفعل ذو صلة بمثال noswap البسيط جدًا على النحو التالي.
لو افترضنا الآن أن في أي وقت تقوم باستدعاء وظيفة، فإن الذاكرة التي تستخدمها
تأتي من أسفل تلك الكتلة الكبيرة من الذاكرة،
التي يمكنك فيها رسم تلك الدالة، على سبيل المثال، هنا على الشاشة،
لأنه يتضح أن في أي وقت تقوم فيه باستدعاء دالة، فإن تلك الوظيفة
تحصل على شريحة من ذاكرتها الخاصة.
إذن على سبيل المثال، main تكون دائمًا البرنامج الأول الذي تستدعيه وظيفة،
ولذلك تحصل على الشريحة الأولى من الذاكرة في أسفل الشاشة هنا.
وبالتالي إذا كان هناك متغيران في main وهما x وy، فهذا مثل قول،
حسنًا ، أعطني جزءًا من الذاكرة يسمّى x وضع القيمة 1 فيه؛
أعطني جزءًا آخر من الذاكرة، وأطلق عليه y، وضَع قيمة فيه هنا.
ولكن تذكّروا، من المثال noswap الأول، أنه تم استدعاء وظيفة swap.
وهذا هو المكدس بالمعنى الحرفي.
أنتم تذهبون إلى قاعة طعام، مقهى، صينية واحدة للطعام، وفوقها واحدة أخرى،
وفوقها واحدة أخرى، وفوقها واحدة أخرى، بحيث يمكن للأشخاص أخذها
ووضع الطعام والأطباق عليها.
وبالمثل تمامًا في هذا النموذج، عندما تستدعي وظيفة،
فإنها تحصل على شريحة الذاكرة الخاصة بها، ولكن حرفيًا، من الناحية النظرية، أعلى
الإطار الموجود على المكدس.
إذن هذه هي قطعة الذاكرة الخاصة بدالة swap لديك،
وهي تحصل، أيضًا، على بعض المساحة.
تحصل على بعض المساحة لمتغير يسمّى a.
وتحصل على بعض المساحة لمتغير يسمّى b.
وخمّنوا ما الذي يدخل إلى هذين المتغيرين في المثال الأول هذا؟
نسخة من x ونسخة من y.
أتعلمون ما الأمر؟
كان بها متغير temp، لذلك تم الحصول عليه لامتلاك بعض المساحة هنا.
إذن سأسمّي هذا tmp.
وتذكّروا أنني قمت بتعيين tmp تساوي a، وبالتالي حصلت على 1.
وماذا حدث بعد؟
حسنًا وماذا فعلت بعد ذلك--
ماذا فعلت؟
دعوني أصل إلى هذا مباشرة.
كان لدينا a تحصل على b.
إذن ما الذي حدث هناك؟
إذن في هذا المثال هنا، تحصل a على القيمة 2، لذا فقد تغيرت.
ثم ماذا حدث هنا، حصلت b على القيمة 1، بالتالي فقد تغيرت.
لذا كانت swap تعمل بمعنى أنه قام بتبديل القيم،
ولكن المشكلة هي، عند تعيد وظيفة، فهذا الجزء من الذاكرة الذي
تم استخدامه سابقًا يتم استرداده حتى يمكن لشخص آخر الآن استخدامه،
كوظيفة أخرى.
إذن قمنا بكل ذلك العمل الشاق وبدون إجراء تبديل، وقمنا بذلك بشكل صحيح،
لقد قمنا بذلك في المكان الخطأ فقط.
لذا على النقيض من ذلك، هذا المثال التالي الذي قمنا به، والذي كان swap.c،
عامَل الذاكرة فقط بطريقة مختلفة قليلاً.
ما يزال لدى Main هذه المرة متغيرين يسمى x، وكان هذا a 1،
والآخر يسمّى y، وكان هذا a 2.
ثم تمت تسمية مبادلة واحدة هذه المرة، مجددًا
لديها متغير يسمّى a ومتغير يسمّى
b، ولكن ما الذي تم تخزينه في a وb؟
حسنًا الآن هم عناوين.
وأنا لا أعرف ما هذا، ولكن دعوني أقول ذلك بشكل اعتباطي أن هذا
هو الموقع 100، هذا الموقع--
دعونا نقول 104.
ولكن يمكنه أن يكون أي شيء، ونحن لا نكترث لهذه النقطة،
قد يشتمل على 0x من الناحية الفنية إذا كان الكمبيوتر يعرض لنا ذلك.
الذي يحدث في a هنا هو 100، والذي يحدث في b هنا هو 104.
وتلك هي عناوين x وy، وتقول التعليمات البرمجية حيث
كنا نستخدم جميع تلك النجوم الجديدة،
انتقل إلى عنوان 100 وخزِّن أي شيء في العنوان 100 في tmp.
ثم انتقل إلى العنوان الذي في b أو 104،
وخزِّن هذا في الموقع int *a، أيًا كان هناك.
بعد ذلك تقول، احصل على تلك القيمة 10، بالمناسبة،
وامضي قدمًا وضَع هذا هنا، إذن الآن قمنا
بعمل مختلف في مكان مختلف.
إذن الآن عندما يكون تنفيذ المبادلة قيد التشغيل، فلا يهم
إذا اختفت ذاكرتها لأنها تحولت أو تغيرت الآن
إلى الذاكرة الأخرى.
التي تم تمريرها للتو مثلما قامت كيلي بتغيير أو تحويل الأكواب
لقد أشرت إليها في الواقع بدلاً من نسخها.
وبعيدًا عن موضوعنا الآن، هناك أجزاء أخرى من الذاكرة التي يتم استخدامها بالفعل.
إذا كانت لديك متغيرات عالمية في أحد البرامج،
يتضح أنه يوجد بين ذاكرة النص وذاكرة
الكومة متغيراتك العالمية، إذا تمت تهيئتها باستخدام القيم
أو لم تتم تهيئتها بستخدام القيم، كما سيحدث مع علامة يساوي،
لكننا لا نهتم كثيرًا بذلك لأغراض اليوم.
وإذا كنت قد سمعت من قبل عن متغيرات البيئة، والتي
سنسمع عنها عندما نصل إلى برمجة الويب، التي يتم أيضًا
تخزينها في مكان آخر في الذاكرة.
ولكن الجزئين الأكثر إثارة للاهتمام من الذاكرة
هما المكدس والكومة، كما في هذه الحالة هنا.
ولكن لسوء الحظ إنه لمن السهل أن تنحرف الأشياء--
أعني، بعضكم جرّب أخطاء التجزئة بالفعل،
ودعونا نفكر لماذا قد يحدث ذلك.
إذن، هنا مثال مُستنبط من التعليمات البرمجية بواسطة تصميم الخطأ،
ولكن دعونا نتحدث عنها فقط باللغة الإنجليزية ما الذي تقوم به هذه السطور.
هذا السطر هنا، يقول، int *x، مرحبًا، جهاز الكمبيوتر،
أعطني متغيرًا سيقوم بتخزين عنوان عدد صحيح.
إذن أعطني مؤشرًا إلى عدد صحيح وهي الطريقة الأكثر حداثة لقول ذلك.
مرحبًا جهاز الكمبيوتر، أعطني متغيرًا آخر يقوم
بتخزين عنوان عدد صحيح وأطلق عليه y.
إذن x وy، هذا كل شيء.
هذا السطر جديد.
مرحبًا جهاز الكمبيوتر، قم بتخصيص مساحة كافية تناسب عددًا صحيحًا.
إذن sizeof int هي الصياغة الجديدة التي رأيناها في وقت سابق لاكتشاف
عدد وحدات البايت في أي عدد صحيح فقط.
الاحتمالات هي أنه سيعود كعدد 4 أو 32 وحدة بايت في معظم أجهزة الكمبيوتر.
إذن فهذا سيقول فقط، مرحبًا أيها المتصفح، أعطني 4 وحدات بايت من الذاكرة
وقم بتخزينها في هذا الموقع.
أو بدلاً من ذلك، قم بتخزينها في هذا المتغير، قم بتخزين هذا المتغير.
إذن ربما سيقول، حسنًا، هنالك أربع وحدات بايت في الموقع 100،
أو توجد أربع وحدات بايت في الموقع 900.
أو في أي مكان، لا يهم، نحن فقط نتذكر ذلك العنوان في x.
وتقول *x، انتقل إلى ذلك العنوان--
100 أو 900، أيًا كان، وضَع الرقم 42 هناك.
يقول هذا السطر التالي، انتقل إلى العنوان في y وضَع الرقم غير المحظوظ - المحوا،
المحوا--
13 هناك.
حسنًا ما هو العنوان في y؟
لم أقم بتخصيصه بعد.
ما هو العنوان في x؟
إنه حيث تخبرني malloc باستخدام مساحة.
ذلك آمن، كان ذلك مثل 100، 900، أيًا كانت القيمة،
ولكن هل قمتُ بتخصيص مساحة لـ y؟
إذن ما نوع القيمة الذي تحتوي عليه، إذا جاز التعبير؟
قيمة ضئيلة.
ربما تكون 0، ربما تكون مثل 32000-- لا نعرف،
لأنك إذا لم تقم بتحديد القيمة، فليس
من الآمن الوثوق بها أو القيام بأي شيء باستخدامها.
سيعطيني هذا على الأرجح إحدى أخطاء التجزئة تلك.
وبالفعل، إذا قمتُ بتشغيل برنامج مثل هذا،
فمن المحتمل جدًا أن أرى بالضبط ذلك النوع من المشاكل.
ربما يكون من الأفضل، رغم ذلك، رؤية هذا بالطريقة التي
سترسم صورة أكثر رسوخًا في الذاكرة، ولهذا، أعتقد أننا سنتناول--
في الدقائق العشر المتبقية لدينا، استخدام بعض من هذه الدقائق
لإلقاء نظرة على شيء قام أصدقاؤنا في جامعة ستانفورد
بإعداده سويًا باستخدام بعض من الرسوم المتحركة الطينية.
إنه يستغرق حوالي ثلاث دقائق، والأمر يستحق رسم
صورة للخطأ الذي يحدث بالضبط
عندما لا تستخدم الذاكرة بشكل صحيح.
إذا كنت تستطيع تخفيف الأضواء.
[VIDEO PLAYBACK]
[MUSIC PLAYING]
- مرحبًا، بينكي.
استيقظي!
حان الوقت لمتعة المؤشر!
- ما هذا؟
التعرّف على المؤشرات؟
أوه رائع!
- بداية جيدة، أعتقد أننا سنحتاج لمؤشرين اثنين.
- حسنًا.
تخصص التعليمات البرمجية تلك مؤشرين يمكن أن يشيرا إلى الأعداد الصحيحة.
- حسنًا.
حسنًا رأيت المؤشرين، لكن لا يبدو أنهما يشيران إلى أي شيء.
- هذا صحيح.
في البداية لا تشير المؤشرات إلى أي شيء.
تسمّى الأشياء التي تُشار إليها باسم pointees
وإعدادهم لخطوة منفصلة.
- أوه، صحيح، صحيح.
علمت ذلك.
pointees منفصلة.
إذن كيف تخصص pointee؟
- حسنًا.
حسنًا تخصص تلك التعليمات البرمجية pointee لعدد صحيح جديد،
ويقوم هذا الجزء بتعيين x للإشارة إليه.
- مهلاً، هذا يبدو أفضل.
إذن اجعله يفعل شيئًا.
- حسنًا.
كيف يمكنك الإشارة إلى المؤشر x لتخزين الرقم 42 في pointee الخاص به؟
بالنسبة إلى تلك الخدعة، أحتاج إلى عصاي السحرية الخاصة بالإسناد المؤشري.
- عصاك السحرية الخاصة بالإسناد المؤشري؟
هذا-- أمر رائع.
- هكذا تبدو التعليمات البرمجية.
سأقوم فقط بإعداد الرقم و--
[POP]
- انظر!
ها هو ينتقل.
إذن، يتبع القيام بالإسناد المؤشري على x السهم للوصول إلى pointee الخاص به.
في هذه الحالة، لتخزين 42 هناك.
مهلاً، حاول استخدامه لتخزين الرقم 13 عبر المؤشر الآخر، y.
- حسنًا.
سأنتقل من هنا فقط إلى y وأقوم بإعداد الرقم 13،
ثم آخذ عصا الإسناد المؤشري وفقط--
[BUZZING] آه!
- أوه مهلاً، هذا لم ينجح.
أقول، يا بينكي، لا أعتقد أن الإسناد المؤشري فكرة جيدة،
لأن إعداد pointee هو خطوة منفصلة
ولا أعتقد أننا فعلنا ذلك من قبل.
- اممم، ملاحظة جيدة.
- أجل.
لقد قمنا بتخصيص مؤشر y، لكننا لم نقم بتعيينها أبدًا لتشير إلى pointee.
- اممم، شديد الملاحظة.
-مهلاً، تبدو جيد هناك، يا بينكي.
هل يمكنك إصلاح ذلك بحيث يشير y إلى pointee نفسه مثل x؟
- بالتأكيد.
سأستخدم عصاي السحرية الخاصة بمهمة المؤشر.
- هل سيتسبب هذا في حدوث مشكلة كالسابق؟
- لا، هذا لا يلمس pointees.
يقوم فقط بتغيير مؤشر واحد للإشارة إلى الشيء نفسه كشيء آخر.
- أوه، أرى ذلك.
الآن يشير y إلى المكان نفسه مثل x.
إذن انتظروا، الآن تم إصلاح y.
لديه pointee.
إذن يمكنك تجريب عصا الإسناد المؤشري مجددًا لإرسال 13 إلى هناك.
- حسنًا.
ها هو يذهب.
- مهلاً، انظر إلى هذا.
يعمل الآن الإسناد المؤشري على y.
ونظرًا لأن المؤشرات تتشارك في pointee واحد، فإن كليهما يرى 13.
- نعم، مشاركة، أيًا كان.
إذن سنقوم بتبديل الأماكن الآن؟
- أوه لاحظ، الوقت ينفد.
- لكن--
[END PLAYBACK]
ديفيد مالان: حسنًا.
إذن آمل أن يرسخ هذا رؤية بصرية أكثر حول بعض هذه الأفكار،
ولكن دعونا الآن نحدد سياقات هذا في مجال من المحتمل أن يكون
أكثر إلمامًا بالطريقتين.
إذن الأولى، البعض منكم قد يعرفه بالفعل، خصوصًا
إذا كانت لديكم خبرة سابقة في البرمجة، من موقع ويب شائع جدًا
باسم Stack Overflow حيث الكثير من المبرمجين
يقومون بنشر الأسئلة وأحيانًا الإجابات أيضًا حول المشاكل الفنية الشائعة.
إذا تساءلت لماذا يسمّى Stack Overflow،
يتضح أنه يختزل إلى هذه الصورة هنا.
ولم يكن هذا خاطئًا أنني رسمت سهمًا واحدًا من الكومة يشير إلى أسفل،
وسهمًا آخر من المكدس يشير للأعلى.
فكلما قمت بتنفيذ دالة malloc، مرة تلو أخرى، تبدأ مساحات أكبر وأكبر،
بالظهور هنا، إذا جاز التعبير، و ستحصل على المزيد والمزيد من المساحة
والتي تذهب إلى هذا الاتجاه.
لكن كلما زادت الدالات التي تطلق عليها اسم-- دالة بعد دالة
بعد دالة بعد دالة، كل واحدة منها
تحصل على شريحتها الخاصة أو إطار الذاكرة هذا، أيضًا، الذي يرتفع لأعلى.
إذن هذا يبدو وكأنه تصميم سيئ للغاية، ولكن بصراحة، ولا يمكن تجنبه حقًا
لأنه إذا كان لديك مساحة محدودة من الذاكرة،
لا يمكنك تجنب بعضها البعض إلى الأبد.
ولذا يوجد خطر أساسي متمثل في تجاوز المكدس،
أو حتى تجاوز الكومة في الاتجاه المعاكس.
إذن موقع Stack Overflow هو إشارة إلى، على سبيل المثال، تسمية
دالات-- كثيرة،كثيرة،كثيرة،كثيرة، كثيرة،كثيرة،كثيرة،كثيرة،
كثيرة للغاية بحيث تتداخل مع أجزاء أخرى أو أجزاء من الذاكرة،
وبالتالي بما في ذلك خطأ التجزئة، وتجاوز سعة التخرين المؤقت للكومة
يكون في الاتجاه المعاكس، ويعرف هذا كثيرًا
عمومًا باسم تجاوزات سعة التخزين المؤقت، وسنرى المزيد من هذه في الأسابيع
المقبلة.
لكن الآن يمكننا مناقشة أمر المؤشرات،
دعونا نقدم ميزة واحدة نهائية ثم وجه مألوف.
إذن يتضح أنه يمكنك في الواقع إنشاء متغيراتك المخصصة
مثلما فعلنا مع سلسلة، ولكن أكثر تطورًا من ذلك.
على سبيل المثال، إذا رغبت في تنفيذ برنامج
يحتوي على عدة طلاب، قد أقوم بفعل شيء من هذا القبيل.
اسأل المستخدم ما هو التسجيل في الصف، ثم امضِ قدمًا
وأعطني مصفوفة من السلاسل، والمعروفة أيضًا باسم
char*s حاليًا بذلك الحجم، وبعد ذلك يمكنني أيضًا الحصول على مصفوفة أخرى للمساكن.
ويمكنني الحصول على مصفوفتين تحتوي واحدة على أسماء الطلاب،
وأخرى على مساكن الطلاب، ويمكنني تتبع أشياء أخرى.
مصفوفة أخرى لعناوين البريد الإلكتروني، ومصفوفة أخرى لأرقام الهواتف--
ولكن هذا يصير فوضويًا بسرعة، لأنه يمكنك أن تتخيل،
إذا كنتُ بحاجة إلى الأسماء والمساكن وعناوين البريد الإلكتروني والهواتف،
فسيصبح هناك الكثير من النسخ واللصق.
ولديّ فقط هذا التصميم الذي توجد لديّ فيه الكثير والكثير من المصفوفات
حيث يشير كل موقع قوس-- مثل قوس 0، قوس 1
افتراضيًا إلى الطالب نفسه عبر جميع تلك المصفوفات، مثل اممم!
تصميم فوضوي للغاية.
إذن، بتلويحة بيدي، دعوني أقوم بالفعل
بإصلاح هذه المشكلة الفورية من البداية بإدخال ميزة جديدة.
يمكنني اختراع أنواع البيانات الخاصة بي.
دعوني أمضي قدمًا وأعلن عن مصفوفة
تسمّى students "الطلاب" بهذا العدد الكبير من الطلاب، ولكن بطلاب بنوع البيانات.
تأتي لغة C بأنواع بيانات float، وbool، وchar، وint، وليس بنوع string، وبالتأكيد ولا بنوع student.
إذن يمكنك إنشاء أنواع بيانات مخصصة لك،
ويمكنك وضعها في ملفات الرؤوس الخاصة بك، والتي لم ننتهِ من أي منها.
ولكن يمكنني الاطلاع على، ويمكنكم مشاهدة المزيد من هذا في مجموعة المسائل التالية.
لذلك لا داعي للقلق إذا كان هذا يبدو مختصرًا إلى حد ما،
فإنه فقط مصمم لأن يكون إعلانًا تشويقيًا هنا.
ودالة struct.h هي الكيفية التي ستعلن أو تحدد بها النوع الخاص بك.
وتكون الكلمة الأساسية حرفيًا هي typedef struct لكي تكتمل البنية، أو بنية البيانات
على نحو أفضل.
ويأتي اسم بنية البيانات في النهاية بعد بعض الأقواس المتعرجة.
ثم بداخل الأقواس المتعرجة التي تقوم بتحديدها فقط،
حسنًا ماذا تريد أن يكون لدى الطالب؟
أرغب أن يكون لديه اسم، ومسكن، وربما رقم هاتف، وربما
عنوان بريد إلكتروني، وأي شيء أريده.
يمكنني تحديدًا الإضافة هنا.
وبذلك الآن في التعليمة البرمجية الفعلية لديّ، يمكن أن تكون لديّ مصفوفة من الطلاب الفعليين،
ويمكنني تحديدًا الوصول إليها باستخدام تدوين العلامات الجديد هذا كهذا مثلاً.
تعرفون أنه يمكنكم الفهرسة بداخل مصفوفة باستخدام تدوين علامة قوس.
ما لم تعرفوه حتى الآن، ربما، أنه إذا كانت هناك في ذلك الموقع
بنية، والتي تعرف أيضًا باسم
struct، فيمكنكم الوصول إلى الاسم، أو المسكن، أو الهاتف، أو البريد الإلكتروني،
أو أي شيء آخر هناك فقط من خلال استخدام تدوين علامة نقطة، والتي تمثل
آخر جزء من بناء الجملة الجديد ليومنا هذا.
وأي شيء آخر على نفس الحال.
يمكنني كتابة برنامج يقول كذا وكذا في مسكن كذا وكذا
بأن أقول فقط احصل على اسم الطالب صاحب الترتيب رقم i ومسكن الطالب صاحب الترتيب رقم i.
ويمكنني أن أكون أكثر براعة، وإذا كنت لا أرغب في طباعة تلك القيم فقط،
فيمكنني تمامًا، الآن، وأنا لا أرى أي مؤشرات فهم--
أو أني رأيتُ مؤشرات وسنفهمها عما قريب
عن طريق مجموعات المسائل والممارسة، يمكنني بالفعل القيام بذلك.
هذه مجرد معاينة خاطفة لسطر في التعليمة البرمجية
والذي يستخدم دالة جديدة تسمّى fopen.
fopen بمعنى فتح هذا الملف، وهي تشتمل على اسم الملف المطلوب فتحه.
ربما تعرفون عن ملفات CSV، إنها مثل جداول بيانات بسيطة،
قيم مفصولة بفاصلة.
و"w" بين علامة الاقتباس وإنهاء الاقتباس تعني كتابة.
لذلك فهذه الدالة تقول افتح الملف الذي يسمى students.csv في وضع الكتابة،
حتى أتمكن من الكتابة في هذا الملف.
لأنه في هذا المثال، كما سترون في الأيام القادمة،
أريد أن أكتب كل شيء في ملف.
لكن من الواضح أنه لاستخدام ملفات، نحتاج لمعرفة ما هو المؤشر،
ومن الغريب إلى حد ما أنها كلها بأحرف كبيرة،
ولكن هناك نوع بيانات في لغة C يسمى "file"، وهو مؤشر.
لاختصار القصة الطويلة، ما سترونه في مجموعة المسائل التالية
ونحن نستكشف عالم التحليلات الجنائية هو القدرة
على استخدام المؤشرات وعدد قليل من الدوال الجديدة لفتح الملفات واستعادة
عنوان ذلك الملف في الذاكرة حتى يمكنك الانتقال إلى ذلك العنوان،
وتغيير محتويات ملف، وإعادة حفظه.
كل منّا يُسَلِّم بأنك يمكنك الانتقال حاليًا إلى File "ملف"، Open "فتح" وFile "ملف"،
Save "حفظ"، لكن ما يحدث في الواقع، هو أن المؤشرات متضمنة،
وأنه يتم تحميل البرامج في الذاكرة، وأن الكمبيوتر
يقوم بالإسناد المؤشري أو ينتقل الى تلك العناوين
ويغيّر ما هو موجود في تلك المواقع في الذاكرة.
الآن لماذا قد ترغب في القيام بهذا؟
حسنًا إليكم هنا، بالطبع، زاميلا-- ربما
تذكرونها من بعض مجموعات المسائل والتعليمات المساعدة.
من الواضح أنه يمكننا محاولة تحسين صورتها هذه من خلال التكبير،
ويوجد هنا تقريبًا نفس الدقة الموجودة في عينيها.
يبدو أني لا أرى بريق أي شعار جنائي
على سترته أو سترتها كبريق عيني زاميلا.
إذا قمتَ بتكبير صورة، والصورة، لو تذكرون، من الأسبوع 0
هي مجرد شبكة من وحدات البكسل أو النقاط، وهذا كل ما توصلتم إليه.
وربما يمكنكم جعلها متجانسة قليلاً أو منسقة الألوان،
لكن لا يمكنكم تحديدًا إجراء تحسين "enhance،" علامة اقتباس-علامة إنهاء الاقتباس،
ورؤية المزيد من البريق في عين زاميلا،
لأن الصورة في نهاية المطاف هي مجرد bitmap "صورة نقطية"، خريطة من وحدات البكسل--
من أعلى لأسفل، من اليسار لليمين.
على سبيل المثال، إليكم وجه مبتسم.
إذا ألقيتم نوعًا ما نظرةً إلى الوراء، فيمكنكم نوعًا ما رؤية وجه مبتسم باللون الأسود
على خلفية بيضاء.
وإذا قررنا للتوّ كأشخاص، فدعونا نمثل النقاط البيضاء
بالواحدات والنقاط السوداء بالأصفار، فقد يكون هذا هو الموجود في الملف،
وهذا ما يراه الشخص.
لذا إذا كانت لدينا القدرة على فتح ذلك من ملف، فقم بتخزينه في الذاكرة،
ومن ثم انتقل باستخدام المؤشرات إلى تلك المواقع في الذاكرة،
فيمكننا حتى تغيير الوجه المبتسم إلى وجه غير سعيد، على سبيل المثال، أو تلوينه
أو القيام بأي عدد من الأشياء له.
الآن بنظرة سريعة، هناك الكثير الذي يحدث في الملفات،
لأن ماهية الملف عبارة عن مجموعة من القواعد التي قررها البشر
حيث إن البشر منذ سنوات أقروا في ملف bitmap "الصورة النقطية"،
ملف BMP-- بأن تنسيق ملفات أقدم كثيرًا لكنه ما يزال شائعًا للصور، أقر البشر
تحديدًا، بأننا كأننا سنضع مجموعة من القيم الخاصة
في وحدات البايت الأولى للملف، ثم بعض القيم الخاصة
أكثر من وحدات بكسل RGB الفعلية في بقية الملف.
إذن فهذا مصمم ليبدو مشفرًا للوهلة الأولى،
وواجبكم المنزلي التالي سيكون استكشاف هذا،
ولكن كل ماهيته عبارة عن قاعدة لما تعنيه الأصفار والواحدات
في هذه المواقع المختلفة.
وبالفعل، فإن التحدي الذي أمامنا سيكون القيام بعدد من الأشياء.
أولاً وقبل كل شيء توضيح--
من فعلها؟
إنه نوع من لغز جريمة قتل يوجد فيه دليل مخفي في صورة،
ولكنها صورة مشوشة قليلاً وسيتعين عليك
اكتشاف الرسائل السرية في الصورة
عن طريق تحميل تلك الصورة فيها، وتعديلها، ووضع نوع من عامل تصفية أحمر اللون
بأعلاها ورؤية الرسالة السرية، لكن سيتم كل ذلك رقميًا؛ ثانيًا،
في الواقع تغيير حجم الصور وأخذ هذا العدد الكبير من وحدات البكسل في هذا الوجه المبتسم
الكبير أو شيء آخر وجعله أكبر،
أو إذا كان واسعًا بدرجة أكبر، فبجعله أصغر
ومعرفة كيفية القيام بذلك التمرين؛
وأخيرًا، لقد قمنا بالتقاط بعض الصور الفوتوغرافية لجميع طاقم العاملين في CS50
في كامبريدج ونيو هافن.
لسوء الحظ لقد أتلفنا بطاقة الذاكرة أو ضيّعناها دون قصد،
لكننا قمنا بصورة تحليلية كبحث جنائي لها، نسخة من جميع الأصفار والواحدات باستخدام جميع
صور طاقم العاملين، وسنحتاج
لكتابة التعليمة البرمجية التي تسترد في الواقع كل ملفات JPEG
أو الصور الفوتوغرافية من تلك البطاقة الرقمية بفتح ملف،
وقراءة ماهيتها ومكانها في تلك الأصفار والواحدات،
وفهمهما، وإعادة كتابتهما تحديدًا
للقرص باستخدام الدوالّ التي سنقدمها لكم في مجموعة
المسائل نفسها.
ولكن بالطبع، إمكانية قيامنا بهذا هو أمر مفروغ منه،
ويمكنكم فقط القيام بأكثر من ذلك بكثير.
وفي الحقيقة، هذا الأسبوع يتمحور حول حل تلك المسائل
لأنها تتعرف على قيود أجهزة الكمبيوتر،
لذلك فكرنا أننا سنختم في الثواني الأخيرة بهذا
المثال الواقعي جدًا من مسلسل فيوتشوراما.
[VIDEO PLAYBACK]
- قم بتكبير كرة الموت هذه.
لماذا ما تزال غير واضحة؟
- هذا كل ما باستطاعتنا من دقة.
فجعلها أكبر لا يجعلها أوضح.
- يفعل ذلك على CSI Miami.
-أُف.
[END PLAYBACK]
ديفيد مالان: وهذا كل شيء بالنسبة إلى CS50، سأراكم المرة القادمة.
[APPLAUSE]