Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

مبانی برنامه‌سازی — بخش دوم: داده‌ها و عملگرها (نسخه‌ی Jupyter Notebook)

مبانی برنامه‌سازی — بخش دوم: داده‌ها و عملگرها (نسخه‌ی Jupyter Notebook)

این نوت‌بوک دو بخش دارد:

  1. نمای کامل (عین فایل HTML): سلول بعدی خود فایل HTML اصلی را با همان رنگ‌ها، انیمیشن‌ها، منوی کناری، جست‌وجو و دکمه‌ی چاپ نمایش می‌دهد.

  2. نسخه‌ی قابل اجرا: همان محتوا به‌صورت سلول‌های متنی و سلول‌های کد C که تک‌تک قابل اجرا هستند.

طرز استفاده (Google Colab یا Jupyter):

  • اول سلول «آماده‌سازی» و سپس سلول «نمای کامل» را اجرا کنید.

  • هر سلول کدی که با %%c شروع می‌شود با gcc کامپایل و اجرا می‌شود. اگر برنامه ورودی می‌خواهد، آن را جلوی %%c بنویسید؛ مثلاً %%c 25\n30 یعنی دو خط ورودی «۲۵» و «۳۰».

  • تکه‌کدهایی که main ندارند خودکار داخل main قرار می‌گیرند. چند نمونه‌ی «غلط‌نمایی» عمداً فقط نمایشی‌اند و اجرا نمی‌شوند.

نمای کامل (عین فایل HTML اصلی)

نسخه‌ی قابل اجرا (متن + سلول‌های کد C)

بخش دوم: داده‌ها و عملگرها

مخاطب: دانشجویان تازه‌وارد رشته کامپیوتر (بدون پیش‌نیاز خاص)

راهنمای استفاده برای مدرس: این متن عمداً به تعداد مشخصی جلسه محدود نشده است. بخش‌های اصلی برای تدریس پایه طراحی شده‌اند و کادرهای «پنجره‌ی بیشتر»، «برای کنجکاوها» و «نکته‌ی مهندسی» محتوای تکمیلی‌اند؛ مدرس می‌تواند بسته به زمان، سطح کلاس و علاقه‌ی دانشجویان آن‌ها را تدریس کند یا فقط نامشان را مطرح کند تا مسیر تحقیق بعدی برای دانشجو روشن شود.

اهداف یادگیری

در پایان این بخش انتظار می‌رود دانشجو بتواند:

  • مفهوم نوع داده، متغیر، مقدار و نمایش داده در حافظه را از هم تفکیک کند.

  • برای یک مسئله‌ی ساده نوع داده‌ی مناسب انتخاب کند و محدودیت بازه و دقت آن را توضیح دهد.

  • ورودی و خروجی قالب‌بندی‌شده را با printf و scanf بنویسد و خطاهای رایج آن‌ها را تشخیص دهد.

  • عملگرهای محاسباتی، رابطه‌ای، منطقی، انتسابی و بیتی را به‌درستی به‌کار ببرد.

  • تفاوت = و ==، تفاوت تقسیم صحیح و اعشاری، و اثر اولویت و associativity عملگرها را تحلیل کند.

  • نمونه‌های ساده‌ی خطاهای syntax / warning / undefined behavior / logic error را از هم تشخیص دهد.

  • در مواجهه با نتیجه‌ی عجیب، به‌جای حدس‌زدن، نوع داده، تبدیل نوع، هشدار کامپایلر و ترتیب ارزیابی را بررسی کند.

پیش‌نیاز و پس‌نیاز مفهومی

پیش‌نیاز: آشنایی مقدماتی با ساختار یک برنامه‌ی C، مفهوم الگوریتم و چرخه‌ی کلی کامپایل/اجرا.
این بخش پایه‌ی مستقیم مباحث بعدی است: شرط‌ها و حلقه‌ها، توابع، آرایه‌ها، رشته‌ها، اشاره‌گرها و مدیریت حافظه همگی بر فهم دقیق نوع داده، عملگر و حافظه تکیه دارند.


فصل ۱: متغیرها و انواع داده

مقدمه

هر برنامه‌ای، در نهایت، کار می‌کند با داده. برای این‌که کامپیوتر بداند این داده‌ها را کجا و چطور نگه دارد، از متغیر (Variable) استفاده می‌کنیم. متغیر را می‌توان مثل یک جعبه در نظر گرفت که یک مقدار در آن نگه‌داری می‌شود و یک نام دارد که با آن به آن جعبه دسترسی پیدا می‌کنیم.

در این خط: int نوع داده است، age نام متغیر، و 20 مقدار اولیه‌ی آن.

سه مفهوم که نباید با هم قاطی شوند: نام، مقدار و محل ذخیره

در سطح مقدماتی می‌گوییم «متغیر مثل جعبه است». این تشبیه مفید است، اما از همین ابتدا بهتر است سه چیز را جدا ببینیم:

  1. نام متغیر (age) چیزی است که برنامه‌نویس در کد می‌نویسد.

  2. نوع متغیر (int) به کامپایلر می‌گوید این مقدار چگونه تفسیر شود و چه عملیات‌هایی روی آن معنادار است.

  3. مقدار (20) محتوای فعلی آن متغیر است؛ در زمان اجرا این مقدار در بخشی از حافظه نگه‌داری می‌شود.

پنجره‌ی بیشتر: چند فصل بعد، با عملگر & می‌توان آدرس یک متغیر را دید و با «اشاره‌گر» مستقیماً درباره‌ی آدرس‌ها صحبت کرد. در این بخش فقط کافی است بدانیم متغیر علاوه بر نام و مقدار، در زمان اجرا یک محل ذخیره هم دارد.

Declaration، Initialization و Assignment

این سه واژه شبیه هم‌اند اما یکی نیستند:

این تفکیک بعداً هنگام خواندن خطاها، کار با const، آرایه‌ها و اشاره‌گرها بسیار مهم می‌شود.

۱. انواع داده پایه

زبان C چند نوع داده‌ی پایه دارد که هرکدام برای ذخیره‌ی نوع خاصی از اطلاعات مناسب‌اند:

نوع دادهکاربرداندازه‌ی معمول (بایت)مثال
intاعداد صحیح۴int x = 10;
longاعداد صحیح بزرگ‌تر۴ یا ۸long x = 100000L;
long longاعداد صحیح خیلی بزرگ۸long long x = 10000000000LL;
floatاعداد اعشاری (دقت کم)۴float pi = 3.14f;
doubleاعداد اعشاری (دقت بالا)۸double pi = 3.14159265;
charیک کاراکتر۱char grade = 'A';
boolمقدار درست/غلط (نیاز به #include <stdbool.h>)۱bool isValid = true;

نمایش بصری اشغال حافظههر خانه = ۱ بایت = ۸ بیت

۸ بیت → ۱ بایت

اعداد زیر اندازه‌های معمول روی بسیاری از سیستم‌های امروزی‌اند؛ استاندارد C برای همه‌ی آن‌ها یک اندازه‌ی ثابت را تضمین نمی‌کند. برای اندازه‌ی واقعی سیستم، sizeof را بررسی کنید.

char1 بایت · 8 بیت

Byte 0

int4 بایت · 32 بیت

Byte 0

Byte 1

Byte 2

Byte 3

float4 بایت · 32 بیت

Byte 0

Byte 1

Byte 2

Byte 3

double8 بایت · 64 بیت

Byte 0

Byte 1

Byte 2

Byte 3

Byte 4

Byte 5

Byte 6

Byte 7

هر مربع کوچک = ۱ بیت هر گروه ۸تایی = ۱ بایت

نکته‌ی مهم: اندازه‌ی دقیق این نوع‌ها به کامپایلر و سیستم‌عامل بستگی دارد. برای دیدن اندازه‌ی واقعی روی سیستم خودتان می‌توانید از عملگر sizeof استفاده کنید:

یک عدد واقعاً چطور در حافظه ذخیره می‌شود؟

حافظه را می‌توان مثل ردیفی از خانه‌های کوچک دید. هر بیت فقط دو حالت دارد: ۰ یا ۱. هر ۸ بیت کنار هم یک بایت می‌سازند.

۸ بیت = ۱ بایت

ارزش بیت1286432168421

5ده‌دهی

00000101

(0×2⁷) + (0×2⁶) + (0×2⁵) + (0×2⁴) + (0×2³) + (1×2²) + (0×2¹) + (1×2⁰)
۴ + ۱ = ۵

13ده‌دهی

00001101

(0×2⁷) + (0×2⁶) + (0×2⁵) + (0×2⁴) + (1×2³) + (1×2²) + (0×2¹) + (1×2⁰)
۸ + ۴ + ۱ = ۱۳

42ده‌دهی

00101010

(0×2⁷) + (0×2⁶) + (1×2⁵) + (0×2⁴) + (1×2³) + (0×2²) + (1×2¹) + (0×2⁰)
۳۲ + ۸ + ۲ = ۴۲

255ده‌دهی

11111111

(1×2⁷) + (1×2⁶) + (1×2⁵) + (1×2⁴) + (1×2³) + (1×2²) + (1×2¹) + (1×2⁰)
۱۲۸ + ۶۴ + ۳۲ + ۱۶ + ۸ + ۴ + ۲ + ۱ = ۲۵۵

مثلاً یک unsigned char با مقدار ۱۳

00001101

هر مربع = ۱ بیتهر ۸ مربع = ۱ بایت00001101₂ = 13₁₀

نکته‌ی مهم: حافظه «عدد ۱۳» را به شکل نوشته‌ی «13» نگه نمی‌دارد؛ داده به الگوی بیت‌ها تبدیل می‌شود. تفسیر این بیت‌ها به نوع داده بستگی دارد.

مبناهای عددی: یک مقدار، چند شیوه‌ی نوشتن

تا اینجا اعداد را با رقم‌های ۰ تا ۹ نوشته‌ایم؛ یعنی در مبنای ۱۰ (ده‌دهی). اما سخت‌افزار رایانه فقط دو حالت ۰ و ۱ می‌شناسد، و برنامه‌نویس‌ها برای خواندن راحت‌تر الگوی بیت‌ها دو مبنای کمکی هم به کار می‌برند: هشت‌هشتی (Octal) و شانزده‌شانزدهی (Hexadecimal، به‌اختصار «هگز»).

نکته‌ی کلیدی این است که مبنا فقط شیوه‌ی نوشتن است، نه تغییر مقدار. «سیزده» همان سیزده است، چه آن را 13 بنویسیم، چه 1101، چه D؛ حافظه در هر سه حالت همان الگوی بیت‌های 00001101 را نگه می‌دارد.

یک مقدار، چهار نوشتار

همه‌ی کارت‌های زیر دقیقاً یک عدد را نشان می‌دهند.

1101 2دودوییBinary · مبنای ۲

15 8هشت‌هشتیOctal · مبنای ۸

13 10ده‌دهیDecimal · مبنای ۱۰

D 16شانزده‌شانزدهیHexadecimal · مبنای ۱۶

00001101

الگوی بیت‌هایی که در حافظه می‌نشیند (۸ بیت = ۱ بایت)

مبناارقام مجازنوشتن در Cچاپ با printfکاربرد متداول
۲ (دودویی)0 و 10b1101 (C23 و افزونه‌ی GCC/Clang)%b در C23؛ پشتیبانی آن به نسخه‌ی کتابخانه‌ی C بستگی داردسخت‌افزار، بیت‌ها، bitmask
۸ (هشت‌هشتی)0 تا 7015 (پیشوند صفر)%oمجوزهای فایل در یونیکس
۱۰ (ده‌دهی)0 تا 913%dمحاسبات و ارتباط روزمره با انسان
۱۶ (هگز)0 تا 9 و A تا F0xD (پیشوند 0x)%x یا %Xآدرس حافظه، رنگ‌ها، محتوای بایت‌ها

چرا دقیقاً هگز و اکتال؟

دلیل اصلی این است که ۱۶ و ۸ توان‌های دقیق عدد ۲ هستند: 16 = 2⁴ و 8 = 2³. در نتیجه هر رقم هگز دقیقاً معادل ۴ بیت و هر رقم هشت‌هشتی دقیقاً معادل ۳ بیت است. این یعنی تبدیل به دودویی و برعکس، بدون هیچ محاسبه‌ای و فقط با «گروه‌بندی» انجام می‌شود (در بخش‌های بعد خواهید دید). ده‌دهی چنین خاصیتی ندارد، چون ۱۰ توانی از ۲ نیست.

  • هگز: یک بایت (۸ بیت) دقیقاً با دو رقم هگز نوشته می‌شود؛ مثلاً 11111111 می‌شود FF. یک عدد ۳۲بیتی هم فقط ۸ رقم هگز می‌خواهد، در حالی که دودویی‌اش ۳۲ رقم است.

  • هشت‌هشتی: در ماشین‌های قدیمی که طول کلمه‌شان مضرب ۳ بود (مثل ۱۲، ۲۴ یا ۳۶ بیت) بسیار طبیعی بود. امروز بیشتر در مجوزهای فایل یونیکس و ساختار 0... در زبان C دیده می‌شود.

نکته‌ی مهم: نوشته‌ی «10» بدون دانستن مبنا معنی ندارد: 10 2 برابر ۲ است، 10 8 برابر ۸، 10 10 برابر ۱۰ و 10 16 برابر ۱۶. به همین دلیل زبان C با پیشوندهای 0b، 0 و 0x مبنا را مشخص می‌کند.

جدول ۰ تا ۱۵ و توان‌های دو

همان‌طور که در ده‌دهی بعد از ۹ رقم‌ها تمام می‌شوند و 10 (یک «دهتایی» و صفر «یکی») می‌آید، در هگز هم بعد از F رقم‌ها تمام می‌شوند و 10 (که یعنی ۱۶) می‌آید. چون برای ۱۰ تا ۱۵ رقم تک‌نمادی نداریم، از حروف A تا F استفاده می‌شود.

ده‌دهیدودویی (۴ بیتی)هشت‌هشتیهگز
0000000
1000111
2001022
3001133
4010044
5010155
6011066
7011177
81000108
91001119
10101012A
11101113B
12110014C
13110115D
14111016E
15111117F

نکته‌ی مهم: ستون «دودویی» را به‌خاطر بسپارید یا هر وقت لازم شد از روی ارزش بیت‌ها (8 4 2 1) بسازید. این جدول ۱۶ ردیفی کلید همه‌ی تبدیل‌های سریع هگز است. حروف هگز بزرگ و کوچک بودنشان فرقی ندارد: 0xff و 0xFF یک مقدارند.

توان‌های دو که بهتر است از بر باشند

n2ⁿ (ده‌دهی)هگزهشت‌هشتی
0111
1222
2444
38810
4161020
5322040
66440100
712880200
8256100400
95122001000
1010244002000
1120488004000
124096100010000
138192200020000
1416384400040000
15327688000100000
166553610000200000

چند عدد از این جدول در عمل بارها پیش می‌آید: 2⁸ = 256 (تعداد حالت‌های یک بایت)، 2¹⁰ = 1024 (یک «کیلو» در دنیای حافظه) و 2¹⁶ = 65536 (تعداد حالت‌های ۱۶ بیت). به همین دلیل بیشینه‌ی یک بایت بدون علامت 255 = 0xFF و بیشینه‌ی ۱۶ بیت بدون علامت 65535 = 0xFFFF است.

تبدیل هر مبنا به ده‌دهی (بسط مکانی)

همه‌ی مبناها از یک قاعده‌ی واحد پیروی می‌کنند: نمایش مکانی (Positional Notation). هر رقم بسته به جایگاهش در یک توان از مبنا ضرب می‌شود و جمع این حاصل‌ضرب‌ها مقدار عدد است. شمارش جایگاه‌ها از سمت راست و از توان صفر شروع می‌شود.

value = dₖ·bᵏ + … + d₂·b² + d₁·b¹ + d₀·b⁰

در این رابطه b مبنا و d رقم‌هاست. در ده‌دهی این کار را بی‌آنکه فکر کنیم می‌کنیم: 352 = 3×100 + 5×10 + 2×1. حالا همین را برای مبناهای دیگر انجام می‌دهیم:

1101 2

1×2³ + 1×2² + 0×2¹ + 1×2⁰ = 8 + 4 + 0 + 1 = 13

755 8

7×8² + 5×8¹ + 5×8⁰ = 448 + 40 + 5 = 493

2F 16

2×16¹ + 15×16⁰ = 32 + 15 = 47  (F = 15)

CAFE 16

12×16³ + 10×16² + 15×16¹ + 14×16⁰
= 49152 + 2560 + 240 + 14 = 51966

نکته‌ی مهم: قبل از ضرب، حروف هگز را به مقدارشان تبدیل کنید (A=10 تا F=15). خطای رایج این است که F را مثل یک رقم تک‌واحدی حساب کنند.

تبدیل ده‌دهی به مبنای دلخواه (تقسیم‌های متوالی)

برای رفتن در جهت مخالف، عدد را پشت‌سرهم بر مبنای مقصد تقسیم می‌کنیم و هر بار باقیمانده را یادداشت می‌کنیم. باقیمانده‌ی هر تقسیم، «پایین‌ترین رقمِ» باقی‌مانده‌ی عدد در آن مبناست؛ پس اولین باقیمانده کم‌ارزش‌ترین رقم است و ما باید باقیمانده‌ها را از آخر به اول بخوانیم. تقسیم‌ها تا وقتی ادامه پیدا می‌کنند که خارج‌قسمت به صفر برسد.

مثال ثابت: تبدیل ۱۳ به دودویی.

تقسیمخارج‌قسمتباقیمانده
13 ÷ 261 ← کم‌ارزش‌ترین رقم
6 ÷ 230
3 ÷ 211
1 ÷ 201 ← پرارزش‌ترین رقم

خواندن باقیمانده‌ها از پایین به بالا: 1101 2. همین روش برای هر مبنای دیگری هم کار می‌کند؛ مثلاً 156 ÷ 16 = 9 با باقیمانده‌ی 12 که رقم هگز C است، و 9 ÷ 16 = 0 با باقیمانده‌ی 9؛ پس 156 10 = 9C 16. به همین ترتیب 100 10 = 144 8.

انیمیشن تقسیم‌های متوالی

یک عدد و یک مبنای مقصد انتخاب کنید و تقسیم‌ها را یکی‌یکی ببینید.

عدد ده‌دهی (۰ تا ۱٬۰۰۰٬۰۰۰) مبنای مقصددودویی (۲)هشت‌هشتی (۸)هگز (۱۶)

پخش دوباره

میان‌بر: دودویی ↔ هشت‌هشتی ↔ هگز

چون ۱۶ و ۸ توان‌های ۲ هستند، برای تبدیل بین دودویی، هشت‌هشتی و هگز هیچ محاسبه‌ای لازم نیست؛ فقط بیت‌ها را گروه‌بندی می‌کنیم.

  • دودویی ← هگز: بیت‌ها را از سمت راست چهارتا‌چهارتا جدا کنید (اگر گروه آخر کم داشت، از چپ صفر اضافه کنید)، سپس هر گروه را با جدول ۰ تا ۱۵ به یک رقم هگز تبدیل کنید.

  • دودویی ← هشت‌هشتی: همین کار، ولی گروه‌های سه‌تایی.

  • هگز یا هشت‌هشتی ← دودویی: هر رقم را با ۴ (یا ۳) بیت جایگزین کنید.

  • هگز ← هشت‌هشتی (و برعکس): از دودویی به‌عنوان واسطه استفاده کنید.

مثال: 101011110011 2

1010

↓A

1111

↓F

0011

↓3

گروه‌های چهارتایی ← AF3 16

101

↓5

011

↓3

110

↓6

011

↓3

گروه‌های سه‌تایی ← 5363 8  (هر دو برابر ۲۸۰۳ ده‌دهی)

برعکسش هم ساده است: 3B7 16 را باز کنید: 3 → 0011، B → 1011، 7 → 0111؛ پس 001110110111 2.

آزمایشگاه بیت‌ها

روی هر بیت کلیک کنید تا ۰ و ۱ شود، و همزمان مقدار آن در همه‌ی مبناها و گروه‌بندی‌اش را ببینید. زیر هر بیت، ارزش آن نوشته شده است.

یا یک عدد ده‌دهی بنویسید (۰ تا ۶۵۵۳۵)

گروه‌بندی ۴تایی (هگز)

گروه‌بندی ۳تایی (هشت‌هشتی)

13

255

1024

2803 (AF3)

51966 (CAFE)

65535

ده‌دهی

دودویی

هشت‌هشتی (در C)

هگز (در C)

در حالت ۳تایی، چون ۱۶ بر ۳ بخش‌پذیر نیست، گروه اول با صفرهای اضافه‌ی سمت چپ کامل شده است.

اعشار در مبناهای مختلف (برای کنجکاوها)

اعداد اعشاری هم در مبناهای دیگر قابل نوشتن‌اند. بخش صحیح مثل قبل تبدیل می‌شود؛ برای بخش اعشاری به‌جای تقسیم، پشت‌سرهم در مبنا ضرب می‌کنیم و هر بار بخش صحیح حاصل را یادداشت می‌کنیم (این بار از بالا به پایین خوانده می‌شود).

مثال: 0.625 در دودویی. 0.625×2 = 1.25 (رقم ۱)، 0.25×2 = 0.5 (رقم ۰)، 0.5×2 = 1.0 (رقم ۱ و پایان). نتیجه: 0.101 2، چون 1/2 + 0/4 + 1/8 = 0.625.

نکته‌ی مهم: فقط کسرهایی که مخرجشان (پس از ساده‌سازی) توانی از ۲ باشد، در دودویی پایان‌پذیرند. عدد 0.1 این‌طور نیست و در دودویی به‌صورت 0.000110011001100… تا بی‌نهایت ادامه دارد. همین دلیل ریشه‌ای خطای نمایش اعشاری در float و double است: حافظه ناچار است این رشته‌ی بی‌پایان را در تعداد محدودی بیت ببرد.

برای کنجکاوها: جزئیات دقیق ذخیره‌ی اعداد اعشاری در استاندارد IEEE 754 تعریف شده است و در درس‌های بعدی (و در منابع انتهای فایل) می‌توانید دنبالش کنید.

هگز و اکتال در دنیای واقعی

هگز و اکتال فقط تمرین مدرسه‌ای نیستند. چند جای واقعی که حتماً با آن‌ها روبه‌رو می‌شوید:

۱) رنگ‌ها در وب و طراحی

رنگ #RRGGBB سه بایت است: شدت قرمز، سبز و آبی، هر کدام با یک بایت (دو رقم هگز) و در بازه‌ی ۰ تا ۲۵۵. به‌عنوان نمونه، دو رنگ اصلی همین فایل را ببینید:

#233870
R = 0x23 = 35 · G = 0x38 = 56 · B = 0x70 = 112

#783C64
R = 0x78 = 120 · G = 0x3C = 60 · B = 0x64 = 100

۲) مجوزهای فایل در یونیکس (هشت‌هشتی)

هر فایل سه دسته مجوز دارد (مالک، گروه، بقیه) و هر دسته سه بیت: خواندن (r)، نوشتن (w)، اجرا (x). چون هر دسته سه بیت است، یک رقم هشت‌هشتی برایش کافی است؛ دستور chmod 755 file یعنی:

مالک

7

111

rwx

گروه

5

101

r-x

بقیه

5

101

r-x

در مجموع rwxr-xr-x؛ یعنی مالک همه‌کاره است و دیگران فقط می‌توانند بخوانند و اجرا کنند.

۳) کاراکترها و کد ASCII

کاراکترده‌دهیهگزهشت‌هشتی
'A'650x410101
'a'970x610141
'0'480x30060
'\n'100x0A012

حروف بزرگ و کوچک انگلیسی در جدول ASCII دقیقاً 0x20 (یعنی ۳۲) با هم فاصله دارند؛ یعنی فقط یک بیت (بیت پنجم) تفاوتشان است. توضیح بیشتر در بخش char، ASCII و Unicode آمده است.

۴) آدرس حافظه، شبکه و ابزارهای اشکال‌زدایی

آدرس‌ها در حافظه (مثل خروجی %p در printf یا ابزارهایی مثل GDB و Valgrind که بعداً می‌بینید)، آدرس MAC کارت شبکه (۶ بایت، مثل 00:1A:2B:3C:4D:5E) و آدرس‌های IPv6 (۸ گروه ۱۶بیتی، هر گروه ۴ رقم هگز) همگی با هگز نوشته می‌شوند. دلیلش همان خاصیت «هر رقم = ۴ بیت» است که اندازه‌ی نوشته را کوتاه و خواندنش را ممکن می‌کند.

۵) برای کنجکاوها: ترتیب بایت‌ها در حافظه (Endianness)

وقتی عددی مثل 0x12345678 بیشتر از یک بایت جا می‌گیرد، سؤال این است که کدام بایت در کم‌ترین آدرس بنشیند. روی بیشتر رایانه‌های امروزی (از جمله x86-64) روش Little-endian رایج است، یعنی کم‌ارزش‌ترین بایت اول می‌آید:

نوشتار معمول عدد

12پرارزش‌ترین345678کم‌ارزش‌ترین

در حافظه (Little-endian)

78آدرس +056+134+212+3

لازم نیست حالا آن را حفظ کنید؛ فقط بدانید اگر روزی بایت‌های یک عدد را در حافظه «برعکس» دیدید، خطا نیست، قاعده است.

نوشتن و چاپ مبناها در C

در C مبنای یک عدد ثابت (Literal) را پیشوند آن مشخص می‌کند. این قاعده در بخش «ثابت‌ها» و در کتاب K&R هم آمده است؛ اما همین‌جا که در مورد مبناها حرف می‌زنیم، جمع‌بندی‌اش بهتر است:

  • بدون پیشوند ← ده‌دهی: 13

  • پیشوند 0x یا 0X ← هگز: 0xD

  • پیشوند 0 ← هشت‌هشتی: 015

  • پیشوند 0b ← دودویی (در C23 استاندارد شده و سال‌هاست در GCC و Clang هم به‌صورت افزونه پشتیبانی می‌شود)

هر چهار متغیر کاملاً یک مقدار دارند؛ پیشوند فقط به کامپایلر می‌گوید رقم‌ها را چطور بخواند و روی نوع یا مقدار تأثیری ندارد.

چاپ یک عدد در مبناهای مختلف

برای چاپ، به‌جای %d از %o، %x یا %X استفاده می‌کنیم. اگر پیشوند هم بخواهیم، پرچم # آن را اضافه می‌کند (جزئیات پرچم‌ها در بخش قالب‌بندی دقیق‌تر خروجی آمده است).

نکته‌ی مهم: %x به‌تنهایی پیشوند 0x را چاپ نمی‌کند؛ خروجی ff است نه 0xff. برای پیشوند از %#x یا 0x%x استفاده کنید.

دام‌های رایج

همین رفتار «صفر ابتدایی = هشت‌هشتی» دلیل می‌شود برنامه‌نویس‌ها در C معمولاً عدد را با صفر پیشین نمی‌نویسند (مثلاً 007 یا 08)، مگر واقعاً قصد هشت‌هشتی داشته باشند.

هگز و اکتال برای کاراکترها

خواندن هگز از ورودی

همان‌طور که %d برای خواندن ده‌دهی است، %x در scanf هگز می‌خواند و %o هشت‌هشتی. متغیر مقصد در این حالت بهتر است unsigned int باشد (مفهوم «بدون علامت» در بخش signed و unsigned آمده است).

برای کنجکاوها

چاپ دودویی بدون %b: اگر کامپایلر شما %b را پشتیبانی نکند، می‌توان بیت‌ها را با عملگرهای بیتی (>> و &) جدا کرد. لازم نیست این کد را همین الان کامل بفهمید؛ در بخش عملگرهای بیتی دلیل کارکردش روشن می‌شود:

عدد منفی در هگز: روی سیستمی که int آن ۳۲ بیت است، عدد -1 یعنی همه‌ی ۳۲ بیت برابر ۱، و در هگز می‌شود ffffffff (مثلاً با printf("%x\n", (unsigned int)-1);). این‌که چرا منفی‌ها این‌طور ذخیره می‌شوند (مکمل دو) در مبحث بخش اعداد علامت‌دار و بدون علامت و درس‌های بعدی بازتر می‌شود.

خطاهای رایج درباره‌ی مبناها

این جدول خطاهایی را جمع کرده که در کلاس‌های مقدماتی بسیار دیده می‌شود:

اشتباه رایجتوضیح و راه‌حل
فکر کردن به اینکه 010 و 10 یکی‌اند010 در C هشت‌هشتی است و برابر ۸. برای ده‌دهی صفر ابتدایی نگذارید.
نوشتن 08 یا 09ارقام ۸ و ۹ در هشت‌هشتی وجود ندارند و کامپایلر خطای «invalid digit in octal constant» می‌دهد.
نوشتن حرف خارج از A تا F در هگز، مثل 0xFGG رقم هگز نیست. کامپایلر آن را به‌عنوان پسوند نامعتبر عدد می‌بیند و خطا می‌دهد.
گمان اینکه 0x10 برابر ۱۰ است0x10 یعنی 1×16 + 0 = 16. پیشوند فقط مبنا را نشان می‌دهد و جزو مقدار نیست.
انتظار پیشوند 0x از %x%x فقط رقم‌ها را چاپ می‌کند. برای پیشوند از %#x استفاده کنید.
فکر کردن به اینکه 0xFF و 255 دو چیز متفاوت در حافظه‌اندهر دو یک int با مقدار ۲۵۵ هستند؛ حافظه به شیوه‌ی نوشتن شما کاری ندارد.
تبدیل گروه‌های دودویی بدون صفر پیشینهر رقم هگز دقیقاً ۴ بیت است. مثلاً 1 باید 0001 نوشته شود، نه 1، وگرنه گروه‌بندی به‌هم می‌ریزد.
خواندن عدد هگز با %dscanf("%d") ورودی ff را عدد نمی‌داند. برای هگز از %x استفاده کنید.
ریختن عددی مثل 0x100 در unsigned charبیشینه‌ی یک بایت بدون علامت ۲۵۵ (یعنی 0xFF) است و 0x100 (۲۵۶) سرریز می‌کند.

چالش‌های کوتاه با پاسخ تاشو

این چالش‌های کوتاه را قبل از باز کردن پاسخ، روی کاغذ حل کنید. هیچ‌کدام به دستور شرطی یا حلقه نیاز ندارند.

چالش ۱ — دودویی به ده‌دهی: مقدار 101101 2 چند است؟

1×32 + 0×16 + 1×8 + 1×4 + 0×2 + 1×1 = 45

چالش ۲ — ده‌دهی به هگز و دودویی: عدد ۲۰۰ را به هگز و دودویی ببرید.

200 ÷ 16 = 12 با باقیمانده‌ی 8؛ سپس 12 ÷ 16 = 0 با باقیمانده‌ی 12 که C است. پس C8 16. با جدول ۰ تا ۱۵: C = 1100 و 8 = 1000، پس 11001000 2.

چالش ۳ — هگز به دودویی و ده‌دهی: مقدار 0x1F چیست؟

1 → 0001 و F → 1111؛ پس 00011111. در ده‌دهی: 1×16 + 15 = 31.

چالش ۴ — میان‌بر گروه‌بندی: 101011110011 2 را مستقیم به هگز و هشت‌هشتی ببرید.

چهارتایی: 1010 | 1111 | 0011 = AF3. سه‌تایی: 101 | 011 | 110 | 011 = 5363. پس AF3 16 و 5363 8.

چالش ۵ — بدون اجرا پیش‌بینی کنید: printf("%d %d %d\n", 10, 010, 0x10); چه چاپ می‌کند؟

10 8 16. عدد اول ده‌دهی است، دومی هشت‌هشتی (۸) و سومی هگز (۱۶).

چالش ۶ — خطایابی: کدام خط‌ها خطای کامپایل می‌دهند؟ int a = 0xFG; و int b = 09; و int c = 0x1F;

دو خط اول خطا می‌دهند. در 0xFG حرف G رقم هگز نیست و در 09 رقم ۹ در هشت‌هشتی وجود ندارد. خط سوم درست است و برابر ۳۱.

چالش ۷ — چند رقم هگز لازم است تا همه‌ی عددهای ۱۶ بیتی را بنویسیم؟ بزرگ‌ترینشان چیست؟

هر رقم هگز ۴ بیت است، پس ۱۶ بیت یعنی ۴ رقم هگز. بزرگ‌ترین مقدار بدون علامت 0xFFFF = 65535 است.

چالش ۸ — ده‌دهی ۴۸۸۷۹ را به هگز ببرید و حدس بزنید چرا معروف است.

48879 ÷ 16 = 3054 باقیمانده 15 (F)؛ 3054 ÷ 16 = 190 باقیمانده 14 (E)؛ 190 ÷ 16 = 11 باقیمانده 14 (E)؛ 11 ÷ 16 = 0 باقیمانده 11 (B). از پایین به بالا: BEEF 16. این کلمه‌ی هگزی که با حروف A تا F ساخته می‌شود، در دنیای برنامه‌نویسی برای نشانه‌گذاری و شوخی بسیار به کار رفته است.

جدول مرجع سریع و منابع این بخش

اگر می‌خواهید…کار کنید
از مبنای b به ده‌دهی برویدهر رقم را در توان مناسب مبنا (از راست و از توان صفر) ضرب و جمع کنید.
از ده‌دهی به مبنای b برویدعدد را پشت‌سرهم بر b تقسیم کنید و باقیمانده‌ها را از آخر به اول بخوانید.
دودویی را به هگز ببریداز راست ۴تا‌۴تا گروه کنید و با جدول ۰ تا ۱۵ هر گروه را رقم کنید.
دودویی را به هشت‌هشتی ببریداز راست ۳تا‌۳تا گروه کنید.
مبنا را در C مشخص کنید0b دودویی، 0 هشت‌هشتی، 0x هگز، بدون پیشوند ده‌دهی.
در printf چاپ کنید%d ده‌دهی، %o هشت‌هشتی، %x/%X هگز؛ با پرچم # پیشوند هم می‌آید.

برای مطالعه‌ی بیشتر درباره‌ی نمایش اعداد و مبناها:

  • Randal E. Bryant & David R. O’Hallaron, Computer Systems: A Programmer’s Perspective (CS:APP)، فصل ۲، بخش «Information Storage» که نماد هگز را به‌روشنی و با تمرین معرفی می‌کند.

  • Brian W. Kernighan & Dennis M. Ritchie, The C Programming Language، بخش «Constants» برای قواعد ثابت‌های هشت‌هشتی و هگز.

  • CS50x، هفته‌ی ۰ (مبنای دو و نمایش اعداد) و هفته‌ی ۴ (حافظه و هگز).

  • Donald E. Knuth, The Art of Computer Programming، جلد ۲، بخش «Positional Number Systems» برای بحث نظری و کامل مبناها.

تفاوت float و double****: هر دو برای اعداد اعشاری هستند، اما double دقت بیشتری دارد (تعداد ارقام اعشار بیشتر) و در اکثر پروژه‌های واقعی ترجیح داده می‌شود، مگر جایی که حافظه بسیار محدود باشد.

long و long long — وقتی int کافی نیست

همان‌طور که در بخش «حوزه‌ی مقادیر و سرریز» خواهیم دید، int معمولاً فقط تا حدود ۲.۱ میلیارد را پوشش می‌دهد. اگر بخواهیم عددی بزرگ‌تر از این (مثلاً جمعیت جهان، یا نتیجه‌ی ضرب چند عدد بزرگ) را نگه داریم، باید از long یا long long استفاده کنیم:

نکات کلیدی:

  • برای چاپ long از %ld و برای long long از %lld استفاده کنید (نه %d، چون %d مخصوص int معمولی است و خروجی اشتباه می‌دهد).

  • پسوند L یا LL در انتهای عدد ثابت (مثل 100000L) به کامپایلر می‌گوید که این عدد را از همان ابتدا با نوع long/long long در نظر بگیرد، نه int — این کار از overflow زودهنگام در محاسبات میانی جلوگیری می‌کند.

  • اندازه‌ی long به پیاده‌سازی وابسته است. استاندارد C برای long long دست‌کم بازه‌ای معادل یک عدد صحیح علامت‌دار ۶۴ بیتی را تضمین می‌کند، اما بهتر است به‌جای فرض‌کردن تعداد بایت‌ها، در صورت نیاز sizeof و حدود تعریف‌شده در هدرها را بررسی کنیم. برای مقادیری که از بازه‌ی معمول int بزرگ‌ترند، long long اغلب انتخاب قابل‌حمل‌تری است.

اعداد علامت‌دار و بدون علامت (signed / unsigned)

نوع‌های صحیح در C می‌توانند علامت‌دار یا بدون علامت باشند:

unsigned نمی‌تواند مقدار منفی را نمایش دهد و در عوض بخش بیشتری از بیت‌ها را برای مقادیر غیرمنفی استفاده می‌کند. نکته‌ی مهم این است که ترکیب signed و unsigned در یک عبارت می‌تواند نتیجه‌های غافلگیرکننده ایجاد کند؛ پس در کد مقدماتی بهتر است بی‌دلیل از unsigned استفاده نکنیم.

برای کنجکاوها: در <stdint.h> نوع‌هایی مانند int32_t و uint64_t وجود دارند که وقتی دقیقاً به تعداد مشخصی بیت نیاز داریم مفیدند. در این درس دانستن نام آن‌ها کافی است؛ جزئیاتشان در برنامه‌نویسی سیستمی اهمیت بیشتری پیدا می‌کند.

دقت اعشاری و خطای نمایش

float و double همه‌ی اعداد اعشاری را دقیق نگه نمی‌دارند. مثلاً مقدار 0.1 در نمایش دودویی متناهی نیست و معمولاً به نزدیک‌ترین مقدار قابل‌نمایش گرد می‌شود:

ممکن است خروجی چیزی نزدیک به 0.30000000000000004 باشد. نتیجه‌ی آموزشی مهم: برای مقایسه‌ی اعداد اعشاری، همیشه x == 0.3 بهترین انتخاب نیست.

پنجره‌ی بیشتر: استاندارد رایج نمایش اعداد اعشاری در سخت‌افزار IEEE 754 نام دارد. فعلاً فقط اسم آن را بدانید؛ در درس معماری کامپیوتر یا محاسبات عددی می‌توان عمیق‌تر به آن برگشت.

اندازه‌ی نوع‌ها: «معمولاً» با «تضمین‌شده» فرق دارد

استاندارد C حداقل‌هایی را تضمین می‌کند، اما الزام نمی‌کند که مثلاً int همیشه دقیقاً ۴ بایت باشد. برای کد قابل‌حمل:

عملگر sizeof خروجی‌ای از نوع size_t می‌دهد و برای چاپ آن از %zu استفاده می‌کنیم.

۲. قواعد نام‌گذاری متغیرها

نام متغیر نمی‌تواند دلخواه باشد؛ باید از قواعد زیر پیروی کند:

  • فقط می‌تواند شامل حروف، اعداد و آندرلاین (_) باشد.

  • نمی‌تواند با عدد شروع شود (2value غلط است، value2 درست است).

  • به بزرگی و کوچکی حروف حساس است (Age و age دو متغیر متفاوت‌اند).

  • نمی‌تواند از کلمات کلیدی زبان (مثل int, for, return) استفاده کند.

  • بهتر است نامی معنادار و خواناکه انتخاب شود (studentCount بهتر از x است).

int studentCount = 30;   // خوب
int 2ndYear = 5;         // خطای کامپایل (شروع با عدد)
int student count = 10;  // خطای کامپایل (فاصله مجاز نیست)

(نمایشی — این نمونه عمداً اجرا نمی‌شود)

سبک‌های رایج نام‌گذاری (Naming Convention): قواعد بالا فقط می‌گویند چه نامی «مجاز» است؛ اما برنامه‌نویس‌ها برای نام‌های چندکلمه‌ای (مثل «تعداد دانشجو») روی چند سبک قرارداد کرده‌اند. خود C سبک خاصی را اجباری نکرده است.

سبکمثالمنشأ و جاهایی که استفاده می‌شود
camelCasestudentCountریشه در جامعه‌ی زبان Smalltalk (دهه‌ی ۱۹۷۰)؛ امروز برای متغیر و تابع در Java و JavaScript رایج است.
PascalCaseStudentCountبه‌نام زبان Pascal (نیکلاوس ویرت، ۱۹۷۰)؛ برای نام کلاس و نوع در Java و C#، و در Windows API.
snake_casestudent_countسنتی در Unix و C؛ کتابخانه‌ی استاندارد C (مثل size_t و strlen)، هسته‌ی لینوکس، Python (استاندارد PEP 8) و Rust.
UPPER_SNAKE_CASEMAX_STUDENTSمخصوص ثابت‌ها و ماکروها در C، مثل INT_MAX در <limits.h>.
نت مجارستانی (Hungarian)iCount ، szNameابداع چارلز سیمونی (Charles Simonyi)، برنامه‌نویس Xerox PARC و بعد Microsoft؛ در Windows API (مثل hWnd) دیده می‌شود، اما امروز کمتر توصیه می‌شود.
kebab-casestudent-countدر C مجاز نیست (- عملگر منفی است)؛ در CSS و آدرس وب‌سایت‌ها استفاده می‌شود.

این سبک‌ها «قانون زبان» نیستند، بلکه قرارداد تیم‌ها و راهنماهای سبک کدنویسی‌اند (مثل Linux kernel coding style، PEP 8 در Python و راهنمای کدنویسی Google). در درس، هر سبکی را انتخاب کردید، در کل برنامه همان را ادامه دهید.

چند ظرافت نام‌گذاری که ارزش دانستن دارند

  • نام‌هایی که با دو آندرلاین (__name) شروع می‌شوند یا الگوهای خاصی از آندرلاین و حرف بزرگ دارند، برای پیاده‌سازی زبان/کتابخانه رزرو شده‌اند؛ برای کد دانشجویی از آن‌ها دوری کنید.

  • نام متغیر معنادار باشد، اما لازم نیست تبدیل به یک جمله‌ی طولانی شود.

  • سبک‌هایی مثل camelCase و snake_case هر دو رایج‌اند؛ مهم‌تر از انتخاب یکی، ثبات در کل پروژه است.

  • مفهوم Scope در فصل توابع کامل‌تر بررسی می‌شود؛ فعلاً بدانید دو متغیر هم‌نام در محدوده‌های متفاوت الزاماً یک متغیر نیستند.

۳. حوزه‌ی مقادیر و سرریز (Overflow)

هر نوع داده، به‌خاطر تعداد بیت‌های محدودی که در حافظه اشغال می‌کند، فقط می‌تواند بازه‌ی مشخصی از اعداد را نگه دارد. برای مثال int معمولاً بین تقریباً -۲٬۱۴۷٬۴۸۳٬۶۴۸ تا ۲٬۱۴۷٬۴۸۳٬۶۴۷ را پوشش می‌دهد.

وقتی نتیجه‌ی یک محاسبه از بازه‌ی نوع داده خارج شود، با سرریز (Overflow) روبه‌رو می‌شویم. نکته‌ی علمی مهم این است که در C، رفتار سرریز عدد صحیح علامت‌دار (signed int) طبق استاندارد «تعریف‌نشده» (Undefined Behavior) است؛ بنابراین نباید روی برگشتن خودکار عدد از انتهای بازه به ابتدای آن حساب کنیم. در بسیاری از ماشین‌های امروزی ممکن است ظاهراً چنین رفتاری دیده شود، اما برنامه‌ی قابل‌اعتماد نباید به آن وابسته باشد. در مقابل، سرریز انواع unsigned طبق حساب پیمانه‌ای تعریف شده است.

این یکی از رایج‌ترین منابع باگ در برنامه‌های واقعی است — همیشه به بازه‌ی نوع داده‌ای که استفاده می‌کنید توجه کنید.

چطور بازه‌ی واقعی نوع‌ها را از خود C بپرسیم؟

به‌جای حفظ‌کردن عددها، می‌توان از هدر <limits.h> استفاده کرد:

برای انواع اعشاری نیز <float.h> ثابت‌هایی درباره‌ی دقت و محدوده ارائه می‌کند.

چهار واژه‌ی خطا که از همین حالا باید بشناسیم

نوع مشکلنمونهمعمولاً چه زمانی دیده می‌شود؟
Syntax Error
  • جا انداختن ;

  • باز یا بسته‌نشدن پرانتز و آکولاد: ( {

  • تمام‌نشدن رشته، یعنی جا انداختن " پایانی

  • نام متغیری که با رقم شروع می‌شود: int 2x;

  • جا انداختن ویرگول بین آرگومان‌ها: printf("%d" x);

زمان کامپایل
Warning
  • تبدیل نوع مشکوک

  • متغیر تعریف‌شده ولی استفاده‌نشده (unused variable)

  • ناهماهنگی format specifier با نوع آرگومان، مثل چاپ double با %d

  • خواندن متغیری که مقدار اولیه نگرفته (uninitialized)

  • جا دادن عدد بزرگ در نوع کوچک‌تر، مثل char c = 300;

زمان کامپایل، ولی ممکن است فایل اجرایی ساخته شود
Runtime Error
  • تقسیم بر صفر صحیح در زمان اجرا

  • باقیمانده‌ی صفر: x % 0

  • دسترسی به حافظه‌ی نامعتبر، مثل خواندن از اشاره‌گر NULL (Segmentation Fault)

  • بازگشت بی‌پایان تابع و پر شدن پشته (Stack Overflow)

  • شکست یک assert هنگام اجرا

هنگام اجرا
Logic Error
  • فرمول اشتباه ولی کد قابل اجرا

  • تقسیم صحیح به‌جای اعشاری: 5 / 2 برابر 2 می‌شود، نه 2.5

  • اولویت اشتباه عملگرها: a + b / 2 به‌جای (a + b) / 2

  • استفاده از = (انتساب) به‌جای == (مقایسه)

  • جابه‌جا شدن نقش دو متغیر، مثل سن و سال تولد در یک فرمول

برنامه اجرا می‌شود، خروجی غلط است

مفهوم مهم‌تر: Undefined Behavior. بعضی عملیات در C آن‌قدر خطرناک‌اند که استاندارد نتیجه‌ی مشخصی برایشان تعریف نمی‌کند؛ مثل سرریز signed int یا دسترسی خارج از محدوده‌ی آرایه. این مفهوم در ادامه‌ی درس بارها برمی‌گردد.

۴. ثابت‌ها (Constants)

گاهی می‌خواهیم مقداری در طول برنامه هرگز تغییر نکند. برای این کار دو روش رایج داریم:

روش اول: کلمه‌ی کلیدی const

روش دوم: دستور پیش‌پردازنده #define

تفاوت اصلی: const یک متغیر واقعی با نوع مشخص است که کامپایلر آن را چک می‌کند؛ #define صرفاً یک جایگزینی متنی قبل از کامپایل است و نوع ندارد. در پروژه‌های مدرن، استفاده از const معمولاً توصیه می‌شود.

نکته‌ی دقیق: const در C به این معناست که از طریق آن نام اجازه‌ی تغییر مقدار نداریم؛ این مفهوم با «ثابت زمان کامپایل» در همه‌ی موقعیت‌ها یکی نیست. از طرف دیگر، #define اصلاً متغیر ایجاد نمی‌کند و مرحله‌ی پیش‌پردازش آن را به‌صورت متنی جایگزین می‌کند. ماکروها قابلیت‌های بیشتری هم دارند، اما استفاده‌ی بی‌احتیاط از آن‌ها می‌تواند خطاهای ظریفی بسازد.

Literalها و پسوندها

خودِ عددهایی که مستقیماً در کد می‌نویسیم literal نام دارند. پسوندها به کامپایلر در تعیین نوع آن‌ها کمک می‌کنند:

42       // معمولاً int
42U      // unsigned int
42L      // long
42LL     // long long
3.14     // double
3.14f    // float

(نمایشی — این نمونه عمداً اجرا نمی‌شود)

این موضوع زمانی مهم می‌شود که محاسبه قبل از ذخیره‌شدن در متغیر سرریز کند.

۵. تبدیل نوع (Type Casting)

گاهی لازم است مقدار یک نوع داده را به نوع دیگری تبدیل کنیم.

تبدیل ضمنی (Implicit Casting): کامپایلر خودش، بدون این‌که از او بخواهیم، تبدیل را انجام می‌دهد.

تبدیل صریح (Explicit Casting): خودمان به‌صراحت نوع را مشخص می‌کنیم.

نکته‌ی رایج برای مبتدیان: تقسیم دو عدد int همیشه نتیجه‌ی int می‌دهد (بخش اعشاری قطع می‌شود)، حتی اگر نتیجه را در یک متغیر double بریزید. برای گرفتن نتیجه‌ی اعشاری، حداقل یکی از دو عدد باید cast شود.

نکته‌ی کوچک — نمایش عدد در مبنای دیگر با printf: همان عددی که در حافظه ذخیره شده را می‌توانید مستقیماً به‌صورت هگزادسیمال یا اکتال هم چاپ کنید، بدون هیچ تبدیل دستی:

این ارتباط مستقیمی با مبحث سیستم‌های عددی دارد که پیش‌تر خوانده‌اید.

تبدیل نوع، promotion و از دست رفتن اطلاعات

تبدیل نوع همیشه بی‌خطر نیست:

همچنین انواع کوچک‌تر مثل char و short در بسیاری از عبارت‌های محاسباتی ابتدا به int ارتقا پیدا می‌کنند؛ به این فرایند integer promotion می‌گویند.

پنجره‌ی بیشتر: مجموعه قوانین تبدیل ضمنی در C با نام usual arithmetic conversions شناخته می‌شود. حفظ کردن جزئیات آن در این مرحله لازم نیست، اما وقتی ترکیبی از int، unsigned، float و double در یک عبارت دیدید، بدانید پشت صحنه قواعد مشخصی برای تبدیل وجود دارد.

تمرین خطایابی کوتاه: خروجی را قبل از اجرا پیش‌بینی کنید

  1. خروجی x چیست؟

  2. خروجی y چیست؟

  3. تبدیل نوع در کدام مرحله باید رخ دهد تا بخش اعشاری از بین نرود؟

جمع‌بندی و تمرین‌های این بخش

  • انواع داده: int, float, double, char, bool

  • قواعد نام‌گذاری معتبر

  • بازه‌ی مقادیر و خطر Overflow

  • ثابت‌ها: const در برابر #define

  • تبدیل نوع: implicit در برابر explicit

تمرین ۱ — محاسبه‌ی سن

برنامه‌ای بنویسید که سن کاربر را در یک int بگیرد، آن را در ۲ (به‌عنوان double) ضرب کند و با تبدیل نوع صریح چاپ کند.

تمرین ۲ — رشد باکتری و کشف Overflow

فرض کنید یک جمعیت باکتری از 1 شروع می‌شود و در هر مرحله دو برابر می‌شود.

برنامه‌ای بنویسید که یک متغیر از نوع int را از 1 شروع کند و در هر مرحله مقدار آن را در 2 ضرب کند. بعد از هر مرحله مقدار را چاپ کنید و ببینید با زیاد شدن مقدار، چه اتفاقی برای عدد می‌افتد.

هدف تمرین: می‌خواهیم Overflow را فقط در تئوری نبینید؛ خودتان رشد سریع عدد را اجرا کنید و ببینید وقتی مقدار از ظرفیت نوع داده عبور کند چه مشکلی به وجود می‌آید.

راهنمایی: فعلاً نیازی به حلقه ندارید. می‌توانید چند بار پشت سر هم بنویسید:

> x *= 2;
> printf("%d\n", x);
> 

و مرحله‌به‌مرحله خروجی را بررسی کنید.

نکته: رفتار Overflow برای int علامت‌دار در استاندارد C تضمین‌شده نیست؛ هدف این تمرین مشاهده‌ی مفهوم سرریز در عمل است. برای آزمایش کاملاً قابل‌پیش‌بینی، بعداً می‌توانید همین ایده را با انواع unsigned بررسی کنید.

تمرین ۳ — مبدل فارنهایت به سلسیوس

برنامه‌ای بنویسید که یک دما بر حسب فارنهایت بگیرد و آن را به سلسیوس تبدیل کند (C = (F - 32) * 5 / 9).

این یکی از قدیمی‌ترین و معروف‌ترین تمرین‌های آموزش C است؛ نسخه‌ی اصلی آن دقیقاً همین برنامه است که در کتاب کلاسیک The C Programming Language نوشته‌ی Brian Kernighan و Dennis Ritchie (معروف به کتاب K&R) به‌عنوان یکی از اولین برنامه‌های آموزشی کتاب معرفی شده است.

تمرین ۴ — محاسبه‌گر BMI

برنامه‌ای بنویسید که قد (بر حسب متر) و وزن (بر حسب کیلوگرم) را بگیرد و شاخص توده‌ی بدنی را حساب کند (BMI = weight / (height * height)).

این تمرین یک نمونه‌ی کلاسیک برای تمرین متغیرهای اعشاری و ترتیب محاسبه است.

تمرین ۵ — شکار خطا: نوع داده‌ی نامناسب

کد زیر را بررسی کنید و بدون اجرا بگویید چه اشکالاتی دارد. سپس آن را اصلاح کنید:

هدف، تشخیص تفاوت کاراکتر و رشته، تبدیل ضمنی و محدودیت دقت float است.

تمرین ۶ — مقایسه‌ی float و double

عدد 1.0 / 3.0 را یک‌بار در float و یک‌بار در double ذخیره کنید و هر دو را با تعداد زیادی رقم اعشار چاپ کنید. تفاوت را توضیح دهید.

تمرین ۷ — بازه‌ی نوع‌ها با <limits.h>

برنامه‌ای بنویسید که CHAR_BIT، INT_MIN، INT_MAX و UINT_MAX را چاپ کند. سپس حدس بزنید سیستم شما برای int چند بیت استفاده می‌کند.


فصل ۲: ورودی/خروجی و عملگرهای پایه

۱. ورودی و خروجی: printf و scanf

printf — چاپ خروجی

printf("Age: %d\n", age);

(نمایشی — این نمونه عمداً اجرا نمی‌شود)

printf از Format Specifierها استفاده می‌کند تا بداند هر متغیر را با چه فرمتی چاپ کند:

Specifierنوع داده
%dint
%ffloat
%lfdouble (در scanf؛ در printf هر دو %f هستند)
%cchar
%sرشته (string / آرایه‌ی کاراکتر)

قالب‌بندی دقیق‌تر خروجی

printf فقط برای چاپ ساده نیست. می‌توان عرض، تعداد رقم اعشار و تراز را هم کنترل کرد:

چند escape sequence پرکاربرد:

دنبالهمعنی
\nرفتن به خط بعد
\tTab
\"چاپ " داخل رشته
\\چاپ backslash

نکته: در printf برای double از %f استفاده می‌شود؛ اما در scanf، %f برای float* و %lf برای double* است. این تفاوت از رایج‌ترین خطاهای مبتدیان است.

scanf — گرفتن ورودی از کاربر

نکته‌ی حیاتی: برای متغیرهای عددی و کاراکتری، باید از علامت & (آدرس متغیر) قبل از نام متغیر استفاده کنید — چون scanf نیاز دارد بداند مقدار را کجا در حافظه بنویسد.

برای رشته‌ها (%s) از & استفاده نمی‌شود، چون نام آرایه به‌خودی‌خود آدرس است — این نکته را در جلسات مربوط به آرایه‌ها و رشته‌ها بیشتر توضیح می‌دهیم.

مقدار بازگشتی scanf را نادیده نگیریم

scanf تعداد ورودی‌هایی را که با موفقیت خوانده و تبدیل کرده برمی‌گرداند:

اگر کاربر به‌جای عدد، متن وارد کند، result می‌تواند 0 شود. در برنامه‌های واقعی باید این حالت را مدیریت کنیم؛ بعد از یادگیری شرط‌ها می‌توانیم ورودی نامعتبر را بهتر کنترل کنیم.

فاصله‌ها در format string

در scanf، وجود فاصله قبل از %c معمولاً عمدی است:

scanf(" %c", &grade);

(نمایشی — این نمونه عمداً اجرا نمی‌شود)

این فاصله باعث می‌شود whitespace باقی‌مانده (مثلاً Enter قبلی) رد شود. بدون آن ممکن است %c همان \n باقی‌مانده را بخواند.

درباره‌ی %s: فقط تا اولین فاصله

scanf("%s", name) برای یک کلمه مناسب است، نه نام کامل چندکلمه‌ای. ضمن آن‌که باید مراقب ظرفیت آرایه بود. شکل محدودشده امن‌تر است:

پنجره‌ی بیشتر: برای خواندن خط کامل، تابع fgets معمولاً انتخاب بهتری است و در مبحث رشته‌ها دوباره به آن برمی‌گردیم.

مثال کامل: چرا نام آرایه‌ی رشته نیاز به & ندارد

برای این‌که این نکته کاملاً ملموس شود، بهتر است هم رشته و هم یک کاراکتر تکی را در یک برنامه با هم مقایسه کنیم و آدرس هرکدام را هم چاپ کنیم:

نمونه خروجی ترمینال (Terminal Output):

Enter your full first name: Banafsheh
Enter your favorite letter: A

--- Result ---
Name: Banafsheh
Address of name (name):     0x7ff7bfe2f820
Address of name (&name[0]): 0x7ff7bfe2f820
Favorite letter: A (Address: 0x7ff7bfe2f853)

نکته‌ی کلیدی از روی همین خروجی: مقدار آدرسی که با name و &name[0] چاپ می‌شود یکسان است. بیان دقیق‌تر این است که نام آرایه در بیشتر عبارت‌ها به اشاره‌گری به عنصر اول آرایه تبدیل می‌شود (array-to-pointer decay). به همین دلیل scanf("%s", name) به اشاره‌گر خانه‌ی اول دسترسی دارد و نیازی به &name نیست. توجه کنید name و &name از نظر نوع یکسان نیستند، هرچند ممکن است هنگام چاپ آدرس مقدار عددی یکسانی نشان دهند. در مقابل، first_letter یک متغیر معمولی است و برای نوشتن در آن باید &first_letter را به scanf بدهیم.

۲. عملگرهای محاسباتی، رابطه‌ای و منطقی

عملگرهای محاسباتی (Arithmetic)

عملگرمعنیمثال
+جمع5 + 2 → 7
-تفریق5 - 2 → 3
*ضرب5 * 2 → 10
/تقسیم5 / 2 → 2 (تقسیم صحیح!)
%باقی‌مانده (Modulo)5 % 2 → 1

کاربرد جالب: رمزنگاری سزار (Caesar Cipher)

یکی از قدیمی‌ترین روش‌های رمزنگاری در تاریخ (که به ژولیوس سزار نسبت داده می‌شود) این است: هر حرف پیام را چند جایگاه در الفبا جلو ببریم. چون هر char در واقع یک عدد (کد ASCII) است، این کار فقط با جمع عددی انجام می‌شود:

برای رمزگشایی، کافی است همین مقدار shift را از حرف رمزشده کم کنیم. نکته‌ی مهم: این نسخه‌ی ساده فقط برای یک حرف تکی و بدون عبور از انتهای الفبا (Z به A) کار می‌کند؛ نسخه‌ی کامل‌تر که همه‌ی این حالت‌ها را هم پوشش بدهد، بعد از یادگیری if و حلقه‌ها قابل تکمیل است.

عملگرهای رابطه‌ای (Relational)

نتیجه‌ی این عملگرها همیشه 0 (غلط) یا 1 (درست) است:

عملگرمعنی
==برابر است با
!=برابر نیست با
<کوچک‌تر
>بزرگ‌تر
<=کوچک‌تر یا مساوی
>=بزرگ‌تر یا مساوی

خطای رایج: استفاده از = به‌جای == برای مقایسه. = عملگر انتساب است، == عملگر مقایسه!

عملگرهای منطقی (Logical)

برای ترکیب چند شرط با هم استفاده می‌شوند:

عملگرمعنیمثال
&&و (AND) — هر دو باید درست باشند(age > 18) && (hasID)
``
!نقیض (NOT)!isValid

افزایش و کاهش: ++ و --

تفاوت x++ و ++x زمانی مهم می‌شود که داخل یک عبارت بزرگ‌تر استفاده شوند:

برای خوانایی، در کدهای مقدماتی بهتر است افزایش/کاهش را تا حد امکان در خط جدا بنویسیم.

ارزیابی کوتاه‌مدار (Short-circuit)

در عملگرهای منطقی، C همیشه لازم نیست هر دو سمت را ارزیابی کند:

(a != 0) && (10 / a > 2)

(نمایشی — این نمونه عمداً اجرا نمی‌شود)

اگر a != 0 غلط باشد، بخش دوم اجرا نمی‌شود. این رفتار short-circuit evaluation نام دارد و بعداً برای نوشتن شرط‌های امن بسیار مفید است.

۳. عملگرهای انتسابی

علاوه بر = ساده، C راه‌های کوتاه‌تری برای «مقداردهی همراه با عملیات» دارد:

عملگرمعادلمثال
+=x = x + yx += 5;
-=x = x - yx -= 5;
*=x = x * yx *= 5;
/=x = x / yx /= 5;
%=x = x % yx %= 5;

۴. عملگرهای بیتی (Bitwise Operators)

عملگرهای بیتی مستقیماً روی بیت‌های نمایش دودویی اعداد صحیح کار می‌کنند. این مبحث ممکن است در شروع کمی انتزاعی باشد، اما پلی مستقیم بین برنامه‌نویسی و معماری کامپیوتر است.

عملگرنامایده‌ی ساده
&AND بیتیفقط بیت‌هایی که در هر دو عدد ۱ هستند، ۱ می‌مانند
``OR بیتی
^XOR بیتیاگر دو بیت متفاوت باشند، خروجی ۱ است
~NOT بیتیهر بیت را معکوس می‌کند
<<شیفت چپبیت‌ها را به چپ می‌برد
>>شیفت راستبیت‌ها را به راست می‌برد

عمل روی بیت‌ها؛ اول با چشم ببینیم

در مثال‌های زیر فقط ۴ بیت را برای ساده‌فهم شدن نشان می‌دهیم. در C، نوع واقعی عدد می‌تواند ۸، ۱۶، ۳۲ یا بیت‌های بیشتری داشته باشد.

AND: &

0101**&**0011

0001

فقط جاهایی ۱ می‌ماند که هر دو بیت ۱ باشند.

OR: |

0101**|**0011

0111

اگر حداقل یکی ۱ باشد، نتیجه ۱ است.

XOR: ^

0101**^**0011

0110

اگر دو بیت متفاوت باشند، نتیجه ۱ است.

NOT: ~

~0101

1010

در این مثال آموزشی ۴ بیتی، هر ۰ و ۱ برعکس می‌شود.

جمع و تفریق دودویی؛ بیت به بیت

جمع: ۵ + ۳ = ۸

  0101
+ 0011
──────
  1000

قاعده‌ی پایه: ۱+۱ یعنی ۰ و ۱ انتقالی به ستون بعد.

تفریق: ۸ − ۳ = ۵

  1000
− 0011
──────
  0101

اگر در یک ستون رقم بالایی کوچک‌تر باشد، از ستون سمت چپ قرض می‌گیریم؛ درست شبیه تفریق ده‌دهی.

شیفت هم جابه‌جایی واقعی بیت‌هاست:

0011**<< 1**0110۳ → ۶

0011**>> 1**0001۳ → ۱

ترفند ذهنی: برای فهم عملگرهای بیتی، همیشه عدد را به بیت‌ها تبدیل کن، عملگر را ستون‌به‌ستون اجرا کن و در پایان دوباره به ده‌دهی برگردان.

مثال:

کاربرد ساده: زوج یا فرد

کم‌ارزش‌ترین بیت یک عدد زوج 0 و یک عدد فرد 1 است:

شیفت و توان‌های دو

برای اعداد unsigned و در محدوده‌ی معتبر، شیفت چپ یک بیت اغلب معادل ضرب در ۲ و شیفت راست معادل تقسیم صحیح بر ۲ دیده می‌شود:

هشدار علمی: قوانین شیفت برای مقادیر علامت‌دار و شیفت‌های خارج از محدوده جزئیات مهمی دارند. برای مثال‌های آموزشی بیتی، unsigned مدل ذهنی امن‌تری است.

Bitmask چیست؟

گاهی هر بیت را به‌عنوان یک «پرچم روشن/خاموش» استفاده می‌کنیم. به این الگو bitmask می‌گویند. این روش در مجوزهای فایل، رجیسترهای سخت‌افزاری، پروتکل‌ها و سیستم‌های نهفته بسیار رایج است. در این درس فقط آشنایی با نام و ایده کافی است.

۵. اولویت (Precedence) و جهت وابستگی (Associativity) عملگرها

عبارت زیر را در نظر بگیرید:

چون * اولویت بیشتری از + دارد، نتیجه 14 است. اما هدف آموزشی مهم‌تر این است: در کد واقعی به‌جای حفظ‌کردن جدول بزرگ اولویت‌ها، با پرانتز قصد خود را واضح کنید.

نمونه‌ای که پرانتز خوانایی را نجات می‌دهد:

int ok = (age >= 18) && (hasId == 1);

(نمایشی — این نمونه عمداً اجرا نمی‌شود)

چند نکته‌ی بسیار مهم:

  • = اولویت پایین‌تری از بسیاری از عملگرها دارد و با == اشتباه نشود.

  • عملگرهای منطقی && و || از عملگرهای رابطه‌ای پایین‌ترند.

  • عملگر شرطی سه‌تایی ?: در بخش کنترل جریان معرفی می‌شود؛ دانستن نامش در این مرحله کافی است.

  • ترتیب اولویت با ترتیب واقعی ارزیابی operands یک چیز نیست؛ C در بعضی عبارت‌ها ترتیب ارزیابی را تضمین نمی‌کند.

پنجره‌ی بیشتر: عبارت‌هایی مثل i = i++ + ++i; نمونه‌های خوبی برای «کد ننویسید!» هستند. واردشدن به جزئیات sequence/ordering در این درس لازم نیست؛ پیام اصلی این است که یک متغیر را چندبار در یک عبارت پیچیده تغییر ندهید.

تمرین تشخیص اولویت

بدون اجرا، مقدار نهایی را پیش‌بینی کنید و سپس با پرانتز بازنویسی کنید:

جمع‌بندی و تمرین‌های این بخش

  • printf برای خروجی، scanf برای ورودی (و اهمیت &)

  • عملگرهای محاسباتی، رابطه‌ای، منطقی

  • عملگرهای انتسابی کوتاه (+=, -=, ...)

تمرین ۱ — بررسی دو عدد

برنامه‌ای بنویسید که دو عدد از کاربر بگیرد و بررسی کند آیا هر دو مثبت هستند یا نه. در صورت مثبت بودن هر دو عدد، مجموع و باقی‌مانده‌ی تقسیم آن‌ها را چاپ کنید.

توجه: در این تمرین نباید از if و else استفاده کنید. هدف این است که با مفاهیم و عملگرهایی که تا اینجا یاد گرفته‌اید، مسئله را حل کنید.

تمرین ۲ — سیستم بررسی PIN

یک برنامه‌ی ساده برای ورود به یک سیستم بنویسید.

یک PIN ثابت مثل 1234 با استفاده از const تعریف کنید. سپس PIN را یک بار از کاربر بگیرید و بررسی کنید که آیا درست است یا نه. در پایان، نتیجه‌ی مناسب را چاپ کنید؛ مثلاً «ورود موفق» یا «PIN اشتباه است».

توجه: در این تمرین نباید از if، else یا حلقه استفاده کنید. مسئله را فقط با مفاهیم و عملگرهایی که تا این بخش یاد گرفته‌اید حل کنید.

تمرین ۳ — دستگاه خودپرداز (ATM)

یک مبلغ پول به ریال (مثلاً 870000 ریال) را از کاربر بگیرید و حساب کنید به چند اسکناس 100000 ریالی و چند اسکناس 10000 ریالی نیاز است.

هدف تمرین: استفاده‌ی عملی از عملگرهای تقسیم (/)، باقی‌مانده (%) و عملگرهای انتسابی ترکیبی مثل /= و %=.

راهنمایی: ابتدا تعداد اسکناس‌های 100000 ریالی را با تقسیم به دست آورید. سپس با استفاده از %= مقدار باقی‌مانده‌ی مبلغ را محاسبه کنید و از روی آن تعداد اسکناس‌های 10000 ریالی را به دست آورید.

تمرین ۴ — تبدیل ثانیه به ساعت، دقیقه و ثانیه

عددی به‌عنوان کل ثانیه‌ها بگیرید (مثلاً 3725) و آن را به قالب ساعت:دقیقه:ثانیه چاپ کنید. فقط با / و % قابل حل است.

تمرین ۵ — تعویض مقدار دو متغیر بدون متغیر کمکی

دو عدد از کاربر بگیرید و مقدار آن‌ها را بدون استفاده از یک متغیر سوم (کمکی) با هم جابه‌جا کنید و نتیجه را چاپ کنید.

تمرین ۶ — بررسی سال کبیسه

یک سال میلادی را از کاربر بگیرید و بررسی کنید که آیا کبیسه است یا نه. قاعده‌ی سال کبیسه در تقویم میلادی به این صورت است:

شرطنتیجه
بر ۴ بخش‌پذیر نباشدکبیسه نیست
بر ۴ بخش‌پذیر باشد، ولی بر ۱۰۰ نهکبیسه است
بر ۱۰۰ بخش‌پذیر باشد، ولی بر ۴۰۰ نه (مثل ۱۹۰۰)کبیسه نیست
بر ۴۰۰ بخش‌پذیر باشد (مثل ۲۰۰۰)کبیسه است

نتیجه را فقط با عملگرهای %، &&، || و ! (بدون if) به‌صورت ۰ یا ۱ چاپ کنید.

تمرین ۷ — کالبدشکافی خطاهای ورودی/خروجی

هر خط را مستقل بررسی کنید و مشخص کنید مشکل از نوع syntax، format mismatch، منطق یا محدودیت ورودی است:

سپس نسخه‌ی صحیح را بنویسید و توضیح دهید چرا اصلاح شده است.

تمرین ۸ — Debug by Warning

کد زیر را با -Wall -Wextra کامپایل کنید و هشدارها را بخوانید:

بدون اجرای برنامه، از روی warningها مشکل را پیدا و اصلاح کنید.

تمرین ۹ — Decode یک بیت‌ماسک ساده

سه قابلیت را با سه بیت در نظر بگیرید: خواندن = بیت 0، نوشتن = بیت 1، اجرا = بیت 2. اگر مقدار مجوز 5 باشد، با & مشخص کنید کدام قابلیت‌ها روشن‌اند. این تمرین مقدمه‌ای کوچک برای درک مجوزهای سطح پایین و پرچم‌هاست.

تمرین ۱۰ — سؤال مفهومی کوتاه

برای هر عبارت فقط بگویید «ایمن و واضح»، «قابل اجرا ولی گمراه‌کننده»، یا «دارای رفتار تعریف‌نشده/نامعتبر» و دلیل بیاورید:

5 / 2
5.0 / 2
(double)(5 / 2)
(double)5 / 2
INT_MAX + 1

(نمایشی — این نمونه عمداً اجرا نمی‌شود)


جدول ارزش منطقی در C

در C هر مقدار صحیح صفر در شرط، false و هر مقدار غیرصفر true در نظر گرفته می‌شود. عملگرهای مقایسه‌ای و منطقی معمولاً نتیجه‌ی 0 یا 1 تولید می‌کنند.

| A | B | A && B | A || B | |---:|---:|---:|---:| | 0 | 0 | 0 | 0 | | 0 | غیرصفر | 0 | 1 | | غیرصفر | 0 | 0 | 1 | | غیرصفر | غیرصفر | 1 | 1 |

این نکته توضیح می‌دهد چرا عبارتی مثل if (x) در C معنادار است؛ هرچند خود if در بخش کنترل جریان آموزش داده می‌شود.

char، ASCII و Unicode: یک تفاوت مهم

char یک واحد عدد صحیح کوچک است و برای ذخیره‌ی بایت/کاراکتر به‌کار می‌رود. در مثال‌های مقدماتی معمولاً از حروف ASCII مثل 'A' استفاده می‌کنیم؛ اما متن فارسی را نمی‌توان با فرض «هر حرف = یک char» تحلیل کرد. متن‌های Unicode معمولاً با رمزگذاری‌هایی مثل UTF-8 ذخیره می‌شوند و یک نویسه‌ی فارسی ممکن است چند بایت اشغال کند.

پنجره‌ی بیشتر: تفاوت «character»، «byte»، «code point» و «encoding» موضوعی مهم در برنامه‌نویسی واقعی است. در این درس فقط لازم است بدانیم مدل ساده‌ی ASCII برای همه‌ی زبان‌های طبیعی کافی نیست.

چند format specifier تکمیلی که بهتر است اسمشان را دیده باشید

قالبکاربرد متداول
%uunsigned int
%ldlong
%lldlong long
%zusize_t، مثلاً خروجی sizeof
%pآدرس؛ آرگومان معمولاً به (void*) تبدیل می‌شود
%%چاپ خود علامت %

قالب اشتباه فقط یک مشکل ظاهری نیست. در توابع variadic مثل printf و scanf، ناهماهنگی format specifier با نوع آرگومان می‌تواند رفتار نادرست یا حتی Undefined Behavior ایجاد کند.

جمع‌بندی کلی بخش دوم

مرور نکات کلیدی کل بخش:

  1. متغیرها و انواع داده: int, float, double, char, bool، قواعد نام‌گذاری، بازه‌ی مقادیر، overflow، ثابت‌ها، تبدیل نوع

  2. ورودی/خروجی: printf/scanf و format specifierها

  3. عملگرها: محاسباتی، رابطه‌ای، منطقی، انتسابی، بیتی، و اولویت اجرای آن‌ها

تمرین نهایی: برنامه‌ای بنویسید که یک عدد صحیح از کاربر بگیرد و با استفاده از عملگر بیتی & مشخص کند زوج است یا فرد، سپس با استفاده از << مقدار آن را دو برابر کرده و چاپ کند.

منابع و مسیر مطالعه‌ی بیشتر

  • Brian W. Kernighan & Dennis M. Ritchie, The C Programming Language (K&R) — مرجع کلاسیک زبان C.

  • CS50x, Week 1: C — نمونه‌ای از شیوه‌ی آموزش متغیرها، نوع‌ها، عملگرها و کامپایل در یک درس مقدماتی علوم کامپیوتر.

  • cppreference: C language — مرجع فنی برای نوع‌ها، تبدیل‌ها، عملگرها و کتابخانه‌ی استاندارد.

  • ISO C / compiler documentation — برای جزئیات دقیق رفتارهای تعریف‌شده، پیاده‌سازی‌وابسته و Undefined Behavior.

  • CSES Problem Set (cses.fi) — مجموعه‌ی سؤال‌های برنامه‌نویسی و الگوریتمی برای تمرین بیشتر؛ در انتخاب برخی تمرین‌های این فایل، با اعمال تغییراتی، از آن کمک گرفته شده است.

  • ابزارهای هوش مصنوعی مثل Claude، ChatGPT و Gemini — برای پرسیدن توضیح بیشتر، مثال و رفع ابهام؛ پاسخ‌هایشان را با منابع مرجع بالا و با اجرای واقعی کد بررسی کنید.

پیشنهاد به دانشجو: لازم نیست در شروع همه‌ی این منابع را خط‌به‌خط بخوانید. مهم است بدانید برای پرسش‌های دقیق‌تر، منابع مرجع وجود دارند و بتوانید در زمان نیاز سراغشان بروید.


تیم تهیه و تدوین

اعضای تیم و تقسیم کار

عضو تیمنقش و مشارکت
ملیکا محمدبیگیتهیه‌ی پروتوتایپ و نسخه‌ی اولیه‌ی محتوا، شامل ساختار کلی مباحث و تمرین‌های اولیه؛ همچنین طراحی اولیه‌ی فایل تمرین‌ها، و در ادامه ساخت دو فرمت دیگر از همین فایل تمرین و بررسی آن‌ها برای اطمینان از یکسان بودن با فایل HTML اصلی
درسا عناصریبازبینی علمی و ویرایشی، توسعه و بهینه‌سازی ساختار آموزشی، افزودن/حذف و بازطراحی تمرین‌ها، افزودن نکات مفهومی و خطاهای رایج، و بررسی ایده‌های آموزشی مورد استفاده در منابع و دوره‌های دانشگاهی معتبر؛ همچنین بررسی علمی فایل تمرین‌ها، سنجش سختی سؤال‌ها و ارتباط آن‌ها با یکدیگر، یکسان‌سازی و مرتب‌سازی فایل تمرین‌ها، اضافه و کم کردن چندین سؤال، و افزودن تذکرات و نکاتی برای استاد تدریس‌کننده در برخی سؤال‌ها؛ و در پایان، تهیه و تدوین فایل نهایی
زینب قاراچشمهبهبود و ارتقای محتوای کارگاه بر پایه‌ی نسخه‌ی اولیه‌ی ملیکا محمدبیگی که پیش‌تر توسط درسا عناصری بررسی شده بود؛ بازبینی علمی محتوا به‌صورت مشترک با درسا عناصری؛ همچنین کمک در یافتن نمونه‌سؤال‌ها و تمرین‌های خوب و مفهومی، از جمله از CSES، و اعمال تغییراتی روی آن‌ها

فرایند تولید و بازبینی

محتوا از یک نسخه‌ی اولیه آغاز شد و سپس با تمرکز بر صحت علمی، پیوستگی پیش‌نیاز/پس‌نیاز، تناسب با دانشجوی ترم اول، تنوع مثال‌ها، خطاهای رایج و تمرین‌های تشخیصی توسعه یافت. در نسخه‌ی حاضر محدودیت تعداد جلسه حذف شده تا مدرس بتواند بر اساس سطح کلاس و زمان واقعی، بین «هسته‌ی اصلی» و «مطالب تکمیلی» انتخاب کند.

شفافیت درباره‌ی استفاده از هوش مصنوعی

برای شفافیت با خوانندگان این فایل، لازم است بگوییم که در تولید این مجموعه از ابزارهای هوش مصنوعی (AI) بهره گرفته‌ایم. این کمک در سه بخش بود:

ایده‌گیریگرفتن پیشنهاد برای ساختار مباحث، مثال‌ها و شیوه‌ی توضیح مفاهیم.

تولید محتواکمک در نگارش پیش‌نویس بخش‌هایی از متن و مثال‌ها.

بیشترین کمکتولید خود کدها و فرمت‌هاتولید کدهای HTML و فرمت‌های Markdown و Jupyter Notebook، که بیشترین سهم کمک هوش مصنوعی در همین بخش بود.

با این حال، محتوای نهایی با نظارت انسانی خودِ ما، یعنی اعضای تیم تهیه و تدوین، بازبینی و تکمیل شده است.