ترجمة آلية تحت المراجعة
الأسبوع 2: المصفوفات
المصدر الأصلي على CS50المحاضرة الثانية (Lecture 2)
- مرحبًا!
- مستويات القراءة
- تصحيح الأخطاء
- التجميع
- المصفوفات
- سلاسل
- طول السلسلة
- وسيطات سطر الأوامر
- حالة الخروج
- التلخيص
مرحبًا! (Welcome!)
- في جلستنا السابقة، تعلمنا عن لغة C، وهي لغة برمجة تعتمد على النصوص.
- هذا الأسبوع، سنلقي نظرة أعمق على العناصر الأساسية الإضافية التي ستدعم أهدافنا المتمثلة في تعلم المزيد عن البرمجة من القاعدة إلى القمة.
- بشكل أساسي، بالإضافة إلى أساسيات البرمجة، تدور هذه الدورة حول حل المشكلات. وبناءً على ذلك، سنركز أيضًا بشكل أكبر على كيفية التعامل مع مشكلات علوم الحاسوب.
- في نهاية الدورة التدريبية، ستتعلم كيفية استخدام هذه العناصر الأساسية المذكورة أعلاه لحل مجموعة كاملة من مشكلات علوم الحاسوب.
- نحن نعتبر العديد من هذه الحلول التي تقدمها علوم الحاسوب أمرًا مفروغًا منه.
مستويات القراءة (Reading Levels)
- إحدى المشكلات الواقعية التي سنحلها في هذه الدورة هي فهم مستويات القراءة.
- بمساعدة بعض زملائك، قدمنا قراءات في مستويات قراءة مختلفة.
- سنقوم بقياس مستويات القراءة هذا الأسبوع كأحد تحديات البرمجة العديدة التي تواجهك.
تصحيح الأخطاء (Debugging)
- سوف يرتكب الجميع أخطاء أثناء البرمجة.
- تصحيح الأخطاء هي عملية تحديد موقع الأخطاء وإزالتها من الشيفرة الخاصة بك.
- يُطلق على إحدى تقنيات تصحيح الأخطاء التي ستستخدمها أثناء هذه الدورة التدريبية لتصحيح أخطاء الشيفرة الخاصة بك تصحيح أخطاء البطة المطاطية، حيث يمكنك التحدث إلى كائن غير حي (أو إلى نفسك) للمساعدة في التفكير في الشيفرة الخاصة بك ولماذا لا تعمل على النحو المنشود. عندما تواجه تحديات مع الشيفرة الخاص بك، فكر في كيفية التحدث بصوت عالٍ إلى بطة مطاطية حول مشكلة الشيفرة. إذا كنت تفضل عدم التحدث إلى بطة بلاستيكية صغيرة، فنحن نرحب بك للتحدث إلى إنسان قريب منك!
- لقد أنشأنا CS50 Duck و CS50.ai كأدوات يمكنها مساعدتك في تصحيح أخطاء الشيفرة الخاصة بك.
-
خذ بعين الاعتبار الشيفرة التالي:
// Missing #include for stdio.h int main(void) { printf("hello, world\n"); }لاحظ كيف يتم استدعاء الدالة
#includeتوجيه لـstdio.hمفقود. هذا الملف الرأسي مطلوب لـ وظيفةprintfلتعمل بشكل صحيح. وبدون ذلك، لن يتعرف المترجم على وظيفةprintfوستؤدي إلى ظهور خطأ. -
بالمثل، ضع في اعتبارك الشيفرة التالي:
// Misspelled stdio.h #include <studio.h> int main(void) { printf("hello, world\n"); }لاحظ كيف
stdio.hبه خطأ إملائي كـstudio.h. سيؤدي هذا الخطأ المطبعي إلى حدوث خطأ في الترجمة لأن المترجم لا يمكنه العثور على ملف يسمىstudio.h. اسم ملف الرأس الصحيح هوstdio.h، والتي تعني "الإدخال/الإخراج القياسي". -
قد ننسى أن نعلن عن نوع المتغير:
// Missing cs50.h, variable's type, semicolon, %s, and second printf argument. #include <stdio.h> int main(void) { name = get_string("What's your name? ") printf("hello, world\n"); }لاحظ وجود أخطاء متعددة. أولاً: نوع لم يتم التصريح عن
name. ثانيا، مكتبةcs50.hمفقودة للسماح لنا باستخدامهاstring. ثالثًا، هناك فاصلة منقوطة مفقودة بعدget_stringمكالمة. رابعا، لا تستخدم عبارةprintfفعليًاnameمتغير. - ستؤدي بعض الأخطاء إلى ظهور رسالة خطأ. والبعض الآخر عبارة عن أخطاء منطقية لن تؤدي إلى ظهور رسالة، ولكنها ستؤدي إلى سلوك غير متوقع في برنامجك. يمكن استخدام عبارة
- ال
يتم استخدام الدالة
printfلتصحيح أخطاء الشيفرة الخاصة بك. خذ بعين الاعتبار ما يلي: -
خذ بعين الاعتبار الصورة التالية من الأسبوع الماضي:

-
خذ بعين الاعتبار الشيفرة التالي الذي يحتوي على خطأ تم إدراجه عمدًا بداخله:
// Buggy example for printf #include <stdio.h> int main(void) { for (int i = 0; i <= 3; i++) { printf("#\n"); } }لاحظ أن هذا الشيفرة يطبع أربع كتل بدلاً من ثلاث.
- النوع
code buggy.cفي النافذة الطرفية واكتب الشيفرة أعلاه. - عند تشغيل هذا الرمز، تظهر أربعة قوالب بدلاً من الثلاثة المقصودة.
-
printfهي طريقة مفيدة جدًا لتصحيح أخطاء الشيفرة الخاصة بك. يمكنك تعديل الشيفرة الخاص بك على النحو التالي:// Buggy example for printf #include <stdio.h> int main(void) { for (int i = 0; i <= 3; i++) { printf("i is %i\n", i); printf("#\n"); } }لاحظ كيف يقوم هذا الشيفرة بإخراج قيمة
iأثناء كل تكرار للحلقة حتى نتمكن من تصحيح الشيفرة الخاصة بنا. -
بتشغيل هذا الشيفرة، سوف ترى العديد من العبارات، بما في ذلك
i is 0,i is 1,i is 2وi is 3. عند رؤية هذا، قد تدرك أن الشيفرة الإضافية تحتاج إلى تصحيح على النحو التالي: تم استبدال#include <stdio.h> int main(void) { for (int i = 0; i < 3; i++) { printf("#\n"); } }لاحظ
<=بـ<. -
يمكن تحسين هذا الشيفرة كما يلي:
// Buggy example for debug50 #include <cs50.h> #include <stdio.h> void print_column(int height); int main(void) { int h = get_int("Height: "); print_column(h); } void print_column(int height) { for (int i = 0; i <= height; i++) { printf("#\n"); } }لاحظ أن تجميع هذا الرمز وتشغيله لا يزال يؤدي إلى حدوث خطأ.
- لمعالجة هذا الخطأ، سوف نستخدم أداة جديدة.
- الأداة الثانية في تصحيح الأخطاء تسمى a مصحح الأخطاء، أداة برمجية أنشأها المبرمجون للمساعدة في تعقب الأخطاء في الشيفرة.
- في VS Code، تم توفير مصحح أخطاء تم تكوينه مسبقًا لك يسمى
debug50. -
لاستخدام مصحح الأخطاء هذا، قم أولاً بتعيين a نقطة التوقف عن طريق النقر على يسار سطر من الشيفرة الخاصة بك، فقط على يسار رقم السطر. عندما تضغط هناك، ستظهر لك نقطة حمراء. تخيل ذلك كعلامة توقف، تطلب من مصحح الأخطاء التوقف مؤقتًا حتى تتمكن من التفكير في ما يحدث في هذا الجزء من الشيفرة الخاصة بك.

- ثانيًا، اركض
debug50 ./buggy. ستلاحظ أنه بعد تشغيل مصحح الأخطاء وسيضيء سطر من الشيفرة الخاصة بك بلون يشبه الذهب. بكل معنى الكلمة، الشيفرة موجود متوقف مؤقتًا في هذا السطر من الشيفرة. لاحظ في الزاوية العلوية اليسرى كيف يتم عرض جميع المتغيرات المحلية، بما في ذلكh، والتي ليس لها قيمة حاليًا. في الجزء العلوي من نافذتك، يمكنك النقر فوق زرstep over، وسيستمر في التحرك عبر الشيفرة الخاصة بك. لاحظ كيف قيمةiيزداد كلما تقدمت خلال الحلقة. - على الرغم من أن هذه الأداة لن تظهر لك مكان الخطأ الخاص بك، إلا أنها ستساعدك على إبطاء السرعة ومعرفة كيفية تشغيل الشيفرة الخاصة بك خطوة بخطوة. يمكنك استخدام
step intoكطريقة لمزيد من البحث في تفاصيل رمز الأخطاء الخاص بك. - الطريقة الثالثة لتصحيح الأخطاء هي التحدث إلى بطة مطاطية أو كائن غير حي أو شخص لوصف المشكلة التي تواجهها والخطوات المحددة التي تتخذها لحل هذه المشكلة كوسيلة لاكتشاف خطأك.
- أخيرًا، ، المعروف أيضًا باسم *CS50 Duck*، يمكن أن يساعدك في تصحيح أخطاء الشيفرة الخاصة بك.
التجميع (Compiling)
- تذكر أنك تعلمت الأسبوع الماضي عن أ مترجم، برنامج كمبيوتر متخصص يقوم بالتحويل الشيفرة المصدرية إلى كود الجهاز التي يمكن للكمبيوتر فهمها.
-
نقوم بالتحويل الشيفرة المصدرية في كود الجهاز باستخدام برنامج خاص جدًا يسمى a مترجم. سنقدم لك اليوم مترجمًا يسمح لك بتحويل الشيفرة المصدرية إلى لغة البرمجة C إلى رمز الجهاز.
flowchart LR in["الشيفرة المصدرية"] --> BOX[" compiler "] BOX --> out["شيفرة الآلة"] -
على سبيل المثال، قد يكون لديك برنامج كمبيوتر يبدو كالتالي:
#include <stdio.h> int main(void) { printf("hello, world\n"); } -
سيأخذ المترجم الشيفرة أعلاه ويحوله إلى كود الجهاز الذي قد يبدو كالتالي:
01010100 01001000 01001001 01010011 00100000 01001001 01010011 00100000 01000011 01010011 00110101 00110000لاحظ أن ما ورد أعلاه للتوضيح فقط. سيكون رمز الجهاز للمشكلة أعلاه أطول بكثير.
-
VS Code، بيئة البرمجة المقدمة لك كطالب CS50، تستخدم مترجمًا يسمى
clang(والتي تعني "الواجهة الأمامية لعائلة لغة C"). - يمكنك إدخال ما يلي في النافذة الطرفية لتجميع الشيفرة الخاصة بك:
clang -o hello hello.c. -
وسيطات سطر الأوامر
يتم توفير في سطر الأوامر ل
clangك-o hello hello.c. - قيد التشغيل
./helloفي نافذة الوحدة الطرفية، يعمل برنامجك على النحو المنشود. -
خذ بعين الاعتبار الشيفرة التالي من الأسبوع الماضي:
#include <cs50.h> #include <stdio.h> int main(void) { string name = get_string("What's your name? "); printf("hello, %s\n", name); } - لتجميع هذا الرمز، يمكنك الكتابة
clang -o hello hello.c -lcs50. - إذا أردت الكتابة
make hello، يقوم بتشغيل أمر ينفذ clang to create an output file that you can run as a user. - تمت برمجة VS Code مسبقًا على هذا النحو
سيقوم
makeبتشغيل العديد من وسائط سطر الأوامر مع clang for your convenience as a user. - بينما يتم تقديم ما ورد أعلاه كتوضيح، بحيث يمكنك فهم عملية ومفهوم تجميع الشيفرة بشكل أعمق، باستخدام
makeفي CS50 جيد تمامًا وهو التوقع! - يتضمن التجميع أربع خطوات رئيسية، بما في ذلك ما يلي:
-
أولاً، المعالجة المسبقة هو المكان الذي توجد فيه ملفات الرأس في الشيفرة الخاصة بك، والتي تم تحديدها بواسطة a
#(such as#include <cs50.h>) يتم نسخها ولصقها بشكل فعال في ملفك. خلال هذه الخطوة، الشيفرة من تم نسخcs50.hإلى برنامجك. وبالمثل، تماما كما يحتوي على الشيفرة الخاصة بك#include <stdio.h>، الشيفرة موجود بداخله يتم نسخstdio.hفي مكان ما على جهاز الكمبيوتر الخاص بك إلى برنامجك. ويمكن تصور هذه الخطوة على النحو التالي:string get_string(string prompt); int printf(string format, ...); int main(void) { string name = get_string("What's your name? "); printf("hello, %s\n", name); } -
ثانيًا، في مرحلة الترجمة (compiling)، يتحول برنامجك إلى شيفرة التجميع (assembly code). ويمكن تصور هذه الخطوة كما يلي:
... main: .cfi_startproc # BB#0: pushq %rbp .Ltmp0: .cfi_def_cfa_offset 16 .Ltmp1: .cfi_offset %rbp, -16 movq %rsp, %rbp .Ltmp2: .cfi_def_cfa_register %rbp subq $16, %rsp xorl %eax, %eax movl %eax, %edi movabsq $.L.str, %rsi movb $0, %al callq get_string movabsq $.L.str.1, %rdi movq %rax, -8(%rbp) movq -8(%rbp), %rsi movb $0, %al callq printf ... -
ثالثًا، في مرحلة التجميع (assembling)، يحوّل المُجمِّع (assembler)، وهو أداة ضمن سلسلة أدوات المترجم، شيفرة التجميع إلى شيفرة الآلة. ويمكن تصور هذه الخطوة كما يلي:
01111111010001010100110001000110 00000010000000010000000100000000 00000000000000000000000000000000 00000000000000000000000000000000 00000001000000000011111000000000 00000001000000000000000000000000 00000000000000000000000000000000 ... -
أخيرًا، في مرحلة الربط (linking)، تُدمج شيفرة الآلة المترجمة مسبقًا من المكتبات التي ضمّنتها مع شيفرتك، ثم يُنتج الملف التنفيذي النهائي.
01111111010001010100110001000110 00000010000000010000000100000000 00000000000000000000000000000000 00000000000000000000000000000000 00000001000000000011111000000000 00000001000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 10100000000000100000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 01000000000000000000000000000000 00000000000000000100000000000000 00001010000000000000000100000000 01010101010010001000100111100101 01001000100000111110110000010000 00110001110000001000100111000111 01001000101111100000000000000000 00000000000000000000000000000000 00000000000000001011000000000000 11101000000000000000000000000000 00000000010010001011111100000000 00000000000000000000000000000000 00000000000000000000000001001000 ...
المصفوفات (Arrays)
- في الأسبوع 0، تحدثنا عن ذلك أنواع البيانات مثل
bool,int,char,string، إلخ. - يتطلب كل نوع بيانات مقدارًا معينًا من موارد النظام (هذه هي الأحجام النموذجية في بيئة CS50):
-
bool1 بايت -
int4 بايت -
long8 بايت -
float4 بايت -
double8 بايت -
char1 بايت -
string؟ بايت
-
-
داخل جهاز الكمبيوتر الخاص بك، لديك كمية محدودة من الذاكرة المتوفرة.

-
فعليًا، على ذاكرة جهاز الكمبيوتر الخاص بك، يمكنك تخيل كيفية تخزين أنواع معينة من البيانات على جهاز الكمبيوتر الخاص بك. قد تتخيل أن أ
char، الذي يتطلب بايت واحد فقط من الذاكرة، قد يبدو كما يلي:
-
وبالمثل،
int، والذي يتطلب 4 بايت، قد يبدو كما يلي:
-
يمكننا إنشاء برنامج يستكشف هذه المفاهيم. داخل المحطة الخاصة بك، اكتب
code scores.cواكتب الشيفرة كما يلي:// Averages three (hardcoded) numbers #include <stdio.h> int main(void) { // Scores int score1 = 72; int score2 = 73; int score3 = 33; // Print average printf("Average: %f\n", (score1 + score2 + score3) / 3.0); }لاحظ أن الرقم الموجود على اليمين عبارة عن قيمة فاصلة عائمة
3.0، بحيث يتم عرض العملية الحسابية كقيمة نقطة عائمة في النهاية. - قيد التشغيل
make scoresيجمع البرنامج. ثم الجري./scoresينفذه. -
يمكنك أن تتخيل كيفية تخزين هذه المتغيرات في الذاكرة:

- المصفوفات عبارة عن سلسلة من القيم المخزنة بالتعاقب في الذاكرة.
-
int scores[3]هي طريقة لإخبار المترجم بتزويدك بثلاثة أماكن متتالية في ذاكرة الحجمintلتخزين ثلاثةscores. بالنظر إلى برنامجنا، يمكنك مراجعة الشيفرة الخاصة بك على النحو التالي: يقوم// Averages three (hardcoded) numbers using an array #include <cs50.h> #include <stdio.h> int main(void) { // Scores int scores[3]; scores[0] = 72; scores[1] = 73; scores[2] = 33; // Print average printf("Average: %f\n", (scores[0] + scores[1] + scores[2]) / 3.0); }لاحظ ذلك تم الآن إنشاء
scores[0]بفحص القيمة في هذا الموقع من الذاكرةindexing intoتم استدعاء المصفوفةscoresفي الموقع0لمعرفة القيمة المخزنة هناك. -
يمكنك أن ترى كيف أنه بينما يعمل الشيفرة أعلاه، لا تزال هناك فرصة لتحسين الشيفرة الخاص بنا. قم بمراجعة الشيفرة الخاصة بك على النحو التالي:
// Averages three numbers using an array and a loop #include <cs50.h> #include <stdio.h> int main(void) { // Get scores int scores[3]; for (int i = 0; i < 3; i++) { scores[i] = get_int("Score: "); } // Print average printf("Average: %f\n", (scores[0] + scores[1] + scores[2]) / 3.0); }لاحظ كيف نقوم بالفهرسة
scoresباستخدام يتم توفيرscores[i]حيثiبواسطةforحلقة. -
يمكننا تبسيط أو مجردة بعيدا حساب المتوسط. قم بتعديل الشيفرة الخاص بك كما يلي: تم الإعلان عن
// Averages three numbers using an array, a constant, and a helper function #include <cs50.h> #include <stdio.h> // Constant const int N = 3; // Prototype float average(int length, int array[]); int main(void) { // Get scores int scores[N]; for (int i = 0; i < N; i++) { scores[i] = get_int("Score: "); } // Print average printf("Average: %f\n", average(N, scores)); } float average(int length, int array[]) { // Calculate average int sum = 0; for (int i = 0; i < length; i++) { sum += array[i]; } return sum / (float) length; }لاحظ أنه تم استدعاء دالة جديدة
average. علاوة على ذلك، لاحظ أconstأو القيمة الثابتة لـ تم الإعلان عنN. والأهم من ذلك، لاحظ كيفaverageتستغرق الوظيفةint array[]، مما يعني أن الوظيفة يمكن أن تتلقى مصفوفة كمعلمة. - لا يمكن للمصفوفات أن تكون حاويات فحسب، بل يمكن تمريرها بين الوظائف.
سلاسل (Strings)
- A
stringهو ببساطة مصفوفة من القيم من النوعchar: مجموعة من الأحرف. -
للاستكشاف
charوstring، اكتبcode hi.cفي نافذة الوحدة الطرفية واكتب الشيفرة كما يلي:// Prints chars #include <stdio.h> int main(void) { char c1 = 'H'; char c2 = 'I'; char c3 = '!'; printf("%c%c%c\n", c1, c2, c3); }لاحظ أن هذا سيؤدي إلى إخراج سلسلة من الأحرف.
-
وبالمثل، قم بإجراء التعديل التالي على الشيفرة الخاص بك:
// Prints chars' ASCII codes #include <stdio.h> int main(void) { char c1 = 'H'; char c2 = 'I'; char c3 = '!'; printf("%i %i %i\n", c1, c2, c3); }لاحظ أنه تتم طباعة رموز ASCII عن طريق الاستبدال
%cمع%i. -
بالنظر إلى الصورة التالية، يمكنك أن ترى كيف أن السلسلة عبارة عن مجموعة من الأحرف التي تبدأ بالحرف الأول وتنتهي بحرف خاص يسمى a
NUL character(ملاحظة: NUL الذي يحتوي على حرف L واحد هو الحرف '\0'، ويختلف عن NULL الذي يحتوي على حرفين L):
-
بتخيل ذلك بالنظام العشري، ستبدو المصفوفة كما يلي:

-
ويمكننا أن نتصور ما سبق على النحو التالي:
// Prints string #include <cs50.h> #include <stdio.h> int main(void) { string s = "HI!"; printf("%s\n", s); }لاحظ أنه يتم تمثيل كافة الأحرف داخل ملف
string. -
لفهم كيفية عمل أ
string، قم بمراجعة الشيفرة الخاصة بك على النحو التالي: تعرض عبارة// Treats string as array #include <cs50.h> #include <stdio.h> int main(void) { string s = "HI!"; printf("%c%c%c\n", s[0], s[1], s[2]); }لاحظ كيف يتم استدعاء الدالة
printfثلاث قيم من المصفوفة الخاصة بنا تسمىs. -
كما كان من قبل، يمكننا الاستبدال
%cمع%iعلى النحو التالي:// Prints string's ASCII codes, including NUL #include <cs50.h> #include <stdio.h> int main(void) { string s = "HI!"; printf("%i %i %i %i\n", s[0], s[1], s[2], s[3]); }لاحظ أن هذا يؤدي إلى طباعة رموز ASCII للسلسلة، بما في ذلك NUL.
-
دعونا نتخيل أننا نريد أن نقول كليهما
HI!وBYE!. قم بتعديل الشيفرة الخاص بك كما يلي:// Multiple strings #include <cs50.h> #include <stdio.h> int main(void) { string s = "HI!"; string t = "BYE!"; printf("%s\n", s); printf("%s\n", t); }لاحظ أنه تم الإعلان عن سلسلتين واستخدامهما في هذا المثال.
-
يمكنك تصور ذلك على النحو التالي:

-
يمكننا تحسين هذا الرمز بشكل أكبر. قم بتعديل الشيفرة الخاص بك كما يلي:
// Array of strings #include <cs50.h> #include <stdio.h> int main(void) { string words[2]; words[0] = "HI!"; words[1] = "BYE!"; printf("%s\n", words[0]); printf("%s\n", words[1]); }لاحظ أنه يتم تخزين كلا السلسلتين ضمن مصفوفة واحدة من النوع
string. -
يمكننا دمج السلسلتين في مصفوفة من السلاسل.
#include <cs50.h> #include <stdio.h> int main(void) { string words[2]; words[0] = "HI!"; words[1] = "BYE!"; printf("%c%c%c\n", words[0][0], words[0][1], words[0][2]); printf("%c%c%c%c\n", words[1][0], words[1][1], words[1][2], words[1][3]); }لاحظ أن مجموعة من تم إنشاء
words. إنها مجموعة من السلاسل. يتم تخزين كل كلمة فيwords.
طول السلسلة (String Length)
-
من المشاكل الشائعة في البرمجة، وربما لغة C بشكل أكثر تحديدًا، اكتشاف طول السلسلة. كيف يمكننا تنفيذ ذلك في الشيفرة؟ اكتب
code length.cفي النافذة الطرفية والرمز كما يلي:// Determines the length of a string #include <cs50.h> #include <stdio.h> int main(void) { // Prompt for user's name string name = get_string("Name: "); // Count number of characters up until '\0' (aka NUL) int n = 0; while (name[n] != '\0') { n++; } printf("%i\n", n); }لاحظ أن هذا الرمز يتكرر حتى يتم العثور على الحرف NUL.
-
يمكن تحسين هذا الشيفرة عن طريق تجريد العد في دالة كما يلي:
// Determines the length of a string using a function #include <cs50.h> #include <stdio.h> int string_length(string s); int main(void) { // Prompt for user's name string name = get_string("Name: "); int length = string_length(name); printf("%i\n", length); } int string_length(string s) { // Count number of characters up until '\0' (aka NUL) int n = 0; while (s[n] != '\0') { n++; } return n; }لاحظ أنه تم استدعاء دالة جديدة
string_lengthبحساب الأحرف حتى يتم تحديد موقع NUL. -
نظرًا لأن هذه مشكلة شائعة في البرمجة، فقد أنشأ مبرمجون آخرون تعليمات برمجية في مكتبة
string.hللعثور على طول السلسلة. يمكنك العثور على طول السلسلة عن طريق تعديل الشيفرة الخاصة بك على النحو التالي:// Determines the length of a string using a function #include <cs50.h> #include <stdio.h> #include <string.h> int main(void) { // Prompt for user's name string name = get_string("Name: "); int length = strlen(name); printf("%i\n", length); }لاحظ أن هذا الشيفرة يستخدم الامتداد مكتبة
string.h، المعلن عنها في أعلى الملف. علاوة على ذلك، فإنه يستخدم وظيفة من تلك المكتبة تسمىstrlen، الذي يحسب طول السلسلة التي تم تمريرها إليها. - يمكن أن يقف الشيفرة الخاص بنا على أكتاف المبرمجين الذين سبقوهم ويستخدمون المكتبات التي أنشأوها.
-
ctype.hهي مكتبة أخرى مفيدة جدًا. تخيل أننا أردنا إنشاء برنامج يقوم بتحويل كافة الأحرف الصغيرة إلى أحرف كبيرة. في النافذة الطرفية، اكتبcode uppercase.cواكتب الشيفرة كما يلي:// Uppercases a string #include <cs50.h> #include <stdio.h> #include <string.h> int main(void) { string s = get_string("Before: "); printf("After: "); for (int i = 0, n = strlen(s); i < n; i++) { if (s[i] >= 'a' && s[i] <= 'z') { printf("%c", s[i] - 32); } else { printf("%c", s[i]); } } printf("\n"); }لاحظ أن هذا الرمز يتكرر من خلال كل قيمة في السلسلة. البرنامج ينظر إلى كل حرف. إذا كان الحرف صغيرًا، فإنه يطرح 32 من قيمة ASCII للحرف لتحويله إلى أحرف كبيرة.
-
بالتذكير بعملنا السابق في الأسبوع الماضي، ربما تتذكر مخطط قيم ASCII هذا:
0 NUL 16 DLE 32 SP 48 0 64 @ 80 P 96 ` 112 p 1 SOH 17 DC1 33 ! 49 1 65 A 81 Q 97 a 113 q 2 STX 18 DC2 34 ” 50 2 66 B 82 R 98 b 114 r 3 ETX 19 DC3 35 # 51 3 67 C 83 S 99 c 115 s 4 EOT 20 DC4 36 $ 52 4 68 D 84 T 100 d 116 t 5 ENQ 21 NAK 37 % 53 5 69 E 85 U 101 e 117 u 6 ACK 22 SYN 38 & 54 6 70 F 86 V 102 f 118 v 7 BEL 23 ETB 39 ’ 55 7 71 G 87 W 103 g 119 w 8 BS 24 CAN 40 ( 56 8 72 H 88 X 104 h 120 x 9 HT 25 EM 41 ) 57 9 73 I 89 Y 105 i 121 y 10 LF 26 SUB 42 * 58 : 74 J 90 Z 106 j 122 z 11 VT 27 ESC 43 + 59 ; 75 K 91 [ 107 k 123 { 12 FF 28 FS 44 , 60 < 76 L 92 \ 108 l 124 | 13 CR 29 GS 45 - 61 = 77 M 93 ] 109 m 125 } 14 SO 30 RS 46 . 62 > 78 N 94 ^ 110 n 126 ~ 15 SI 31 US 47 / 63 ? 79 O 95 _ 111 o 127 DEL - عند وجود حرف ASCII صغير (a-z).
32بطرحه منه، ينتج عنه النسخة الكبيرة من نفس الحرف. لاحظ أن هذا يعمل فقط مع أحرف ASCII من a إلى z، وليس مع الأحرف المحركة أو غير ASCII. -
بينما يقوم البرنامج بما نريده، هناك طريقة أسهل باستخدام مكتبة
ctype.h. قم بتعديل برنامجك كما يلي:// Uppercases string using ctype library (and an unnecessary condition) #include <cs50.h> #include <ctype.h> #include <stdio.h> #include <string.h> int main(void) { string s = get_string("Before: "); printf("After: "); for (int i = 0, n = strlen(s); i < n; i++) { if (islower(s[i])) { printf("%c", toupper(s[i])); } else { printf("%c", s[i]); } } printf("\n"); }لاحظ أن البرنامج يتكرر خلال كل حرف من السلسلة. ال تم تمرير الدالة
touppers[i]. يتم تحويل كل حرف (إذا كانت صغيرة) إلى أحرف كبيرة. -
من الجدير بالذكر ذلك
toupperيعرف تلقائيًا الأحرف الكبيرة والأحرف الصغيرة فقط. وبالتالي، يمكن تبسيط الشيفرة الخاصة بك على النحو التالي:// Uppercases string using ctype library #include <cs50.h> #include <ctype.h> #include <stdio.h> #include <string.h> int main(void) { string s = get_string("Before: "); printf("After: "); for (int i = 0, n = strlen(s); i < n; i++) { printf("%c", toupper(s[i])); } printf("\n"); }لاحظ أن هذا الرمز يُكتب بأحرف كبيرة على سلسلة باستخدام مكتبة
ctype. - يمكنك أن تقرأ عن كافة إمكانيات
مكتبة
ctypeعلى صفحات الدليل.
وسيطات سطر الأوامر (Command-Line Arguments)
-
Command-line argumentsهي تلك الوسائط التي يتم تمريرها إلى برنامجك في سطر الأوامر. على سبيل المثال، كل تلك البيانات التي كتبتها بعدclangتعتبر وسيطات سطر الأوامر. يمكنك استخدام هذه الحجج في برامجك الخاصة! -
في نافذتك الطرفية، اكتب
code greet.cواكتب الشيفرة كما يلي:// Uses get_string #include <cs50.h> #include <stdio.h> int main(void) { string answer = get_string("What's your name? "); printf("hello, %s\n", answer); }لاحظ أن هذا يقول
helloللمستخدم. -
مع ذلك، ألن يكون من الجيد أن تكون قادرًا على قبول الحجج حتى قبل تشغيل البرنامج؟ قم بتعديل الشيفرة الخاص بك كما يلي:
// Prints a command-line argument #include <cs50.h> #include <stdio.h> int main(int argc, string argv[]) { if (argc == 2) { printf("hello, %s\n", argv[1]); } else { printf("hello, world\n"); } }لاحظ أن هذا البرنامج يعرف كليهما
argcعدد وسائط سطر الأوامر وargv، وهو عبارة عن مصفوفة من السلاسل التي تم تمريرها كوسائط في سطر الأوامر. - لذلك، باستخدام بناء جملة هذا البرنامج، يتم تنفيذه
./greet Davidسيؤدي إلى قول البرنامجhello, David. -
يمكنك طباعة كل من وسيطات سطر الأوامر بما يلي:
// Prints command-line arguments #include <cs50.h> #include <stdio.h> int main(int argc, string argv[]) { for (int i = 0; i < argc; i++) { printf("%s\n", argv[i]); } }لاحظ كيف يقوم هذا الشيفرة بطباعة كل وسيطة سطر أوامر على السطر الخاص به. الوسيط الأول (argv[0]) هو دائمًا اسم البرنامج نفسه، متبوعًا بأي وسيطات تقدمها عند تشغيل البرنامج.
الخروج من الحالة (Exit Status)
- عندما ينتهي البرنامج، يتم توفير رمز خروج خاص للكمبيوتر.
- عند خروج البرنامج بدون خطأ، يظهر رمز الحالة
يتم توفير
0للكمبيوتر. في كثير من الأحيان، عند حدوث خطأ يؤدي إلى إنهاء البرنامج، تظهر حالة يتم توفير1للكمبيوتر. -
يمكنك كتابة برنامج كالتالي يوضح ذلك عن طريق الكتابة
code status.cوكتابة الشيفرة كالتالي:// Returns explicit value from main #include <cs50.h> #include <stdio.h> int main(int argc, string argv[]) { if (argc != 2) { printf("Missing command-line argument\n"); return 1; } printf("hello, %s\n", argv[1]); return 0; }لاحظ أنه إذا فشلت في تقديم
./status David، سوف تحصل على حالة الخروج1. ومع ذلك، إذا قمت بتوفير./status David، سوف تحصل على حالة الخروج0. - يمكنك الكتابة
echo $?في المحطة لمعرفة حالة الخروج لأمر التشغيل الأخير. - يمكنك أن تتخيل كيف يمكنك استخدام أجزاء من البرنامج أعلاه للتحقق مما إذا كان المستخدم قد قدم العدد الصحيح من وسيطات سطر الأوامر.
التلخيص (Summing Up)
في هذا الدرس، تعلمت المزيد من التفاصيل حول تجميع البيانات وكيفية تخزينها داخل جهاز الكمبيوتر. وعلى وجه التحديد، تعلمت…
- بشكل عام، كيفية عمل المترجم.
- كيفية تصحيح الشيفرة الخاصة بك باستخدام أربع طرق.
- كيفية استخدام المصفوفات داخل الشيفرة الخاصة بك.
- كيف تقوم المصفوفات بتخزين البيانات في أجزاء متتالية من الذاكرة.
- كيف أن السلاسل هي مجرد صفائف من الأحرف.
- كيفية التفاعل مع المصفوفات في الشيفرة الخاصة بك.
- كيف يمكن تمرير وسيطات سطر الأوامر إلى برامجك.
نراكم في المرة القادمة!