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

تصريف ++C: من الشيفرة المصدرية إلى ملف تنفيذي باستخدام g++

كيف يحوّل ++C ملف المصدر .cpp إلى ملف تنفيذي أصلي: صرّف باستخدام g++ (أو clang++/MSVC)، وشغّل الملف الثنائي، واقرأ أخطاء المصرّف عندما يحدث خلل.

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

صرّف أولًا، ثم شغّل

الآن وقد ثبّتّ مصرّفًا، تشغيل برنامج ++C هو دورة من خطوتين. على عكس لغة سكربتية يقرأ فيها المُفسِّر ملفك سطرًا سطرًا، يقوم ++C أولًا بـتصريف شيفرتك المصدرية إلى ملف تنفيذي أصلي - ملف ثنائي قائم بذاته من شيفرة الآلة - ثم تقوم أنت بـتشغيل ذلك الملف الثنائي مباشرةً. لا يوجد مُفسِّر يجلس بين برنامجك والمعالج وقت التشغيل.

هذا الفصل هو سبب سرعة ++C، وهو أيضًا سبب توقفك عند خطأ في الصياغة وقت التصريف بدلًا من منتصف التشغيل. يفحص المصرّف الملف كاملًا قبل أن يُنتج أي شيء.

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

دورة التصريف والتشغيل على جهازك

لنفترض أنك حفظت هذا باسم main.cpp:

#include <iostream>
using namespace std;

int main() {
    cout << "Hello from the terminal" << endl;
    return 0;
}

افتح طرفية في المجلد الذي يحوي الملف واستدعِ المصرّف. المصرّف الأكثر شيوعًا هو g++ (جزء من GCC)؛ ويعمل clang++ بالطريقة نفسها تمامًا لكل ما هنا:

g++ main.cpp -o main

إذا صُرِّفت الشيفرة بلا مشكلات، لا يطبع g++ شيئًا وينشئ ملفًا جديدًا بجوار مصدرك: ملفًا تنفيذيًا اسمه main (أو main.exe على Windows). هذا الملف الثنائي هو شيفرة آلة - ليس نصًا قابلًا للقراءة، وهو مرتبط بنظام تشغيلك ومعالجك. والآن شغّله:

./main
Hello from the terminal

على Windows ستشغّله بصيغة main.exe (أو فقط main) من الطرفية نفسها. أما ./ على macOS وLinux فيُخبر الصدفة بأن "البرنامج هنا تمامًا في هذا المجلد، وليس في مكان ما ضمن PATH" - خطوة ينساها المبتدئون، ثم يتساءلون لماذا يقول maincommand not found.

ماذا يفعل -o (و a.out)

تُسمّي الراية -o المخرجات. أغفلها وسيظل g++ يصرّف، لكنه يكتب الملف التنفيذي باسم افتراضي: a.out على macOS/Linux، وa.exe على Windows.

g++ main.cpp        # produces a.out, not main
./a.out

يُربك هذا الناس باستمرار: يصرّفون، ولا يرون أخطاء، ويشغّلون ./main، فيحصلون على No such file or directory - لأن الملف الثنائي اسمه في الحقيقة a.out. مرّر -o دائمًا لتعرف بالضبط ما الذي تشغّله.

تُصرّف مرة واحدة عند كل تغيير في المصدر. بعد ذلك يصبح الملف التنفيذي مستقلًا - يمكنك تشغيل ./main مئة مرة دون إعادة تصريف. ولا تشغّل g++ مجددًا إلا بعد تعديل ملف .cpp.

اختيار معيار ++C

لـ ++C إصدارات - C++11 وC++14 وC++17 وC++20 وC++23 - يضيف كل منها ميزات للغة. لكن المفارقة: يختار مصرّفك معيارًا افتراضيًا قد يكون أقدم مما تتوقع، فقد تفشل الشيفرة الحديثة في التصريف دون سبب واضح. حدّد المعيار صراحةً بـ -std:

g++ -std=c++17 main.cpp -o main
g++ -std=c++20 main.cpp -o main

إليك شيفرة تستخدم ميزة من C++17 (structured bindings). تُصرّف جيدًا في المحرّر، لكنها على جهازك تحتاج -std=c++17 أو أحدث:

إن رأيت يومًا خطأً مثل 'structured bindings' only available with '-std=c++17'، فالإصلاح ليس في شيفرتك - بل في إضافة الراية -std الصحيحة. على امتداد هذه الدورة، افترض C++17 أو أحدث.

فعّل التحذيرات

قد يُصرَّف برنامج ++C بلا مشكلات ويظل خاطئًا. وإن طلبتَ منه، سيشير المصرّف إلى الشيفرة المريبة قبل أن تلدغك وقت التشغيل. أضف -Wall -Wextra:

g++ -std=c++17 -Wall -Wextra main.cpp -o main

تأمّل هذا البرنامج. يُصرَّف دون -Wall، لكن فيه عيبًا حقيقيًا - قراءة متغيّر لم تُسنَد إليه أي قيمة، وهو سلوك غير معرّف:

#include <iostream>
using namespace std;

int main() {
    int count;                 // never initialized
    cout << count << endl;     // reads garbage - undefined behavior
    return 0;
}

مع تفعيل التحذيرات، يُنبّه المصرّف إليه:

warning: 'count' is used uninitialized [-Wuninitialized]

اعتد التصريف بـ -Wall -Wextra منذ اليوم الأول. التحذيرات هي مراجعة شيفرة مجانية يقدّمها المصرّف؛ وتجاهلها هو الطريق الذي تبقى به العيوب الدقيقة على قيد الحياة. والإصلاح هنا ببساطة int count = 0;.

قراءة أخطاء المصرّف

عندما يرفض g++ شيفرتك، يذكر الملف والسطر وما الذي حدث من خطأ. وتعلّم قراءة هذه الرسائل هو نصف الطريق للخروج من المأزق. وإليك الخطأ الكلاسيكي - فاصلة منقوطة ناقصة:

#include <iostream>
using namespace std;

int main() {
    cout << "Oops"      // no semicolon
    return 0;
}
main.cpp:5:5: error: expected ';' before 'return'
    5 |     return 0;
      |     ^~~~~~

بضعة أمور تستحق الانتباه. يُبلّغ الخطأ عن السطر 5، لكن الخطأ الفعلي في السطر 4 - لا يدرك المصرّف غياب الفاصلة المنقوطة إلا حين يبلغ الرمز (token) التالي. لذا حين يشير خطأ إلى سطر يبدو سليمًا، تفقّد السطر الذي قبله. أما main.cpp:5:5 فهو الملف ثم السطر ثم العمود. أصلح الشيء الوحيد الذي يسمّيه وأعد التصريف - وقاوم التخمين.

أخطاء المصرّف تعني أن شيئًا لم يُشغَّل بعد. وهي تختلف عن أخطاء وقت التشغيل، حيث يبدأ برنامجك ثم ينهار. والتقاط الأخطاء وقت التصريف، قبل أن يعمل البرنامج أصلًا، هو من أعظم نقاط قوة ++C.

برنامج للتحقق من السلامة

شغّل هذا في المحرّر، أو احفظه باسم main.cpp ونفّذ g++ -std=c++17 -Wall main.cpp -o main ثم ./main على جهازك. إذا ظهرت الأسطر الثلاثة، فإن سلسلة أدواتك تعمل من البداية إلى النهاية:

تظهر هنا ثلاثة أشياء ستتعرّف إليها كما ينبغي قريبًا: متغيّر من نوع int، وvector (مصفوفة ++C القابلة لتغيير الحجم)، وcout للإخراج. يكفي الآن أن يُصرَّف البرنامج ويطبع الأسطر الثلاثة بالترتيب.

التالي: صياغة ++C

لقد صرّفت وشغّلت بضعة برامج، لكننا تجاوزنا علامات الترقيم - أسطر #include، والأقواس المعقوفة، والفواصل المنقوطة، وint main()، ولماذا يبدو كل سطر على نحوه. تُفكّك الصفحة التالية صياغة ++C قطعةً قطعةً، حتى تكفّ البنية عن الشعور بأنها مجرّد قالب جاهز وتبدأ في اكتساب المعنى.

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

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

احفظ شيفرتك باسم main.cpp، وافتح طرفية في ذلك المجلد، ونفّذ g++ main.cpp -o main لإنتاج ملف تنفيذي. ثم شغّله بـ ./main على macOS/Linux أو main.exe على Windows. تُصرّف مرة واحدة؛ ويمكنك تشغيل الملف الثنائي الناتج بقدر ما تشاء.

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

تحدّد -o اسم ملف المخرجات. يُنشئ g++ main.cpp -o hello ملفًا تنفيذيًا اسمه hello. إذا أغفلت -o، يستخدم g++ الاسم الافتراضي a.out (أو a.exe على Windows) - ولهذا كثيرًا ما يُشغّل المبتدئون ملفًا لم يدركوا أنهم أنشأوه.

كيف أصرّف ++C بمعيار محدّد مثل C++17 أو C++20؟

مرّر الراية -std: ‏g++ -std=c++17 main.cpp -o main أو -std=c++20. بدونها يستخدم المصرّف معياره الافتراضي، الذي قد يكون أقدم مما تتوقع، لذا قد تفشل ميزات أحدث مثل structured bindings أو <ranges> في التصريف حتى تحدّد المعيار صراحةً.

Coddy programming languages illustration

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

ابدأ الآن