C Preprocessors

C Preprocessors

מעבדי קדם הן תוכנות שמעבדות את קוד המקור לפני תחילת ההידור בפועל. הם אינם חלק מתהליך ההידור אלא פועלים בנפרד ומאפשרים למתכנתים לשנות את הקוד לפני ההידור.

  • זהו השלב הראשון שעובר קוד המקור C בעת ההמרה לקובץ הפעלה.
  • הסוגים העיקריים של הנחיות קדם-מעבד הם  מאקרו הידור מותנה של הכללת קבצים והנחיות אחרות כמו #undef #pragma וכו'.
  • בעיקר הנחיות אלו משמשות כדי להחליף קטע נתון של קוד C בקוד C אחר. לדוגמה, אם נכתוב '#define PI 3.14' אז PI מוחלף ב-3.14 על ידי הפרה-מעבד.
C Preprocessors

סוגי Preprocessors C

ניתן לסווג את כל הפרה-מעבדים לעיל ל-4 סוגים:

מאקרו

מאקרו משמשים להגדרת קבועים או ליצירת פונקציות המוחלפות על ידי המעבד הקדם לפני הידור הקוד. שני המעבדים הקדם #לְהַגדִיר ו #undef משמשים ליצירה והסרה של פקודות מאקרו ב-C.

#לְהַגדִיר ערך סמלי
#undef אֲסִימוֹן

שבו לאחר עיבוד מקדים את אֲסִימוֹן יורחב לזה עֵרֶך בתוכנית.

דוּגמָה:

C
   #include         // Macro Definition   #define LIMIT 5   int     main  (){      for     (  int     i     =     0  ;     i      <     LIMIT  ;     i  ++  )     {      printf  (  '%d   n  '       i  );      }      return     0  ;   }   

תְפוּקָה
0 1 2 3 4  

בתוכנית לעיל לפני תחילת הקומפילציה המילה LIMIT מוחלפת ב-5. המילה 'לְהַגבִּיל' בהגדרת המאקרו נקראת תבנית מאקרו ו '5' הוא הרחבת מאקרו.

פֶּתֶק אין נקודה-פסיק (;) בסוף הגדרת המאקרו. הגדרות מאקרו אינן צריכות נקודה-פסיק כדי להסתיים.

יש גם כמה מאקרו מוגדרים מראש ב-C אשר שימושיים במתן פונקציות שונות לתוכנית שלנו.

מאקרו שהוגדר בעבר יכול להיות לא מוגדר באמצעות מעבד קדם-#undef. למשל בקוד למעלה

C
   #include         // Macro Definition   #define LIMIT 5   // Undefine macro   #undef LIMIT   int     main  (){      for     (  int     i     =     0  ;     i      <     LIMIT  ;     i  ++  )     {      printf  (  '%d   n  '       i  );      }      return     0  ;   }   


תְפוּקָה:

 ./Solution.c: In function 'main': ./Solution.c:13:28: error: 'MAX' undeclared (first use in this function) printf('MAX is: %dn' MAX); ^ ./Solution.c:13:28: note: each undeclared identifier is reported only once for each function it appears in  

מאקרו עם ויכוחים

אנחנו יכולים גם להעביר ארגומנטים לפקודות מאקרו. פקודות מאקרו אלו פועלות באופן דומה לפונקציות. לְדוּגמָה

# לְהַגדִיר foo(a b) a + b
#define func(r) r * r

תן לנו להבין את זה עם תוכנית:

C
   #include         // macro with parameter   #define AREA(l b) (l * b)   int     main  (){      int     a     =     10       b     =     5  ;          // Finding area using above macro      printf  (  '%d'       AREA  (  a       b  ));      return     0  ;   }   

תְפוּקָה
Area of rectangle is: 50  

הֶסבֵּר: בתוכנית לעיל המאקרו AREA(l b) מוגדר לחשב את השטח של מלבן על ידי הכפלה שלו אורך (l) ו רוחב (ב) . כַּאֲשֵׁר AREA(א ב) נקרא זה מתרחב ל (א*ב) והתוצאה מחושבת ומודפסת.

אנא עיין סוגי מאקרו ב-C לדוגמאות וסוגים נוספים.

הכללת קובץ

הכללת קבצים מאפשרת לך לכלול קבצים חיצוניים (ספריות קבצי כותרות וכו') בתוכנית הנוכחית. זה נעשה בדרך כלל באמצעות #לִכלוֹל הנחיה שיכולה לכלול גם קבצים מוגדרי מערכת וגם קבצים מוגדרים על ידי המשתמש.

תַחבִּיר

ישנן שתי דרכים לכלול קובצי כותרות.

#לִכלוֹל
#לִכלוֹל 'שם קובץ'

ה ' <' ו '>' בסוגריים תגיד למהדר לחפש את הקובץ ב- ספרייה רגילה בְּעוֹד מרכאות כפולות ( ' ' ) תגיד למהדר לחפש את קובץ הכותרת בספריית קובץ המקור.

דוּגמָה:

C
   // Includes the standard I/O library   #include            int     main  ()     {      printf  (  'Hello World'  );          return     0  ;   }   

תְפוּקָה
Hello World 

קומפילציה מותנית

קומפילציה מותנית מאפשר לך לכלול או לא לכלול חלקים מהקוד בהתאם לתנאים מסוימים. זה שימושי ליצירת קוד ספציפי לפלטפורמה או לניפוי באגים. יש את הנחיות הקדם-מעבד המותנות הבאות: #if #ifdef #ifndef else #elif ו-#endif

תַחבִּיר

התחביר הכללי של מעבדי קדם מותנים הוא:

#אִם
// קוד כלשהו
#elif
// עוד קצת קוד
#אַחֵר
// עוד קצת קוד
#endif

ההנחיה #endif משמשת לסגירת הנחיות הפתיחה #if #ifdef ו-#ifndef.

דוּגמָה

C
   #include         // Defining a macro for PI   #define PI 3.14159   int     main  (){       // Check if PI is defined using #ifdef   #ifdef PI      printf  (  'PI is defined  n  '  );   // If PI is not defined check if SQUARE is defined   #elif defined(SQUARE)      printf  (  'Square is defined  n  '  );   // If neither PI nor SQUARE is defined trigger an error   #else      #error 'Neither PI nor SQUARE is defined'   #endif   // Check if SQUARE is not defined using #ifndef   #ifndef SQUARE      printf  (  'Square is not defined'  );   // If SQUARE is defined print that it is defined   #else      printf  (  'Square is defined'  );   #endif      return     0  ;   }   

תְפוּקָה
PI is defined Square is not defined 

הֶסבֵּר: קוד זה משתמש בהנחיות קדם-מעבד מותנות ( #ifdef #elif ו #ifndef ) כדי לבדוק אם פקודות מאקרו מסוימות ( פַּיִי ו מְרוּבָּע ) מוגדרים. מכיוון ש-PI מוגדר, התוכנית מדפיסה ' PI מוגדר ' ואז בודק אם SQUARE אינו מוגדר ומדפיס ' ריבוע אינו מוגדר '.

הנחיות אחרות

מלבד הנחיות הקדם-מעבד הראשיות C מספקת גם הנחיות אחרות לניהול התנהגות מהדר וניפוי באגים.

#פרגמה:

מספק הנחיות ספציפיות למהדר כדי לשלוט בהתנהגותו. הוא משמש לביטול יישור ערכות אזהרות וכו'.

תַחבִּיר

#פרגמה הוֹרָאָה

חלק מהנחיות #pragma נדונות להלן: 

  1. #פרגמה סטארט-אפ: הנחיות אלו עוזרות לנו לציין את הפונקציות הדרושות להפעלה לפני הפעלת התוכנית (לפני שהפקד עובר ל-main()).
  2. יציאה #פרגמה : הנחיות אלו עוזרות לנו לציין את הפונקציות הדרושות להפעלה ממש לפני יציאת התוכנית (ממש לפני שהפקד חוזר מ-main()).

דוּגמָה

C
   #include         void     func1  ();   void     func2  ();   // specifying funct1 to execute at start   #pragma startup func1   // specifying funct2 to execute before end   #pragma exit func2   void     func1  ()     {     printf  (  'Inside func1()  n  '  );     }   void     func2  ()     {     printf  (  'Inside func2()  n  '  );     }   int     main  (){      void     func1  ();      void     func2  ();      printf  (  'Inside main()  n  '  );      return     0  ;   }   

תְפוּקָה
Inside main()  

הקוד לעיל יפיק את הפלט כפי שניתן לעיל כאשר הוא רץ על מהדרים של GCC בעוד שהפלט הצפוי היה:

פלט צפוי

 Inside func1() Inside main() Inside func2()   

זה קורה מכיוון ש-GCC לא תומך בהפעלה או יציאה של #pragma. עם זאת, אתה יכול להשתמש בקוד שלהלן עבור הפלט הצפוי במהדרים של GCC. 

C
   #include         void     func1  ();   void     func2  ();   void     __attribute__  ((  constructor  ))     func1  ();   void     __attribute__  ((  destructor  ))     func2  ();   void     func1  ()   {      printf  (  'Inside func1()  n  '  );   }   void     func2  ()   {      printf  (  'Inside func2()  n  '  );   }   int     main  ()   {      printf  (  'Inside main()  n  '  );      return     0  ;   }   

תְפוּקָה
Inside func1() Inside main() Inside func2()  

בתוכנית לעיל השתמשנו בכמה תחבירים ספציפיים כך שאחת הפונקציות מופעלת לפני הפונקציה הראשית והשנייה מופעלת אחרי הפונקציה הראשית.

צור חידון