C: المتغيرات وأنواع البيانات

المتغيرات أشبه بصناديق مسماة - كل صندوق له اسم ونوع، ولا يمكنه احتواء إلا النوع الصحيح من العناصر. ضع الشيء الخطأ، والأمور تسوء.

1. ما هو المتغير

المتغير هو موقع ذاكرة مسمى في برنامجك يستخدم لتخزين البيانات. فكر فيه كصندوق:


2. تعريف المتغير وتهيئته

(1) تعريف متغير

تعريف متغير يخبر المترجم: "أحتاج صندوقا، هذا اسمه وهذا نوع الأشياء التي يحتويها."

C
int age;
float weight;
char blood_type;

بعد التعريف، قيمة المتغير غير معرفة (قيمة عشوائية) - تحتوي على أي بيانات عشوائية متبقية في ذلك الموقع من الذاكرة. لا تستخدم أبدا متغيرا غير مهيأ!

(2) تهيئة متغير

التهيئة تعني إعطاء المتغير قيمة ابتدائية في نفس وقت تعريفه:

C
int age = 20;
float weight = 65.5f;
char blood_type = 'A';
💡 نصيحة: تهيأ المتغيرات دائما عند تعريفها. هذه عادة جيدة تساعدك في تجنب العديد من الأخطاء الصعبة التتبع الناتجة عن قيم غير معرفة.

(3) التعريف أولا، ثم الإسناد

يمكنك أيضا التعريف أولا ثم الإسناد لاحقا:

C
int age;
age = 20;

له نفس التأثير، لكن الطريقة ذات الخطوتين أكثر تباعدا، ما يسهل استخدام المتغير خطأ بين التعريف والإسناد بينما لا يزال غير مهيأ.

(4) تعريف عدة متغيرات من نفس النوع

C
int a = 1, b = 2, c = 3;
⚠️ ملاحظة: تعريف وتهيئة عدة متغيرات في نفس السطر يقلل القابلية للقراءة. إذا كان لكل متغير قيمة ابتدائية معبرة، اكتبها في أسطر منفصلة.


3. قواعد تسمية المتغيرات

لدى C قواعد صارمة لأسماء المتغيرات:

يجب الالتزام بها:

ينبغي الالتزام بها:

الاسم صالح؟ ملاحظات
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

أعداد الفاصلة العائمة ذات دقة محدودة ولا يمكنها تمثيل جميع الكسور العشرية بدقة. مثلا:

C
float f = 0.1f;
printf("%.20f\n", f);
TEXT 📖 للعرض فقط
0.10000000149011611938

0.1 هو كسر ثنائي متكرر لانهائيا، لذا float لا يمكنه تخزينه بدقة. هذه خاصية جوهرية لتمثيل الفاصلة العائمة، وليست خطأ.

🔥 خطأ شائع: استخدم double افتراضيا حين تحتاج أعدادا عشرية. يوفر دقة أعلى ولا يكون أبطأ من float على المعالجات الحديثة. فضّل float فقط على الأنظمة المدمجة المحدودة الذاكرة.

(3) نوع المحارف

النوع char هو في جوهره عدد صحيح بحجم بايت واحد، يُستخدم عادة لتخزين المحارف. تُخزن المحارف في الذاكرة كأكواد ASCII:

C
char ch = 'A';
printf("%c\n", ch);
printf("%d\n", ch);
TEXT 📖 للعرض فقط
A
65

كود ASCII لـ 'A' هو 65. استخدام %c يطبع المحرف، بينما %d يطبع القيمة العددية. هذا يُظهر أن المحارف والأعداد الصحيحة قابلة للتبادل في C.

⚠️ ملاحظة: 'A' هي محرف (علامات اقتباس مفردة)، بينما "A" هي سلسلة نصية (علامات اقتباس مزدوجة، مع \0 زائدة). هما مختلفان تماما - لا تخلط بينهما أبدا.

(4) النوع _Bool

أدخل C99 النوع _Bool، الذي يمكنه تخزين 0 و 1 فقط. أدرج <stdbool.h> لاستخدام bool و true و false:

C
#include <stdbool.h>

bool is_valid = true;
bool is_empty = false;

5. معامل sizeof

sizeof هي كلمة مفتاحية في C (وليست دالة) تُرجع عدد البايتات التي يشغلها نوع بيانات أو متغير:

▶ مثال

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;
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
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 تعمل أيضا مع المتغيرات:

C
int age = 20;
printf("%zu\n", sizeof(age));

6. الإخراج المنسق بالتفصيل

(1) تنسيق الأعداد الصحيحة

C
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);
TEXT 📖 للعرض فقط
عشري: 42
ثماني: 52
سداسي عشري (صغير): 2a
سداسي عشري (كبير): 2A
ثماني ببادئة: 052
سداسي عشري ببادئة: 0x2a

(2) تنسيق أعداد الفاصلة العائمة

C
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);
TEXT 📖 للعرض فقط
افتراضي: 3.141593
منزلتان عشريتان: 3.14
8 منازل عشرية: 3.14159265
تدوين علمي: 3.141593e+00
عرض 12.2f: [       3.14]

7. نطاقات قيم الأنواع

توفر المكتبة القياسية لـ C ملفات رأس للاستعلام عن نطاق كل نوع:

(1) limits.h (الأنواع الصحيحة)

C
#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 (أنواع الفاصلة العائمة)

C
#include <float.h>

printf("أرقام float المعنوية: %d\n", FLT_DIG);
printf("أرقام double المعنوية: %d\n", DBL_DIG);
printf("أقصى قيمة لـ float: %e\n", FLT_MAX);

▶ مثال

▶ مثال

C
#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;
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
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

▶ مثال

C
#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;
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
صغير: a (ASCII 97)
كبير: A (ASCII 65)

محارف الأرقام:
المحرف '0' كود ASCII = 48
المحرف '0' القيمة العددية = 0
💡 نصيحة: أكواد ASCII للأحرف الكبيرة والصغيرة تختلف بمقدار 32. اطرح 32 من حرف صغير للحصول على مكافئه الكبير، وأضف 32 للعكس. طرح '0' من محرف رقمي يعطي قيمته العددية - هذا أساس تحويل المحارف إلى أعداد.


❓ أسئلة شائعة

س ما الفرق بين int و long long؟ متى أستخدم long long؟
ج int عادة 4 بايت وأقصى قيمة حوالي 2.1 مليار؛ long long هي 8 بايت وأقصى قيمة حوالي 9.2 كوينتيليون. استخدم long long حين لا يكفي نطاق int (مثلا حساب المضروبات، حسابات الأعداد الكبيرة). للبرمجة اليومية، int كاف - لا تجعل long long افتراضيا، فهي تهدر الذاكرة وقد تكون أبطأ على بعض المنصات.
س هل أختر float أم double؟
ج اجعل double افتراضيا. double تمتلك 15-16 رقما معنويا مقابل 6-7 لـ float - فرق دقة كبير. على المعالجات الحديثة، حسابات double بنفس سرعة float. فضّل float فقط على الأجهزة المدمجة المحدودة الذاكرة.
س هل char موقّع أم غير موقّع؟
ج يعتمد على المترجم والمنصة. معيار C لا يحدد ما إذا كان char موقّعا - المترجمات تقرر. إذا كنت تحتاج علامة مضمونة، استخدم signed char أو unsigned char صراحة. لمحارف ASCII (0-127)، لا يهم. للقيم فوق 127، يجب أن تكون صريحا.
س لماذا لا يجب أن أستخدم متغيرات غير مهيأة؟
ج المتغيرات المحلية غير المهيأة تُخزن على المكدس وتحتوي على أي بيانات عشوائية متبقية في الذاكرة (قيم عشوائية). استخدامها يؤدي إلى سلوك غير معرف - البرنامج قد يعمل أحيانا وينهار أحيانا أخرى، بنتائج مختلفة كل مرة. هذه الأخطاء صعبة التتبع للغاية، لذا تهيأ المتغيرات دائما عند التعريف.

📖 ملخص

📝 تمارين

  1. اكتب برنامجا يستخدم sizeof لطباعة أحجام جميع الأنواع الصحيحة وأنواع الفاصلة العائمة على حاسوبك. قارن النتائج مع زميل لترى إن تطابقت.
  2. اكتب برنامجا يطبع أكواد ASCII للأحرف من 'a' إلى 'z'، خمسة في كل سطر، باستخدام %-5d للمحاذاة.
  3. احسب واطبع نتيجة INT_MAX + 1 (تجاوز عدد صحيح). قارن المخرجات مع INT_MIN وفكر لماذا يحدث هذا.
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%