-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathChapter_II-5.tex
749 lines (629 loc) · 27.9 KB
/
Chapter_II-5.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
\chapter{المعالج القبلي (\textenglish{Preprocessor})}
بعد كل المعلومات المتعبة التي تلقّيتها في الفصول حول الجداول، النصوص والمؤشرات، فسنقوم بالتوقف قليلا. لقد تعلّمت أشياء جديدة كثيرة في الفصول السابقة، لن يكون لديّ مانع من أن نسترجع أنفاسنا قليلا.
هذا الفصل يتحدّث عن المعالج القبلي، هذا البرنامج الّذي يعمل مباشرة قبل الترجمة.\\
لا تخطئ: المعلومات التي به ستكون مهمّة لك. لكنّها ستكون أقل تعقيدًا من الّتي تعلّمتها مؤخّرًا.
\section{\texttt{include}}
كما شرحت لك في الفصول الأولى من الكتاب، نجد في الشفرات المصدريّة سطورا خاصّة تسمّى بـ\textbf{توجيهات المعالج القبلي} (\textenglish{Preprocessor directives}).\\
هذه السطور لديها الخاصيّة التالية: تبدأ دائما بالرمز
\InlineCode{\#}.
لذا فمن السهل التعرّف عليها.
التوجيهة الوحيدة التي رأيناها لحدّ الآن هي
\InlineCode{\#include}.\\
هذه التوجيهة تسمح لنا بتضمين محتوى ملف في آخر. قلت لك هذا من قبل.\\
نحن نحتاجها في تضمين الملفات ذات الصيغة
\InlineCode{.h}
كملفات
\InlineCode{.h}
الخاصّة بالمكتبات
(\InlineCode{stdlib.h}، \InlineCode{stdio.h}، \InlineCode{string.h}، \InlineCode{math.h}\dots)،
و أيضًا ملفات
\InlineCode{.h}
الخاصّة بنا.
لنضمّن ملفًا ذو صيغة
\InlineCode{.h}
موجودًا في نفس المجلّد الذي ثبتنا فيه \textenglish{IDE}
(أي البيئة التطويرية كـ\textenglish{Code::Blocks}
مثلا)، نستعمل علامات الترتيب
\InlineCode{< >}
كالتالي:
\begin{Csource}
#include <stdlib.h>
\end{Csource}
بينما لتضمين ملفّ
\InlineCode{.h}
موجود في المجلّد الذي به مشروعنا، فسنقوم يذلك باستخدام علامتي الاقتباس كالتالي:
\begin{Csource}
#include "myfile.h"
\end{Csource}
في الحقيقة، المعالج القبلي يتمّ تشغيله قبل الترجمة. يبحث في كلّ ملفاتك عن توجيهات المعالج القبلي، تلك الأسطر المشهورة التي تبدأ بـ\InlineCode{\#}.\\
عندما يجد التوجيهة
\InlineCode{\#include}،
يقوم بإدراج محتوى الملفّ في مكان وجود
\InlineCode{\#include}.
افترض أن لديّ ملفّا
\InlineCode{file.c}
يحتوي الشفرة الخاصة بالدوال التي كتبتها، ولدي ملف
\InlineCode{file.h}
يحتوي نماذج الدوال التي هي موجودة بالملف
\InlineCode{file.c}،
يمكن تلخيص ذلك بالمخطط التالي.
\begin{figure}[H]
\centering
\includegraphics[width=0.5\textwidth]{Chapter_II-5_file-c-file-h}
\end{figure}
كل محتوى الملف
\InlineCode{file.h}
سيتم وضعه داخل الملف
\InlineCode{file.c}
في مكان التوجيهة\\
\InlineCode{\#include "file.h"}.
تخيّل أن لدينا في الملف
\InlineCode{file.c}
التالي:
\begin{Csource}
#include "file.h"
int myFunction(int something, double stupid)
{
/* The code of the function */
}
void anotherFunction(int value)
{
/* The code of the function */
}
\end{Csource}
و في الملف
\InlineCode{file.h}:
\begin{Csource}
int myFunction(int something, double stupid);
void anotherFunction(int value);
\end{Csource}
عندما يمر المعالج القبلي بهذه الشفرة، قبل أن تتم ترجمة الملف
\InlineCode{file.c}،
سيضع كما قلت محتوى الملف
\InlineCode{file.h}
في الملف
\InlineCode{file.c}.
في النهاية، يعني أن الملف
\InlineCode{file.c}
\textit{قُبَيْل}
الترجمة سيحتوي التالي:
\begin{Csource}
int myFunction(int something, double stupid);
void anotherFunction(int value);
int myFunction(int something, double stupid)
{
/* The code of the function */
}
void anotherFunction(int value)
{
/* The code of the function */
}
\end{Csource}
محتوى
\InlineCode{.h}
تمّ إدخاله مكان
\InlineCode{\#include}.
هذا ليس بالأمر المعقد لفهمه، ولعلّ بعض القراء يشكك في أن الأمر يحصل بهذه الطريقة.\\
مع هذه الشروحات الإضافيّة، أتمنّى أنّ يوافقني الجميع.
\InlineCode{\#include}
لا تفعل أي شيء سوى إحضار محتوى ملف وتضمينه في آخر، من المهمّ فهم هذا الأمر جيّدا.
\begin{information}
إن كنّا قد قررنا وضع النماذج في ملفّات
\InlineCode{.h}
بدل ملفّات
\InlineCode{.c}،
فهذا من المبدأ.
بالطبع، كان بإمكاننا وضع نماذج الدوال في أعلى الملفات
\InlineCode{.c}
بأنفسنا (قد نفعل هذا أحيانا في بعض البرامج الصغيرة)، لكن لأسباب تنظيميّة، من المنصوح به جدّا وضع النماذج في ملفّات
\InlineCode{.h}.
عندما يكبر برنامجك ويصبح لديك الكثير من ملفّات
\InlineCode{.c}
يعتمدون على نفس
\InlineCode{.h}،
ستكون سعيدا لأنّك لن تضطرّ إلى نسخ ولصق النماذج الخاصّة بنفس الدوال عدّة مرّات!
\end{information}
\section{\texttt{define}}
سنتعرف الآن على توجيهة معالج جديدة وهي
\InlineCode{\#define}.
هذه التوجيهة تسمح بالتصريح عن
\textbf{ثابت معالج قبلي}.
هذا يسمح بإرفاق قيمة بعبارة.\\
إليك مثالا:
\begin{Csource}
#define INITIAL_NUMBER_OF_LIVES 3
\end{Csource}
يجب أن تكتب بالترتيب:
\begin{itemize}
\item \InlineCode{\#define}.
\item الكلمة التي تريد ربط القيمة بها.
\item قيمة الكلمة.
\end{itemize}
احذر: رغم التشابه (خصوصا في الاسم الذي اعتدنا كتابته بحروف كبيرة)، فهذه مختلفة كثيرا عن الثوابت التي تعلّمناها حتّى الآن، مثل:
\begin{Csource}
const int INITIAL_NUMBER_OF_LIVES = 3;
\end{Csource}
الثوابت تأخذ حيّزا في الذاكرة. حتى وإن لم تتغير قيمتها فإن العدد 3 مخزّن في مكان ما من الذاكرة. هذا ليس هو الحال مع ثوابت المعالج القبلي!
كيف تعمل؟ في الواقع، \InlineCode{\#define}
تستبدل في شفرتك المصدريّة كلّ الكلمات بقيمتهم الموافقة. هذا تقريبا مثل عمليّة البحث والاستبدال
(\textenglish{Search / Replace})
الموجودة في برنامج
\textenglish{Word}
مثلا. إذن السطر:
\begin{Csource}
#define INITIAL_NUMBER_OF_LIVES 3
\end{Csource}
يستبدل في الملف كلّ
\InlineCode{INITIAL\_NUMBER\_OF\_LIVES}
بالرقم 3.
هذا مثال على ملف
\InlineCode{.c}
قبل مرور المعالج القبلي:
\begin{Csource}
#define INITIAL_NUMBER_OF_LIVES 3
int main(int argc, char *argv[])
{
int lives = INITIAL_NUMBER_OF_LIVES;
/* Code ... */
\end{Csource}
و بعدما يمرّ المعالج القبلي:
\begin{Csource}
int main(int argc, char *argv[])
{
int lives = 3;
/* Code ... */
\end{Csource}
قبل الترجمة، كلّ
\InlineCode{\#define}
يتمّ استبدالها بالقيمة الموافقة. المترجم "يرى" الملفّ بعد مرور المعالج القبلي، حيث تكون الاستبدالات قد تمت.
\begin{question}
ما الفائدة بالنسبة للثوابت التي رأيناها حتّى الآن؟
\end{question}
كما قلت لك، هي لا تأخذ مكانا في الذاكرة. هذا منطقيّ، نظرا لأنّه عند الترجمة لا يتبقّى سوى الأرقام في الشفرة المصدريّة.
توجد فائدة أخرى وهي أنّ الاستبدال يتمّ في كامل الملف حيث توجد
\InlineCode{\#define}.
إن قمت بتعريف ثابت في الذاكرة داخل دالّة، فلن يكون صالحا إلّا داخل تلك الدالّة، ثمّ يتمّ حذفه بعد نهايتها.
بينما بالنسبة لـ\InlineCode{\#define}
فإنها تُطبّق على كلّ دوال الملف، وهذا قد يكون عمليّا جدّا في بعض الحالات.
هل من مثال واقعيّ لاستخدام
\InlineCode{\#define}؟\\
هذا ما لن تتأخّر عن فعله. عندما تفتح نافذة في
\textenglish{C}، قد تحتاج إلى تعريف ثوابت المعالج القبلي لتحديد أبعاد النافذة:
\begin{Csource}
#define WINDOW_WIDTH 800
#define WINDOW_HEIGTH 600
\end{Csource}
الفائدة هي أنّه إن أردت تغيير حجم الواجهة (لأنّها تبدو لك صغيرة جدّا)، فيكفي أن تغيّر
\InlineCode{\#define}
و تعيد ترجمة الشفرة.
لاحظ أنّ
\InlineCode{\#define}
تكون عادة في ملفات
\InlineCode{.h}
مع نماذج الدوال (بإمكانك أن ترى
\InlineCode{.h}
الخاصّة بالمكتبات مثل
\InlineCode{stdlib.h}،
ستجد الكثير من
\InlineCode{\#define}).\\
\InlineCode{\#define}
إذن هي "مسهّلات وصول"، يمكنك تعديل حجم نافذه عن طريق تعديل
\InlineCode{\#define}
بدل الذهاب للبحث في الدوال عن الموضع الذي تفتح فيه النافذة لتعديل الأبعاد. هذا ربح وقت للمبرمج.
كملخّص، ثوابت المعالج القبلي تسمح بـ"إعداد" برنامجك قبل ترجمته. إنّها أشبه بطريقة إعدادات صغيرة.
\subsection{\texttt{define} من أجل حجم جدول}
نستخدم كثيرا
\InlineCode{\#define}
من أجل تعريف حجم الجداول. نكتب مثلا:
\begin{Csource}
#define MAX_SIZE 1000
int main(int argc, char *argv[])
{
char string1[MAX_SIZE], string2[MAX_SIZE];
// ...
\end{Csource}
\begin{question}
ولكن\dots كنت أعتقد أنّه لا يمكننا وضع متغيّر أو ثابت بين القوسين المربعين أثناء تعريف جدول؟
\end{question}
نعم هذا صحيح، لكنّ
\InlineCode{MAX\_SIZE}
ليس متغيّرا ولا ثابتا. في الواقع لقد قلت لك، المعالج القبلي يحوّل الملف قبل الترجمة إلى:
\begin{Csource}
int main(int argc, char *argv[])
{
char string1[1000], string2[1000];
// ...
\end{Csource}
و هذا شيء صحيح.
بتعريف
\InlineCode{MAX\_SIZE}
بهذه الطريقة، يمكنك استخدامها لإنشاء جداول ذات حجوم محدّدة. إذا صارت في المستقبل غير كافية، فليس عليك سوى تعديل سطر
\InlineCode{\#define}،
إعادة الترجمة، وجداول
\InlineCode{char}
تأخذ القيمة الجديدة الّتي حددتها.
\subsection{الحسابات في \texttt{define}}
من الممكن القيام بحسابات صغيرة في \InlineCode{\#define}.\\
مثلا، هذه الشفرة تنشئ ثابتا
\InlineCode{WINDOW\_WIDTH}،
و آخر
\InlineCode{WINDOW\_HEIGHT}،
ثمّ ثالثا
\InlineCode{PIXELS\_NUMBER}،
الذي يحوي عدد البيكسلات المعروضة داخل النافذة (الحساب بسيط: العرض $\times$ الطول).
\begin{Csource}
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
#define PIXELS_NUMBER (WINDOW_WIDTH * WINDOW_HEIGHT)
\end{Csource}
قيمة
\InlineCode{PIXELS\_NUMBER}
يتمّ استبدالها قبل الترجمة بالشفرة التالية:\\
\InlineCode{(WINDOW\_WIDTH * WINDOW\_HEIGHT)}،
أي
($800 \times 600$)،
و تعطينا
$480000$.
ضع دائما حساباتك بين قوسين كما أفعل من باب الاحتياط لكي تعزل العمليّة.
يمكنك القيام بكل العمليات القاعدية التي تعرفها: جمع (+)، طرح (-)، ضرب (*)، قسمة (/)، ترديد (\%).
\subsection{الثوابت مسبقة التعريف}
بالإضافة إلى الثوابت التي أنت عرّفتها، فإنه توجد ثوابت معرّفة من قِبَل المعالج القبلي.
كل من هذه الثوابت تبدأ وتنتهي برمزي
\textenglish{underscore} \InlineCode{\_}
(تجده في لوحة المفاتيح تحت الرقم 8 أعلى اللوحة بالنسبة للتخطيط
\textenglish{AZERTY}).
\begin{itemize}
\item \InlineCode{\_\_LINE\_\_}: يعطي رقم السطر الحالي من الشفرة.
\item \InlineCode{\_\_FILE\_\_}: يعطي اسم الملف الحالي.
\item \InlineCode{\_\_DATE\_\_}: يعطي تاريخ ترجمة الشفرة.
\item \InlineCode{\_\_TIME\_\_}: تعطي وقت ترجمة الشفرة.
\end{itemize}
قد تكون هذه الثوابت مفيدة لمعالجة الأخطاء، مثال:
\begin{Csource}
printf("Error in the line n° %d of the file %s\n", __LINE__, __FILE__);
printf("This file has been compiled on %s at %s\n", __DATE__, __TIME__);
\end{Csource}
\begin{Console}
Error in the line n° 9 of the file main.c
This file has been compiled on 13 Jan 2006 at 19:21:10
\end{Console}
\subsection{المعرّفات البسيطة}
إنه من الممكن أن نكتب بكل بساطة:
\begin{Csource}
#define CONSTANT
\end{Csource}
دون إعطاء القيمة.\\
هذا يعني للمعالج القبلي أنّ الكلمة
\InlineCode{CONSTANT}
معرّفة، بكلّ بساطة. ليست لها قيمة لكنّها "موجودة".
\begin{question}
ما الفائدة من ذلك؟
\end{question}
الفائدة قد لا تبدو واضحة كما كان الأمر في السابق، لكن لهذا فائدة وسنكتشفها بسرعة.
\section{الماكرو (\textenglish{Macro})}
كنا قد رأينا بانه باستعمال \InlineCode{\#define}،
بإمكاننا أن نطلب من المعالج القبلي استبدال كلمة بقيمتها في الشفرة بأكملها. مثال:
\begin{Csource}
#define NUMBER 9
\end{Csource}
و الذي يعني أنّ جميع
\InlineCode{NUMBER}
في الشفرة يتمّ استبدالها بـ9. لقد رأينا أنّها تعمل كوظيفة بحث واستبدال يقوم بها المعالج القبلي قبل الترجمة.
لديّ خبر جديد! في الواقع
\InlineCode{\#define}
أقوى من هذا بكثير. فهي قادرة على الاستبدال بـ\dots شفرة مصدرية بأكملها! عندما نستخدم
\InlineCode{\#define}
للبحث واستبدال كلمة بشفرة مصدرية نقول أننا أنشأنا
\textbf{ماكرو
(\textenglish{Macro})}.
\subsection{ماكرو بدون معاملات}
هذا مثال عن ماكرو بسيطة:
\begin{Csource}
#define COUCOU() printf("Coucou");
\end{Csource}
الشيء الذي تغيّر هو القوسين الذين أضفناهما بعد الكلمة المفتاحيّة (هنا
\InlineCode{COUCOU()}).
سنرى فائدتهما بعد قليل.
فلنجرب الماكرو داخل الشفرة المصدرية:
\begin{Csource}
#define COUCOU() printf("Coucou");
int main(int argc, char *argv[])
{
COUCOU()
return 0;
}
\end{Csource}
\begin{Console}
Coucou
\end{Console}
أعلم أنّ هذا ليس شيئا جديدا حاليّا. لكنّ الّذي عليك فهمه، هو أن الماكرو عبارة عن بضعة أسطر من الشفرة التي يتم استبدالها مباشرة في الشفرة قبل الترجمة.\\
الشفرة التي كتبناها تصبح هكذا قبل الترجمة:
\begin{Csource}
int main(int argc, char *argv[])
{
printf("Coucou");
return 0;
}
\end{Csource}
إذا فهمت هذا فقد فهمت مبدأ عمل الماكرو.
\begin{question}
لكن، هل يمكننا أن نضع سطرًا واحدا فقط من الشفرة في كلّ ماكرو؟
\end{question}
لا، لحسن الحظ يمكنك وضع عدّة أسطر من الشفرة في المرّة. يكفي وضع
\InlineCode{\textbackslash}
قبل كلّ سطر جديد، مثل هذا:
\begin{Csource}
#define TELL_YOUR_STORY() printf("Hello, my name is Brice\n"); \
printf("I live at Nice\n"); \
printf("I love rice\n");
int main(int argc, char *argv[])
{
TELL_YOUR_STORY()
return 0;
}
\end{Csource}
\begin{Console}
Hello, my name is Brice
I live at Nice
I love rice
\end{Console}
كما تلاحظ في
\InlineCode{main}،
أنّ استدعاء الماكرو لا يوضع بعده فاصلة منقوطة في النهاية. في الواقع، لأنها توجيهة خاصة بالمعالج القبلي ولا تحتاج إلى أن تنتهي بفاصلة منقوطة.
\subsection{ماكرو بالمعاملات}
لحدّ الآن، رأينا كيف نقوم بإنشاء ماكرو بدون معاملات، أي بقوسين فارغين. الفائدة من هذا النوع من الماكرو أنّه يفيد في "اختصار" شفرة طويلة، خاصّة إذا كانت ستتكرّ كثيرا في شفرتك المصدريّة.
لكن الماكرو تصبح مفيدة أكثر عندما نضع لها الأقواس. هذا يعمل تقريبا مثل الدوال:
\begin{Csource}
#define ADULT(age) if (age >= 18) \
printf("You are adult\n");
int main(int argc, char *argv[])
{
ADULT(22)
return 0;
}
\end{Csource}
\begin{Console}
You are adult
\end{Console}
\begin{information}
يمكننا مثلا إضافة \InlineCode{else}
لكي نُظهر على الشاشة: أنت لست بالغًا
"\textenglish{You are not adult}".
حاول القيام بذلك، الأمر ليس صعبا. لا تنس وضع الشرطة الخلفيّة
\InlineCode{\textbackslash}
قبل السطر الجديد.
\end{information}
مبدأ الماكرو بسيط جدّا:
\begin{Csource}
#define ADULT(age) if (age >= 18) \
printf("You are adult\n");
\end{Csource}
نقوم بوضع اسم "متغير" بين القوسين، والّذي نسميه
\InlineCode{age}.
في كلّ شفرة الماكرو،
\InlineCode{age}
سيتم استبداله بالعدد المحدد عند الاستدعاء (هنا 22).
أي أن الشفرة المصدريّة السابقة بعد مرور المعالج القبلي مباشرة تصبح هكذا:
\begin{Csource}
int main(int argc, char *argv[])
{
if (22 >= 18)
printf("You are adult\n");
return 0;
}
\end{Csource}
تم استبدال السطر الذي يستدعي الماكرو بالشفرة التي تحتويه الماكرو، وتم تعويض "المتغير"
\InlineCode{age}
بقيمته مباشرة في الشفرة المصدريّة للاستبدال.
يمكننا إنشاء ماكرو بعدة معاملات:
\begin{Csource}
#define ADULT(age, name) if (age >= 18)\
printf("You are adult %s\n", name);
int main(int argc, char *argv[])
{
ADULT(22, "Maxime")
return 0;
}
\end{Csource}
هذا كلّ ما يمكننا أن نقوله حول الماكرو والمميزات التي تقدّمها لنا. يمكنك تذكّر أنّه مجرّد استبدال للشفرة المصدرية يمكنه استخدام المعاملات.
\begin{information}
في الواقع، أنت لست بحاجة أن تتعامل كثيرًا مع الماكرو، لكن اعلم أن مكتبات معقدة كـ\textenglish{wxWidgets}
و \textenglish{Qt}
(مكتبات لإنشاء الواجهات الرسوميّة) تستعملان بكثرة الماكرو. لهذا من المستحسن أن تتعلّم كيف تعمل الأمور من الآن كي لا تضيع لاحقا.
\end{information}
\section{الشروط}
أجل: يمكننا أن نستعمل الشروط في لغة المعالج القبلي! لاحظ كيف تعمل:
\begin{Csource}
#if condition
/* Code to compile if the condition is true */
#elif condition2
/* Else, compile this code if the condition2 is true */
#endif
\end{Csource}
الكلمة المفتاحية
\InlineCode{\#if}
تسمح بإدراج شرط معالج قبلي،
\InlineCode{\#elif}
تعني
\InlineCode{else if}.
الأمر يتوقف عندما نضع
\InlineCode{\#endif}،
تلاحظ أنه لا توجد حاضنتان في لغة المعالج القبلي.
الفائدة هي أننا سنتمكن من إجراء
\textbf{ترجمة شرطية
(\textenglish{Conditional compilation})}.\\
في الواقع، إن كان الشرط محققا فإن الشفرة التالية ستتم ترجمتها، وإلّا فسيتم حذفه ولن يكون جزءً من البرنامج النهائي.
\subsection{
\texttt{\#ifdef}
و
\texttt{\#ifndef}
}
سنرى الآن الفائدة من استعمال
\InlineCode{\#define}
لتعريف ثابت دون إعطائه أيّ قيمة، مثلما علّمتك من قبل:
\begin{Csource}
#define CONSTANT
\end{Csource}
في الواقع، يمكننا استعمال الشرط
\InlineCode{\#ifdef}
لنقول "إن كان الثابت معرّفا".
بالنسبة لـ\InlineCode{\#ifndef}،
فهذا يعني "إن كان الثابت غير معرّف".
يمكننا أن نتخيل هذا:
\begin{Csource}
#define WINDOWS
#ifdef WINDOWS
/* Source code for Windows */
#endif
#ifdef LINUX
/* Source code for Linux */
#endif
#ifdef MAC
/* Source code for Mac */
#endif
\end{Csource}
هذا مثال عن برنامج متعدد المنصات
(\textenglish{multi-platform})
للتلاؤم مع النظام مثلا.\\
إذن، يجب من أجل كلّ نظام إعادة ترجمة الشفرة (هذا ليس أمرا سحريّا).
إن كنت في
\textenglish{Windows}
فستكتب
\InlineCode{\#define WINDOWS}
في الأعلى وتعيد الترجمة.\\
إن أردت الترجمة لـ\textenglish{Linux}
فسيكون عليك تغيير
\InlineCode{\#define}
لوضع
\InlineCode{\#define LINUX}
و تعيد الترجمة. هذه المرّة الجزء الخاصّ بـ\textenglish{Linux}
الّذي ستتمّ ترجمته أمّا باقي الشروط فلن تكون محققة يعني أنه سيتم تجاهلها.
\subsection{\texttt{\#ifndef} لتفادي التضمينات اللامنتهية}
\InlineCode{\#ifndef}
مهمّة جدّا في الملفّات
\InlineCode{.h}
لتجنّب "التضمينات اللامنتهية".
\begin{question}
ماذا يعني التضمين اللامنتهي؟
\end{question}
هذا أمر بسيط، تخيل أن لدينا ملفأً
\InlineCode{A.h}
و ملفأً
\InlineCode{B.h}،
الملف
\InlineCode{A.h}
يحتوي
\InlineCode{\#include}
للملف
\InlineCode{B.h}.
إذا فالملف
\InlineCode{B.h}
مضمّن الآن بـ\InlineCode{A.h}.\\
و هنا يبدأ المشكل، تخيّل أن الملف
\InlineCode{B.h}
يحتوي نفسه على
\InlineCode{\#include}
للملف
\InlineCode{A.h}!
هذا يحدث أحيانا في البرمجة! يعني أن الملف الأول بحاجة إلى الثاني والثاني بحاجة إلى الأول أيضا.
إن فكّرنا قليلا، فسنعرف أنّ هذا ما سيحصل:
\begin{itemize}
\item الحاسوب يقرأ
\InlineCode{A.h}
و يجد بأن عليه تضمين
\InlineCode{B.h}.
\item يقوم بقراءة
\InlineCode{B.h}
فيجد بأن عليه تضمين
\InlineCode{A.h}.
\item يضمّن
\InlineCode{A.h}
في
\InlineCode{B.h}،
لكن داخل
\InlineCode{A.h}
يجد بأنّه يحتاج إلى تضمين
\InlineCode{B.h}!
\item يكرّر، يرى أنّ
\InlineCode{B.h}
و يجد أنّه يجب عليه تضمين
\InlineCode{A.h}.
\item إلخ.
\end{itemize}
قد تظنّ أنّ هذا الأمر لا نهاية له!\\
في الحقيقة، من كثرة التضمينات، سيتوقّف المعالج القبلي قائلا "لقد سئمت من التضمينات!" وهذا ما يعطّل الترجمة.
كيف السبيل لوقف هذا الكابوس المريع؟ إليك الحيلة. أطلب منك فعل هذا في
\textbf{كلّ ملفّات
\InlineCode{.h}
الخاصّة بك} بدون استثناء:
\begin{Csource}
#ifndef DEF_FILENAME // If the constant has not been defined, the file then has never been included
#define DEF_FILENAME // We define the constant so the file will not be included the next time
/* Content of your file .h (other #include, prototypes, #define...) */
#endif
\end{Csource}
أي أننا نضع كل محتوى الملف
\InlineCode{.h}
(بما في ذلك
\InlineCode{\#include}،
النماذج،
\InlineCode{\#define})
بين \InlineCode{\#ifndef}
و \InlineCode{\#endif}.
هل فهمت كيف تعمل الشفرة؟ أوّل مرّة رأيت هذه التقنيّة كنت مشوّشا كثيرا: سأحاول أن أشرح.
تخيل أن الملف
\InlineCode{.h}
يتم تضمينه للمرة الأولى، سيقرأ الحاسوب الشرط "إذا كان الثابت
\InlineCode{DEF\_FILENAME}
لم يتم تعريفه". بما أنه يتمّ قراءة الملف للمرة الأولى، فإن الثابت لم يتم تعريفه بعد، فسيقوم المعالج القبلي بالدخول إلى داخل
\InlineCode{if}.
أوّل تعليمة سيجدها هي:
\begin{Csource}
#define DEF_FILENAME
\end{Csource}
الآن لقد تم تعريف الثابت. في المرّة القادمة التي يتم فيها تضمين الملف، لن يكون الشرط فيها صحيحا ولهذا لن نخاطر بإعادة تضمين الملف من جديد.
يمكنك تسمية اسم الثابت كما تريد، أنا اعتدت على تسميته
\InlineCode{DEF\_FILENAME}.
الشيء الأهمّ، والّذي أتمنّى أنّك فهمته جيّدا، هو أن تغيّر اسم الثابت من ملف
\InlineCode{.h}
إلى آخر. يجب ألّا يكون نفس الثابت في كلّ ملفّات
\InlineCode{.h}،
و إلا فلن تتم قراءة سوى أول ملف
\InlineCode{.h}
و الباقية سيتم تجاهلها!\\
إذا فلتغيّر
\InlineCode{FILENAME}
إلى اسم الملف الحالي.
\begin{information}
أنصحك بإلقاء نظرة على
\InlineCode{.h}
الخاصّة بالمكتبات القياسيّة المتواجدة في حاسوبك، سترى بأنها
كلها
مكتوبة بنفس المبدأ
(\InlineCode{\#ifndef}
في البداية و
\InlineCode{\#endif}
في النهاية). هذا يضمن عدم إمكانيّة حصول تضمينات لامنتهية.
\end{information}
\section*{ملخّص}
\begin{itemize}
\item المعالج القبلي هو برنامج يحلّل الشفرة المصدريّة، ويقوم بإجراء تغييرات عليها قبل الترجمة.
\item تعليمة المعالج القبلي
\InlineCode{\#include}
تسمح بإدراج محتوى ملف آخر.
\item تعليمة
\InlineCode{\#define}
تسمح بتعريف ثابت معالج قبلي. يتم استبدال كلمة مفتاحيّة بقيمة في الشفرة المصدريّة.
\item الماكرو هي مجموعة أسطر من الشفرة الجاهزة معرّفة بـ\InlineCode{\#define}.
يمكنها أن تقبل معاملات.
\item من الممكن كتابة شروط في لغة المعالج القبلي لاختيار ما يجب ترجمته، نستعمل عادة
\InlineCode{\#if}،
\InlineCode{\#elif}
و
\InlineCode{\#endif}.
\item لنتجنب أنّ ملفًّا
\InlineCode{.h}
يتمّ تضمينه عددا لامنتهيا من المرّات، نحميه بمجموعة من ثوابت المعالج القبلي والشروط. كلّ ملفّاتك
\InlineCode{.h}
المستقبليّة يجب حمايتها بهذه الطريقة.
\end{itemize}