یه عبارت مثل:
i = i + 2
که در اون متغیر سمت چپ بلافاصله در سمت راست هم تکرار شده، میتونه به شکل کوتاهتر نوشته بشه:
i += 2
به این عملگر، عملگر انتساب (+=) گفته میشه.
بیشتر عملگرهای دوتایی (مثل + که دو عملوند داره — یکی سمت چپ و یکی سمت راست) یه نسخهی انتسابی هم دارن، به شکل op=، که op میتونه یکی از موارد زیر باشه:
+ - * / % << >> & ^ |
اگه expr1 و expr2 دو تا عبارت باشن، آنگاه:
expr1 op= expr2
معادل اینه که بنویسیم:
expr1 = (expr1) op (expr2)
با این تفاوت که در حالت اول، expr1 فقط یک بار محاسبه میشه.
دقت کن به پرانتزهای دور expr2 — مثلاً:
x *= y + 1
یعنی:
x = x * (y + 1)
و نه این:
x = x * y + 1
به عنوان مثال، تابع زیر تعداد بیتهایی که مقدارشون ۱ هست رو در عدد ورودی میشماره:
/* bitcount: count 1 bits in x */
int bitcount(unsigned x)
{
int b;
for (b = 0; x != 0; x >>= 1)
if (x & 01)
b++;
return b;
}
اینجا آرگومان x به عنوان unsigned تعریف شده تا وقتی به راست شیفت داده میشه، بیتهای خالی با صفر پر بشن، نه با بیت علامت — مهم نیست روی چه ماشینی برنامه اجرا میشه.
علاوه بر کوتاهتر بودن، عملگرهای انتساب یه مزیت دیگه هم دارن: با طرز فکر انسانها سازگارترن
ما معمولاً میگیم «۲ تا به i اضافه کن» یا «i رو ۲ تا زیاد کن»، نه اینکه بگیم «مقدار i رو بگیر، با ۲ جمع کن، بعد نتیجه رو بذار تو i».
برای همین، عبارت i += 2 طبیعیتر و خواناتر از i = i + 2 هست.
حتی توی عبارتهای پیچیدهتر مثل:
yyval[yypv[p3+p4] + yypv[p1]] += 2
استفاده از عملگر انتساب باعث میشه کد قابلفهمتر بشه، چون خواننده لازم نیست با دقت بررسی کنه که آیا اون دو عبارت طولانی واقعاً یکی هستن یا نه.
علاوه بر این، استفاده از عملگرهای انتساب ممکنه به کامپایلر کمک کنه تا کد بهینهتری تولید کنه.
قبلاً دیدیم که عملگر انتساب خودش یه مقدار (value) هم داره و میتونه داخل عبارتها استفاده بشه.
رایجترین مثالش اینه:
while ((c = getchar()) != EOF)
...
بقیهی عملگرهای انتسابی مثل +=, -=, و غیره هم میتونن داخل عبارتها بیان،
هرچند این کار کمتر اتفاق میافته.
در تمام این حالتها، نوع (type) یه عبارت انتسابی برابر نوع عملوند سمت چپشه،
و مقدار نهایی هم همون مقداریه که بعد از انتساب بهدست میاد.
تمرین ۹-۲:
در سیستم عددی «تکمیل دو» (two’s complement)، عبارت زیر:
x &= (x - 1)
بیت ۱ سمت راست (آخرین ۱) رو از x حذف میکنه.
توضیح بده چرا این اتفاق میافته.
با استفاده از این نکته، یه نسخه سریعتر از تابع bitcount بنویس.