C: المتغيرات وأنواع البيانات
المتغيرات أشبه بصناديق مسماة - كل صندوق له اسم ونوع، ولا يمكنه احتواء إلا النوع الصحيح من العناصر. ضع الشيء الخطأ، والأمور تسوء.
1. ما هو المتغير
المتغير هو موقع ذاكرة مسمى في برنامجك يستخدم لتخزين البيانات. فكر فيه كصندوق:
- اسم المتغير: الملصق على الصندوق، لتتمكن من إيجاده
- نوع البيانات: نوع العناصر التي يمكن للصندوق احتواؤها
- قيمة المتغير: ما بداخل الصندوق حاليا
- عنوان الذاكرة: موقع الصندوق في المستودع
2. تعريف المتغير وتهيئته
(1) تعريف متغير
تعريف متغير يخبر المترجم: "أحتاج صندوقا، هذا اسمه وهذا نوع الأشياء التي يحتويها."
int age;
float weight;
char blood_type;
بعد التعريف، قيمة المتغير غير معرفة (قيمة عشوائية) - تحتوي على أي بيانات عشوائية متبقية في ذلك الموقع من الذاكرة. لا تستخدم أبدا متغيرا غير مهيأ!
(2) تهيئة متغير
التهيئة تعني إعطاء المتغير قيمة ابتدائية في نفس وقت تعريفه:
int age = 20;
float weight = 65.5f;
char blood_type = 'A';
(3) التعريف أولا، ثم الإسناد
يمكنك أيضا التعريف أولا ثم الإسناد لاحقا:
int age;
age = 20;
له نفس التأثير، لكن الطريقة ذات الخطوتين أكثر تباعدا، ما يسهل استخدام المتغير خطأ بين التعريف والإسناد بينما لا يزال غير مهيأ.
(4) تعريف عدة متغيرات من نفس النوع
int a = 1, b = 2, c = 3;
3. قواعد تسمية المتغيرات
لدى C قواعد صارمة لأسماء المتغيرات:
يجب الالتزام بها:
- يمكن أن تحتوي فقط على أحرف (a-z، A-Z) وأرقام (0-9) وشرطات سفلية (_)
- يجب أن تبدأ بحرف أو شرطة سفلية - لا يمكن أن تبدأ برقم
- لا يمكن أن تكون كلمة مفتاحية في C (مثل
int،return،if، إلخ) - حساسة لحالة الأحرف (
Ageوageمتغيران مختلفان)
ينبغي الالتزام بها:
- الأسماء يجب أن تكون معبرة: استخدم
student_countبدلا منsc - استخدم أسلوب snake_case (أحرف صغيرة + شرطات سفلية)
- تجنب الأسماء التي تبدأ بشرطتين سفليتين (مثل
__count) - المترجمات تستخدمها داخليا - تجنب الأسماء الطويلة جدا (أكثر من 31 حرفا قد لا تدعمها جميع المترجمات)
| الاسم | صالح؟ | ملاحظات |
|---|---|---|
age |
صالح | قصير ومعبر |
_count |
صالح | يبدأ بشرطة سفلية |
student_name |
صالح | أسلوب snake_case |
2nd_place |
غير صالح | يبدأ برقم |
my-score |
غير صالح | يحتوي على شرطة (تُفسّر كطرح) |
int |
غير صالح | كلمة مفتاحية |
Age |
صالح لكن غير موصى به | الحرف الكبير الأول يُحفظ عادة لأسماء الأنواع |
4. أنواع البيانات الأساسية
نوع بيانات C يحدد مقدار الذاكرة التي يشغلها المتغير، ونطاق القيم الذي يمكنه احتواؤها، وكيف تُفسّر البيانات. فيما يلي أنواع البيانات الأساسية في C99:
(1) الأنواع الصحيحة
| النوع | الحجم (بايت) | النطاق النموذجي | محدد التنسيق |
|---|---|---|---|
char |
1 | -128 ~ 127 | %c أو %d |
unsigned char |
1 | 0 ~ 255 | %u |
short |
2 | -32768 ~ 32767 | %hd |
unsigned short |
2 | 0 ~ 65535 | %hu |
int |
4 | -2147483648 ~ 2147483647 | %d |
unsigned int |
4 | 0 ~ 4294967295 | %u |
long |
4 أو 8 | يعتمد على المنصة | %ld |
unsigned long |
4 أو 8 | يعتمد على المنصة | %lu |
long long |
8 | -9223372036854775808 ~ 9223372036854775807 | %lld |
int كاف. استخدم long long حين تحتاج نطاقا أكبر، و char للمحارف. short نادر الاستخدام على المنصات الحديثة لأن int فعالة بنفس القدر عادة.
int بحجم 4 بايت، وهو الحال على معظم المنصات الحديثة. لكن معيار C يشترط فقط أن يكون int بايتين على الأقل. الحجم الفعلي يعتمد على المنصة - استخدم sizeof للتأكد.
(2) أنواع الفاصلة العائمة
| النوع | الحجم (بايت) | الأرقام المعنوية | النطاق (القيمة المطلقة) | محدد التنسيق |
|---|---|---|---|---|
float |
4 | 6-7 | 1.2e-38 ~ 3.4e38 | %f |
double |
8 | 15-16 | 2.2e-308 ~ 1.8e308 | %lf |
long double |
8 أو أكثر | 18+ | يعتمد على المنصة | %Lf |
أعداد الفاصلة العائمة ذات دقة محدودة ولا يمكنها تمثيل جميع الكسور العشرية بدقة. مثلا:
float f = 0.1f;
printf("%.20f\n", f);
0.10000000149011611938
0.1 هو كسر ثنائي متكرر لانهائيا، لذا float لا يمكنه تخزينه بدقة. هذه خاصية جوهرية لتمثيل الفاصلة العائمة، وليست خطأ.
double افتراضيا حين تحتاج أعدادا عشرية. يوفر دقة أعلى ولا يكون أبطأ من float على المعالجات الحديثة. فضّل float فقط على الأنظمة المدمجة المحدودة الذاكرة.
(3) نوع المحارف
النوع char هو في جوهره عدد صحيح بحجم بايت واحد، يُستخدم عادة لتخزين المحارف. تُخزن المحارف في الذاكرة كأكواد ASCII:
char ch = 'A';
printf("%c\n", ch);
printf("%d\n", ch);
A
65
كود ASCII لـ 'A' هو 65. استخدام %c يطبع المحرف، بينما %d يطبع القيمة العددية. هذا يُظهر أن المحارف والأعداد الصحيحة قابلة للتبادل في C.
'A' هي محرف (علامات اقتباس مفردة)، بينما "A" هي سلسلة نصية (علامات اقتباس مزدوجة، مع \0 زائدة). هما مختلفان تماما - لا تخلط بينهما أبدا.
(4) النوع _Bool
أدخل C99 النوع _Bool، الذي يمكنه تخزين 0 و 1 فقط. أدرج <stdbool.h> لاستخدام bool و true و false:
#include <stdbool.h>
bool is_valid = true;
bool is_empty = false;
5. معامل sizeof
sizeof هي كلمة مفتاحية في C (وليست دالة) تُرجع عدد البايتات التي يشغلها نوع بيانات أو متغير:
▶ مثال
#include <stdio.h>
int main(void) {
printf("char: %zu bytes\n", sizeof(char));
printf("short: %zu bytes\n", sizeof(short));
printf("int: %zu bytes\n", sizeof(int));
printf("long: %zu bytes\n", sizeof(long));
printf("long long: %zu bytes\n", sizeof(long long));
printf("float: %zu bytes\n", sizeof(float));
printf("double: %zu bytes\n", sizeof(double));
return 0;
}
char: 1 bytes
short: 2 bytes
int: 4 bytes
long: 4 bytes
long long: 8 bytes
float: 4 bytes
double: 8 bytes
sizeof تُرجع نوع size_t، الذي محدد تنسيقه هو %zu. إذا كان مترجمك لا يدعم %zu (بعض الإصدارات القديمة)، يمكنك استخدام %lu بدلا منه.
sizeof تعمل أيضا مع المتغيرات:
int age = 20;
printf("%zu\n", sizeof(age));
6. الإخراج المنسق بالتفصيل
(1) تنسيق الأعداد الصحيحة
int n = 42;
printf("عشري: %d\n", n);
printf("ثماني: %o\n", n);
printf("سداسي عشري (صغير): %x\n", n);
printf("سداسي عشري (كبير): %X\n", n);
printf("ثماني ببادئة: %#o\n", n);
printf("سداسي عشري ببادئة: %#x\n", n);
عشري: 42
ثماني: 52
سداسي عشري (صغير): 2a
سداسي عشري (كبير): 2A
ثماني ببادئة: 052
سداسي عشري ببادئة: 0x2a
(2) تنسيق أعداد الفاصلة العائمة
double pi = 3.141592653589793;
printf("افتراضي: %f\n", pi);
printf("منزلتان عشريتان: %.2f\n", pi);
printf("8 منازل عشرية: %.8f\n", pi);
printf("تدوين علمي: %e\n", pi);
printf("عرض 12.2f: [%12.2f]\n", pi);
افتراضي: 3.141593
منزلتان عشريتان: 3.14
8 منازل عشرية: 3.14159265
تدوين علمي: 3.141593e+00
عرض 12.2f: [ 3.14]
7. نطاقات قيم الأنواع
توفر المكتبة القياسية لـ C ملفات رأس للاستعلام عن نطاق كل نوع:
(1) limits.h (الأنواع الصحيحة)
#include <limits.h>
printf("int min: %d\n", INT_MIN);
printf("int max: %d\n", INT_MAX);
printf("char max: %d\n", CHAR_MAX);
وحدات الماكرو الشائعة:
| ماكرو | المعنى |
|---|---|
INT_MIN |
أدنى قيمة لـ int |
INT_MAX |
أقصى قيمة لـ int |
CHAR_MIN |
أدنى قيمة لـ char |
CHAR_MAX |
أقصى قيمة لـ char |
LONG_MAX |
أقصى قيمة لـ long |
LLONG_MAX |
أقصى قيمة لـ long long |
(2) float.h (أنواع الفاصلة العائمة)
#include <float.h>
printf("أرقام float المعنوية: %d\n", FLT_DIG);
printf("أرقام double المعنوية: %d\n", DBL_DIG);
printf("أقصى قيمة لـ float: %e\n", FLT_MAX);
▶ مثال
▶ مثال
#include <stdio.h>
#include <limits.h>
#include <float.h>
int main(void) {
char ch = 'Z';
int num = 100;
float f = 3.14f;
double d = 2.718281828;
printf("char '%c' = ASCII %d, size = %zu\n", ch, ch, sizeof(ch));
printf("int %d, size = %zu, range = [%d, %d]\n", num, sizeof(num), INT_MIN, INT_MAX);
printf("float %.6f, size = %zu, digits = %d\n", f, sizeof(f), FLT_DIG);
printf("double %.9f, size = %zu, digits = %d\n", d, sizeof(d), DBL_DIG);
return 0;
}
char 'Z' = ASCII 90, size = 1
int 100, size = 4, range = [-2147483648, 2147483647]
float 3.140000, size = 4, digits = 6
double 2.718281828, size = 8, digits = 15
▶ مثال
#include <stdio.h>
int main(void) {
char lower = 'a';
char upper = lower - 32;
printf("صغير: %c (ASCII %d)\n", lower, lower);
printf("كبير: %c (ASCII %d)\n", upper, upper);
printf("\nمحارف الأرقام:\n");
char digit = '0';
printf("المحرف '%c' كود ASCII = %d\n", digit, digit);
printf("المحرف '%c' القيمة العددية = %d\n", digit, digit - '0');
return 0;
}
صغير: a (ASCII 97)
كبير: A (ASCII 65)
محارف الأرقام:
المحرف '0' كود ASCII = 48
المحرف '0' القيمة العددية = 0
'0' من محرف رقمي يعطي قيمته العددية - هذا أساس تحويل المحارف إلى أعداد.
❓ أسئلة شائعة
📖 ملخص
- المتغيرات مواقع ذاكرة مسماة؛ حدد النوع والاسم عند التعريف، وتهيأ دائما
- الأسماء يجب أن تبدأ بحرف أو شرطة سفلية، وحساسة لحالة الأحرف، ولا يمكن استخدام الكلمات المفتاحية
- الأنواع الصحيحة: char (1 بايت)، short (2)، int (4)، long (4 أو 8)، long long (8)
- أنواع الفاصلة العائمة: float (4 بايت، 6-7 أرقام معنوية)، double (8 بايت، 15-16 رقما معنويا)
- sizeof تُرجع حجم النوع أو المتغير بالبايت؛ limits.h و float.h يوفران نطاقات القيم
📝 تمارين
- اكتب برنامجا يستخدم sizeof لطباعة أحجام جميع الأنواع الصحيحة وأنواع الفاصلة العائمة على حاسوبك. قارن النتائج مع زميل لترى إن تطابقت.
- اكتب برنامجا يطبع أكواد ASCII للأحرف من 'a' إلى 'z'، خمسة في كل سطر، باستخدام %-5d للمحاذاة.
- احسب واطبع نتيجة INT_MAX + 1 (تجاوز عدد صحيح). قارن المخرجات مع INT_MIN وفكر لماذا يحدث هذا.