[LinuxFocus-icon]
الأولى  |  الخارطة  |  فهرس  |  بحث

أخبار | محفوظات | روابط | عن المجلة
[an error occurred while processing this directive]
[Lorne Bailey]
المؤلف Lorne Bailey
<sherm_pbody(at)yahoo.com>

نبذة عن الكاتب:

Lorneيعيش في شيكاغو و يعمل مستشارا مختصا في Oracle و يحضر شهادة الماجستير.



ترجمه إلى العربية:
yacine laalaoui <yacine.laalaoui(at)caramail.com>

الفهرس:

 

GCC - الأصل

[Illustration]

نبذة مختصرة:

هذا المقال يعرفك على أساسيات لغة البرمجة C و كيفية استعمال المصرفcompilerGCC، نحن على ثقة أننا سنجعلك تستطيع أن تنفذ المصرف بأمر على السطر لبرنامج بسيط بلغة C و بعد ذلك سنرى ما يجري حاليا و كيف تستطيع مراقبة تصريف برامجك، و كذلك نظرة أخرى على الفلي -debugger-

 

 قواعد  GCC

لا تستطيع أن تتخيل  تصريف برامج حرة بمصرف   ذي  مصدر مغلق، مصرف خاص؟ و  كيف تطلع على ما يجري في  برنامجك المنفذ؟ قد يحتوي على أبوابا خلفيةback door  أو حصان طروادة  Trojan. السيد Ken Thompso أحد  أشهر الهاكر كتب مصرفا يترك خلفه أبوابا في برنامج الدخول 'login'، و يخلق حصان طروادة حينما يصرف المصرف نفسه. طالع وصفه في .

 لحسن الحظ لدينا GCC، عندما  تنفذ configure; make; make install فإن gcc  يعمل بجد خلف الستار.  كيف تجعل gcc يعمل لصالحك، سنبدأ بتأليف برنامج للعب الورق شيئا فشيئا لنبرز وظائف المصرف، سنبدا من خط الانطلاق  أي من تنفيذ التصريف إلى إنشاء برنامج قابل للتنفيذ،  و نعبر المراجل التي يأخذها برنامج بلغة C و مختلف الخيارات المستعملة في الأمر gcc، هذه المراحل هي التصريف الأولي    gcc -E   Pre-compile  ثم التصريفCompile  gcc  ثم التجميع as     Assembly ثم الربط   Link ld

.

 

البداية...

أولا سنتعلم تنفيذ المصرف، لذا نكتب البرنامج  الذي يعرض على الشاشة ترحيبا.

#include <stdio.h>

int main()
{ printf("Hello World!\n"); }

احفظه باسم game.c  ثم صرّفه بـ:

gcc game.c
المصرف يصنع برنامجا منفذا يسميه تلقائيا a.out يمكنك تنفيذه بالأمر : 
a.out
Hello World

كلما صرفنا برنامجا ينتج ملف a.out  يعوض البرامج السالفة. فإن أردت تسمية البرنامج القابل للتنفيذ باسم آخر غيّر الاسم التلقائي، عليك إضافة الخيار -o  مرفقا باسم الملف الناتج   و لنسمه game مثلا. سمه ما تشاء فالمصرف لا يفرض تسمية معينة.
gcc -o game game.c
game
Hello World

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

 بعدها سنصنع ملفا رأسيا يحتوي تعريف  المتغيرات و الإجراءات  و الوظائف و ما تحتاجه من أشياء

#ifndef DECK_H
#define DECK_H

#define DECKSIZE 52

typedef struct deck_t
{
  int card[DECKSIZE];
  /* number of cards used */
  int dealt;
}deck_t;

#endif /* DECK_H */

احفظ هذا الملف باسم deck.h،  بعد ذلك نعدل ملف game.c ، فاكتب في السطر الثاني "include "deck.h، في السطر الخامس أضف deck_t deck;، ثم صرّفه ثانية، فإذا وجدت أخطاء صححها و أعد التصريف.

gcc -o game game.c

 

التصريف الأولي  

كيف يتعرف المصرف على نوع deck_t؟ في  مرحلة التصريف الأولي يدرج المصرف الملف "deck.h"في الملف game.c جراء التوجيهة التي تلي #.  نفذ التصريف الأولي بـالخيار  -E في أمر gcc .

gcc -E -o game_precompile.txt game.c
wc -l game_precompile.txt
  3199 game_precompile.txt
لاحظ أن الملف game_precompile.txt  الناتج من التصريف الأولي فيه 3200 سطرا جاءت معظمها من الملف stdio.h .

 أثناء التصريف الأولي تجري العمليات :   

  1. نسخ الملفات المدرحة بـ #include . في ملف المصدر.
  2. تعويض الثوابت المعرفة بـ #define بقيمها الفعلية.
  3. استبدال التعليمات المركبة macro في مواضعها.
 

تعريف الثوابت يمكنك من استخدام ثابت ما مثل DECKSIZE  لتعويض عدد أوراق اللعب، في كامل مصدر البرنامج، فإذا ما غيرنا قيمة الثابت يتغير في كامل المصدر أثناء التصريف الأولي.هذه المرحلة  لا تجرى وحدها، بل تسبق التصريف عادة تلقائيا.

 

التصريف

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

 

التجميع

يتحول البرنامج من رمز تجميعي assembly code  إلى رمز شيئي  object code. الرمز الشيئي لا يمكن تنفيذه لكنه مرصوص بإحكام، الخيار -c  يحول الملف إلى ملف  شيئي  لاحقته .o

gcc -c game.c
فلنعد إلى مثالنا، و نضيف إليه دالة function لخلط الأوراق عشوائيا  في الجدول 'drawn' الذي يمنع تكرار الأوراق.
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "deck.h"

static time_t seed = 0;

void shuffle(deck_t *pdeck)
{
  /* Keeps track of what numbers have been used */
  int drawn[DECKSIZE] = {0};
  int i;

  /* One time initialization of rand */
  if(0 == seed)
  {
    seed = time(NULL);
    srand(seed);
  }
  for(i = 0; i < DECKSIZE; i++)
  {
    int value = -1;
    do
    {
      value = rand() % DECKSIZE;
    }
    while(drawn[value] != 0);

    /* mark value as used */
    drawn[value] = 1;

    /* debug statement */
    printf("%i\n", value);
    pdeck->card[i] = value;
  }
  pdeck->dealt = 0;
  return;
}

احفظ الملف باسم shuffle.c، صرفه  ثم نفذه  ليخلط الأوراق و يعرضها أمامك بالدالة printf لنعرف ما يجري في برنامجنا، هذه الدالة تستعمل عادة للفلي ( فلى يفلي فليا= debbuge) ، الذي سنتحدث عنه لاحقا.

تذكر أمرين هامين:
  1. الوسائط ( parameters) تمرر (pass) في الدوال بقيمتها(by value)، أو بعنوانها (by address)  بالعامل  "&" . الوسيط الممر بعنوانه يمكن للدالة تغييره أثناء عملها، يمكن استعمال المتغيرات الشاملةglobal variables، لكن يستحسن أن لا تستعمل إلا نادرا، و تذكر أن المؤشرات ذات مكانة  مهمة في C لذا عليك فهمها جيدا.
  2. يعتبر نظام التشغيل أي برنامج دالة رئيسية تسمى main ، فالملف shuffle.c لا يمكن تنفيذه، استعمله في ملف آخر.

نفذ الأمر

gcc -c shuffle.c
الذي ينتج عنه الملف shuffle.o إن لم ترتكب أخطاء في shuffle.c . حرر الملف game.c  و أضف بعد السطر السابع الذي فيه تعريف المتغير deck  لسطر الآتي:
shuffle(&deck);

أعد صنع البرنامج القابل للتنفيذ، انتبه فالبرنامج به أخطاء

gcc -o game game.c

/tmp/ccmiHnJX.o: In function `main':
/tmp/ccmiHnJX.o(.text+0xf): undefined reference to `shuffle'
collect2: ld returned 1 exit status
البرنامج صحيح شكليا لكن الأخطاء ناتجة عن مرحلة ربط  الأجزاء،  أي أننا لم نعلم الحاسوب عن مكان الدوال المستدعاة. فما هي مرحلة ربط الأجزاء؟ 

الربط

آخر مرحلة هي الربط، أي   جعل البرنامج  قابلا للتنفيذ بالأمر :

gcc -o game game.o shuffle.o
إذ يحول الأجزاء الشيئية إلى برنامج قابل للتنفيذ. إذ تتلاحم الملفات الشيئية shuffle.o  و game.o  بعبارة أخرى ربط الإجراءات  الموجودة في الملفات الرأسية بما هو موجود في الملف  الرئيسي main مثل shuffle  مع  deck.h  أو printf   مع stdio.h .

 

 

خيارات أخرى مهمة

الخيار -Wall يعرض كل الأخطاء و التنبيهات في برنامجك مما يجعله محمولا قدر الإمكان، فإذا نفذته على برنامجنا ظهر لك :

game.c:9: warning: implicit declaration of function `shuffle'
التي تذكرنا أن علينا تعريف الدالة shuffle في الملف الرأسي deck.h  هكذا :
void shuffle(deck_t *pdeck);
مما سيحجب هذا التنبيه.

خيار آخر يستعمل لتحسين جودة البرنامج القابل للتنفيذ ( جعله أمثل optimization) هو الخيار -#O  مثل -O2  الذي يجعل البرنامج أسرع، لن تلاحظ شيئا، لكن البرامج الكبيرة جدا تبدو أسرع بعد تحسينها، الرقم 2 أو غيره يحدد مستوى التحسين..

 

الفلي

برنامجنا يعمل جيدا، و نستطيع عرض نتائجه بـ

game | sort - n | less
لكن ما عسانا نفعل إن لم يكن كذلك، كيف يمكننا تدقيق برنامجنا لاستكشاف الأخطاء؟

يمكنك تفحص البرنامج بفليه بالمفلاة  ( أداة للفلي)  إما gdb على سطر الأوامر أو  KDbg  على الواجهة الرسومية. إذا استعملت Kdbg  اختر البرنامج القابل للتنفيذ من اللائحة file->executable ، ثم اضغط F5 أو اختر القائمة File->Run   فترى النتائج في نافذة على حدى ، و إلا عليك إعادة التصريف بالأمر :

gcc -g -c shuffle.c game.c
gcc -g -o game game.o shuffle.o
حاول مرة أخرى لترى النتائج، و نفس العمل على gdb  الذي يعمل على سطر الأوامر.

الفلي مهم جدا لذا خصص وقتا لتعلم استعمال Kdbg و gdb لاستكشاف الأخطاء و ما أحوجك لذلك، و يمكنك تحديد نقاط لتوقيف التنفيذ، بأن تنقر بزر الفأرة الأيسر على السطر  الذي به الدالة shuffle فتظهر لك نقطة حمراء صغيرة بعد هذا السطر، اضغط الزر F5 فيتوقف التنفيذ عند هذه النقطة بالذات، و بالضغط على F8 ستدقق ما يجري داخل الدالة shuffle، و إذا وضعت مؤشر الفأرة على متغير ما ترى قيمته الحالية.

 

الخلاصة

يحتوي هذا المقال على كيفية تصريف  برنامج C و فليه  مرورا بكل مراحل التصريف بـ gcc .  لإنشاء برنامج منفذ و التي منها مرحلة الربط (  النهائية)، فمن خلال هذا نأمل أن تستطيع تبدأ البرمجة بلغة C و استعمال المصرف gcc  و الفلي  مثل Kdbg و gdb.

و هناط أيضا مصادر أخرى للمعلومات كالأمر man و على الصفحات info حول المصرف gcc أو ld.

 

الروابط

 

تعقيبك على هذا المقال

لكل مقال صفحة خاصة بالتعقيبات، أرسل تعقيبا أو اطلع على تعقيبات الآخرين.
 صفحة التعقيبات 

الصفحات برعاية طاقم لينكس فوكُس
© Lorne Bailey, FDL
LinuxFocus.org

اضغط هنا للتنبيه عن خطأ أو لارسال ملاحظاتك إلى لينكس فوكُس
معلومات عن الترجمة:
en --> -- : Lorne Bailey <sherm_pbody(at)yahoo.com>
en --> ar: yacine laalaoui <yacine.laalaoui(at)caramail.com>

2002-05-09, generated by lfparser version 2.27