تعطل التجميع: مرجع غير محدد — كيفية العثور على الخطأ وإصلاحه

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

«Undefined reference» — العدو الرئيسي للمبتدئين في C++ (وليس فقط)

\n\n

أنت تكتب الكود، كل شيء يبدو مثالياً، المترجم صامت، تضغط على «تجميع» — وفجأة… جدار من النص الأحمر. البطل الرئيسي لهذا الكابوس هو undefined reference (أو unresolved external symbol). هل هذا مألوف؟ إذا كنت قد كتبت بلغة C++ ولو مرة واحدة، فمن المؤكد أنك واجهت هذا الخطأ. إنه يخيف المبتدئين، لكنه في الحقيقة مجرد إشارة: «أعرف أي دالة تتحدث عنها، لكنني لا أستطيع العثور على تنفيذها».

\n\n

في هذا الدليل التعليمي، سنشرح ما هو خطأ المرجع غير المحدد، ولماذا يحدث، والأهم من ذلك — كيفية إصلاحه. ستتعرف على 5 من أكثر الأسباب شيوعاً وستحصل على قائمة مراجعة جاهزة لحل المشكلة. هيا بنا!

\n\n

ما هو undefined reference / unresolved external symbol؟

\n\n

لنبدأ بالنظرية، ولكن بشكل بسيط جداً. تتكون عملية بناء برنامج C++ من مرحلتين:

\n
    \n
  • الترجمة (Compilation) — يتم تحويل كل ملف .cpp إلى ملف كائن (.o أو .obj). يتحقق المترجم من بناء الجملة ويتذكر الدوال المعلنة (النماذج الأولية).
  • \n
  • الربط (Linking) — يقوم الرابط (linker) بدمج جميع ملفات الكائن والمكتبات في ملف تنفيذي واحد. في هذه المرحلة، يبحث عن تنفيذات جميع الدوال الموعودة.
  • \n
\n\n

يحدث خطأ undefined reference (في Visual Studio — unresolved external symbol) تحديداً في مرحلة الربط. لقد نجحت الترجمة، لكن الرابط لم يجد جسم الدالة. إنه يصرخ: «لقد قلت إن الدالة foo() موجودة، لكنني لا أستطيع العثور على مكان تعريفها!».

\n\n

أهم 5 أسباب لخطأ undefined reference في C++

\n\n

دعنا نستعرض السيناريوهات الأكثر شيوعاً مع أمثلة حقيقية. في كل حالة، سأعرض الكود الخاطئ وطريقة الإصلاح.

\n\n

1. لقد أعلنت عن دالة ولكنك لم تعرّفها

\n

كلاسيكي. لقد كتبت النموذج الأولي للدالة لكنك نسيت التنفيذ.

\n
// main.cpp\n#include <iostream>\n\nvoid hello(); // إعلان (نموذج أولي)\n\nint main() {\n    hello();\n    return 0;\n}\n// تنفيذ hello() مفقود!\n
\n

خطأ الربط: undefined reference to `hello()'. الإصلاح: أضف جسم الدالة.

\n\n

2. لم تقم بتضمين ملف التنفيذ في عملية البناء

\n

هذه مشكلة شائعة عند العمل مع عدة ملفات .cpp. لنفترض أنك أعلنت دالة في utils.h، وعرفتها في utils.cpp، لكنك نسيت إضافة utils.cpp إلى أمر الترجمة.

\n
// utils.h\nvoid printMessage();\n\n// utils.cpp\n#include <iostream>\n#include "utils.h"\nvoid printMessage() {\n    std::cout << "Hello!" << std::endl;\n}\n\n// main.cpp\n#include "utils.h"\nint main() {\n    printMessage();\n    return 0;\n}\n
\n

إذا قمت بترجمة main.cpp فقط (g++ main.cpp)، فلن يرى الرابط utils.cpp. الإصلاح: قم بترجمة كلا الملفين معاً: g++ main.cpp utils.cpp -o program.

\n\n

3. تم تعريف الفئة أو القالب في .cpp بدلاً من .h

\n

هذا ألم خاص بلغة C++. إذا قمت بتعريف دالة الفئة داخل ملف .cpp، لكنك قمت بتضمين ملف الرأس فقط، فلن يجد الرابط التنفيذ.

\n
// MyClass.h\nclass MyClass {\npublic:\n    void doSomething();\n};\n\n// MyClass.cpp\n#include "MyClass.h"\nvoid MyClass::doSomething() { /* ... */ }\n\n// main.cpp\n#include "MyClass.h"\nint main() {\n    MyClass obj;\n    obj.doSomething(); // خطأ: undefined reference\n    return 0;\n}\n
\n

الإصلاح: تأكد من أن MyClass.cpp يشارك في عملية البناء (انظر النقطة 2).

\n\n

4. ربط غير صحيح للمكتبات (مثل math.h)

\n

بعض المكتبات (مثل المكتبة الرياضية libm) تتطلب تحديداً صريحاً

المدونات

توصيات الكتب