يشمل ذلك الحراسة

في لغات البرمجة C و C++ ، يعتبر #include guard ، والذي يسمى أحيانًا حارس الماكرو أو حارس الرأس أو حارس الملف ، وسيلة لتجنب مشكلة التضمين المزدوج عند التعامل مع توجيه التضمين .

يقوم المعالج المسبق للغة C بمعالجة توجيهات التضمين مثل تضمين Foo.h نصيًا ، ويقوم بتضمين رمز هذا الملف في نسخة من الملف الرئيسي تسمى غالبًا وحدة الترجمة .#include"Foo.h"

مع ذلك، إذا #includeظهر توجيهٌ لملفٍ مُعينٍ عدة مراتٍ أثناء عملية الترجمة، فسيتم تكرار الكود فعليًا في ذلك الملف. وإذا كان الملف المُضمّن يتضمن تعريفًا، فقد يتسبب ذلك في خطأٍ أثناء الترجمة بسبب قاعدة التعريف الواحد ، التي تنص على أنه لا يجوز تكرار التعريفات (مثل تعريف فئة) في وحدة الترجمة. #includeتمنع الحراس هذا التكرار من خلال تعريف ماكرو مُعالِج مُسبق عند تضمين ملف رأس لأول مرة. وفي حال تضمين ملف الرأس هذا مرةً ثانية، #includeسيمنع الحارس ترجمة الكود الفعلي الموجود داخله.

يُعدّ #pragma once بديلاً عن #includeالحراس . هذا التوجيه غير القياسي ولكنه مدعوم بشكل شائع بين مترجمات لغتي C و C++ له نفس الغرض الذي يؤديه الحارس، ولكنه يحتوي على كود أقل ولا يتطلب تعريف متغير.#include

تُغني الوحدات البرمجية ، التي تم تقديمها في C++20 ، عن الحاجة إلى استخدام #includeالحواجز، نظرًا لعدم معالجتها بواسطة المعالج المسبق. ولا يمكن استيراد الوحدات البرمجية إلا مرة واحدة كحد أقصى إلى وحدة الترجمة .

تضمين مزدوج

مثال

يوضح كود C التالي مشكلة حقيقية قد تنشأ في حالة #includeغياب إجراءات الحماية:

الملف "Grandparent.h"

struct Foo { int member ; };

الملف "Parent.h"

#include "Grandparent.h"

الملف "Child.c"

#include "Grandparent.h" #include "Parent.h"

نتيجة

// من "Grandparent.h" struct Foo { int member ; }; // من "Parent.h" struct Foo { int member ; };

هنا، يحتوي الملف Child.c بشكل غير مباشر على نسختين من النص الموجود في ملف الرأس Grandparent.h . يتسبب هذا في خطأ أثناء الترجمة ، حيث سيتم تعريف نوع البنية مرتين. في لغة C++، يُسمى هذا انتهاكًا لقاعدة التعريف الواحد .Foo

استخدام حراس التضمين

مثال

يُستخدم نفس الكود المذكور في القسم السابق مع إضافة #includeبعض الشروط. يقوم معالج C المسبق بمعالجة ملفات الرأس، بما في ذلك ملفات `<header>`، ثم يُجري عليها معالجةً إضافيةً بشكلٍ متكرر . سينتج عن ذلك ملف مصدر قابل للاستخدام.

الملف "Grandparent.h"

#ifndef GRANDPARENT_H #define GRANDPARENT_Hstruct Foo { int member ; };#endif // GRANDPARENT_H

الملف "Parent.h"

#include "Grandparent.h"

الملف "Child.c"

#include "Grandparent.h" #include "Parent.h"

خطوة وسيطة

// محتويات من "Grandparent.h" #ifndef GRANDPARENT_H // لم يتم تعريف GRANDPARENT_H #define GRANDPARENT_H// تم لصق هذا التعريف struct Foo { int member ; };#endif // GRANDPARENT_H// محتويات من "Parent.h" #ifndef GRANDPARENT_H // تم تعريف GRANDPARENT_H مسبقًا #define GRANDPARENT_H// لم يتم لصق هذا التعريف struct Foo { int member ; };#endif // GRANDPARENT_H

نتيجة

struct Foo { int member ; };

هنا، يحتوي أول تضمين لملف " Grandparent.h " على تعريف الماكرو . عندما يتضمن ملف " Child.c " ملف " Grandparent.h " للمرة الثانية (مع تضمين ملف " Parent.h ")، ولأن الاختبار يُرجع قيمة خاطئة، يتخطى المعالج المسبق إلى تعريف الماكرو ، متجنبًا بذلك التعريف الثاني . يتم تجميع البرنامج بنجاح.GRANDPARENT_H#ifndef#endifstruct Foo

مناقشة

قد يستخدم المبرمجون اصطلاحات تسمية مختلفة لماكرو الحماية . تشمل الأشكال الشائعة الأخرى للمثال أعلاه ( مع استبدال معلومات الوقت المناسبة)، والأسماء المُولَّدة من مُعرِّف فريد عالمي (UUID ). (مع ذلك، فإن الأسماء التي تبدأ بشرطة سفلية واحدة وحرف كبير (في لغتي C وC++) أو أي اسم يحتوي على شرطتين سفليتين (في لغة C++ فقط)، مثل و ، محجوزة لتطبيق اللغة ولا ينبغي للمستخدم استخدامها. [ 1 ] [ 2 ] )GRANDPARENT_INCLUDEDCREATORSNAME_YYYYMMDD_HHMMSS_GRANDPARENT_HGRANDPARENT__H

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

الصعوبات

لكي #includeتعمل الحراس بشكل صحيح، يجب على كل حارس اختبار وتعيين ماكرو معالج مسبق مختلف بشكل مشروط. لذلك، #includeيجب على أي مشروع يستخدم الحراس وضع نظام تسمية متناسق لحراس التضمين، والتأكد من أن هذا النظام لا يتعارض مع نظام تسمية أي ملفات رأسية خارجية يستخدمها، أو مع أسماء أي ماكرو مرئي على مستوى النظام.

لهذا السبب، توفر معظم تطبيقات لغتي C وC++ #pragma onceتوجيهًا غير قياسي. يضمن هذا التوجيه، المُدرج في بداية ملف الرأس، تضمين الملف مرة واحدة فقط. أما لغة Objective-C (وهي مجموعة فرعية من لغة C) فلديها #importتوجيه يعمل تمامًا مثل التوجيه السابق #include، باستثناء أنه يُضمّن كل ملف مرة واحدة فقط، مما يُغني عن الحاجة إلى #includeشروط الحماية. [ 3 ]

لغات أخرى

تدعم بعض اللغات تحديد أنه يجب تضمين الكود مرة واحدة فقط، في الملف المُضمِّن، بدلاً من تضمينه في الملف المُضمَّن (كما هو الحال مع حراس التضمين في لغتي C/C++ و #pragma once):

  • يستخدم PL/I%INCLUDE هذه العبارة كمكافئ #includeلتوجيه C. كما يدعم IBM Enterprise PL/I العبارة %XINCLUDEالتي "تُدمج نصًا خارجيًا في البرنامج المصدر إذا لم يكن مُضمنًا مسبقًا". (كما يوفر عبارة XPROCEDUREأخرى، مشابهة لعبارة PROCEDUREأخرى، تتجاهل التكرار الثاني وما يليه لنص XPROCEDUREيحمل نفس الاسم). [ 4 ]
  • توجيهات لغة Objective-C#import (انظر أعلاه)
  • PHP 's include_once[ 5 ]

انظر أيضاً

مراجع

  1. معيار C++ (ISO/IEC 14882) القسم 17.4.3.1.2/1
  2. معيار C (ISO/IEC 9899) القسم 7.1.3/1.
  3. "Objective C: Defining Classes" . developer.apple.com . 2014-09-17 . تم الاطلاع عليه بتاريخ 2018-10-03 .
  4. شركة آي بي إم (أغسطس 2017). دليل لغة Enterprise PL/I لنظام z/OS، الإصدار 5، الإصدار 1 (ملف PDF) . صفحة 257. تاريخ الاطلاع: 7 أبريل 2022 . 
  5. "include_once (PHP Language Reference)" .