P01NT
0x01 الأساسيات 02/19

هيكلية البرنامج

هيكلية برنامج C الأساسية: التضمين include والدالة main والتعليقات والمتغيرات العامة والدوال — وأثر كل جزء منها في الـ binary.

· 3 دقائق قراءة · 505 كلمة
المحتويات

بسم الله الرحمن الرحيم.

في هذا المقال سنتعرف على هيكلية تركيب البرنامج الأساسي في لغة C. تتكون الهيكلية من أجزاء أساسية يلتزم بها كل برنامج C:

#include <stdio.h> // تضمين مكتبات النظام
// تعريف الثوابت (Constants) أو المتغيرات العامة (Global Variables)


// دوال أخرى


int main(){
    // الكود الأساسي هنا
    // المتغيرات، الدوال، الحلقات، الشروط
    return 0; // إرجاع القيمة للمشغل
}


// دوال أخرى

1. التضمين (include)

مثال: #include <stdio.h> — حيث يستخدم لاستيراد المكتبات المدمجة. هذه المكتبة تحديداً مسؤولة عن الإدخال والإخراج، واختصارها stdio يعني:

Standard Input/Output (إدخال/إخراج قياسي).

ولاحقة .h تعني header — وهي ملفات الترويسة التي تعرّف الدوال قبل استخدامها.

نقطة مهمة لمجال RE: هذه الاستدعاءات (مثل printf, scanf) ستظهر لاحقاً في جدول الاستيراد (Import Table) الخاص بملف الـ PE القابل للتنفيذ. عندما نرى دالة مستوردة في الـ imports، نعرف أن البرنامج يستخدمها — وهذا أول خيط في أي تحليل.

2. الدالة main()

هي نقطة الانطلاق عند تنفيذ أي برنامج C — يتم تنفيذ الكود داخل هذه الدالة أولاً. صيغها الشائعة:

int main(void)                  // بدون معاملات
int main(int argc, char *argv[]) // مع معاملات سطر الأوامر
  • argc: عدد المعاملات.
  • argv: مصفوفة من النصوص تمثل المعاملات نفسها.

نقطة مهمة لمجال RE: النوع الثاني يهمك جداً، لأن برامج كثيرة تُحلل مع معاملات سطر الأوامر — وهذه المعاملات ستجدها في الـ stack عند الدخول إلى main في الـ disassembly.

عن return 0;

تُستخدم return 0; لإرجاع قيمة إلى المشغّل (نظام التشغيل). القيمة 0 تعني أن البرنامج نجح، وأي قيمة غير صفرية تعني فشلاً أو خطأ.

ملاحظة: في C الحديثة (C99+) إذا انتهى main دون return، يفترض المترجم return 0 ضمنياً.

3. التعليقات (Comments)

تُضاف التعليقات باستخدام // لسطر واحد أو /* */ لمقاطع متعددة الأسطر:

// هذا تعليق لسطر واحد

/*
   هذا تعليق
   متعدد الأسطر
*/

نقطة مهمة لمجال RE: التعليقات لا تظهر إطلاقاً في الـ assembly — لأنها تُحذف في مرحلة المعالجة المسبقة (التي شرحناها في المقال السابق). أي تعليق في الكود المصدر لا يفيد المحلل إلا كدليل على نية المبرمج الأصلية.

4. المتغيرات العامة (Global Variables)

تُعرف خارج أي دالة، وتكون متاحة لكل دوال البرنامج. في الـ RE ستلاحظ أنها تُخزَّن في قسم خاص من الملف القابل للتنفيذ (بيانات ثابتة مثل .data و .bss) وليس على الـ stack — وهذا فرق أساسي بين المتغيرات العامة والمتغيرات المحلية، وهو ما سنفصّله في سلسلة الـ assembly.

5. الدوال الأخرى

تُعرَّف الدوال قبل main أو بعدها. إن عُرّفت بعد main يجب التصريح عنها قبلها بـ بروتوتايب (prototype) حتى يعرفها المترجم:

int add(int a, int b); // بروتوتايب: التصريح فقط

int main(){
    int result = add(3, 5);
    return 0;
}

int add(int a, int b){ // التعريف الفعلي
    return a + b;
}

تمرين عملي

اكتب البرنامج التالي وطبّق عليه مراحل الترجمة التي تعلمتها في المقال السابق:

#include <stdio.h>

int main(void){
    printf("Hello, RE!\n");
    return 0;
}
gcc -E hello.c -o hello.i    # شاهد كيف يتوسع #include إلى آلاف الأسطر
gcc -S hello.i -o hello.s    # شاهد printf تتحول إلى call
gcc -c hello.s -o hello.o
gcc hello.o -o hello

لاحظ كيف يختفي كل من #include والتعليقات في المرحلة الأولى، وكيف يظهر call printf في الـ assembly.