درک چرایی وجود switch: چرا برای مقایسه یک متغیر با چند مقدار ثابت، تمیزتر از else if طولانی است.
یادگیری شکل کامل switch: کلیدواژههای switch، case، break، default.
فهم رفتار fall-through (عدم وجود break) و این که چرا فراموش کردن break یک باگ شایع است.
نوشتن برنامههای واقعی با switch: ماشینحساب ساده، تبدیل درجه، روز هفته و کلیدهای جهتنما.
تشخیص این که چه زمانی از switch استفاده کنیم و چه زمانی از if/else.
چرا دستور switch را داریم؟
فرض کنید یک متغیر به نام op داریم که یکی از چهار کاراکتر +، -، *، / را در خود نگه میدارد و میخواهیم متناسب با آن یک عملیات ریاضی انجام دهیم. اگر بخواهیم با if/else if بنویسیم، چیزی شبیه این در میآید:
main.cppcpp
هر خط یکی از مقادیر ممکن op را با == مقایسه میکند. این کار درست است ولی پر تکرار است: نام متغیر op در هر شرط یکیبار، و کلیدواژه == هم در هر شرط یکیبار تکرار میشود.
زبان C++ برای همین الگو — «یک متغیر را با چند مقدار ثابت مقایسه کن» — یک ساختار تمیزتر دارد: دستور switch. در switch، متغیر فقط یکبار در پرانتز نوشته میشود و هر مقدار ممکن با یک برچسب case معرفی میشود. در ادامه این درس، جزئیات کامل switch را همراه چند مثال کاربردی خواهیم دید.
شکل کلی switch
شکل استاندارد دستور switch شامل چهار بخش است:
کلیدواژه switch و در پرانتز متغیری که میخواهیم با مقادیر ثابت مقایسه شود.
برچسبهای case که هرکدام یک مقدار ثابت را مشخص میکنند.
دستور break در پایان هر case برای خروج از switch.
برچسب default برای زمانی که هیچ case مطابقت نداشته باشد (معادل else در if/else).
نکته: در C++ مقدار داخل case باید ثابت (literal یا const) باشد؛ نمیتوان در case یک متغیر یا یک بازه (مثل case 1..10) نوشت.
ساختار کامل یک دستور switch با case، break و defaultcpp
$خروجی برنامه
ورودی: 1
خروجی: one
ورودی: 5
خروجی: many
در حال باز کردن ویرایشگر…
بررسی جزء به جزء: case، break، default
بگذارید هر کلیدواژه را جداگانه توضیح دهیم:
switch (x) — عبارت داخل پرانتز مقدار خود را با برچسبهای case مقایسه میکند. این عبارت باید از یک نوع صحیح (مثل int، char، short، long، یا enum) باشد. نمیتوان از float، double یا std::string در switch استفاده کرد.
case مقدار: — یک برچسب است، نه یک شرط. یعنی «اگر مقدار x برابر این ثابت شد، از این نقطه شروع به اجرا کن». به : در پایان case دقت کنید؛ این نقطه، پایان برچسب است.
break; — پایان آن شاخه از switch است و باعث میشود کنترل برنامه از کل بلوک switch خارج شود. اگر break را فراموش کنید، اجرا به case بعدی «سر میخورد» (به این پدیده fall-through میگویند — در بخش بعدی عمیقتر توضیح میدهیم).
default: — اختیاری است ولی نوشتنش معمولاً خوب است. اگر هیچ case با مقدار x مطابقت نداشته باشد، بلوک default اجرا میشود. معادل else در if/else.
تله شماره ۱: فراموش کردن break
مهمترین خطای مبتدیان در switch، فراموش کردن break است. وقتی break نباشد، اجرای برنامه از case ای که مطابقت داشته از آن جا شروع میشود و تا پایان switch ادامه مییابد، مگر این که به یک break برسد. به این رفتار fall-through میگویند.
مثلاً اگر x = 1 باشد و breakها را حذف کنیم:
main.cppcpp
خروجی واقعی: one two many، در حالی که انتظار داشتیم فقط one چاپ شود.
قاعده طلایی: همیشه break; را در پایان هر case بنویسید، مگر این که عمداً fall-through را بخواهید (در پایین درس یک مثال میبینیم).
مثال ۱: ماشینحساب ساده با switch
این مثال کلاسیکترین کاربرد switch است. دو عدد a و b را میگیریم، سپس یک کاراکتر op (عملگر) را با cin میخوانیم و بر اساس مقدار op، عملیات ریاضی متناسب را انجام میدهیم.
نکته مهم: برای جلوگیری از خطای تقسیم بر صفر، باید قبل از انجام a / b بررسی کنیم که b صفر نباشد. در نسخه اصلی جزوه این بررسی وجود نداشت و اگر کاربر b = 0 وارد میکرد، برنامه با خطای زمان اجرا (Runtime Error) مواجه میشد.
دریافت دو عدد و یک عملگر (+, -, *, /) و نمایش نتیجه با switchcpp
$خروجی برنامه
ورودی: 6 3 *
خروجی: 18
ورودی: 6 0 /
خروجی: Error: division by zero
در حال باز کردن ویرایشگر…
مقایسه: همان ماشینحساب با if/else
برای این که ببینیم چرا switch تمیزتر است، همان ماشینحساب را با if/else if مینویسیم. هر دو نسخه از نظر کارایی تقریباً برابرند و کامپایلرهای مدرن معمولاً همان کد ماشین را تولید میکنند، اما از نظر خوانایی، نسخه switch واضحتر است چون به خواننده میگوید «داریم یک متغیر را با چند مقدار ثابت مقایسه میکنیم».
نکته: در نسخه if/else باید op == را در هر شرط تکرار کنیم، در حالی که در switch این کار یکبار در switch (op) انجام میشود. هر چه تعداد caseها بیشتر شود، این تمیزی بیشتر خودش را نشان میدهد.
نسخه اشتباه جزوه
کد دارای باگcpp
نسخه اصلاحشده و استاندارد
کد صحیحcpp
مثال ۲: تبدیل درجه عددی به حرفی با char
یکی دیگر از کاربردهای رایج switch، تبدیل یک مقدار به یک دستهبندی است. مثلاً تبدیل نمره عددی به درجه حروفی. چون switch فقط مقادیر ثابت را قبول میکند، باید ابتدا نمره را با تقسیم بر ۱۰ به یک عدد ۰ تا ۱۰ تبدیل کنیم و سپس روی آن switch بزنیم.
مثلاً نمره ۸۵ → ۸، نمره ۷۳ → ۷، نمره ۹۲ → ۹. این ترفند بسیار پرکاربرد است.
برنامهای که نمره ۰ تا ۱۰۰ میگیرد و A/B/C/D/F چاپ میکندcpp
$خروجی برنامه
ورودی: 85
خروجی: B
ورودی: 92
خروجی: A
ورودی: 40
خروجی: F
استفاده عمدی از fall-through برای دستهبندی
در مثال بالا دیدیم که case 10: هیچ دستوری نداشت و بلافاصله case 9: آمد. این یک استفاده عمدی از fall-through است! چون case 10break ندارد، اجرا به case 9 سر میخورد و همانجا دستورات اجرا میشود.
این تکنیک برای این است که «چند مقدار به یک خروجی یکسان نگاشت شوند». مثلاً نمره ۱۰۰ و نمره ۹۰ تا ۹۹ هر دو باید A چاپ کنند، پس case 10 به case 9 میریزد.
نکته خوانایی: اگر این کار را میکنید، خوب است یک کامنت بنویسید تا خواننده بداند این fall-through عمدی است:
main.cppcpp
این کار از این که بعداً کسی فکر کند break را فراموش کردهاید، جلوگیری میکند.
مثال ۳: روزهای هفته با switch
این یک مثال کلاسیک دیگر است که در آن هر مقدار متغیر به یک رشته ثابت نگاشت میشود. کد بسیار مستقیم است، هیچ تکرار == یا شرط پیچیدهای ندارد.
تبدیل عدد ۱ تا ۷ به نام روز هفتهcpp
$خروجی برنامه
ورودی: 3
خروجی: Doshanbe
ورودی: 9
خروجی: Invalid day
در حال باز کردن ویرایشگر…
مثال ۴: کلیدهای جهتنما (Arrow Keys / WASD)
در بازیهای ساده (بهویژه بازیهای قدیمی DOS با کتابخانه graphics.h و conio.h)، گرفتن ورودی از کلیدهای جهتنما یک کار رایج است. در نسخه اصلی جزوه از تابع getch() استفاده شده بود که در کامپایلرهای مدرن (مثل GCC یا Clang) بهصورت پیشفرض پشتیبانی نمیشود.
توضیح تاریخی: در محیط Turbo C++ قدیمی، توابع زیر وجود داشتند:
getch() — یک کاراکتر از کاربر میگرفت بدون نیاز به فشردن Enter و بدون نمایش آن روی صفحه.
kbhit() — بررسی میکرد که آیا کاربر کلیدی فشار داده است یا خیر (بدون توقف برنامه).
در کامپایلرهای مدرن، میتوانیم از cin.get(ch) بهعنوان جایگزین استفاده کنیم (با این تفاوت که نیاز به Enter دارد).
کد اسکی کاراکترها: در جدول ASCII، حرف A کد ۶۵، S کد ۸۳، D کد ۶۸ و W کد ۸۷ است. وقتی روی char یک switch میزنیم، مقایسه با کاراکترهای A، S و... انجام میشود. دقت کنید که C++ حساس به بزرگی/کوچکی حروف است: a و A دو case متفاوت هستند.
تبدیل کلیدهای W/A/S/D به جهت حرکت (Up/Left/Down/Right)cpp
$خروجی برنامه
ورودی: W
خروجی: Up
ورودی: s
خروجی: Down
در حال باز کردن ویرایشگر…
توضیح: کلیدهای فلشی واقعی کد اسکی دوبایتی دارند
در نسخه اصلی جزوه، یک نکته در کادر با عنوان «ASCII» بود: کلیدهای فلشی واقعی روی کیبورد (↑ ↓ ← →) کد اسکی دوبایتی دارند. یعنی وقتی کلید فلشی را فشار میدهید، دو بایت ارسال میشود: اول یک 0 یا 0xE0 (بهاصطلاح extended key prefix) و سپس یک بایت دیگر که کد جهت را مشخص میکند (مثلاً ۷۲ برای ↑، ۸۰ برای ↓، ۷۵ برای ←، ۷۷ برای →).
به همین دلیل است که در بازیهای مدرن، معمولاً بهجای کلیدهای فلشی، از کلیدهای WASD استفاده میشود، چون هر کدام یک کاراکتر اسکی تکبایتی هستند و خواندنشان با getch() سادهتر است.
برای خواندن کلیدهای فلشی واقعی، باید دو بار getch() صدا زد: اولی پیشوند را میخواند و دومی کد جهت را. این یک موضوع پیشرفته است و در این مرحله از یادگیری نیازی به آن ندارید؛ فقط همینقدر بدانید که اگر در یک مثال ساده با switch (ch) روی کلیدهای فلشی کار کردید و نشد، دلیل همین دوبایتی بودن است.
فلوچارت: ماشینحساب با switch
flow.svg
اصلاح خطاهای نسخه اصلی جزوه
در نسخه اصلی جزوه (صفحه ۱۸) چند خطای فنی وجود داشت که در این درس اصلاح شدند:
۱. عدم وجود کتابخانهها: هیچ #include یا using namespace std; نوشته نشده بود. در C++ مدرن برای cin و cout به #include <iostream> نیاز داریم.
۲. Switch با حرف بزرگ: در یک کد نوشته شده بود Switch(ch). C++ به بزرگی و کوچکی حروف حساس است؛ کلیدواژه درست switch (با s کوچک) است.
۳. نبود return 0;: در تابع main() نوشتن return 0; بهعنوان علامت موفقیتآمیز بودن برنامه استاندارد است. در C++ مدرن، int main() درست است (نه void main() یا فقط main()).
۴. تقسیم بر صفر: در case '/': نوشته شده بود cout << a / b; بدون بررسی اینکه آیا b صفر است یا نه. اگر کاربر b = 0 وارد کند، برنامه با خطای زمان اجرا قطع میشود. در نسخه اصلاحشده، ابتدا if (b != 0) بررسی میکنیم.
۵. کد ناقص کلیدهای جهتنما: در نسخه اصلی، فقط case های A و S نوشته شده بود و بقیه بریده بود. در این درس، همه چهار کلید کامل نوشته شدند.
چه زمانی از switch و چه زمانی از if/else؟
قاعده کلی:
از switch استفاده کنید وقتی: یک متغیر را با چند مقدار ثابت مقایسه میکنید. مثلاً روز هفته، نوع عملگر، کلید فشردهشده، یا دستهبندی نمره.
از if/else استفاده کنید وقتی: شرطهای پیچیده دارید؛ مثلاً بازهها (if (x > 0 && x < 100))، ترکیب چند متغیر (if (a + b > c && b + c > a))، یا مقایسه با مقادیر اعشاری/رشتهای.
محدودیتهای switch که باید بدانید:
۱. فقط با انواع صحیح (integral) کار میکند: int، char، enum، long و غیره. برای float، double و std::string نمیتوان switch نوشت — خطای کامپایل میگیرید. bool هم از نظر فنی یک نوع صحیح است و switch روی آن مجاز است، اما فقط دو مقدار دارد و برای همان دو حالت، if/else تمیزتر و خواناتر است.
۲. هر case فقط یک مقدار ثابت است، نه یک بازه. نمیتوان نوشت case 1..10:.
۳. در case نمیتوان از متغیر استفاده کرد؛ باید literal یا const باشد.
۴. default اختیاری است ولی نوشتنش توصیه میشود تا رفتار برنامه برای ورودیهای پیشبینینشده روشن باشد.
برنامهای بنویسید که یک عدد از ۱ تا ۷ بگیرد و نام روز هفته را به فارسی چاپ کند (۱ = شنبه، ...، ۷ = جمعه). اگر عدد خارج بازه بود، پیام «نامعتبر» چاپ کند.
نمایش راهنما
از switch با case 1 تا case 7 و default استفاده کنید.
02
تمرین•حل کامل دارد
برنامهای بنویسید که شمارهی یک ماه از ۱ تا ۱۲ را بگیرد و تعداد روزهای آن ماه را در تقویم هجری شمسی چاپ کند. فرض کنید سال کبیسه نیست، پس اسفند ۲۹ روز است. اگر عدد خارج از بازه بود، پیام «نامعتبر» چاپ کند.
نمایش راهنما
در تقویم شمسی، ماههای ۱ تا ۶ (فروردین تا شهریور) ۳۱ روز، ماههای ۷ تا ۱۱ (مهر تا بهمن) ۳۰ روز و اسفند (ماه ۱۲) در سال غیرکبیسه ۲۹ روز است. ماههای هماندازه را با case های پشتسرهم و بدون break گروهبندی کنید و default را فراموش نکنید.
03
تمرین•حل کامل دارد
برنامهای بنویسید که یک عدد صحیح مثبت تکرقمی (۱ تا ۹) بگیرد و معادل فارسی آن را چاپ کند (۱ = یک، ۲ = دو، ...، ۹ = نه). اگر خارج بازه بود، پیام «نامعتبر» چاپ کند.
نمایش راهنما
از switch با case 1 تا case 9 استفاده کنید و در هر case کلمه فارسی معادل را چاپ کنید.
04
تمرین•حل کامل دارد
برنامه ماشینحساب کامل بنویسید که عملگرهای +، -، *، /، % (باقیمانده) را پشتیبانی کند. برای / و % بررسی تقسیم بر صفر انجام دهید.
نمایش راهنما
دو case '/' و '%' را جداگانه بنویسید و در هر دو بررسی کنید b != 0 باشد. در C++ عملگر باقیمانده % است.
// نکات-کلیدی۶
01دستور switch برای مقایسه یک متغیر با چند مقدار ثابت طراحی شده و خواناتر از زنجیرههای طولانی if/else if است.
02هر case باید با break خاتمه یابد، مگر این که عمداً بخواهید fall-through ایجاد کنید (مثلاً برای دستهبندی چند مقدار به یک خروجی).
03default معادل else است و برای مواقعی که هیچ case مطابقت ندارد اجرا میشود؛ نوشتنش اختیاری ولی توصیهشده است.
04switch فقط با انواع صحیح (int، char، enum) کار میکند؛ برای float، double یا string نمیتوان از آن استفاده کرد.
05هر case فقط یک مقدار ثابت است، نه یک بازه؛ برای بازهها باید از if/else استفاده کنید.
06در برنامههای واقعی، قبل از تقسیم یا باقیمانده همیشه بررسی کنید مخرج صفر نباشد تا از خطای زمان اجرا جلوگیری شود.
گام بعدی پیشنهادی
حلقه for — شروع تکرار
اولین قدم در دنیای حلقهها: از چاپ اعداد تا فاکتوریل و شمارش زوج/فرد
نام درس، موضوع یا کلمه کلیدی را بنویسید و با Enter به آن بروید
if(op =='+'){ cout << a + b;}elseif(op =='-'){ cout << a - b;}elseif(op =='*'){ cout << a * b;}elseif(op =='/'){ cout << a / b;}else{ cout <<"unknown";}
1#include<iostream>2usingnamespace std;34intmain(){5int x =0;6 cin >> x;78switch(x){9case0:10// اگر x برابر ۰ باشد11 cout <<"zero"<< endl;12break;13case1:14// اگر x برابر ۱ باشد15 cout <<"one"<< endl;16break;17case2:18 cout <<"two"<< endl;19break;20default:21// در غیر این صورت (معادل else)22 cout <<"many"<< endl;23break;24}2526return0;27}
1#include<iostream>2usingnamespace std;34intmain(){5int a =0, b =0;6char op =0;7 cin >> a >> b >> op;89switch(op){10case'+':11 cout << a + b << endl;12break;13case'-':14 cout << a - b << endl;15break;16case'*':17 cout << a * b << endl;18break;19case'/':20if(b !=0){21 cout << a / b << endl;22}else{23 cout <<"Error: division by zero"<< endl;24}25break;26default:27 cout <<"Unknown operator"<< endl;28break;29}3031return0;32}
1// نسخه if/else — درست کار میکند ولی پر تکرار است2#include<iostream>3usingnamespace std;45intmain(){6int a =0, b =0;7char op =0;8 cin >> a >> b >> op;910if(op =='+'){11 cout << a + b << endl;12}elseif(op =='-'){13 cout << a - b << endl;14}elseif(op =='*'){15 cout << a * b << endl;16}elseif(op =='/'){17if(b !=0) cout << a / b << endl;18else cout <<"Error"<< endl;19}else{20 cout <<"Unknown"<< endl;21}22return0;23}
1// نسخه switch — تمیزتر و خواناتر برای همان کار2#include<iostream>3usingnamespace std;45intmain(){6int a =0, b =0;7char op =0;8 cin >> a >> b >> op;910switch(op){11case'+': cout << a + b << endl;break;12case'-': cout << a - b << endl;break;13case'*': cout << a * b << endl;break;14case'/':15if(b !=0) cout << a / b << endl;16else cout <<"Error"<< endl;17break;18default: cout <<"Unknown"<< endl;break;19}20return0;21}
1#include<iostream>2usingnamespace std;34intmain(){5int score =0;6 cin >> score;78// تبدیل نمره به یک عدد ۰ تا ۱۰9int g = score /10;1011switch(g){12case10:13case9:14 cout <<"A"<< endl;15break;16case8:17 cout <<"B"<< endl;18break;19case7:20 cout <<"C"<< endl;21break;22case6:23 cout <<"D"<< endl;24break;25case5:26case4:27case3:28case2:29case1:30case0:31 cout <<"F"<< endl;32break;33default:34 cout <<"Invalid score"<< endl;35break;36}3738return0;39}
1// نسخه مدرن با cin.get — نیاز به Enter دارد اما قابل اجرا در همه کامپایلرهاست2#include<iostream>3usingnamespace std;45intmain(){6char ch =0;7 cout <<"Press W/A/S/D: ";8 cin.get(ch);910switch(ch){11case'w':12case'W':13 cout <<"Up"<< endl;14break;15case'a':16case'A':17 cout <<"Left"<< endl;18break;19case's':20case'S':21 cout <<"Down"<< endl;22break;23case'd':24case'D':25 cout <<"Right"<< endl;26break;27default:28 cout <<"Not a direction key"<< endl;29break;30}3132return0;33}
flow.svg100%
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.