آشنایی با عملگرهای افزایش (`++`) و کاهش (`--`) و معادل آنها با `a = a + 1`
درک تفاوت دقیق **prefix** (`++a`) و **postfix** (`a++`) با مثال گامبهگام
یادگیری پنج عملگر انتساب مرکب (`+=`، `-=`، `*=`، `/=`، `%=`) و معادل کامل آنها
تحلیل عبارتهای ترکیبی مثل `2 * a++` و درک ترتیب ارزیابی
شناخت رفتار نامشخص (Undefined Behavior) در عبارتهای مثل `i = i++ + ++i` و چگونگی پرهیز از آن
چرا `++` اختراع شد؟
افزودن یک واحد به متغیر، یکی از پرکاربردترین عملیاتها در برنامهنویسی است: شمارش حلقه، شمردن تعداد ورودیها، حرکت اندیس آرایه و...
در ابتدا مینوشتیم:
main.cppcpp
این کار درست است اما طولانی. C++ (و C قبل از آن) یک نوشتار کوتاهتر اختراع کرد: عملگر افزایش (++) و عملگر کاهش (--). بنابراین:
a = a + 1 ← a++ یا ++a
a = a - 1 ← a-- یا --a
نام «C++» خود از همین عملگر گرفته شده! یعنی «C بهعلاوه یک قدم به جلو». این عملگرها فقط برای افزایش یا کاهش یک واحدی کار میکنند؛ برای افزایش چند واحدی باید از += استفاده کنید (که در ادامه میآید).
نکته: هر دو شکل a++ و ++a مقدار a را یکی زیاد میکنند؛ اما در عبارتهای ترکیبی، ترتیب ارزیابی متفاوت است که در ادامه بررسی میکنیم.
مثال ۱: استفاده پایه از ++ و --
وقتی ++ یا -- به تنهایی بهعنوان یک دستور کامل نوشته شوند، فرقی نمیکند قبل یا بعد از متغیر بیایند. در هر دو حالت، مقدار متغیر یکی زیاد یا کم میشود.
تفاوت فقط زمانی ظاهر میشود که از این عملگرها داخل یک عبارت بزرگتر استفاده کنیم (مثلاً در b = a++).
افزایش و کاهش یک واحدی متغیر acpp
$خروجی برنامه
After a++: 6
After ++a: 7
After a--: 6
After --a: 5
در حال باز کردن ویرایشگر…
Prefix در برابر Postfix: ترتیب ارزیابی
این یکی از مهمترین تفاوتهای ظریف C++ است که مصاحبههای فنی زیادی روی آن تمرکز دارند:
++a (Prefix / پیشافزایش)
ابتدا مقدار a یکی زیاد میشود
سپس مقدار جدید در عبارت استفاده میشود
خلاصه: «اول افزایش، بعد استفاده»
a++ (Postfix / پسافزایش)
ابتدا مقدار فعلی a در عبارت استفاده میشود
سپس a یکی زیاد میشود
خلاصه: «اول استفاده، بعد افزایش»
مثال ملموس با a = 5:
b = ++a → ابتدا a میشود ۶، سپس b = a = 6. پس a = 6، b = 6.
b = a++ → ابتدا b = a = 5، سپس a میشود ۶. پس a = 6، b = 5.
قاعده طلایی برای یادآوری:
نماد قبل از متغیر (++a) یعنی «قبل از استفاده، افزایش بده»
نماد بعد از متغیر (a++) یعنی «استفاده کن، بعد افزایش بده»
اگر فقط بهعنوان دستور مستقل (نه داخل عبارت) استفاده شوند، فرقی ندارند.
مثال ۲: مقایسه دقیق b = a++ و b = ++a
این برنامه هر دو حالت را با مقدار اولیه a = 5 امتحان میکند تا تفاوت را به وضوح ببینید. در حالت postfix، b مقدار قدیمی a (یعنی ۵) را میگیرد؛ در حالت prefix، b مقدار جدید a (یعنی ۶) را میگیرد.
این الگو در حلقهها هم مصداق دارد: while (a++) در مقابل while (++a) رفتار متفاوتی نشان میدهد.
تفاوت postfix و prefix را با چاپ مقادیر مشاهده کنیدcpp
$خروجی برنامه
Postfix: a = 6, b = 5
Prefix: a = 6, b = 6
در حال باز کردن ویرایشگر…
تحلیل گامبهگام: `a = d++`
این مثال از صفحه ۱۱ جزوه است و یکی از کلاسیکترین تلههای postfix را نشان میدهد:
main.cppcpp
گامهای ارزیابیa = d++:
۱. مقدار فعلی d (یعنی ۱۰) برای انتساب به a ارزیابی میشود.
۲. سپس d یکی زیاد میشود → d = 11.
۳. مقدار ذخیره شده در a همان ۱۰ است.
پس خروجی:
main.cppcpp
اگر به جای آن مینوشتیم a = ++d:
اول d میشد ۱۱
سپس a = 11
خروجی: 11 و 11
عملگرهای انتساب مرکب (Compound Assignment)
وقتی میخواهیم یک عملیات را روی متغیر اعمال کنیم و نتیجه را در همان متغیر ذخیره کنیم، C++ یک نوشتار خلاصه فراهم کرده است: عملگرهای انتساب مرکب.
جدول کامل پنج عملگر انتساب مرکب حسابی:
عملگر
مثال
معادل کامل
معنی
+=
a += b
a = a + b
جمع و ذخیره
-=
a -= b
a = a - b
تفریق و ذخیره
*=
c *= 10
c = c * 10
ضرب و ذخیره
/=
d /= 10
d = d / 10
تقسیم و ذخیره
%=
a %= 3
a = a % 3
باقیمانده و ذخیره
مزایا:
۱. کوتاهتر: یکبار نام متغیر نوشته میشود.
۲. کمخطاتر: اگر نام متغیر طولانی باشد (مثل students_count)، احتمال اشتباه تایپی کاهش مییابد.
۳. خواناتر: قصد برنامهنویس واضحتر است: «این متغیر را تغییر بده، نه متغیر دیگری را».
نکته: اولویت این عملگرها پایین است (مانند =) و از راست به چپ اجرا میشوند.
مثال ۳: هر پنج انتساب مرکب در یک برنامه
این برنامه هر پنج عملگر انتساب مرکب را روی متغیر a (با مقدار اولیه ۲۰) اعمال میکند و در هر مرحله مقدار جدید را چاپ میکند. به هر خط نگاه کنید و معادل کامل آن را در ذهن بازسازی کنید تا الگو در ذهنتان تثبیت شود.
نشان دادن معادل کامل هر عملگر با مقداردهی اولیه a = 20cpp
$خروجی برنامه
a += 5 -> a = 25
a -= 5 -> a = 15
a *= 5 -> a = 100
a /= 5 -> a = 4
a %= 3 -> a = 2
a += b (b=7) -> a = 27
در حال باز کردن ویرایشگر…
تحلیل عبارت ترکیبی `2 * a++`
این مثال از صفحه ۱۲ جزوه است. عبارت 2 * a++ با a = 5 را گامبهگام تحلیل میکنیم:
گام ۱: مقدار فعلی a (یعنی ۵) در عبارت استفاده میشود → 2 * 5 = 10.
گام ۲: سپس a یکی زیاد میشود → a = 6.
نتیجه: مقدار عبارت برابر ۱۰ است و a در پایان برابر ۶ میشود.
main.cppcpp
اگر به جای آن مینوشتیم 2 * ++a:
اول a میشد ۶
سپس 2 * 6 = 12
خروجی: 12 6
این نشان میدهد که postfix یا prefix بودن، در عبارتهای ترکیبی تأثیر جدی بر نتیجه میگذارد.
مثال ۴: چاپ ردیف ستاره با ++ (پیشنمایش حلقه for)
این الگو را در درسهای بعدی (حلقه for) به طور کامل یاد میگیریم، اما همینجا آن را بهعنوان کاربرد ++ میبینیم. هدف: چاپ دو ردیف ستاره و کلمه hello بین آنها. این الگو در صفحه ۱۱ جزوه آمده بود.
ایده: یک حلقه با شمارنده i از ۱ تا ۸، در هر گام یک ستاره چاپ میکند. گام حلقه i++ است که شمارنده را یکی زیاد میکند.
استفاده از ++ در شمارنده حلقه — الگوی پرکاربردcpp
$خروجی برنامه
* * * * * * * *
hello
* * * * * * * *
در حال باز کردن ویرایشگر…
رفتار نامشخص (Undefined Behavior): یک تله خطرناک
یکی از خطرناکترین تلههای C++ نوشتن عبارتهایی است که در آنها یک متغیر در یک عبارت دو بار تغییر میکند. مثلاً:
main.cppcpp
این عبارت در استاندارد C++ رفتار نامشخص (Undefined Behavior) دارد؛ یعنی استاندارد نمیگوید کدام ++ اول ارزیابی شود. هر کامپایلر ممکن است نتیجه متفاوتی بدهد:
GCC ممکن است بدهد: 4
Clang ممکن است بدهد: 5
MSVC ممکن است بدهد: 6
و حتی بدتر: با تغییر سطح بهینهسازی (-O0، -O2) در همان کامپایلر، خروجی میتواند تغییر کند!
چرا؟ چون دو عملگر ++ روی همان i در یک عبارت، درگیر نظم ارزیابی (Sequence Points) میشوند که استاندارد آن را نامشخص گذاشته است.
قاعده طلایی: هیچگاه در یک عبارت، یک متغیر را دو بار تغییر ندهید. به جای آن، کد را به چند خط ساده بشکنید:
main.cppcpp
اگر در مصاحبه یا کنکور چنین عبارتی دیدید، جواب صحیح «Undefined Behavior» است، نه یک عدد مشخص!
اصلاح اشتباهات جزوه: علامتهای نامعتبر در انتساب
در صفحه ۱۲ جزوه، جدولی از قوانین انتساب مرکب نوشته شده بود که در آن چند اشتباه تایپی دستنویس وجود داشت:
۱. سطر اول a۰=b; → a=a۰b; نوشته شده بود که در آن ۰ یک صفر فارسی است به جای عملگر واقعی. به نظر میرسد منظور a %= b (باقیمانده) بوده، یا یک placeholder کلی برای الگو بوده است.
۲. سطر ششم 1=; به جای %=; (یا حداقل =1) نوشته شده بود؛ این کاملاً نامعتبر است.
۳. در پاورقی نوشته شده بود «اولویت ++ بیشتر از -- است» که اشتباه است. در استاندارد C++، ++ و --همتراز هستند و هر دو اولویت یکسانی دارند. تفاوت فقط در prefix یا postfix بودن است، نه در نوع عملگر.
همیشه قوانین را از منابع معتبر (مثل cppreference.com) بررسی کنید، نه از جزوههای دستنویس.
نسخه اشتباه جزوه
کد دارای باگcpp
نسخه اصلاحشده و استاندارد
کد صحیحcpp
چه زمانی `++` و چه زمان `+= 1`؟
هرچند a++ و a += 1 از نظر نتیجه یکسان هستند، اما عرفهای فرهنگی وجود دارد:
++ را برای شمارشهای یک واحدی به کار ببرید: شمارنده حلقه (i++)، شمارنده تعداد (count++)، حرکت اندیس آرایه (p++).
+= را برای افزایش با مقدار دلخواه به کار ببرید: sum += value، total += price * quantity.
+= را حتی برای += 1 ترجیح دهید اگر در یک عبارت بزرگتر قرار دارد و خوانایی مهم است؛ مثلاً if ((count += 1) >= 10) { ... }.
در حلقه for، هر سه حالت زیر معادلاند، اما i++ رایجترین است:
main.cppcpp
فلوچارت: تحلیل b = a++
گامبهگام ارزیابی عبارت b = a++ با a = 5. این فلوچارت نشان میدهد چرا postfix ابتدا مقدار قدیمی را میدهد و سپس a را افزایش میدهد.
برنامهای بنویسید که یک متغیر `count` را از ۰ شروع کند و در یک حلقه ۱۰ بار آن را با `count++` افزایش دهد. در نهایت مقدار نهایی را چاپ کند.
نمایش راهنما
از for (int i = 0; i < 10; i++) { count++; } استفاده کنید.
02
تمرین•حل کامل دارد
برنامهای بنویسید که عدد `a = 100` را با عملگرهای انتساب مرکب بهترتیب `-=` ۲۰، `/=` ۲ و `+=` ۵ کند و مقدار نهایی را چاپ کند.
نمایش راهنما
بهترتیب بنویسید: a -= 20; a /= 2; a += 5;. مقدار نهایی باید 45 باشد.
03
تمرین•حل کامل دارد
خروجی قطعه کد زیر را بدون اجرا پیشبینی کنید:
```cpp
int a = 5, b;
b = ++a; // prefix
cout << a << " " << b << endl;
a = 5;
b = a++; // postfix
cout << a << " " << b << endl;
```
نمایش راهنما
در prefix (++a) اول a یک واحد افزایش مییابد و سپس در عبارت استفاده میشود: a=6 و b=6. در postfix (a++) اول مقدار فعلی در b ذخیره میشود و سپس a افزایش مییابد: b=5 و سپس a=6. توجه: عبارتهای مثل b = ++a + a++ رفتار نامشخص (UB) دارند چون a در یک expression دو بار تغییر میکند؛ هرگز چنین کدی ننویسید.
04
تمرین•حل کامل دارد
برنامهای بنویسید که n عدد صحیح را بگیرد و با استفاده از `sum += x` مجموع آنها را حساب کند و سپس میانگین را بهصورت اعشاری چاپ کند.
نمایش راهنما
در بدنه حلقه بنویسید: cin >> x; sum += x;. در پایان از (float)sum / n برای خروجی اعشاری استفاده کنید.
// نکات-کلیدی۶
01عملگرهای `++` و `--` برای افزایش و کاهش یک واحدی متغیر استفاده میشوند و معادل `a = a + 1` و `a = a - 1` هستند.
02تفاوت `++a` (prefix) و `a++` (postfix): در prefix اول افزایش و بعد استفاده، در postfix اول استفاده و بعد افزایش.
03پنج عملگر انتساب مرکب: `+=`، `-=`، `*=`، `/=`، `%=`. هر کدام معادل `a = a OP b` هستند.
04اولویت `++` و `--` در شکل prefix با عملگرهای یگانی همتراز است؛ اما شکل postfix (مثل `a++`) اولویت بالاتری از prefix دارد.
05عبارتهایی مثل `i = i++ + ++i` رفتار نامشخص (UB) دارند و نباید نوشته شوند؛ هرگز یک متغیر را دو بار در یک عبارت تغییر ندهید.
06در حلقه `for`، `i++` و `++i` از نظر نتیجه یکساناند اما `++i` گاهی در C++ کمی سریعتر است (برای انواع پیچیدهتر).
گام بعدی پیشنهادی
کاراکترها و کدهای ASCII
از نوع char تا کدهای اسکی، تبدیل حروف بزرگ/کوچک و خواندن کلید بدون Enter با getch
نام درس، موضوع یا کلمه کلیدی را بنویسید و با Enter به آن بروید
a = a +1;
1#include<iostream>2usingnamespace std;34intmain(){5int a =5;67 a++;// a = 68 cout <<"After a++: "<< a << endl;910++a;// a = 711 cout <<"After ++a: "<< a << endl;1213 a--;// a = 614 cout <<"After a--: "<< a << endl;1516--a;// a = 517 cout <<"After --a: "<< a << endl;1819return0;20}
1#include<iostream>2usingnamespace std;34intmain(){5int a, b;67// حالت 1: postfix (a++)8 a =5;9 b = a++;// b = 5 (مقدار قدیمی)، سپس a = 610 cout <<"Postfix: a = "<< a <<", b = "<< b << endl;11// خروجی: a = 6, b = 51213// حالت 2: prefix (++a)14 a =5;15 b =++a;// اول a = 6، سپس b = 616 cout <<"Prefix: a = "<< a <<", b = "<< b << endl;17// خروجی: a = 6, b = 61819return0;20}
int d =10, a;a = d++;cout << a << endl << d;
1011
1#include<iostream>2usingnamespace std;34intmain(){5int a;67 a =20; a +=5; cout <<"a += 5 -> a = "<< a << endl;// 258 a =20; a -=5; cout <<"a -= 5 -> a = "<< a << endl;// 159 a =20; a *=5; cout <<"a *= 5 -> a = "<< a << endl;// 10010 a =20; a /=5; cout <<"a /= 5 -> a = "<< a << endl;// 411 a =20; a %=3; cout <<"a %= 3 -> a = "<< a << endl;// 21213// معادل کامل: a = a + b14int b =7;15 a =20;16 a += b;// یعنی: a = a + b = 20 + 7 = 2717 cout <<"a += b (b=7) -> a = "<< a << endl;// 271819return0;20}
int a =5;int result =2* a++;// result = 10, a = 6cout << result <<" "<< a;// خروجی: 10 6
1#include<iostream>2usingnamespace std;34intmain(){5// ردیف اول ستارهها6for(int i =1; i <=8; i++){7 cout <<"* ";8}9 cout << endl;1011 cout <<"hello"<< endl;1213// ردیف دوم ستارهها14for(int i =1; i <=8; i++){15 cout <<"* ";16}17 cout << endl;1819return0;20}
int i =1;i = i+++++i;// خطرناک!
int i =1;int a = i++;// a = 1, i = 2int b =++i;// i = 3, b = 3i = a + b;// i = 4
1// جدول اشتباه در جزوه:2// a۰=b; -> a=a۰b; (۰ صفر فارسی! باید عملگر باشد)3// 1=; -> 1=; (نامعتبر)4// ++ > -- (اشتباه: همترازند)56int a =5;71= a;// خطای سینتکس8// یا9a ۰=3;// خطای سینتکس: کاراکتر فارسی غیرمجاز10
1#include<iostream>2usingnamespace std;34intmain(){5int a;67 a =5; a +=3; cout << a << endl;// 8 (a = a + 3)8 a =5; a -=3; cout << a << endl;// 2 (a = a - 3)9 a =5; a *=3; cout << a << endl;// 15 (a = a * 3)10 a =5; a /=3; cout << a << endl;// 1 (a = a / 3, صحیح)11 a =5; a %=3; cout << a << endl;// 2 (a = a % 3)1213return0;14}
for(int i =0; i < n; i++){...}for(int i =0; i < n;++i){...}for(int i =0; i < n; i +=1){...}
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.