• 0
المـقـدام

ربط ملفات السورس والهيدر

سؤال

السلام عليكم ورحمة الله وبركاته


 


 


كيف يقوم الكمبيلر بربط ملفات cpp & .h


حيث في أغلب الأحيان يكون هناك ملفات لتعريف دوال الكلاسات


وأيضا يكون ملف لـ MAIN  وجميعهم يستدعوا header file  واحد ؟؟؟


 


 


 


:huh:  :huh:  :huh:  :huh:  :huh:

1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

3 إجابة على هذا السؤال .

  • 0

ملفات الـheader يأتي دورها في مرحلة التحظير للمعالجة preprocessing، لو كان عندك ثلاث ملفات file1.cpp و file2.cpp و header.h كالتالي:
 

/* header.h */#ifndef __HEADER_H__#define __HEADER_H__void foo(int x);#endif
/* file1.cpp */#include "header.h"int main(int argc, char **argv){  foo(123);  return 0;}
/* file2.cpp */#include <iostream>#include "header.h"void foo(int x){  std::cout << "foo(" << x << ")\n";}

الملف file1.cpp أدرج الملف الرأسي header.h، أثناء هذه المرحلة سيعالج المصرف لغة الـpreprocessing، الأوامر المبدوءة بـ#، سيدرج محتويات الملف المضمن في #include ويستبدل #define بما يقابلها، لينتج في الأخير ملف كهذا (يختلف بين مصرف وآخر):
 

# 1 "file1.cpp"# 1 "<command-line>"# 1 "file1.cpp"# 1 "header.h" 1void foo(int x);  <- محتوى الملف الرأسي أدرج في نفس الملف# 3 "file1.cpp" 2int main(int argc, char **argv){  foo(123);  return 0;}

يمكن عرض ناتج هذه المرحلة في gcc باستخدام الخيار E:

g++ -E file1.cpp

لاحظ أن void foo(int x); أدرجت الآن في file2.cpp، باقي تلك الرموز يستخدمها المصرف في المراحل التالية، مثلاً لو كان هناك خطأ سيعرف موقعه ورقم سطره. هنا انتهت مرحلة التحظير للمعالجة وانتهى دور الملفات الرأسية.

 

 

بعد هذه المرحلة تبدأ مرحلة البناء وهي أكثر المراحل تعقيد تجد تفاصيلها في كتب بناء المصرفات، في نهاية هذه المرحلة تحول الوحدة (كل ملف cpp مستقل) إلى تعليمات أسمبلي للمعالج المستهدف، الملف file1.cpp سيتحول إلى:

    .file    "file1.cpp"    .intel_syntax noprefix    .def    ___main;    .scl    2;    .type    32;    .endef    .text    .globl    _main    .def    _main;    .scl    2;    .type    32;    .endef_main:LFB0:    .cfi_startproc    push    ebp    .cfi_def_cfa_offset 8    .cfi_offset 5, -8    mov    ebp, esp    .cfi_def_cfa_register 5    and    esp, -16    sub    esp, 16    call    ___main    mov    DWORD PTR [esp], 123    <- تمرير المتغير للدالة    call    __Z3fooi            <- استدعاءها، الإسم الغريب لتمكين خاصية زيادة التحميل    mov    eax, 0    leave    .cfi_restore 5    .cfi_def_cfa 4, 4    ret    .cfi_endprocLFE0:    .ident    "GCC: (GNU) 4.8.1"    .def    __Z3fooi;    .scl    2;    .type    32;    .endef

يمكن اظهاره باستخدام الخيار S ويعطيك ملف اسمه file1.S به تعاليمات أسمبلي بصيغة at&t (ويمكنك أيضاً إضافة masm=intel للإظهار التعليمات بصيغة أنتل، كما في الملف السابق):

g++ -S -masm=intel file1.cpp

هنا انتهى دول المصرف.

 

وبدأ دور المجمع (في gcc اسمه gnu assembler واسم الملف as) الذي يحول تعليمات الأسمبلي تلك إلى مايقابها في لغة الآلة (الأرقام التي يفسرها المعالج)، مثلاً التعليمة mov ebx, 123 والتي تنقل الرقم 123 إلى المسجل ebx ستتحول إلى 0xbb 0x7b 0x00 0x00 0x00، فـ0xbb يفهمه المعالج بأنه نقل رقم 32 بت إلى المسجل ebx و 0x7b 0x00 0x00 0x00 رقم هو123 بطول 32 بت (هكس).

 

المجمع سيضع تلك التعليمات داخل مف كائن object file بالإمتداد .o يضم تلك التعليمات التي حولها المجمع للغة الآلة وبعض المعلومات الأخرى،  يمكن القفز لتلك المرحلة عن طريق الخيار c:

g++ -c file2.cpp

لاحظ أنه حتى الآن يتم بناء الملفات file1.cpp و file2.cpp بشكل مستقل عن الآخر، هذا مفيد جداً عند بناء برنامج كبير، فيمكنك مثلاً بناء file1.cpp والحصول على ملف الـobject، طالما أنك لم تغير في file2.cpp فلست بحاجة لإعادة بناءه في كل مرة.

 

بعد هذه المرحلة سينتج الملف file2.o، الخطوات تنطبق أيضاً على file1.cpp، لينتج file2.o، الآن تبدأ مرحلة الربط linking وفيها يلتقي ملفي الـobject الذين نتجا من file1.cpp و file2.cpp، هنا يقوم الـlinker بالتحقق من مواقع الدوال (file1.cpp تستدعي foo والموجودة في الوحدة file2.cpp، الـlinker يتولى التأكد من هذا الشيء)، وكذلك يتولى مسؤولية الربط الثابت أو الديناميكي بالدوال الموجودة في مكتبات خارجية، هذه المرحلة الأخيرة والتي ينتج فيها الملف التنتفيذي للنظام المستهدف.

 

المصرف وبيئات التطوير تخفي كثير من هذه التفاصيل.

 

انظر أيضاً لهذه المقالات:

 

[++C] وحدات اللغة

[++C] مفاهيم أساسية

تم تعديل بواسطه Mr.B
4

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

ماشاء الله اخ Mr.B

 

لاحظ أنه حتى الآن يتم بناء الملفات file1.cpp و file2.cpp بشكل مستقل عن الآخر، هذا مفيد جداً عند بناء برنامج كبير، فيمكنك مثلاً بناء file1.cpp والحصول على ملف الـobject، طالما أنك لم تغير في file2.cpp فلست بحاجة لإعادة بناءه في كل مرة.

هل تقصد ان المصرف سيقوم بهذا اوتوماتيكيا . اي انه لن يقوم بترجمة وحدة اذا كانت مترجمة من قبل ؟ .

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

لا، يلزم أن تقوم بهذا يدوياً، لكن ممكن أن تحتوي بعض بيئات التطوير ميزة تجنب إعادة بناء الواحدات التي لم تعدل، لكن المصرف لا يقوم بهذا.

لو كان لديك ملف كبير اسمه large.cpp ولاتغير عليه، لاحاجة لأن تعيد بناءه في كل مرة، يمكنك بناءه مرة واحدة وتوليد ملف object عن طريق الخيار c في gcc:

g++ -Wall -c large.cpp

الآن يمكنك أن تعمل linking فقط مع الملف الناتج large.o، وهذا لايأخذ وقت:

g++ -Wall file.cpp large.o
تم تعديل بواسطه Mr.B
1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
زوار
This topic is now closed to further replies.

  • يستعرض القسم حالياً   0 members

    لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .