المسارات
/
C++
C++
/
التمارين
/
مؤسسة Speedywagon
مؤسسة Speedywagon

مؤسسة Speedywagon

تمرين تعلّمي

مقدمة

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

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

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

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

  1. مثل المراجع، تتجنب المؤشرات النسخ وتساعد على تقليل بصمة الموارد في برنامجك.
  2. وعلى عكس المراجع، يمكن إعادة إسناد المؤشرات إلى كائنات مختلفة.
  3. كما يمكن للمؤشرات أن تشير إلى قيمة فارغة، للإشارة إلى أنها لا تشير حاليًا إلى أي كائن.

الصياغة العامة

يتضمن تعريف المؤشر في ++C تحديد نوع البيانات الذي يشير إليه المؤشر، يليه نجمة (*) واسم المؤشر. عند تعريف المؤشرات، لا يتم تهيئتها تلقائيًا. وبدون إسناد صريح، يحمل المؤشر عادةً قيمة غير محددة، يُشار إليها غالبًا بـ"عنوان عشوائي". وفي حين أن بعض المترجمات قد تهيّئ المؤشرات إلى nullptr، فإن هذا السلوك غير مضمون في كل المترجمات، لذا من الضروري ألا تعتمد عليه. من أفضل الممارسات تهيئة المؤشرات الخام صراحةً والتحقق من أنها ليست فارغة قبل استخدامها لتجنب مشكلات محتملة.

int* ptr{nullptr}; // Declares a pointer and makes sure it is not invalid

لإسناد عنوان متغير إلى مؤشر، تستخدم عامل أخذ العنوان (&). ويتم إلغاء الإشارة إلى المؤشر باستخدام عامل الإشارة غير المباشرة (*).

std::string opponent{"Solomon Lane"};
// 'ethan' points to the address of the string opponent
std::string* ethan{&opponent};
// Instead of ethan's, the opponent's name address is given to the passPort
std::string passportName{*ethan};

انتبه: يجب إلغاء الإشارة صراحةً، بينما كانت المراجع تعمل ببساطة كاسم مستعار.

حساب المؤشرات

يتيح لك حساب المؤشرات إجراء عمليات حسابية على المؤشرات، وهو مفيد بشكل خاص عند التعامل مع المصفوفات. إضافة عدد صحيح إلى مؤشر تجعله يشير إلى عنصر مختلف.

// Stargate Coordinate Code
int gateCode[] = {462, 753, 218, 611, 977};
// 'ptr' points to the first element of 'gateCode'
int* ptr{&gateCode[0]};
// Accesses the third Stargate address through pointer arithmetic
int dialedAddress{*(ptr + 2)};
// Chevron encoded! Dialing Stargate address:
openStarGate(dialedAddress);
Caution

قد يؤدي حساب المؤشرات في ++C بسهولة إلى سلوك غير معرّف إذا لم يتم التعامل معه بحذر. وقد يظهر السلوك غير المعرّف في صورة نتائج غير متوقعة للبرنامج، أو انهيارات، أو حتى ثغرات أمنية. ومن أشهر الأمثلة على عواقب السلوك غير المعرّف ما حدث في انفجار صاروخ آريان 5 عام 1996، حيث أدى استثناء برمجي ناتج عن تحويل عدد عشري 64-بت إلى عدد صحيح مُوقّع 16-بت إلى فشل كارثي.

الوصول إلى متغيرات الأعضاء

في ++C، يُستخدم العامل -> للوصول إلى أعضاء كائن عبر مؤشر إليه. وهو اختصار يبسّط الوصول إلى أعضاء الكائنات التي تشير إليها المؤشرات. على سبيل المثال، إذا كان ptr مؤشرًا إلى كائن له متغير عضو x، فبدلًا من استخدام (*ptr).x، يمكنك استخدام ptr->x مباشرةً. يعزز هذا العامل وضوح الكود ويقلل الإسهاب عند التعامل مع مؤشرات إلى كائنات.

إليك مثالًا موجزًا، مع بنية Superhero لها متغير عضو superpower. تُنشئ الدالة الرئيسية مؤشرًا dianaPrince إلى كائن Superhero (يمثّل Wonder Woman). ويُستخدم العامل -> للوصول إلى متغير العضو superpower، مستعرضًا "حبل الحقيقة" الشهير لدى Wonder Woman.

struct Superhero {
    std::string superpower;
};
Superhero wonder_woman{};
Superhero* dianaPrince = &wonder_woman;
dianaPrince->superpower = "Lasso of Truth";
// Using the -> operator to access member variable superpower:
std::cout << "Wonder Woman, possesses the mighty " << dianaPrince->superpower;

المؤشرات مقابل المراجع

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

التعليمات

مرحبًا أيها المهندس! أنت أحد آخر المخضرمين في مؤسسة Speedywagon، وهي منظمة سرية ظلّت لعقود تحارب تهديدات قديمة مثل Pillar Men. وخلال هذه الجهود، أمضيت سنوات في صيانة الأنظمة التقنية للمؤسسة، المبنية على مزيج من التقنيات المتطورة والمكتبات القديمة.

غير أن المستشعرات التي تتعقّب أنشطة Pillar Men أصبحت معطّلة في الآونة الأخيرة. أنظمة المؤسسة قديمة، والكود يتفاعل مع مكتبة C++ قديمة لا يمكن تحديثها. مهمتك هي تنفيذ أربع دوال أساسية تراقب نشاط مستشعرات Pillar Men باستخدام مكتبة قديمة الطراز قائمة على المؤشرات.

بصفتك مهندس C++ عصريًا، قد تفضّل استخدام المؤشرات الذكية، لكن للأسف، الكود القديم يفرض احترام الأساليب القديمة. قد يكون مصير البشرية معلّقًا بهذه المؤشرات، فتقدّم بحذر، وليكن دليلك طاقة Hamon.

Note

بما أن قراءات المستشعرات قد تكون ضخمة، نوفّر نسخة تجريبية من struct تُستخدم في المكتبة الفعلية. الكود مُنفَّذ بالفعل لك في ملف الترويسة.

struct pillar_men_sensor {
    int activity{};
    std::string location{};
    std::vector<int> data{};
};

1. التحقق من اتصال المستشعر (connection_check)

مهمتك الأولى هي التأكد من أن مستشعر Pillar Men متصل بشكل صحيح. لا نريد إنذارات كاذبة تُطلقها مستشعرات غير متصلة. ستكتب دالة connection_check تتحقق من صلاحية مؤشر المستشعر عبر فحص nullptr.

  • عرّف دالة تقبل مؤشرًا إلى struct من نوع pillar_men_sensor.
  • ينبغي أن تُرجع الدالة true إذا لم يكن مؤشر المستشعر nullptr، وfalse في غير ذلك.
pillar_men_sensor* sensor{nullptr};
bool isConnected = connection_check(sensor);
// isConnected => false

2. إحصاء نشاط المستشعرات (activity_counter)

Pillar Men يتربصون في الظلال، ونحتاج إلى معرفة ما إذا كانت المستشعرات قد رصدت أي نشاط. ستكتب دالة activity_counter التي تأخذ مصفوفة من المستشعرات وسعة تشير إلى عدد المستشعرات في المصفوفة.

  • عرّف دالة تقبل مؤشرًا إلى العنصر الأول في مصفوفة وسعة المصفوفة.
  • استخدم حساب المؤشرات للتنقل عبر مصفوفة المستشعرات وتجميع قراءات النشاط.
  • أرجِع النشاط المُجمَّع.
pillar_men_sensor sensor_array[3] = {{0}, {101}, {22}};
int totalActivity = activity_counter(sensor_array, 3);
// totalActivity => 123

3. التحكم في الإنذار (alarm_control)

لا ينبغي لكل مستشعر أن يُطلق إنذارًا إلا عند وجود خطر حقيقي. تضمن دالة alarm_control ألا يُطلق المستشعر إنذارًا إلا إذا كان مستوى نشاطه أكبر من 0. ينبغي أن تتحقق هذه الدالة أيضًا من كون المستشعر nullptr لمنع انهيار النظام.

  • عرّف دالة تقبل مؤشرًا إلى pillar_men_sensor.
  • ينبغي أن تتحقق الدالة أولًا مما إذا كان المستشعر nullptr. فإن كان كذلك، أرجِع false.
  • إذا كان المستشعر صالحًا وكان نشاطه أكبر من 0، فأرجِع true؛ وإلا فأرجِع false.
pillar_men_sensor db{9008, "songokunoie", {7, 7, 7}};
bool alarm = alarm_control(&db);
// alarm => true

4. فحص البيانات بحثًا عن شذوذ باستخدام دالة uv_alarm

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

عرّف دالة uv_alarm في فضاء الأسماء speedywagon. ينبغي أن:

  • تأخذ مؤشرًا إلى struct من نوع pillar_men_sensor كمعامل لها.
  • تُرجع false إذا كان مؤشر المستشعر nullptr.
  • تستدعي دالة uv_light_heuristic وتمرّر عنوان مصفوفة data الخاصة بالمستشعر.
  • تُرجع true إذا كانت القيمة التي أرجعتها uv_light_heuristic أكبر من مستوى sensor->activity، وإلا فأرجِع false.
تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة
C++ Exercism

مستعد لبدء مؤسسة Speedywagon؟

سجّل في Exercism لتتعلّم وتتقن C++ عبر 19 مفهومًا100 تمرين، وإرشاد بشري حقيقي، وكل ذلك مجانًا.