Menu
flag Ar iconالعربيةdown icon

جملة switch في لغة C: الحالات وbreak والانسياب

كيف تختار جملة switch في C فرعًا واحدًا من قائمة ثوابت - عناوين case، ولماذا تهمّ break، والانسياب المقصود وغير المقصود، وdefault، والتفريع على المحارف والتعدادات، ومتى تتفوّق switch على سلسلة else-if.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

قيمة واحدة وثوابت كثيرة

سلسلة else if التي تواصل مقارنة المتغيّر نفسه بثوابت هي نمط له جملة مخصّصة في C:

if (choice == 1)      { /* ... */ }
else if (choice == 2) { /* ... */ }
else if (choice == 3) { /* ... */ }
else                  { /* ... */ }

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

غيّر choice وشغّل مجددًا. تجعل هذه البنية مجموعة القيم المشروعة مرئية من نظرة واحدة، وهذا هو السبب الحقيقي لتفضيلها هنا.

الصياغة وقواعد عناوين case

switch (integerExpression) {
    case CONSTANT_1:
        statements
        break;
    case CONSTANT_2:
        statements
        break;
    default:
        statements
        break;
}

أربعة قيود تفرضها C:

  • يجب أن يكون تعبير switch من نوع صحيح - int أو char أو short أو long أو enum. لا float ولا double ولا سلسلة نصية.
  • يجب أن يكون كل عنوان case تعبيرًا ثابتًا معروفًا وقت الترجمة. فـ case n: حيث n متغيّر خطأ؛ أما case 3 + 4: وcase MAX: (ثابت #define أو ثابت تعداد) فمقبولان.
  • لا يجوز أن تحمل حالتان القيمة نفسها. فالتكرار خطأ ترجمة، وهو شبكة أمان صغيرة لكنها حقيقية.
  • لا تستطيع الحالة التعبير عن مجال. فـ case 1 ... 5: امتداد من GCC لا C معيارية؛ استخدم if للمجالات.

وdefault اختيارية وقد تظهر في أي موضع من الكتلة، وإن كان وضعها أخيرًا عرفًا شبه شامل. ودونها، تتخطّى القيمة غير المطابقة الـ switch كلها ببساطة.

break: لماذا ليست اختيارية

أكثر ما يفاجئ في switch بلغة C هو أن عناوين case أهداف قفز لا صناديق. فبمجرّد وصول التنفيذ إلى عنوان، يواصل عبر كل جملة تحته - متجاوزًا عناوين الحالات الأخرى - حتى break أو القوس المعقوف المغلق.

تطبع الـ switch الأولى one وtwo وthree - فقد طابقت case 1 ثم نفّذت كل ما بعدها. وتطبع الثانية one فقط. ويُسمّى ذلك السلوك الانسياب، ونسيان break هو خطأ switch الكلاسيكي في C: فالبرنامج خاطئ بصمت لا أنه يفشل في الترجمة.

ترجم بـ gcc -Wall -Wextra -Wimplicit-fallthrough فيشير المترجم إلى كل حالة تنساب إلى التالية.

وbreak في الحالة الأخيرة زائدة تقنيًا - فالكتلة تنتهي هناك على أي حال. اكتبها مع ذلك، حتى لا تُنشئ إضافةُ حالة جديدة تحتها لاحقًا انسيابًا صامتًا.

الانسياب المقصود

الانسياب خاصية حين يكون مقصودًا. فتكديس العناوين دون جمل بينها هو الطريقة المعتادة لقول "هذه القيم تفعل الشيء نفسه":

ثلاثة عناوين تتشارك جسمًا واحدًا، بلا شيفرة مكرّرة وبلا شرط || طويل. ولا تحذّر المترجمات من هذا الشكل، لأنه لا توجد جمل لينساب عبرها.

أما الانسياب مع وجود جمل بينها - حيث تُنفَّذ الحالة 1 ثم تواصل عن قصد إلى الحالة 2 - فأندر ويستحق تعليقًا صريحًا، إذ يحتاج القارئ إلى إخباره بأن غياب break كان قرارًا:

switch (level) {
    case 3:
        printf("verbose\n");
        /* fall through */
    case 2:
        printf("info\n");
        /* fall through */
    case 1:
        printf("errors\n");
        break;
}

و/* fall through */ هو التعليق الذي يتعرّف عليه تحذير GCC، فهو يُسكِت التشخيص أيضًا.

التفريع على المحارف

لأن char نوع صحيح، فإن تصنيف المحارف يناسبه بطبيعته:

لاحظ أن case 'a': تستخدم علامتي اقتباس مفردتين - أي ثابت محرف، وهو ليس إلا القيمة الصحيحة لذلك المحرف. أما case "a": فسلسلة نصية ولن تُترجَم.

التفريع على التعدادات

أفضل اقتران في C هو switch مع تعداد. فتصبح عناوين الحالات أسماء مقروءة، وتستطيع المترجمات التحذير حين لا تعالج الـ switch كل عناصر التعداد:

أمران يستحقان النقل من هنا. كل حالة تنفّذ return، فلا حاجة إلى break - إذ إن return تغادر الدالة كليًا، وهو ما ينهي الـ switch أيضًا. ولا توجد default: فمع -Wswitch (المتضمَّنة في -Wall)، يجعل تركُ default المترجمَ يحذّر يوم يضيف أحدهم حالة خامسة وينسى هذه الدالة. وكانت default ستخفي ذلك.

تعريف المتغيّرات داخل switch

جسم switch كتلة واحدة، فالتعريف في إحدى الحالات مرئي - وإن لم يكن مهيّأً بالضرورة - في الحالات التي بعدها. والقفز فوق تهيئة خطأ في C:

switch (n) {
    case 1:
        int x = 10;        // error in C: a label cannot precede a declaration this way
        printf("%d\n", x);
        break;
    case 2:
        printf("%d\n", x); // x exists here, but was never initialised
        break;
}

غلّف الحالة بأقواس معقوفة خاصة بها حين تحتاج إلى متغيّرات محلّية:

switch (n) {
    case 1: {
        int x = 10;
        printf("%d\n", x);
        break;
    }
    case 2:
        printf("two\n");
        break;
}

switch أم if-else؟

لجأ إلى switch حين يتحقّق كل ما يلي: أنت تفحص تعبيرًا واحدًا، ونوعه صحيح، وأنت تقارنه بـثوابت ثابتة. ومعالجة القوائم، وآلات حالات التعدادات، وتصنيف المحارف، وتوزيع الأوامر هي الحالات النموذجية. والمكسب هو وضوح القراءة، وفحص القيم المكرّرة، ومترجم قادر على التحذير من عناصر تعداد غير معالجة.

ولجأ إلى if-else حين تتضمّن الفحوص مجالات (score >= 90)، أو قيمًا ذات فاصلة عائمة، أو سلاسل نصية (تحتاج strcmp)، أو عدة متغيّرات مختلفة، أو شروطًا مركّبة بـ && و||. فلا شيء من ذلك يمكن التعبير عنه كعنوان حالة.

ونادرًا ما يكون الأداء هو العامل الحاسم. فالمترجمات تحوّل الـ switch الكثيفة إلى جدول قفز والمتناثرة إلى مقارنات، فمع حفنة من الحالات لا يمكن التمييز بين الصيغتين. اختر الصيغة التي تعبّر عن القصد.

الأسئلة الشائعة

كيف تعمل جملة switch في لغة C؟

تقيّم switch (expr) تعبيرًا صحيحًا مرة واحدة، وتقفز إلى عنوان case الذي يطابق ثابته، وتنفّذ من هناك حتى تصادف break أو نهاية الـ switch. وإن لم يطابق شيء، قفزت إلى default إن وُجد، وإلا تخطّت الكتلة كلها.

لماذا أحتاج break داخل switch في لغة C؟

لأن عناوين case أهداف قفز لا كتل منفصلة. فبدون break يستمرّ التنفيذ مباشرةً إلى جمل الحالة التالية - وهذا ما يُسمّى الانسياب. ونسيان break أشيع أخطاء switch في C، والراية gcc -Wimplicit-fallthrough تنبّه إليه.

هل يمكن استخدام switch مع سلسلة نصية في لغة C؟

لا. تتطلّب switch في C تعبيرًا صحيحًا، فتعمل قيم int وchar وenum، أما السلاسل النصية والأعداد العشرية والمجالات فلا. وللتفريع بحسب سلسلة نصية، استخدم strcmp في سلسلة if-else، أو حوّل السلسلة إلى تعداد أولًا ثم فرّع عليه.

متى أستخدم switch بدلًا من if-else في لغة C؟

استخدم switch حين تقارن عددًا صحيحًا أو محرفًا واحدًا بقائمة من الثوابت الثابتة - أوامر القوائم، وحالات التعدادات، وتصنيف المحارف. واستخدم سلسلة if-else حين تتضمّن الفحوص مجالات أو عدة متغيّرات أو أي شيء غير المساواة بثابت معروف وقت الترجمة.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن