سؤال

لقد رأيت الكثير من الأسئلة حول هذا ، لكنني سأطرح السؤال بشكل مختلف بدون رمز محدد. هل هناك طريقة بسهولة تحديد ما الذي يتسبب في أن يكون النوع غير مكتمل؟ في حالتي ، أستخدم رمز شخص ما elses وأنا متأكد تمامًا من أنني لا أملك الرؤوس بشكل صحيح ، ولكن (بما أن أجهزة الكمبيوتر تفعل هذه الأشياء بشكل أسرع وأفضل من مقل العيون) هل هناك طريقة لجعل المترجم ليقول ، "يا هذا فكر في لديك النوع X في السطر 34 ولكن هذا في الواقع مفقود"الخطأ نفسه نفسه يظهر فقط عند تعيينه ، وهو أمر غير مفيد للغاية.

هل كانت مفيدة؟

المحلول

لقد رأيت سؤالًا في اليوم الآخر حيث استخدم شخص ما عن غير قصد نوعًا غير مكتمل من خلال تحديد شيء مثل

struct a {
    int q; 
}; 
struct A *x; 
x->q = 3;

عرف المترجم ذلك struct A كان هيكلًا على الرغم من A كونه غير محدد تمامًا ، بحكم struct الكلمة الرئيسية.

كان ذلك في C ++ ، حيث هذا الاستخدام struct غير نمطية (واتضح ، يمكن أن يؤدي إلى إطلاق النار على القدم). في C إذا قمت بذلك

typedef struct a {
    ...
} a;

ثم يمكنك استخدام a كـ typename وحذف struct في وقت لاحق. سيؤدي ذلك إلى قيادة برنامج التحويل البرمجي ليمنحك خطأ معرفًا غير محدد لاحقًا ، بدلاً من النوع غير المكتمل ، إذا كنت أخطأ في الاسم أو نسيت رأسًا.

نصائح أخرى

سبب آخر محتمل هو إشارة غير مباشرة. إذا كان رمز يشير إلى بنية لم يتم تضمينها في ملف C الحالي ، فسيشكو برنامج التحويل البرمجي.

A-> B-> C // خطأ إذا لم يتم تضمين B في ملف C الحالي

ماذا تقصد ، فإن الخطأ يظهر فقط عند تعيينه؟ على سبيل المثال على دول مجلس التعاون الخليجي ، مع عدم وجود مهمة في الأفق:

int main() {
    struct blah *b = 0;
    *b; // this is line 6
}

incompletetype.c:6: error: dereferencing pointer to incomplete type.

الخطأ هو في السطر 6 ، هذا هو المكان الذي استخدمت فيه نوعًا غير مكتمل كما لو كان نوعًا كاملاً. كنت بخير حتى ذلك الحين.

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

بدلا من ذلك (نقطة جيدة ، البطاطا) ، يكون الخطأ في السطر حيث b تم تعريفه عندما تكون أنت عنى لتحديد نوع ما موجود بالفعل ، ولكن تم تحديده بالفعل blah. العثور على تعريف المتغير b لا ينبغي أن يكون صعبا للغاية في معظم الحالات. يمكن أن تفعل IDES عادةً من أجلك ، وربما لا يمكن إزعاج تحذيرات البرمجيات. إنه بعض التعليمات البرمجية الشنيعة ، إذا لم تتمكن من العثور على تعريفات الأشياء التي تستخدمها.

أنا لا أفهم بالضبط ما هي المشكلة. النوع غير المكتمل ليس هو النوع الذي "مفقود". النوع غير المنافس هو نوع أعلن لكن لا مُعرف (في حالة أنواع الهيكل). للعثور على الإعلان غير المحدد أمر سهل. أما بالنسبة للاكتشاف التعريف المفقود ... لن يساعدك المترجم هنا ، لأن هذا هو ما تسبب في الخطأ في المقام الأول.

أحد الأسباب الرئيسية لأخطاء النوع غير المكتمل في C هي الأخطاء المطبعية في أسماء الأنواع ، والتي تمنع المترجم من مطابقة اسم واحد إلى الآخر (كما هو الحال في مطابقة الإعلان مع التعريف). ولكن مرة أخرى ، لا يمكن للمترجم مساعدتك هنا. المترجم لا يقوم بتخمينات حول الأخطاء المطبعية.

يُظهر هذا الخطأ عادة ما إذا كان اسم البني الخاص بك يختلف عن تهيئة بنيتك في الكود ، لذلك عادةً ما يجد C اسم البنية التي تضع إذا قمت بإشارة مؤشر إلى هذا المؤشر ، فسيظهر الخطأ.

حل

في حديثه عن لغة C ، لقد وجدت للتو أن رمز الإعلان التالي سيكون الحل ؛

typedef struct ListNode
{
    int data;
    ListNode * prev;
    ListNode * next;
} ListNode;

كقاعدة عامة ، أعطي نفس الاسم لكل من تعريف النوع واسم البنية ؛

typedef struct X
{
    // code for additional types here
    X* prev; // reference to pointer
    X* next; // reference to pointer
} X;

ب - عينات مشكلة

حيث تعتبر الإعلانات التالية غير مكتملة من قبل gcc المترجم عند تنفيذ البيان التالي. ؛

removed->next->prev = removed->prev;

وأحصل على نفس الخطأ لرمز dereferencing الذي تم الإبلاغ عنه في إخراج الخطأ ؛

>gcc Main.c LinkedList.c -o Main.exe -w
LinkedList.c: In function 'removeFromList':
LinkedList.c:166:18: error: dereferencing pointer to incomplete type 'struct ListNode'
     removed->next->prev = removed->prev;

لكل من الملف الاساسي الإعلانات المدرجة أدناه ؛

typedef struct
{
    int data;
    ListNode * prev;
    ListNode * next;
} ListNode;

بالإضافة إلى هذا واحد ؛

typedef struct ListNodeType
{
    int data;
    ListNode * prev;
    ListNode * next;
} ListNode;

خارج السيناريوهات المحتملة التي تنطوي على تحسين برنامج كامل ، تم إنشاء رمز الكود لشيء مثل:

struct foo *bar;
struct foo *test(struct foo *whatever, int blah)
{
  return blah ? whatever: bar;
}

لن يتأثر تمامًا بما الأعضاء struct foo قد تحتوي. نظرًا لأن إنشاء الأدوات المساعدة ستعمل بشكل عام على إعادة ترجمة أي وحدة تجميع تظهر فيها التعريف الكامل للهيكل ، حتى عندما لا يمكن أن تؤثر هذه التغييرات فعليًا على الكود الذي تم إنشاؤه لها ، فمن الشائع حذف تعريفات الهيكل الكاملة من وحدات التجميع التي لا تحتاج فعليًا لهم ، وهذا الإغفال لا يستحق عمومًا تحذيرًا.

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

بالمناسبة ، هناك موقف آخر حيث يسمح المعيار للمترجم أن يتطلب تعريف اتحاد كامل أن يكون مرئيًا ولكنه لن يتطلب تشخيصًا: إذا بدأ هيكلان بتسلسل أولي شائع ، ونوع الاتحاد الذي يحتوي على كلاهما مرئي عندما يكون المترجم هو رمز المعالجة الذي يستخدم مؤشر أحد أنواع الهيكل لتفقد عضوًا في هذا التسلسل الأولي المشترك ، يتعين على المترجم أن يدرك أن مثل هذا الرمز قد يصل إلى العضو المقابل لهيكل من النوع الآخر. لا أعرف ما هي المجمعين إذا كان هناك أي يتوافق مع المعيار عندما يكون نوع الاتحاد الكامل مرئيًا ولكن ليس عندما لا يكون [GCC عرضة لإنشاء رمز غير متطابق في كلتا الحالتين ما لم يكن -fno-strict-aliasing يتم استخدام العلم ، وفي هذه الحالة ، ستولد رمزًا مطابقًا في كلتا الحالتين] ولكن إذا أراد المرء كتابة رمز يستخدم قاعدة CIS بطريقة ضمان السلوك الصحيح على المترجمين المطابقة ، فقد يحتاج المرء إلى ضمان هذا النوع الكامل النقابي التعريف مرئي ؛ قد يؤدي الفشل في القيام بذلك إلى توليد برمجي صمت.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top