Menu
flag Ar iconالعربيةdown icon

كيفية ترجمة برنامج C وتشغيله (gcc خطوة بخطوة)

حوّل ملف ‎.c إلى برنامج يعمل: أمر gcc، وما تفعله المعالجة الأولية والترجمة والربط فعلًا، والرايات التي تستحقّ الاستخدام منذ اليوم الأول، وكيفية قراءة الأخطاء عند الفشل.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

لغة C لغة مترجَمة: لا شيء يعمل حتى يترجم مترجم شيفرتك المصدرية إلى شيفرة آلة خاصة بمعالجك ونظام تشغيلك. وتلك الخطوة أمر واحد، لكن معرفة ما تفعله تحوّل معظم رسائل الخطأ من غامضة إلى بديهية.

الأمران

احفظ هذا باسم hello.c:

#include <stdio.h>

int main(void) {
    printf("Hello, C!\n");
    return 0;
}

ثم، في المجلّد نفسه:

gcc hello.c -o hello
./hello

الأمر الأول يترجم. والثاني يشغّل النتيجة. وعلى ويندوز يكون أمر التشغيل hello في موجّه الأوامر أو .\hello في PowerShell - فلا بادئة ./، لأن ويندوز يبحث في الدليل الحالي افتراضيًا وصدفات يونكس لا تفعل ذلك عمدًا.

وإن حذفت -o hello، سمّى gcc الخرج a.out (أو a.exe)، ولهذا تنتهي الدروس الأقدم بـ ./a.out. سمِّ خرجك دائمًا؛ فذلك يكلّف أربعة محارف ويوفّر الارتباك.

وإليك البرنامج نفسه في محرّر المتصفح، الذي يؤدّي الخطوتين نيابة عنك:

ما الذي تفعله "الترجمة" فعلًا

يبدو gcc hello.c -o hello خطوة واحدة. لكنه أربع خطوات، ويمكن أن تفشل كل واحدة منها بنوع خاص بها من الأخطاء.

1. المعالجة الأولية. قبل ترجمة أي شيفرة C، يعالج المعالج الأولي كل سطر يبدأ بـ #. فـ #include <stdio.h> يُستبدل حرفيًا بمحتويات ملف الترويسة ذاك؛ وتُوسَّع ماكرو #define؛ وتُبقى كتل #ifdef أو تُحذف. والخرج ملف C واحد كبير لم يبقَ فيه أي سطر بـ #. ويمكنك رؤيته:

gcc -E hello.c

يطبع ذلك مئات الأسطر - وجُلّها stdio.h وهو يُلصق.

2. الترجمة. تُحلَّل شيفرة C المعالَجة وتُفحَص أنواعها وتُترجَم إلى لغة تجميع لمعالجك. ومن هنا تأتي الأخطاء الصياغية وأخطاء الأنواع والتحذيرات.

3. التجميع. تصبح لغة التجميع ملف كائن - شيفرة آلة فيها مواضع فارغة حيث تذهب الاستدعاءات لدوال ملفات أخرى.

gcc -c hello.c   # produces hello.o, stops before linking

4. الربط. تُخاط ملفات الكائن معًا مع مكتبة C القياسية، ويُملأ كل موضع فارغ بعنوان حقيقي، فتكون النتيجة ملفًا تنفيذيًا. ومن هنا تأتي أخطاء "undefined reference".

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

رايات تستحقّ الاستخدام منذ اليوم الأول

gcc file.c -o prog المجرّد يقبل بصمت كثيرًا من الشيفرات الخطرة. وأربع رايات تغيّر ذلك.

gcc -std=c17 -Wall -Wextra -g hello.c -o hello
  • -Wall تفعّل التحذيرات الشائعة. ورغم اسمها فهي ليست "كل" التحذيرات - بل المجموعة المعقولة منها.
  • -Wextra تضيف المزيد، بما في ذلك الوسائط غير المستخدمة وبعض أخطاء المقارنة.
  • -std=c17 تثبّت معيار اللغة ليعني كودك الشيء نفسه على كل جهاز. استخدم -std=c99 إن كنت تتبع مادة أقدم.
  • -g تحفظ معلومات التنقيح، فيستطيع gdb أو lldb عرض أسطر مصدرك الفعلية عند الانهيار.

واثنتان أخريان ستريدهما في النهاية:

  • -O2 تفعّل التحسين لبناءات الإصدار. أبقِها مطفأة أثناء التعلّم: فالشيفرة المحسّنة أصعب تنقيحًا، والتحذيرات تتزحزح أحيانًا.
  • -fsanitize=address,undefined (في gcc وclang) تجعل البرنامج يُجهض برسالة واضحة لحظة قراءته خارج الحدود أو وقوعه في سلوك غير معرَّف. وهي أنفع راية تعليمية في C على الإطلاق.

جرّب التحذيرات بنفسك. هذا البرنامج يُترجَم ويعمل، لكن فيه علّتين حقيقيتين:

لم تُعطَ count قيمة قطّ، فيطبع البرنامج أي بايتات صادف وجودها هناك. ومع -Wall يقول المترجم ذلك: 'count' is used uninitialized. والتحذيرات في C ليست ضجيجًا تقريبًا أبدًا - عامِلها كأخطاء لم تصطدم بها بعد.

ترجمة أكثر من ملف

البرامج الحقيقية موزّعة على ملفات. مرّرها كلها إلى gcc:

gcc -std=c17 -Wall main.c utils.c -o myprog

أو ترجم كلًّا على حدة واربط في النهاية، وهو ما تفعله أنظمة البناء حتى لا يؤدّي تغيير ملف واحد إلى إعادة بناء كل شيء:

gcc -c main.c      # -> main.o
gcc -c utils.c     # -> utils.o
gcc main.o utils.o -o myprog

وبعض المكتبات تحتاج راية ربط صريحة. والمكتبة الرياضية هي التي يصادفها كل مبتدئ:

gcc calc.c -o calc -lm

فبدون -lm، يُترجَم استخدام sqrt من math.h بنجاح ثم يفشل عند الربط بـ undefined reference to sqrt - فالتصريح كان في الترويسة، لكن الشيفرة كانت في مكتبة لم يطلبها أحد.

قراءة الأخطاء

أخطاء C موجزة لكنها متّسقة. وثلاثة أمثلة تغطّي معظم ما ستراه في البداية.

الفاصلة المنقوطة الناقصة يُبلَّغ عنها في السطر التالي، لأن المترجم واصل القراءة:

hello.c:5:5: error: expected ';' before 'return'

والإصلاح يخصّ السطر 4، لا السطر 5. وكلما أشار خطأ إلى سطر يبدو سليمًا، فافحص السطر الذي قبله.

الترويسة الناقصة تبدو كلغز عن دالة كتبت اسمها بشكل صحيح بوضوح:

hello.c:4:5: warning: implicit declaration of function 'printf'

يعني ذلك أن المترجم لم يرَ قطّ تصريحًا عن printf، فقد نسيت #include <stdio.h>. وفي C99 وما بعده يكون هذا خطأً لا مجرّد تحذير.

فشل الربط لا يحمل رقم سطر إطلاقًا:

/usr/bin/ld: main.o: in function `main':
main.c:(.text+0x1a): undefined reference to `helper'

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

أصلِح أول خطأ دائمًا. فأخطاء C تتتالى - سطر سيّئ واحد يمكن أن يولّد عشرين رسالة، تختفي تسع عشرة منها عند إصلاح الأولى.

رموز الخروج

return 0 من main ليست زينة. إنها حالة خروج البرنامج، ويمكن للصدفة قراءتها:

بعد التشغيل، تطبع echo $? على ماك أو لينكس (أو echo %errorlevel% على ويندوز) ذلك العدد. وتستخدمه السكريبتات وأدوات البناء لتقرّر هل تواصل. والعُرف مطلق: 0 نجاح، وغير الصفر رمز خطأ من اختيارك. ويعرّف stdlib.h كلًّا من EXIT_SUCCESS وEXIT_FAILURE إن فضّلت الأسماء.

ومنذ C99، يُعامَل بلوغ نهاية main دون return على أنه return 0 - لكن كتابتها أوضح، وهي مطلوبة في كل دالة أخرى.

الأسئلة الشائعة

كيف أترجم برنامج C وأشغّله؟

احفظ الشيفرة باسم hello.c، ثم شغّل gcc hello.c -o hello لبنائه و./hello لتشغيله (‏hello فقط على ويندوز). وإن لم يُعثر على gcc، فعليك تثبيت مترجم أولًا.

ماذا تفعل الراية gcc -o؟

-o تسمّي ملف الخرج. فـ gcc hello.c -o hello يُنتج ملفًا تنفيذيًا اسمه hello. وبدون -o يكتب gcc إلى a.out (أو a.exe على ويندوز)، ولهذا تشغّل كثير من الدروس القديمة ./a.out.

أي رايات gcc ينبغي أن أستخدمها دائمًا؟

gcc -std=c17 -Wall -Wextra -g yourfile.c -o yourprog. فـ -Wall -Wextra تفعّلان التحذيرات التي تلتقط علّات حقيقية، و-std=c17 تثبّت إصدار اللغة، و-g تحفظ رموز التنقيح ليعرض المنقّح شيفرتك المصدرية. وأضف -O2 حين تريد السرعة في بناء إصدار.

ماذا تعني "undefined reference to" في لغة C؟

إنه خطأ رابط: قبل المترجم استدعاءً لدالة، لكن لم يُعثر على أي تعريف لها. والأسباب الشائعة خطأ مطبعي في الاسم، أو نسيان ترجمة ملف ‎.c ثانٍ، أو استخدام دالة رياضية دون ربط المكتبة الرياضية (‏-lm).

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن