<?xml version="1.0"?>
<rss version="2.0"><channel><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x644;&#x63A;&#x629; C</title><link>https://academy.hsoub.com/programming/c/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x644;&#x63A;&#x629; C</description><language>ar</language><item><title>&#x62A;&#x637;&#x628;&#x64A;&#x642; &#x639;&#x645;&#x644;&#x64A; &#x644;&#x628;&#x646;&#x627;&#x621; &#x628;&#x631;&#x646;&#x627;&#x645;&#x62C; &#x62A;&#x646;&#x641;&#x64A;&#x630;&#x64A; &#x645;&#x646; &#x634;&#x64A;&#x641;&#x631;&#x629; &#x645;&#x635;&#x62F;&#x631;&#x64A;&#x629; &#x628;&#x644;&#x63A;&#x629; C</title><link>https://academy.hsoub.com/programming/c/%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%B9%D9%85%D9%84%D9%8A-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%AA%D9%86%D9%81%D9%8A%D8%B0%D9%8A-%D9%85%D9%86-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D9%85%D8%B5%D8%AF%D8%B1%D9%8A%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-c-r1931/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_03/------.png.641c9d2f571ea096ebf799dd93fcd5b0.png" /></p>
<p>
	تعرّفنا في <a href="https://academy.hsoub.com/programming/advanced/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%85%D9%84%D9%81-%D9%82%D8%A7%D8%A8%D9%84-%D9%84%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-executable-file-%D9%85%D9%86-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%85%D8%B5%D8%AF%D8%B1%D9%8A%D8%A9-source-code-r1930/" rel="">المقال السابق</a> من سلسلة <a href="https://academy.hsoub.com/tags/%D9%85%D8%AF%D8%AE%D9%84%20%D9%84%D8%B9%D9%84%D9%85%20%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8/" rel="">مدخل لعلم الحاسوب</a> على الخطوات الثلاث لبناء ملف قابل للتنفيذ هي: التصريف Compiling والتجميع Assembling والربط Linking، وسنطبّق في هذا المقال هذه الخطوات عمليًا لبناء ملف قابل للتنفيذ.
</p>

<p>
	تابع فيما الخطوات التي جرى اتخاذها لبناء تطبيق بسيط خطوة بخطوة. لاحظ أن الأمر <code>gcc</code> يشغّل برنامجَ تشغيلٍ driver program يخفي معظم الخطوات عنك، وهذا هو ما تريده بالضبط في ظل الظروف العادية، لأن الأوامر والخيارات الدقيقة للحصول على ملف قابلٍ للتنفيذ على نظام حقيقي يمكن أن تكون معقدة للغاية وخاصةً بكل معمارية على حدة.
</p>

<p>
	سنشرح عملية التصريف في المثالين التاليين، حيث سنستخدم ملفين مصدريين مكتوبين <a href="https://academy.hsoub.com/programming/c/" rel="">بلغة C</a>، إذ يعرّف أحدهما الدالة الرئيسية <code>main()‎</code> التي تُعَد نقطة الدخول الأولية، ويصرّح الملف الآخر عن دالة مساعدة، وهناك متغير عام واحد.
</p>

<p>
	إليك مثال مرحبًا بالعالم Hello World:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_4708_8" style=""><span class="pun">#</span><span class="pln">include </span><span class="pun">&lt;</span><span class="pln">stdio</span><span class="pun">.</span><span class="pln">h</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">‫/*</span><span class="pln"> </span><span class="pun">نحتاج</span><span class="pln"> </span><span class="pun">نموذجًا</span><span class="pln"> </span><span class="pun">أوليًا</span><span class="pln"> </span><span class="pun">ليعرف</span><span class="pln"> </span><span class="pun">المصرّف</span><span class="pln"> </span><span class="pun">نوع</span><span class="pln"> </span><span class="pun">الدالة‫</span><span class="pln"> function</span><span class="pun">()‎</span><span class="pln"> </span><span class="pun">*/</span><span class="pln">
int function</span><span class="pun">(</span><span class="pln">char </span><span class="pun">*</span><span class="pln">input</span><span class="pun">);</span><span class="pln">

</span><span class="pun">‫/*</span><span class="pln"> </span><span class="pun">‫بما</span><span class="pln"> </span><span class="pun">أن</span><span class="pln"> </span><span class="pun">هذا</span><span class="pln"> </span><span class="pun">المتغير</span><span class="pln"> </span><span class="pun">ساكن</span><span class="pln"> static</span><span class="pun">،</span><span class="pln"> </span><span class="pun">فيمكننا</span><span class="pln"> </span><span class="pun">تعريفه</span><span class="pln"> </span><span class="pun">في</span><span class="pln"> </span><span class="pun">كلٍّ</span><span class="pln"> </span><span class="pun">من</span><span class="pln"> </span><span class="pun">الملفين</span><span class="pln"> hello</span><span class="pun">.</span><span class="pln">c </span><span class="pun">و</span><span class="pln">function</span><span class="pun">.</span><span class="pln">c </span><span class="pun">*/</span><span class="pln">
static int i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">100</span><span class="pun">;</span><span class="pln">

</span><span class="com">/* هذا متغير عام */</span><span class="pln">
int global </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">

int main</span><span class="pun">(</span><span class="pln">void</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
  </span><span class="pun">‫/*</span><span class="pln"> </span><span class="pun">‫يجب</span><span class="pln"> </span><span class="pun">أن</span><span class="pln"> </span><span class="pun">تعيد</span><span class="pln"> </span><span class="pun">الدالة</span><span class="pln"> function</span><span class="pun">()‎</span><span class="pln"> </span><span class="pun">قيمة</span><span class="pln"> </span><span class="pun">المتغير</span><span class="pln"> </span><span class="pun">العام</span><span class="pln"> global </span><span class="pun">*/</span><span class="pln">
  int ret </span><span class="pun">=</span><span class="pln"> function</span><span class="pun">(</span><span class="str">"Hello, World!"</span><span class="pun">);</span><span class="pln">
  exit</span><span class="pun">(</span><span class="pln">ret</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إليك مثال على دالة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4708_10" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">

</span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">100</span><span class="pun">;</span><span class="pln">

</span><span class="pun">‫</span><span class="com">/* مُصرّح عنه بأنه خارجي‫ extern لأنه مُعرَّف في الملف hello.c */</span><span class="pln">
</span><span class="kwd">extern</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> global</span><span class="pun">;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> function</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">input</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
  printf</span><span class="pun">(</span><span class="str">"%s\n"</span><span class="pun">,</span><span class="pln"> input</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> global</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	التصريف Compiling
</h2>

<p>
	لكل المصرِّفات خيارٌ لتنفيذ الخطوة الأولى من التصريف فقط مثل استخدام الراية <code>‎-S</code> لوضع الخرج في ملف يحمل اسم ملف الدخل نفسه ولكن مع اللاحقة <code>‎.s</code>، وبالتالي يمكننا عرض الخطوة الأولى باستخدام الأمر <code>gcc -S</code> كما هو موضح في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4708_12" style=""><span class="pln">$ gcc </span><span class="pun">-</span><span class="pln">S hello</span><span class="pun">.</span><span class="pln">c
$ gcc </span><span class="pun">-</span><span class="pln">S function</span><span class="pun">.</span><span class="pln">c
$ cat function</span><span class="pun">.</span><span class="pln">s
  </span><span class="pun">.</span><span class="pln">file   </span><span class="str">"function.c"</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pred</span><span class="pun">.</span><span class="pln">safe_across_calls p1</span><span class="pun">-</span><span class="pln">p5</span><span class="pun">,</span><span class="pln">p16</span><span class="pun">-</span><span class="pln">p63
  </span><span class="pun">.</span><span class="pln">section        </span><span class="pun">.</span><span class="pln">sdata</span><span class="pun">,</span><span class="str">"aw"</span><span class="pun">,</span><span class="lit">@progbits</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">align </span><span class="lit">4</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">type   i</span><span class="com">#, @object</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">size   i</span><span class="com">#, 4</span><span class="pln">
i</span><span class="pun">:</span><span class="pln">
  data4   </span><span class="lit">100</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">section        </span><span class="pun">.</span><span class="pln">rodata
  </span><span class="pun">.</span><span class="pln">align </span><span class="lit">8</span><span class="pln">
</span><span class="pun">.</span><span class="pln">LC0</span><span class="pun">:</span><span class="pln">
  stringz </span><span class="str">"%s\n"</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">text
  </span><span class="pun">.</span><span class="pln">align </span><span class="lit">16</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">global function</span><span class="com">#</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">proc function</span><span class="com">#</span><span class="pln">
function</span><span class="pun">:</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">prologue </span><span class="lit">14</span><span class="pun">,</span><span class="pln"> </span><span class="lit">33</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">save ar</span><span class="pun">.</span><span class="pln">pfs</span><span class="pun">,</span><span class="pln"> r34
  alloc r34 </span><span class="pun">=</span><span class="pln"> ar</span><span class="pun">.</span><span class="pln">pfs</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">vframe r35
  mov r35 </span><span class="pun">=</span><span class="pln"> r12
  adds r12 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">16</span><span class="pun">,</span><span class="pln"> r12
  mov r36 </span><span class="pun">=</span><span class="pln"> r1
  </span><span class="pun">.</span><span class="pln">save rp</span><span class="pun">,</span><span class="pln"> r33
  mov r33 </span><span class="pun">=</span><span class="pln"> b0
  </span><span class="pun">.</span><span class="pln">body
  </span><span class="pun">;;</span><span class="pln">
  st8 </span><span class="pun">[</span><span class="pln">r35</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> r32
  addl r14 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">@ltoffx</span><span class="pun">(.</span><span class="pln">LC0</span><span class="pun">),</span><span class="pln"> r1
  </span><span class="pun">;;</span><span class="pln">
  ld8</span><span class="pun">.</span><span class="pln">mov r37 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">r14</span><span class="pun">],</span><span class="pln"> </span><span class="pun">.</span><span class="pln">LC0
  ld8 r38 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">r35</span><span class="pun">]</span><span class="pln">
  br</span><span class="pun">.</span><span class="pln">call</span><span class="pun">.</span><span class="pln">sptk</span><span class="pun">.</span><span class="pln">many b0 </span><span class="pun">=</span><span class="pln"> printf</span><span class="com">#</span><span class="pln">
  mov r1 </span><span class="pun">=</span><span class="pln"> r36
  </span><span class="pun">;;</span><span class="pln">
  addl r15 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">@ltoffx</span><span class="pun">(</span><span class="pln">global</span><span class="com">#), r1</span><span class="pln">
  </span><span class="pun">;;</span><span class="pln">
  ld8</span><span class="pun">.</span><span class="pln">mov r14 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">r15</span><span class="pun">],</span><span class="pln"> global</span><span class="com">#</span><span class="pln">
  </span><span class="pun">;;</span><span class="pln">
  ld4 r14 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">r14</span><span class="pun">]</span><span class="pln">
  </span><span class="pun">;;</span><span class="pln">
  mov r8 </span><span class="pun">=</span><span class="pln"> r14
  mov ar</span><span class="pun">.</span><span class="pln">pfs </span><span class="pun">=</span><span class="pln"> r34
  mov b0 </span><span class="pun">=</span><span class="pln"> r33
  </span><span class="pun">.</span><span class="pln">restore sp
  mov r12 </span><span class="pun">=</span><span class="pln"> r35
  br</span><span class="pun">.</span><span class="pln">ret</span><span class="pun">.</span><span class="pln">sptk</span><span class="pun">.</span><span class="pln">many b0
  </span><span class="pun">;;</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">endp function</span><span class="com">#</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">ident  </span><span class="str">"GCC: (GNU) 3.3.5 (Debian 1:3.3.5-11)"</span></pre>

<p>
	تُعَد عملية التجميع Assembly معقدة قليلًا، ولكن يجب أن تكون قادرًا على معرفة مكان تعريف المتغير <code>i</code> بوصفه <code>data4</code> أي 4 بايتات أو 32 بتًا بحجم النوع <code>int</code>، ومكان تعريف الدالة <code>function</code> (بالشكل <code>function:‎</code>) واستدعاء الدالة <code>printf()‎</code>.
</p>

<p>
	أصبح لدينا الآن ملفا تجميع جاهزين لتجميعهما في شيفرة الآلة البرمجية machine code.
</p>

<h2>
	التجميع Assembly
</h2>

<p>
	التجميع هو عملية مباشرة إلى حد ما، ويُطلَق على المجمّع <code>as</code> ويأخذ وسائطًا بطريقة مماثلة للأمر <code>gcc</code>.
</p>

<p>
	إليك مثال عن التجميع:
</p>

<pre class="ipsCode">$ as -o function.o function.s
$ as -o hello.o hello.s
$ ls
function.c  function.o  function.s  hello.c  hello.o  hello.s
</pre>

<p>
	تنتج عن عملية التجميع التعليمات المُصرَّفة Object Code، حيث تكون هذه الشيفرة جاهزةً لربطها مع بعضها البعض في الملف النهائي القابل للتنفيذ. يمكنك تخطي الاضطرار إلى استخدام المُجمِّع يدويًا من خلال استدعاء المصرِّف مع الراية <code>‎-c</code> التي تحوّل ملف الدخل مباشرةً إلى شيفرة كائن، وتضعها في ملف له البادئة نفسها ولكن مع اللاحقة <code>‎.o</code>.
</p>

<p>
	لا يمكننا فحص شيفرة التعليمات المُصرَّفة مباشرةً لأنها في صيغة ثنائية، ولكن يمكننا استخدام بعض الأدوات لفحص ملفات التعليمات المُصرَّفة مثل الأداة <code>readelf --symbols</code> التي ستعرض الرموز الموجودة في ملف الكائن كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4708_14" style=""><span class="pln">$ readelf </span><span class="pun">--</span><span class="pln">symbols </span><span class="pun">./</span><span class="pln">hello</span><span class="pun">.</span><span class="pln">o

</span><span class="typ">Symbol</span><span class="pln"> table </span><span class="str">'.symtab'</span><span class="pln"> contains </span><span class="lit">15</span><span class="pln"> entries</span><span class="pun">:</span><span class="pln">
  </span><span class="typ">Num</span><span class="pun">:</span><span class="pln">    </span><span class="typ">Value</span><span class="pln">          </span><span class="typ">Size</span><span class="pln"> </span><span class="typ">Type</span><span class="pln">    </span><span class="typ">Bind</span><span class="pln">   </span><span class="typ">Vis</span><span class="pln">      </span><span class="typ">Ndx</span><span class="pln"> </span><span class="typ">Name</span><span class="pln">
    </span><span class="lit">0</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  LOCAL  DEFAULT  UND
    </span><span class="lit">1</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS hello</span><span class="pun">.</span><span class="pln">c
    </span><span class="lit">2</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">1</span><span class="pln">
    </span><span class="lit">3</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">3</span><span class="pln">
    </span><span class="lit">4</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">4</span><span class="pln">
    </span><span class="lit">5</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">5</span><span class="pln">
    </span><span class="lit">6</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">4</span><span class="pln"> OBJECT  LOCAL  DEFAULT    </span><span class="lit">5</span><span class="pln"> i
    </span><span class="lit">7</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">6</span><span class="pln">
    </span><span class="lit">8</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">7</span><span class="pln">
    </span><span class="lit">9</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">8</span><span class="pln">
   </span><span class="lit">10</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">10</span><span class="pln">
   </span><span class="lit">11</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000004</span><span class="pln">     </span><span class="lit">4</span><span class="pln"> OBJECT  GLOBAL DEFAULT    </span><span class="lit">5</span><span class="pln"> global
   </span><span class="lit">12</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">    </span><span class="lit">96</span><span class="pln"> FUNC    GLOBAL DEFAULT    </span><span class="lit">1</span><span class="pln"> main
   </span><span class="lit">13</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  GLOBAL DEFAULT  UND function
   </span><span class="lit">14</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  GLOBAL DEFAULT  UND exit

$ readelf </span><span class="pun">--</span><span class="pln">symbols </span><span class="pun">./</span><span class="pln">function</span><span class="pun">.</span><span class="pln">o

</span><span class="typ">Symbol</span><span class="pln"> table </span><span class="str">'.symtab'</span><span class="pln"> contains </span><span class="lit">14</span><span class="pln"> entries</span><span class="pun">:</span><span class="pln">
  </span><span class="typ">Num</span><span class="pun">:</span><span class="pln">    </span><span class="typ">Value</span><span class="pln">          </span><span class="typ">Size</span><span class="pln"> </span><span class="typ">Type</span><span class="pln">    </span><span class="typ">Bind</span><span class="pln">   </span><span class="typ">Vis</span><span class="pln">      </span><span class="typ">Ndx</span><span class="pln"> </span><span class="typ">Name</span><span class="pln">
    </span><span class="lit">0</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  LOCAL  DEFAULT  UND
    </span><span class="lit">1</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS function</span><span class="pun">.</span><span class="pln">c
    </span><span class="lit">2</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">1</span><span class="pln">
    </span><span class="lit">3</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">3</span><span class="pln">
    </span><span class="lit">4</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">4</span><span class="pln">
    </span><span class="lit">5</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">5</span><span class="pln">
    </span><span class="lit">6</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">4</span><span class="pln"> OBJECT  LOCAL  DEFAULT    </span><span class="lit">5</span><span class="pln"> i
    </span><span class="lit">7</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">6</span><span class="pln">
    </span><span class="lit">8</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">7</span><span class="pln">
    </span><span class="lit">9</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">8</span><span class="pln">
   </span><span class="lit">10</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">10</span><span class="pln">
   </span><span class="lit">11</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">   </span><span class="lit">128</span><span class="pln"> FUNC    GLOBAL DEFAULT    </span><span class="lit">1</span><span class="pln"> function
   </span><span class="lit">12</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  GLOBAL DEFAULT  UND printf
   </span><span class="lit">13</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  GLOBAL DEFAULT  UND global</span></pre>

<p>
	يُعَد هذا الخرج معقدًا للغاية، ولكن يجب أن تكون قادرًا على فهم الكثير منه مثل:
</p>

<ul>
	<li>
		لاحظ الرمز الذي يحمل الاسم <code>i</code> في الخرج <code>hello.o</code>، حيث يُسبَق هذا الرمز بالكلمة <code>LOCAL</code> أي أنه محلي، لأننا صرّحنا عنه بأنه ساكن <code>static</code>، وبالتالي يُميَّز على أنه محلي لملف الكائن.
	</li>
	<li>
		لاحظ المتغير <code>global</code> في الخرج نفسه المُعرَّف على أنه متغير عام <code>GLOBAL</code>، مما يعني أنه مرئي خارج هذا الملف، وتكون الدالة الرئيسية <code>main()‎</code> مرئية من خارج الملف.
	</li>
	<li>
		لاحظ أن الرمز <code>function</code> له النوع <code>UND</code> أو غير مُعرَّف Undefined من أجل استدعاء الدالة <code>function()‎</code>، أي أن الأمر متروك للرابط Linker للعثور على عنوان الدالة.
	</li>
	<li>
		لاحظ الرموز الموجودة في الملف <code>function.c</code> وكيفية ملاءمتها مع الخرج.
	</li>
</ul>

<h2>
	الربط Linking
</h2>

<p>
	يُعَد استدعاء الرابط المُسمَّى <code>ld</code> عمليةً معقدة للغاية على نظام حقيقي، لذلك نترك عملية الربط للأمر <a href="https://academy.hsoub.com/questions/18293-%D9%85%D8%A7-%D8%A7%D9%84%D9%81%D8%B1%D9%82-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%B5%D8%B1%D9%91%D9%90%D9%81-gcc-%D9%88-g-%D9%81%D9%8A-%D8%AA%D8%B5%D8%B1%D9%8A%D9%81-%D9%85%D9%84%D9%81%D8%A7%D8%AA-c%D8%9F/?sortby=date" rel=""><code>gcc</code></a>، ولكن يمكننا التعرّف على ما يفعله داخليًا باستخدام الراية <code>‎-v</code> التي ترمز إلى Verbose أي مُفصَّلة.
</p>

<p>
	إليك مثال عن عملية الربط:
</p>

<pre class="ipsCode">/usr/lib/gcc-lib/ia64-linux/3.3.5/collect2 -static 
/usr/lib/gcc-lib/ia64-linux/3.3.5/../../../crt1.o 
/usr/lib/gcc-lib/ia64-linux/3.3.5/../../../crti.o 
/usr/lib/gcc-lib/ia64-linux/3.3.5/crtbegin.o 
-L/usr/lib/gcc-lib/ia64-linux/3.3.5 
-L/usr/lib/gcc-lib/ia64-linux/3.3.5/../../.. 
hello.o 
function.o 
--start-group 
-lgcc 
-lgcc_eh 
-lunwind 
-lc 
--end-group 
/usr/lib/gcc-lib/ia64-linux/3.3.5/crtend.o 
/usr/lib/gcc-lib/ia64-linux/3.3.5/../../../crtn.o
</pre>

<p>
	أول شيء تلاحظه هو استدعاء برنامج بالاسم <code>collect2</code> وهو عبارة عن مُغلِّف للرابط <code>ld</code>، ويستخدم الأمر <code>gcc</code> داخليًا. الشيء الآخر الذي ستلاحظه هو ملفات الكائنات التي تبدأ بالرمز <code>crt</code> أي أنها مُحدَّدة للرابط. يُوفّر الأمر <code>gcc</code> ومكتبات النظام هذه الدوال التي تحتوي على الشيفرة البرمجية المطلوبة لبدء البرنامج. لا تُعَد الدالة الرئيسية <code>main()‎</code> أول دالة مُستدعاة عند تشغيل البرنامج، بل تُستدعَى أولًا الدالة <code>‎_start</code> الموجودة في ملفات الكائنات <code>crt</code>، حيث تضبط هذه الدالة بعض الإعدادات العامة التي لا يجب أن يقلق مبرمجو التطبيقات بشأنها.
</p>

<p>
	يُعَد تسلسل المسار الهرمي معقدًا للغاية، ولكن يمكننا أن نرى أن الخطوة الأخيرة هي ربط بعض ملفات الكائنات الإضافية وهي:
</p>

<ul>
	<li>
		<code>crt1.o</code>: توفره مكتبات النظام libc، ويحتوي على الدالة <code>‎_start</code> التي تُعَد أول شيء يُستدعَى في البرنامج.
	</li>
	<li>
		<code>crti.o</code>: توفّره مكتبات النظام.
	</li>
	<li>
		<code>crtbegin.o</code>
	</li>
	<li>
		<code>crtsaveres.o</code>
	</li>
	<li>
		<code>crtend.o</code>
	</li>
	<li>
		<code>crtn.o</code>
	</li>
</ul>

<p>
	يمكنك أن ترى بعد ذلك أننا نربط ملفي الكائنات <code>hello.o</code> و<code>function.o</code>، ثم نحدّد بعض المكتبات الإضافية باستخدام رايات <code>‎-l</code>، حيث تُعَد هذه المكتبات خاصةً بالنظام ومطلوبة لكل برنامج. الراية الرئيسية هي الراية <code>‎-lc</code> التي تجلب مكتبة C التي تحتوي على جميع الدوال المشتركة مثل الدالة <code>printf()‎</code>. نربط بعد ذلك مرة أخرى بعض ملفات كائنات النظام التي تطبّق بعض عمليات التنظيف بعد انتهاء البرامج. تُعَد هذه التفاصيل معقدة، إلا أن مفهومها واضح ومباشر.
</p>

<p>
	سنربط بعد ذلك جميع ملفات التعليمات المُصرَّفة مع بعضها البعض في ملف واحد قابل للتنفيذ وجاهز للتشغيل.
</p>

<h2>
	الملف القابل للتنفيذ Executable
</h2>

<p>
	سندخل في مزيد من التفاصيل حول الملف القابل للتنفيذ لاحقًا، ولكن يمكننا إجراء فحص بطريقة مماثلة لملفات الكائنات لمعرفة ما يحدث.
</p>

<p>
	إليك مثال عن ملف قابل للتنفيذ:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4708_17" style=""><span class="pln">ianw@lime</span><span class="pun">:~/</span><span class="pln">programs</span><span class="pun">/</span><span class="pln">csbu</span><span class="pun">/</span><span class="pln">wk7</span><span class="pun">/</span><span class="pln">code$ gcc </span><span class="pun">-</span><span class="pln">o program hello</span><span class="pun">.</span><span class="pln">c function</span><span class="pun">.</span><span class="pln">c
ianw@lime</span><span class="pun">:~/</span><span class="pln">programs</span><span class="pun">/</span><span class="pln">csbu</span><span class="pun">/</span><span class="pln">wk7</span><span class="pun">/</span><span class="pln">code$ readelf </span><span class="pun">--</span><span class="pln">symbols </span><span class="pun">./</span><span class="pln">program

</span><span class="typ">Symbol</span><span class="pln"> table </span><span class="str">'.dynsym'</span><span class="pln"> contains </span><span class="lit">11</span><span class="pln"> entries</span><span class="pun">:</span><span class="pln">
  </span><span class="typ">Num</span><span class="pun">:</span><span class="pln">    </span><span class="typ">Value</span><span class="pln">          </span><span class="typ">Size</span><span class="pln"> </span><span class="typ">Type</span><span class="pln">    </span><span class="typ">Bind</span><span class="pln">   </span><span class="typ">Vis</span><span class="pln">      </span><span class="typ">Ndx</span><span class="pln"> </span><span class="typ">Name</span><span class="pln">
    </span><span class="lit">0</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  LOCAL  DEFAULT  UND
    </span><span class="lit">1</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000de0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> OBJECT  GLOBAL DEFAULT  ABS _DYNAMIC
    </span><span class="lit">2</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">   </span><span class="lit">176</span><span class="pln"> FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2</span><span class="pun">.</span><span class="lit">2</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">)</span><span class="pln">
    </span><span class="lit">3</span><span class="pun">:</span><span class="pln"> </span><span class="lit">600000000000109c</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  GLOBAL DEFAULT  ABS __bss_start
    </span><span class="lit">4</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">   </span><span class="lit">704</span><span class="pln"> FUNC    GLOBAL DEFAULT  UND exit@GLIBC_2</span><span class="pun">.</span><span class="lit">2</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">)</span><span class="pln">
    </span><span class="lit">5</span><span class="pun">:</span><span class="pln"> </span><span class="lit">600000000000109c</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  GLOBAL DEFAULT  ABS _edata
    </span><span class="lit">6</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000fe8</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> OBJECT  GLOBAL DEFAULT  ABS _GLOBAL_OFFSET_TABLE_     </span><span class="lit">7</span><span class="pun">:</span><span class="pln"> </span><span class="lit">60000000000010b0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  GLOBAL DEFAULT  ABS _end
    </span><span class="lit">8</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  WEAK   DEFAULT  UND </span><span class="typ">_Jv_RegisterClasses</span><span class="pln">
    </span><span class="lit">9</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">   </span><span class="lit">544</span><span class="pln"> FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2</span><span class="pun">.</span><span class="lit">2</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">)</span><span class="pln">
   </span><span class="lit">10</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  WEAK   DEFAULT  UND __gmon_start__

</span><span class="typ">Symbol</span><span class="pln"> table </span><span class="str">'.symtab'</span><span class="pln"> contains </span><span class="lit">127</span><span class="pln"> entries</span><span class="pun">:</span><span class="pln">
  </span><span class="typ">Num</span><span class="pun">:</span><span class="pln">    </span><span class="typ">Value</span><span class="pln">          </span><span class="typ">Size</span><span class="pln"> </span><span class="typ">Type</span><span class="pln">    </span><span class="typ">Bind</span><span class="pln">   </span><span class="typ">Vis</span><span class="pln">      </span><span class="typ">Ndx</span><span class="pln"> </span><span class="typ">Name</span><span class="pln">
    </span><span class="lit">0</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  LOCAL  DEFAULT  UND
    </span><span class="lit">1</span><span class="pun">:</span><span class="pln"> </span><span class="lit">40000000000001c8</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">1</span><span class="pln">
    </span><span class="lit">2</span><span class="pun">:</span><span class="pln"> </span><span class="lit">40000000000001e0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">2</span><span class="pln">
    </span><span class="lit">3</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4000000000000200</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">3</span><span class="pln">
    </span><span class="lit">4</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4000000000000240</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">4</span><span class="pln">
    </span><span class="lit">5</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4000000000000348</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">5</span><span class="pln">
    </span><span class="lit">6</span><span class="pun">:</span><span class="pln"> </span><span class="lit">40000000000003d8</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">6</span><span class="pln">
    </span><span class="lit">7</span><span class="pun">:</span><span class="pln"> </span><span class="lit">40000000000003f0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">7</span><span class="pln">
    </span><span class="lit">8</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4000000000000410</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">8</span><span class="pln">
    </span><span class="lit">9</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4000000000000440</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT    </span><span class="lit">9</span><span class="pln">
    </span><span class="lit">10</span><span class="pun">:</span><span class="pln"> </span><span class="lit">40000000000004a0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">10</span><span class="pln">
    </span><span class="lit">11</span><span class="pun">:</span><span class="pln"> </span><span class="lit">40000000000004e0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">11</span><span class="pln">
    </span><span class="lit">12</span><span class="pun">:</span><span class="pln"> </span><span class="lit">40000000000005e0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">12</span><span class="pln">
    </span><span class="lit">13</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4000000000000b00</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">13</span><span class="pln">
    </span><span class="lit">14</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4000000000000b40</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">14</span><span class="pln">
    </span><span class="lit">15</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4000000000000b60</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">15</span><span class="pln">
    </span><span class="lit">16</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4000000000000bd0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">16</span><span class="pln">
    </span><span class="lit">17</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4000000000000ce0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">17</span><span class="pln">
    </span><span class="lit">18</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000db8</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">18</span><span class="pln">
    </span><span class="lit">19</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000dd0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">19</span><span class="pln">
    </span><span class="lit">20</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000dd8</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">20</span><span class="pln">
    </span><span class="lit">21</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000de0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">21</span><span class="pln">
    </span><span class="lit">22</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000fc0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">22</span><span class="pln">
    </span><span class="lit">23</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000fd0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">23</span><span class="pln">
    </span><span class="lit">24</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000fe0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">24</span><span class="pln">
    </span><span class="lit">25</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000fe8</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">25</span><span class="pln">
    </span><span class="lit">26</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000001040</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">26</span><span class="pln">
    </span><span class="lit">27</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000001080</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">27</span><span class="pln">
    </span><span class="lit">28</span><span class="pun">:</span><span class="pln"> </span><span class="lit">60000000000010a0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">28</span><span class="pln">
    </span><span class="lit">29</span><span class="pun">:</span><span class="pln"> </span><span class="lit">60000000000010a8</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">29</span><span class="pln">
    </span><span class="lit">30</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">30</span><span class="pln">
    </span><span class="lit">31</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">31</span><span class="pln">
    </span><span class="lit">32</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">32</span><span class="pln">
    </span><span class="lit">33</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">33</span><span class="pln">
    </span><span class="lit">34</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">34</span><span class="pln">
    </span><span class="lit">35</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">35</span><span class="pln">
    </span><span class="lit">36</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">36</span><span class="pln">
    </span><span class="lit">37</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">37</span><span class="pln">
    </span><span class="lit">38</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">38</span><span class="pln">
    </span><span class="lit">39</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> SECTION LOCAL  DEFAULT   </span><span class="lit">39</span><span class="pln">
    </span><span class="lit">40</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">41</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">42</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">43</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">44</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">45</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">46</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">&lt;</span><span class="pln">command line</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="lit">47</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">48</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">&lt;</span><span class="pln">command line</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="lit">49</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">&lt;</span><span class="pln">built</span><span class="pun">-</span><span class="pln">in</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="lit">50</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS abi</span><span class="pun">-</span><span class="pln">note</span><span class="pun">.</span><span class="pln">S
    </span><span class="lit">51</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">52</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS abi</span><span class="pun">-</span><span class="pln">note</span><span class="pun">.</span><span class="pln">S
    </span><span class="lit">53</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">54</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS abi</span><span class="pun">-</span><span class="pln">note</span><span class="pun">.</span><span class="pln">S
    </span><span class="lit">55</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">&lt;</span><span class="pln">command line</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="lit">56</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">57</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">&lt;</span><span class="pln">command line</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="lit">58</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">&lt;</span><span class="pln">built</span><span class="pun">-</span><span class="pln">in</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="lit">59</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS abi</span><span class="pun">-</span><span class="pln">note</span><span class="pun">.</span><span class="pln">S
    </span><span class="lit">60</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS init</span><span class="pun">.</span><span class="pln">c
    </span><span class="lit">61</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">62</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">63</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS initfini</span><span class="pun">.</span><span class="pln">c
    </span><span class="lit">64</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">65</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">&lt;</span><span class="pln">command line</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="lit">66</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">67</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">&lt;</span><span class="pln">command line</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="lit">68</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">&lt;</span><span class="pln">built</span><span class="pun">-</span><span class="pln">in</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="lit">69</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">70</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4000000000000670</span><span class="pln">   </span><span class="lit">128</span><span class="pln"> FUNC    LOCAL  DEFAULT   </span><span class="lit">12</span><span class="pln"> gmon_initializer
    </span><span class="lit">71</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">72</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">73</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS initfini</span><span class="pun">.</span><span class="pln">c
    </span><span class="lit">74</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">75</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">&lt;</span><span class="pln">command line</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="lit">76</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">77</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">&lt;</span><span class="pln">command line</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="lit">78</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">&lt;</span><span class="pln">built</span><span class="pun">-</span><span class="pln">in</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="lit">79</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">buildd</span><span class="pun">/</span><span class="pln">glibc</span><span class="pun">-</span><span class="lit">2.3</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">80</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="kwd">auto</span><span class="pun">-</span><span class="pln">host</span><span class="pun">.</span><span class="pln">h
    </span><span class="lit">81</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">&lt;</span><span class="pln">command line</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="lit">82</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">&lt;</span><span class="pln">built</span><span class="pun">-</span><span class="pln">in</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="lit">83</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000fc0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  LOCAL  DEFAULT   </span><span class="lit">22</span><span class="pln"> __CTOR_LIST__
    </span><span class="lit">84</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000fd0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  LOCAL  DEFAULT   </span><span class="lit">23</span><span class="pln"> __DTOR_LIST__
    </span><span class="lit">85</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000fe0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  LOCAL  DEFAULT   </span><span class="lit">24</span><span class="pln"> __JCR_LIST__
    </span><span class="lit">86</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000001088</span><span class="pln">     </span><span class="lit">8</span><span class="pln"> OBJECT  LOCAL  DEFAULT   </span><span class="lit">27</span><span class="pln"> dtor_ptr
    </span><span class="lit">87</span><span class="pun">:</span><span class="pln"> </span><span class="lit">40000000000006f0</span><span class="pln">   </span><span class="lit">128</span><span class="pln"> FUNC    LOCAL  DEFAULT   </span><span class="lit">12</span><span class="pln"> __do_global_dtors_aux    
    </span><span class="lit">88</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4000000000000770</span><span class="pln">   </span><span class="lit">128</span><span class="pln"> FUNC    LOCAL  DEFAULT   </span><span class="lit">12</span><span class="pln"> __do_jv_register_classes
    </span><span class="lit">89</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS hello</span><span class="pun">.</span><span class="pln">c
    </span><span class="lit">90</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000001090</span><span class="pln">     </span><span class="lit">4</span><span class="pln"> OBJECT  LOCAL  DEFAULT   </span><span class="lit">27</span><span class="pln"> i
    </span><span class="lit">91</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS function</span><span class="pun">.</span><span class="pln">c
    </span><span class="lit">92</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000001098</span><span class="pln">     </span><span class="lit">4</span><span class="pln"> OBJECT  LOCAL  DEFAULT   </span><span class="lit">27</span><span class="pln"> i
    </span><span class="lit">93</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="kwd">auto</span><span class="pun">-</span><span class="pln">host</span><span class="pun">.</span><span class="pln">h
    </span><span class="lit">94</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">&lt;</span><span class="pln">command line</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="lit">95</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln">    LOCAL  DEFAULT  ABS </span><span class="pun">&lt;</span><span class="pln">built</span><span class="pun">-</span><span class="pln">in</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="lit">96</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000fc8</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  LOCAL  DEFAULT   </span><span class="lit">22</span><span class="pln"> __CTOR_END__
    </span><span class="lit">97</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000fd8</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  LOCAL  DEFAULT   </span><span class="lit">23</span><span class="pln"> __DTOR_END__
    </span><span class="lit">98</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000fe0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  LOCAL  DEFAULT   </span><span class="lit">24</span><span class="pln"> __JCR_END__
    </span><span class="lit">99</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000de0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> OBJECT  GLOBAL DEFAULT  ABS _DYNAMIC
    </span><span class="lit">100</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4000000000000a70</span><span class="pln">   </span><span class="lit">144</span><span class="pln"> FUNC    GLOBAL HIDDEN   </span><span class="lit">12</span><span class="pln"> __do_global_ctors_aux
    </span><span class="lit">101</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000dd8</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  GLOBAL DEFAULT  ABS __fini_array_end
    </span><span class="lit">102</span><span class="pun">:</span><span class="pln"> </span><span class="lit">60000000000010a8</span><span class="pln">     </span><span class="lit">8</span><span class="pln"> OBJECT  GLOBAL HIDDEN   </span><span class="lit">29</span><span class="pln"> __dso_handle
    </span><span class="lit">103</span><span class="pun">:</span><span class="pln"> </span><span class="lit">40000000000009a0</span><span class="pln">   </span><span class="lit">208</span><span class="pln"> FUNC    GLOBAL DEFAULT   </span><span class="lit">12</span><span class="pln"> __libc_csu_fini
    </span><span class="lit">104</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">   </span><span class="lit">176</span><span class="pln"> FUNC    GLOBAL DEFAULT  UND printf@@GLIBC_2</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">105</span><span class="pun">:</span><span class="pln"> </span><span class="lit">40000000000004a0</span><span class="pln">    </span><span class="lit">32</span><span class="pln"> FUNC    GLOBAL DEFAULT   </span><span class="lit">10</span><span class="pln"> _init
    </span><span class="lit">106</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4000000000000850</span><span class="pln">   </span><span class="lit">128</span><span class="pln"> FUNC    GLOBAL DEFAULT   </span><span class="lit">12</span><span class="pln"> function
    </span><span class="lit">107</span><span class="pun">:</span><span class="pln"> </span><span class="lit">40000000000005e0</span><span class="pln">   </span><span class="lit">144</span><span class="pln"> FUNC    GLOBAL DEFAULT   </span><span class="lit">12</span><span class="pln"> _start
    </span><span class="lit">108</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000001094</span><span class="pln">     </span><span class="lit">4</span><span class="pln"> OBJECT  GLOBAL DEFAULT   </span><span class="lit">27</span><span class="pln"> global
    </span><span class="lit">109</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000dd0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  GLOBAL DEFAULT  ABS __fini_array_start
    </span><span class="lit">110</span><span class="pun">:</span><span class="pln"> </span><span class="lit">40000000000008d0</span><span class="pln">   </span><span class="lit">208</span><span class="pln"> FUNC    GLOBAL DEFAULT   </span><span class="lit">12</span><span class="pln"> __libc_csu_init
    </span><span class="lit">111</span><span class="pun">:</span><span class="pln"> </span><span class="lit">600000000000109c</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  GLOBAL DEFAULT  ABS __bss_start
    </span><span class="lit">112</span><span class="pun">:</span><span class="pln"> </span><span class="lit">40000000000007f0</span><span class="pln">    </span><span class="lit">96</span><span class="pln"> FUNC    GLOBAL DEFAULT   </span><span class="lit">12</span><span class="pln"> main
    </span><span class="lit">113</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000dd0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  GLOBAL DEFAULT  ABS __init_array_end
    </span><span class="lit">114</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000dd8</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  WEAK   DEFAULT   </span><span class="lit">20</span><span class="pln"> data_start
    </span><span class="lit">115</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4000000000000b00</span><span class="pln">    </span><span class="lit">32</span><span class="pln"> FUNC    GLOBAL DEFAULT   </span><span class="lit">13</span><span class="pln"> _fini
    </span><span class="lit">116</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">   </span><span class="lit">704</span><span class="pln"> FUNC    GLOBAL DEFAULT  UND exit@@GLIBC_2</span><span class="pun">.</span><span class="lit">2</span><span class="pln">
    </span><span class="lit">117</span><span class="pun">:</span><span class="pln"> </span><span class="lit">600000000000109c</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  GLOBAL DEFAULT  ABS _edata
    </span><span class="lit">118</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000fe8</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> OBJECT  GLOBAL DEFAULT  ABS _GLOBAL_OFFSET_TABLE_   
    </span><span class="lit">119</span><span class="pun">:</span><span class="pln"> </span><span class="lit">60000000000010b0</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  GLOBAL DEFAULT  ABS _end
    </span><span class="lit">120</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000db8</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  GLOBAL DEFAULT  ABS __init_array_start
    </span><span class="lit">121</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000001080</span><span class="pln">     </span><span class="lit">4</span><span class="pln"> OBJECT  GLOBAL DEFAULT   </span><span class="lit">27</span><span class="pln"> _IO_stdin_used
    </span><span class="lit">122</span><span class="pun">:</span><span class="pln"> </span><span class="lit">60000000000010a0</span><span class="pln">     </span><span class="lit">8</span><span class="pln"> OBJECT  GLOBAL DEFAULT   </span><span class="lit">28</span><span class="pln"> __libc_ia64_register_back
    </span><span class="lit">123</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6000000000000dd8</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  GLOBAL DEFAULT   </span><span class="lit">20</span><span class="pln"> __data_start
    </span><span class="lit">124</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  WEAK   DEFAULT  UND </span><span class="typ">_Jv_RegisterClasses</span><span class="pln">
    </span><span class="lit">125</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">   </span><span class="lit">544</span><span class="pln"> FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
    </span><span class="lit">126</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0000000000000000</span><span class="pln">     </span><span class="lit">0</span><span class="pln"> NOTYPE  WEAK   DEFAULT  UND __gmon_start__</span></pre>

<p>
	إليك بعض الأشياء التي يجب ملاحظتها:
</p>

<ul>
	<li>
		لاحظ طريقة بناء الملف القابل للتنفيذ السهلة.
	</li>
	<li>
		لاحظ وجود نوعين من جداول الرموز هما: <code>dynsym</code> و<code>symtab</code>. سنشرح كيفية عمل رموز <code>dynsym</code> لاحقًا، ولكن لاحظ أن بعضها يحمل الرمز <code>@</code>.
	</li>
	<li>
		لاحظ الرموز العديدة المُضمَّنة من ملفات الكائنات الإضافية، حيث يبدأ الكثير منها بالرمز <code>__</code> لتجنب التعارض مع الأسماء التي يختارها المبرمج. اقرأ واختر الرموز التي ذكرناها سابقًا من ملفات الكائنات واكتشف إن تغيرت بأيّ شكل من الأشكال.
	</li>
</ul>

<p>
	ترجمة -وبتصرُّف- للقسم <a href="https://www.bottomupcs.com/compilation_example.xhtml" rel="external nofollow">A practical example</a> من فصل <a href="https://www.bottomupcs.com/chapter06.xhtml" rel="external nofollow">The Toolchain</a> من كتاب <a href="https://www.bottomupcs.com/" rel="external nofollow">Computer Science from the Bottom Up</a> لصاحبه Ian Wienand.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/os-embedded-systems/%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D8%A7%D9%84%D9%82%D8%A7%D8%A8%D9%84%D8%A9-%D9%84%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D9%81%D9%8A-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%88%D8%AA%D9%85%D8%AB%D9%8A%D9%84%D9%87%D8%A7-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%B5%D9%8A%D8%BA%D8%A9-elf-r1941/" rel="">الملفات القابلة للتنفيذ في نظام التشغيل وتمثيلها باستخدام الصيغة ELF</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/advanced/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%85%D9%84%D9%81-%D9%82%D8%A7%D8%A8%D9%84-%D9%84%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-executable-file-%D9%85%D9%86-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%85%D8%B5%D8%AF%D8%B1%D9%8A%D8%A9-source-code-r1930/" rel="">كيفية إنشاء ملف قابل للتنفيذ Executable File من شيفرة برمجية مصدرية Source Code</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/cpp/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%B5%D8%B1%D9%8A%D9%81-%D9%88%D8%A8%D9%86%D8%A7%D8%A1-%D8%A7%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D9%88%D8%A8%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-cpp-r1189/" rel="">كيفية تصريف وبناء البرامج المكتوبة بلغة Cpp</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/cpp/%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B5%D8%B1%D9%8A%D9%81-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%A7%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D9%88%D8%A8%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-cpp-%D9%88%D8%A3%D9%87%D9%85-%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D8%A8%D9%86%D8%A7%D8%A1-r1186/" rel="">أنظمة التصريف المستخدمة لبناء البرامج المكتوبة بلغة Cpp وأهم أخطاء عملية البناء</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1931</guid><pubDate>Thu, 23 Mar 2023 16:03:00 +0000</pubDate></item><item><title>&#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; &#x639;&#x645;&#x644;&#x64A;&#x629; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1831/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_12/639d98b508ad9_------C-.png.7eec03d4db7f92454321c5012a1b603d.png" /></p>

<p>
	بعد أن تكلمنا عن المكتبات القياسية المعرفة حسب المعيار، لم يبقَ إلا توضيح هيئة البرامج الكاملة المكتوبة بهذه اللغة، وسنتطرق في هذا المقال إلى بعض الأمثلة التي توضح كيفية جمع هذه العناصر لبناء البرامج.
</p>

<p>
	إلا أن هناك بعض النقاط التي يجب مناقشتها في لغة سي قبل عرض هذه الأمثلة.
</p>

<h2>
	وسطاء الدالة main
</h2>

<p>
	تقدم وسطاء الدالة <code>main</code> فرصةً مفيدةً لكل من يكتب البرامج ويشغلها في بيئة مُستضافة hosted environment وذلك بإعطاء المعاملات للبرنامج، ويُستخدم ذلك عادةً لتوجيه البرنامج لكيفية تنفيذ مهامه، ومن الشائع أن تكون هذه المعاملات هي أسماء ملفات تُمرّر مثل وسيط.
</p>

<p>
	يبدو تصريح الدالة <code>main</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9451_6" style="">
<span class="typ">int</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> argc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">argv</span><span class="pun">[]);</span></pre>

<p>
	يُشير التصريح إلى أن الدالة <code>main</code> تُعيد عددًا صحيحًا، وتُمرّر هذه القيمة عادةً، أو حالة الخروج exit status في البيئات المُستضافة، مثل أنظمة دوس DOS أو يونيكس UNIX إلى مفسّر <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر الأوامر</a> command line interpreter، فعلى سبيل المثال تُستخدم حالة الخروج في نظام يونيكس للدلالة على إتمام البرنامج لمهمته بنجاح (تمثلّه القيمة صفر)، أو حدوث خطأ أثناء التنفيذ (تمثًله قيمة غير صفرية). اتّبع المعيار هذا الاصطلاح أيضًا، إذ تُستخدم <code>exit(0)‎</code> لإعادة حالة النجاح إلى البيئة المُستضافة وأي قيمة أخرى تدلّ على حدوث خطأ ما، وستُترجم <code>exit</code> القيمة لمعناها إذا كانت البيئة المستضافة تستخدم اصطلاحًا مخالفًا. بما أن الترجمة معرفة حسب التنفيذ، فمن الأفضل استخدام القيمتان المُعرّفتان في ملف الترويسة <code>&lt;stdlib.h&gt;</code>، وهما: <code>EXIT_SUCCESS</code> و <code>EXIT_FAILURE</code>.
</p>

<p>
	هناك على الأقل وسيطان للدالة <code>main</code>، وهُما: <code>argc</code> و <code>argv</code>، إذ يدل أولهما على عدد الوسطاء المزودة للبرنامج، بينما يدل الثاني على <a href="https://academy.hsoub.com/programming/c/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1675/" rel="">مصفوفة </a>من <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-pointers-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1682/" rel="">المؤشرات</a> تشير إلى <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">سلاسل نصية</a> تمثّل الوسطاء، وهي من النوع "مصفوفة من المؤشرات تشير إلى محرف <code>char</code>"، وتُمرّر هذه الوسطاء إلى البرنامج باستخدام مفسّر سطر الأوامر الخاص بالبيئة المُستضافة، أو لغة التحكم بالوظائف job control language.
</p>

<p>
	يُعدّ تصريح الوسيط <code>argv</code> أوّل مصادفة للمبرمجين المبتدئين مع المؤشرات التي تشير إلى مصفوفات من المؤشرات، وقد يكون هذا محيّرًا بعض الشيء في البداية، إلا أن الأمر بسيط الفهم. بما أن <code>argv</code> تُستخدم للإشارة إلى مصفوفة السلاسل النصية، يكون تصريحها على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9451_14" style="">
<span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">argv</span><span class="pun">[]</span></pre>

<p>
	تذكر أيضًا أن اسم المصفوفة يُحوّل إلى عنوان أول عنصر ضمنها عندما تُمرّر إلى دالة، وهذا يعني أنه يمكننا التصريح عن <code>argv</code> كما يلي: <code>char **argv</code> والتصريحان يؤديان الغرض ذاته في هذه الحالة.
</p>

<p>
	سترى تصريح الدالة <code>main</code> مكتوبًا بهذه الطريقة معظم الأحيان، والتصريح التالي مكافئ للتصريح السابق:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9451_16" style="">
<span class="typ">int</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> argc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">**</span><span class="pln">argv</span><span class="pun">);</span></pre>

<p>
	تُهيًّأ وسطاء الدالة <code>main</code> عند بداية تشغيل البرنامج على نحوٍ موافق للشروط التالية:
</p>

<ul>
<li>
		الوسيط <code>argc</code> أكبر من الصفر.
	</li>
	<li>
		يمثّل <code>argv[argc]‎</code> مؤشرًا فارغًا null.
	</li>
	<li>
		تمثّل العناصر بدءًا من <code>argv[0]‎</code> وصولًا إلى <code>argv[argc-1]‎</code> مؤشرات تشير إلى سلاسل نصية يُحدِّد البرنامج معناها.
	</li>
	<li>
		يحتوي العنصر <code>argv[0]‎</code> السلسلة النصية التي تحتوي اسم البرنامج أو سلسلة نصية فارغة إذا لم تكن هذه المعلومة متاحة، وتمثل العناصر المتبقية من <code>argv</code> الوسطاء المزودة للبرنامج. يُزوّد محتوى السلاسل النصية إلى البرنامج بحالة الأحرف الصغيرة lower-case في حال توفر الدعم فقط للأحرف الوحيدة single.
	</li>
</ul>
<p>
	لتوضيح هذه النقطة، إليك مثالًا عن برنامج يكتب وسطاء الدالة <code>main</code> إلى خرج البرنامج القياسي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9451_18" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> argc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">**</span><span class="pln">argv</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">argc</span><span class="pun">--)</span><span class="pln">
                printf</span><span class="pun">(</span><span class="str">"%s\n"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">argv</span><span class="pun">++);</span><span class="pln">
        exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 1]
</p>

<p>
	إذا كان اسم البرنامج <code>show_args</code> وكانت وسطاءه <code>abcde</code> و <code>text</code> و <code>hello</code> عند تشغيله، ستكون حالة الوسطاء وقيمة <code>argv</code> موضّحة في الشكل التالي:
</p>

<p style="text-align: center;">
	<img alt="حالة الوسطاء وقيمة argv" class="ipsImage ipsImage_thumbnailed" data-fileid="114300" data-unique="qudok3y34" src="https://academy.hsoub.com/uploads/monthly_2022_12/014Arguments_to_a_program.png.3a6d9129d62021a0ac334d54b80e1b81.png" style="width: 396px; height: auto;"></p>

<p style="text-align: center;">
	[شكل 1 وسطاء البرنامج]
</p>

<p>
	تنتقل <code>argv</code> إلى العنصر التالي عند كل زيادة لها، وبالتالي وبعد أول تكرار للحلقة ستُشير <code>argv</code> إلى المؤشر الذي بدوره يشير إلى الوسيط <code>abcde</code>، وهذا الأمر موضح بالشكل التالي:
</p>

<p style="text-align: center;">
	<img alt="الوسيط abcde" class="ipsImage ipsImage_thumbnailed" data-fileid="114301" data-unique="zoixmw6ek" src="https://academy.hsoub.com/uploads/monthly_2022_12/015Arguments_to_a_program_after_incrementing_argv.png.71734f912c04fabe37206b69d879eca6.png" style="width: 396px; height: auto;"></p>

<p style="text-align: center;">
	[شكل 2 وسطاء البرنامج بعد زيادة <code>argv</code>]
</p>

<p>
	سيعمل البرنامج على النظام الذي جرّبنا فيه البرنامج السابق عن طريق كتابة اسمه، ثم كتابة وسطاءه وفصلهم بمسافات فارغة. إليك ما الذي يحدث (الرمز <code>$</code> هو رمز الطرفية):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9451_25" style="">
<span class="pln">$ show_args abcde text hello
show_args
abcde
text
hello
$</span></pre>

<h2>
	تفسير وسطاء البرنامج
</h2>

<p>
	الحلقة المُستخدمة لفحص وسطاء البرنامج في المثال السابق شائعة الاستخدام في <a href="https://academy.hsoub.com/programming/c/%D8%A8%D9%86%D9%8A%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1607/" rel="">برامج سي C</a> وستجدها في العديد من البرامج الأخرى، ويُعد استخدام "الخيارات options" للتحكم بسلوك البرنامج طريقةً شائعة أيضًا (تُدعى أيضًا في بعض الأحيان المُبدّلات switches أو الرايات flags)، إذ يدل الوسيط الذي يبدأ بالمحرف <code>-</code> على أنه وسيط يقدّم حرفًا وحيدًا أو أكثر يشير إلى خيار، ويمكن تشغيل الخيارات سويًا أو على نحوٍ منفرد:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9451_28" style="">
<span class="pln">progname </span><span class="pun">-</span><span class="pln">abxu file1 file2
progname </span><span class="pun">-</span><span class="pln">a </span><span class="pun">-</span><span class="pln">b </span><span class="pun">-</span><span class="pln">x </span><span class="pun">-</span><span class="pln">u file1 file2</span></pre>

<p>
	يحدّد كلًا من الخيارات جانبًا معينًا من مزايا البرنامج، وقد يُسمح لكل خيار بأخذ وسيط خاص به امتدادًا لهذه الفكرة، فعلى سبيل المثال إذا كان الخيار <code>‎-x</code> يأخذ وسيطًا خاصًا به، سيبدو ذلك على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9451_30" style="">
<span class="pln">progname </span><span class="pun">-</span><span class="pln">x arg file1</span></pre>

<p>
	وبذلك، فإن <code>arg</code> مرتبطة مع الخيار. تسمح لنا دالة <code>options</code> في الأسفل بأتمتة معالجة أسلوب الاستخدام هذا عن طريق الدعم الإضافي (شائع الاستخدام إلا أنه قد عفا عليه الزمن) لإمكانية تقديم خيار الوسيط مباشرةً بعد حرف الخيار كما يلي:
</p>

<pre class="ipsCode">
progname -xarg file1
</pre>

<p>
	تُعيد برامج الخيارات السابقة في كلٍّ من الحالتين المحرف 'x' وتضبط المؤشر العام global المسمى <code>OptArg</code> ليشير إلى القيمة <code>arg</code>.
</p>

<p>
	يجب أن يقدم البرنامج لائحةً من أحرف الخيارات الصالحة بهيئة سلسلة نصية حتى نستطيع استخدام برامج الخيارات، عندما يُلحق حرفٌ ضمن هذه السلسلة النصية بالنقطتين ':'، فهذا يعني أن ما يتبع حرف الخيار هو وسيط. يُستدعى برنامج <code>options</code> مرارًا عند تشغيل البرنامج حتى انتهاء أحرف الخيار.
</p>

<p>
	يبدو أن الدوال التي تقرأ السلاسل النصية وتبحث عن تشكيلات مختلفة أو أنماط ضمن السلسلة صعبة القراءة، وإن كان في ذلك عزاءً لكن ليست عملية القراءة بتلك البساطة فعليًا، والشيفرة البرمجية التي تطبّق الخيارات هي واحدةٌ من هذه الدوال، إلا أنها ليست الأصعب ضمن هذا التصنيف.
</p>

<p>
	تفحص الدالة <code>options()‎</code> أحرف الخيار ووسطاء الخيار من قائمة <code>argv</code>، وتُعيد استدعاءات متتابعة للدالة أحرف خيار متتابعة متوافقة مع واحدة من بنود القائمة <code>legal</code>. قد تتطلب أحرف الخيار وسطاء خيار ويُشار إلى ذلك بالنقطتين ':' اللتين تتبعان الحرف في القائمة <code>legal</code>. على سبيل المثال، تشير لائحة <code>legal</code> التي تحتوي على "ab:c" على أن <code>a</code> و <code>b</code> و <code>c</code> جميعها خيارات صالحة وأن <code>b</code> تأخذ وسيط خيار، ويُمرّر وسيط الخيار فيما بعد إلى الدالة التي استُدعيت سابقًا في قيمة المؤشر العام المُسمّى <code>OptArg</code>. يُعطي <code>OptIndex</code> السلسلة النصية التالية في مصفوفة <code>argv[]‎</code> التي لم تُعالج بعد من قبل الدالة <code>options()‎</code>.
</p>

<p>
	تُعيد الدالة <code>options()‎</code> القيمة ‎-1 إذا لم يكُن هناك أي أحرف خيار أخرى، أو إذا عُثر على <code>SwitchChar</code> مضاعف، ويُجبر ذلك الدالة <code>options()‎</code> على إنهاء عملية معالجة الخيارات؛ بينما تُعيد <code>?</code> إذا كان هناك خيار لا ينتمي إلى مجموعة <code>legal</code>، أو إذا عُثر على خيار ما يحتاج لوسيط دون وجود وسيط يتبعه.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9451_32" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;string.h&gt;</span><span class="pln">

</span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="typ">SwitchChar</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'-'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="typ">Unknown</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'?'</span><span class="pun">;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> </span><span class="typ">OptIndex</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">       </span><span class="com">// يجب أن يكون أول خيار هو‫ argv‫[‫1] </span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">OptArg</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> NULL</span><span class="pun">;</span><span class="pln">    </span><span class="com">// مؤشر عام لوسيط الخيار </span><span class="pln">

</span><span class="typ">int</span><span class="pln"> options</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> argc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">argv</span><span class="pun">[],</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">legal</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">posn </span><span class="pun">=</span><span class="pln"> </span><span class="str">""</span><span class="pun">;</span><span class="pln">  </span><span class="com">// ‫الموضع في  argv‫[‫OptIndex]‫</span><span class="pln">
        </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">legal_index </span><span class="pun">=</span><span class="pln"> NULL</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> letter </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(!*</span><span class="pln">posn</span><span class="pun">){</span><span class="pln">
    </span><span class="com">// ‫لا يوجد المزيد من args أو SwitchChar أو حرف خيار</span><span class="pln">
                               </span><span class="kwd">if</span><span class="pun">((</span><span class="typ">OptIndex</span><span class="pln"> </span><span class="pun">&gt;=</span><span class="pln"> argc</span><span class="pun">)</span><span class="pln"> </span><span class="pun">||</span><span class="pln">
                        </span><span class="pun">(*(</span><span class="pln">posn </span><span class="pun">=</span><span class="pln"> argv</span><span class="pun">[</span><span class="typ">OptIndex</span><span class="pun">])</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="typ">SwitchChar</span><span class="pun">)</span><span class="pln"> </span><span class="pun">||</span><span class="pln">
                        </span><span class="pun">!*++</span><span class="pln">posn</span><span class="pun">)</span><span class="pln">
                                </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
                </span><span class="com">// ‫إيجاد SwitchChar مضاعف </span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">(*</span><span class="pln">posn </span><span class="pun">==</span><span class="pln"> </span><span class="typ">SwitchChar</span><span class="pun">){</span><span class="pln">
                        </span><span class="typ">OptIndex</span><span class="pun">++;</span><span class="pln">
                        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        letter </span><span class="pun">=</span><span class="pln"> </span><span class="pun">*</span><span class="pln">posn</span><span class="pun">++;</span><span class="pln">
        </span><span class="kwd">if</span><span class="pun">(!(</span><span class="pln">legal_index </span><span class="pun">=</span><span class="pln"> strchr</span><span class="pun">(</span><span class="pln">legal</span><span class="pun">,</span><span class="pln"> letter</span><span class="pun">))){</span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">(!*</span><span class="pln">posn</span><span class="pun">)</span><span class="pln">
                        </span><span class="typ">OptIndex</span><span class="pun">++;</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Unknown</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="kwd">if</span><span class="pun">(*++</span><span class="pln">legal_index </span><span class="pun">!=</span><span class="pln"> </span><span class="str">':'</span><span class="pun">){</span><span class="pln">
                </span><span class="com">/*لا يوجد وسيط للخيار */</span><span class="pln">
                </span><span class="typ">OptArg</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> NULL</span><span class="pun">;</span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">(!*</span><span class="pln">posn</span><span class="pun">)</span><span class="pln">
                        </span><span class="typ">OptIndex</span><span class="pun">++;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">(*</span><span class="pln">posn</span><span class="pun">)</span><span class="pln">
                        </span><span class="com">// ‫لا يوجد مسافة فارغة بين opt و opt arg </span><span class="pln">
                        </span><span class="typ">OptArg</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> posn</span><span class="pun">;</span><span class="pln">
                </span><span class="kwd">else</span><span class="pln">
                        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">argc </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="pun">++</span><span class="typ">OptIndex</span><span class="pun">){</span><span class="pln">
                                posn </span><span class="pun">=</span><span class="pln"> </span><span class="str">""</span><span class="pun">;</span><span class="pln">
                                </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Unknown</span><span class="pun">;</span><span class="pln">
                        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln">
                                </span><span class="typ">OptArg</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> argv</span><span class="pun">[</span><span class="typ">OptIndex</span><span class="pun">];</span><span class="pln">
                posn </span><span class="pun">=</span><span class="pln"> </span><span class="str">""</span><span class="pun">;</span><span class="pln">
                </span><span class="typ">OptIndex</span><span class="pun">++;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> letter</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 2]
</p>

<h2>
	برنامج لإيجاد الأنماط
</h2>

<p>
	نقدّم في هذا القسم برنامجًا كاملًا يستخدم أحرف الخيار مثل وسطاء للبرنامج بهدف التحكم بطريقة عمله.
</p>

<p>
	يُعالج البرنامج أولًا أي وسيط يمثل خيارًا، ويُحفظ الوسيط الأول -ليس خيار- بكونه "سلسلة بحث نصية search string"، في حين تُستخدم أي وسطاء أخرى متبقية لتحديد أسماء الملفات التي يجب أن تُقرأ دخلًا للبرنامج، وإذا لم يُعثر على أي اسم ملف فسيقرأ البرنامج من دخله القياسي بدلًا من ذلك، وإذا وُجد تطابق لسلسلة البحث النصية ضمن سطر من نص الدخل، يُطبع كامل السطر على الخرج القياسي.
</p>

<p>
	تُستخدم الدالة <code>options</code> لمعالجة جميع أحرف الخيار المزودة للبرنامج، ويميّز برنامجنا هنا خمسة خيارات، هي: <code>‎-c</code> و <code>‎-i</code> و <code>‎-l</code> و <code>‎-n</code> و <code>‎-v</code>، ولا يُشترط لأي من الخيارات السابقة أن تُتبع بوسيط اختياري. يحدد الخيار -أو الخيارات- سلوك البرنامج عند تشغيله على النحو التالي:
</p>

<ul>
<li>
		الخيار <code>‎-c</code>: يطبع البرنامج عدد الأسطر الكلية الموافقة لسلسلة البحث النصية التي عُثر عليها في ملف -أو ملفات- الدخل، ولا تُطبع أي أسطر نصية.
	</li>
	<li>
		الخيار <code>‎-i</code>: تُتجاهل حالة الأحرف لكل من سطر ملف الدخل وسلسلة البحث النصية عند البحث عن تطابق بينها.
	</li>
	<li>
		الخيار <code>‎-l</code>: يُطبع كل سطر نصي على الخرج مسبوقًا برقم السطر المفحوص في ملف الدخل الحالي.
	</li>
	<li>
		الخيار <code>‎-n</code>: يُطبع كل سطر نصي على الخرج مسبوقًا باسم الملف الذي يحتوي هذا السطر.
	</li>
	<li>
		الخيار <code>‎-v</code>: يطبع البرنامج الأسطر فقط دون مطابقة سلسلة البحث النصية المزودة.
	</li>
</ul>
<p>
	يُعيد البرنامج بعد الانتهاء من تنفيذه حالةً تدل على واحدة من الحالات التالية:
</p>

<ul>
<li>
		الحالة <code>EXIT_SUCCESS</code>: عُثر على تطابق واحد على الأقل.
	</li>
	<li>
		الحالة <code>EXIT_FAILURE</code>: لم يُعثر على أي تطابق، أو حدث خطأ ما.
	</li>
</ul>
<p>
	يعتمد البرنامج جدًا على دوال المكتبة القياسية لإنجاز الجزء الأكبر من العمل، فعلى سبيل المثال تُعالج جميع الملفات باستخدام دوال <code>stdio</code>. لاحظ اعتماد جوهر البرنامج أيضًا على مطابقة السلاسل النصية باستخدام استدعاءات لدالة <code>strstr</code>.
</p>

<p>
	إليك الشيفرة البرمجية الكاملة الخاصة بالبرنامج. حتى يعمل البرنامج، عليك طبعًا تصريفه مع الشيفرة البرمجية الخاصة به التي تعالج أحرف الخيارات، والتي استعرضناها سابقًا.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9451_41" style="">
<span class="com">/*
برنامج بسيط يطبع الأسطر من ملف نصي بحيث يحوي ذلك السطر الكلمة المزودة في سطر الأوامر
*/</span><span class="pln">

</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;string.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;ctype.h&gt;</span><span class="pln">

</span><span class="com">/*
* تصاريح لبرنامج الأنماط
*
*/</span><span class="pln">

</span><span class="com">#define</span><span class="pln"> CFLAG </span><span class="lit">0x001</span><span class="pln">     </span><span class="com">// احصِ عدد الأسطر المتطابقة فقط</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> IFLAG </span><span class="lit">0x002</span><span class="pln">     </span><span class="com">// تجاهل حالة الأحرف </span><span class="pln">
</span><span class="com">#define</span><span class="pln"> LFLAG </span><span class="lit">0x004</span><span class="pln">     </span><span class="com">// اعرض رقم السطر</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> NFLAG </span><span class="lit">0x008</span><span class="pln">     </span><span class="com">// اعرض اسماء ملفات الدخل</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> VFLAG </span><span class="lit">0x010</span><span class="pln">     </span><span class="com">// اعرض السطور التي لاتتطابق</span><span class="pln">

</span><span class="kwd">extern</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="typ">OptIndex</span><span class="pun">;</span><span class="pln">    </span><span class="com">// الدليل الحالي للمصفوفة‫  ‫argv‫[‎]</span><span class="pln">
</span><span class="kwd">extern</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">OptArg</span><span class="pun">;</span><span class="pln">    </span><span class="com">/* مؤشر وسيط الخيار العام

/*
* ‫جلب وسطاء سطر الأوامر إلى الدالة main‫()
*/</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> options</span><span class="pun">(</span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">**,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*);</span><span class="pln">

</span><span class="com">/*
تسجيل الخيارات المطلوبة للتحكم بسلوك البرنامج
*/</span><span class="pln">

</span><span class="kwd">unsigned</span><span class="pln"> set_flags</span><span class="pun">(</span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">**,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*);</span><span class="pln">

</span><span class="com">/*
تفقد كل سطر من الدخل لحالة المطابقة
*/</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> look_in</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*,</span><span class="pln"> </span><span class="kwd">unsigned</span><span class="pun">);</span><span class="pln">

</span><span class="com">/*
اطبع سطرًا من ملف الدخل إلى الخرج القياسي بالتنسيق المُحدد بواسطة خيارات سطر الأوامر
*/</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> print_line</span><span class="pun">(</span><span class="kwd">unsigned</span><span class="pln"> mask</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fname</span><span class="pun">,</span><span class="pln">
                </span><span class="typ">int</span><span class="pln"> lnno</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">text</span><span class="pun">);</span><span class="pln">


</span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln">
                </span><span class="com">/* الخيارات الممكنة للنمط */</span><span class="pln">
        </span><span class="pun">*</span><span class="typ">OptString</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"cilnv"</span><span class="pun">,</span><span class="pln">
                </span><span class="com">/*الرسالة التي ستُعرض عندما تُدخل الخيارات بصورةٍ غير صحيحة */</span><span class="pln">
        </span><span class="pun">*</span><span class="pln">errmssg </span><span class="pun">=</span><span class="pln"> </span><span class="str">"usage: pattern [-cilnv] word [filename]\n"</span><span class="pun">;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> argc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">argv</span><span class="pun">[])</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">unsigned</span><span class="pln"> flags </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> success </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">search_string</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">argc </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">2</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln"> errmssg</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        flags </span><span class="pun">=</span><span class="pln"> set_flags</span><span class="pun">(</span><span class="pln">argc</span><span class="pun">,</span><span class="pln"> argv</span><span class="pun">,</span><span class="pln"> </span><span class="typ">OptString</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">argv</span><span class="pun">[</span><span class="typ">OptIndex</span><span class="pun">])</span><span class="pln">
                search_string </span><span class="pun">=</span><span class="pln"> argv</span><span class="pun">[</span><span class="typ">OptIndex</span><span class="pun">++];</span><span class="pln">
        </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln"> errmssg</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">flags </span><span class="pun">&amp;</span><span class="pln"> IFLAG</span><span class="pun">){</span><span class="pln">
                </span><span class="com">/*تجاهل حالة الحرف والتعامل فقط مع الأحرف الصغيرة */</span><span class="pln">
                </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p</span><span class="pun">;</span><span class="pln">
                </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">p </span><span class="pun">=</span><span class="pln"> search_string </span><span class="pun">;</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p </span><span class="pun">;</span><span class="pln"> p</span><span class="pun">++)</span><span class="pln">
                        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">isupper</span><span class="pun">(*</span><span class="pln">p</span><span class="pun">))</span><span class="pln">
                                </span><span class="pun">*</span><span class="pln">p </span><span class="pun">=</span><span class="pln"> tolower</span><span class="pun">(*</span><span class="pln">p</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">argv</span><span class="pun">[</span><span class="typ">OptIndex</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">){</span><span class="pln">
                </span><span class="com">// لم يُزوّد أي اسم ملف، لذا نستخدم‫ stdin </span><span class="pln">
                success </span><span class="pun">=</span><span class="pln"> look_in</span><span class="pun">(</span><span class="pln">NULL</span><span class="pun">,</span><span class="pln"> search_string</span><span class="pun">,</span><span class="pln"> flags</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">argv</span><span class="pun">[</span><span class="typ">OptIndex</span><span class="pun">]</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> NULL</span><span class="pun">)</span><span class="pln">
                success </span><span class="pun">+=</span><span class="pln"> look_in</span><span class="pun">(</span><span class="pln">argv</span><span class="pun">[</span><span class="typ">OptIndex</span><span class="pun">++],</span><span class="pln">
                                search_string</span><span class="pun">,</span><span class="pln"> flags</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">flags </span><span class="pun">&amp;</span><span class="pln"> CFLAG</span><span class="pun">)</span><span class="pln">
                printf</span><span class="pun">(</span><span class="str">"%d\n"</span><span class="pun">,</span><span class="pln"> success</span><span class="pun">);</span><span class="pln">

        exit</span><span class="pun">(</span><span class="pln">success </span><span class="pun">?</span><span class="pln"> EXIT_SUCCESS </span><span class="pun">:</span><span class="pln"> EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">unsigned</span><span class="pln"> set_flags</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> argc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">**</span><span class="pln">argv</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">opts</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">unsigned</span><span class="pln"> flags </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> ch </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">while</span><span class="pun">((</span><span class="pln">ch </span><span class="pun">=</span><span class="pln"> options</span><span class="pun">(</span><span class="pln">argc</span><span class="pun">,</span><span class="pln"> argv</span><span class="pun">,</span><span class="pln"> opts</span><span class="pun">))</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">){</span><span class="pln">
                </span><span class="kwd">switch</span><span class="pun">(</span><span class="pln">ch</span><span class="pun">){</span><span class="pln">
                        </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'c'</span><span class="pun">:</span><span class="pln">
                                flags </span><span class="pun">|=</span><span class="pln"> CFLAG</span><span class="pun">;</span><span class="pln">
                                </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
                        </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'i'</span><span class="pun">:</span><span class="pln">
                                flags </span><span class="pun">|=</span><span class="pln"> IFLAG</span><span class="pun">;</span><span class="pln">
                                </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
                        </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'l'</span><span class="pun">:</span><span class="pln">
                                flags </span><span class="pun">|=</span><span class="pln"> LFLAG</span><span class="pun">;</span><span class="pln">
                                </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
                        </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'n'</span><span class="pun">:</span><span class="pln">
                                flags </span><span class="pun">|=</span><span class="pln"> NFLAG</span><span class="pun">;</span><span class="pln">
                                </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
                        </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'v'</span><span class="pun">:</span><span class="pln">
                                flags </span><span class="pun">|=</span><span class="pln"> VFLAG</span><span class="pun">;</span><span class="pln">
                                </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
                        </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'?'</span><span class="pun">:</span><span class="pln">
                                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln"> errmssg</span><span class="pun">);</span><span class="pln">
                                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> flags</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


</span><span class="typ">int</span><span class="pln"> look_in</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">infile</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">pat</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">unsigned</span><span class="pln"> flgs</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">in</span><span class="pun">;</span><span class="pln">
        </span><span class="com">/*
‫يخزن [0]line سطر الدخل كما يُقرأ
‫بينما يحول line[1]‎ السطر إلى حالة أحرف صغيرة إن لزم الأمر    
         */</span><span class="pln">
        </span><span class="kwd">char</span><span class="pln"> line</span><span class="pun">[</span><span class="lit">2</span><span class="pun">][</span><span class="pln">BUFSIZ</span><span class="pun">];</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> lineno </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> matches </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">infile</span><span class="pun">){</span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">((</span><span class="pln">in </span><span class="pun">=</span><span class="pln"> fopen</span><span class="pun">(</span><span class="pln">infile</span><span class="pun">,</span><span class="pln"> </span><span class="str">"r"</span><span class="pun">))</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">){</span><span class="pln">
                        perror</span><span class="pun">(</span><span class="str">"pattern"</span><span class="pun">);</span><span class="pln">
                        </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln">
                in </span><span class="pun">=</span><span class="pln"> stdin</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">fgets</span><span class="pun">(</span><span class="pln">line</span><span class="pun">[</span><span class="lit">0</span><span class="pun">],</span><span class="pln"> BUFSIZ</span><span class="pun">,</span><span class="pln"> in</span><span class="pun">)){</span><span class="pln">
                </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">line_to_use </span><span class="pun">=</span><span class="pln"> line</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">
                lineno</span><span class="pun">++;</span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">flgs </span><span class="pun">&amp;</span><span class="pln"> IFLAG</span><span class="pun">){</span><span class="pln">
                        </span><span class="com">/* حالة تجاهل */</span><span class="pln">
                        </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p</span><span class="pun">;</span><span class="pln">
                        strcpy</span><span class="pun">(</span><span class="pln">line</span><span class="pun">[</span><span class="lit">1</span><span class="pun">],</span><span class="pln"> line</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]);</span><span class="pln">
                        </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">p </span><span class="pun">=</span><span class="pln"> line</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">;</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p </span><span class="pun">;</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p</span><span class="pun">++)</span><span class="pln">
                                </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">isupper</span><span class="pun">(*</span><span class="pln">p</span><span class="pun">))</span><span class="pln">
                                        </span><span class="pun">*</span><span class="pln">p </span><span class="pun">=</span><span class="pln"> tolower</span><span class="pun">(*</span><span class="pln">p</span><span class="pun">);</span><span class="pln">
                        line_to_use </span><span class="pun">=</span><span class="pln"> line</span><span class="pun">[</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">

                </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">strstr</span><span class="pun">(</span><span class="pln">line_to_use</span><span class="pun">,</span><span class="pln"> pat</span><span class="pun">)){</span><span class="pln">
                        matches</span><span class="pun">++;</span><span class="pln">
                        </span><span class="kwd">if</span><span class="pun">(!(</span><span class="pln">flgs </span><span class="pun">&amp;</span><span class="pln"> VFLAG</span><span class="pun">))</span><span class="pln">
                                print_line</span><span class="pun">(</span><span class="pln">flgs</span><span class="pun">,</span><span class="pln"> infile</span><span class="pun">,</span><span class="pln"> lineno</span><span class="pun">,</span><span class="pln"> line</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]);</span><span class="pln">
                </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">flgs </span><span class="pun">&amp;</span><span class="pln"> VFLAG</span><span class="pun">)</span><span class="pln">
                        print_line</span><span class="pun">(</span><span class="pln">flgs</span><span class="pun">,</span><span class="pln"> infile</span><span class="pun">,</span><span class="pln"> lineno</span><span class="pun">,</span><span class="pln"> line</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        fclose</span><span class="pun">(</span><span class="pln">in</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> matches</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> print_line</span><span class="pun">(</span><span class="kwd">unsigned</span><span class="pln"> mask</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fname</span><span class="pun">,</span><span class="pln">
                        </span><span class="typ">int</span><span class="pln"> lnno</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">text</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">mask </span><span class="pun">&amp;</span><span class="pln"> CFLAG</span><span class="pun">)</span><span class="pln">
                </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">mask </span><span class="pun">&amp;</span><span class="pln"> NFLAG</span><span class="pun">)</span><span class="pln">
                printf</span><span class="pun">(</span><span class="str">"%s:"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fname </span><span class="pun">?</span><span class="pln"> fname </span><span class="pun">:</span><span class="pln"> </span><span class="str">"stdin"</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">mask </span><span class="pun">&amp;</span><span class="pln"> LFLAG</span><span class="pun">)</span><span class="pln">
                printf</span><span class="pun">(</span><span class="str">" %d :"</span><span class="pun">,</span><span class="pln"> lnno</span><span class="pun">);</span><span class="pln">
        printf</span><span class="pun">(</span><span class="str">"%s"</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 3]
</p>

<h2>
	مثال أكثر طموحا
</h2>

<p>
	أخيرًا نقدّم هنا مجموعةً من البرامج المصمّمة للتلاعب بملف بيانات وحيد والتعامل معه بطريقة مترابطة وسليمة؛ إذ تهدف هذه البرامج لمساعدتنا بتتبع نتائج عدة لاعبين يتنافسون مع بعضهم بعضًا في لعبةٍ ما، مثل الشطرنج، أو الإسكواش على سبيل المثال.
</p>

<p>
	يمتلك كل لاعب تصنيفًا من واحد إلى n، إذ تمثل n عدد اللاعبين الكلي وواحد تصنيف أعلى لاعب. يستطيع اللاعبون من تصنيف منخفض تحدي لاعبين آخرين فوق تصنيفهم وينتقل اللاعب إلى تصنيف اللاعب الآخر الأعلى منه إذا انتصر عليه، وفي هذه الحالة يُنقل اللاعب الخاسر وأي لاعبين آخرين بين اللاعب الخاسر والفائز إلى تصنيف واحد أقل، وتبقى التصنيفات مثل ما هي إن لم ينتصر اللاعب الأقل تصنيفًا.
</p>

<p>
	لتقديم بعض الضوابط للتوازن في التصنيف، يمكن لأي لاعب أن يتحدى لاعبًا أعلى منه تصنيفًا، إلا أن التحديات مع اللاعبين ذوي التصنيف الذي يزيد عن ثلاثة أو أقل مراتب هي الوحيدة التي ستسمح لهذا اللاعب بالتقدم في التصنيف، وهذا من شأنه أن يُجبر اللاعبين الجدُد المضافين إلى أسفل التصنيف أن يلعبوا أكثر من لعبة واحدة للوصول إلى أعلى التصنيف.
</p>

<p>
	هناك ثلاث مهام أساسية يجب تنفيذها للمحافظة على تتبع سليم لنتائج التصنيفات:
</p>

<ul>
<li>
		طباعة التصنيف.
	</li>
	<li>
		إضافة لاعبين جدُد.
	</li>
	<li>
		تسجيل النتائج.
	</li>
</ul>
<p>
	سيأخذ تصميم برنامجنا هنا صورة ثلاثة برامج جزئية لتنفيذ كل واحدة من هذه المهام على حدة، ومن الواضح بعد اتخاذنا لهذا القرار أن هناك عددًا من العمليات التي يحتاجها كل برنامج على نحوٍ مشترك بين البرامج الثلاث؛ فعلى سبيل المثال، ستحتاج البرامج الثلاثة إلى قراءة سجلات اللاعب من ملف البيانات وستحتاج اثنان من البرامج على الأقل لكتابة سجلات اللاعب إلى ملف البيانات.
</p>

<p>
	قد يكون الخيار الجيد هنا هو تصميم مكتبة من <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1646/" rel="">الدوال</a> التي تتلاعب بسجلات اللاعبين وملف البيانات، واستخدام هذه المكتبة مع البرامج الثلاثة للتعامل مع تصنيف اللاعبين، إلا أننا بحاجة تعريف هيكل البيانات الذي سيمثل سجلات اللاعب قبل ذلك. تتألف المعلومات الدنيا اللازمة لإنشاء سجل لكل لاعب من اسمه وتصنيفه، إلا أننا سنحتفظ بعدد التحديات التي فاز بها اللاعب، إضافةً للتحديات التي خسرها وآخر لعبة لعبها لمنح بعض الإمكانيات الإحصائية عند تشكيل لائحة التصنيف، ومن الواضح أن هذه المجموعة من المعلومات يجب تخزينها في هيكل ما.
</p>

<p>
	نجد التصريح عن هيكل اللاعب والتصريح عن دوال مكتبة اللاعب في ملف الترويسة <code>player.h</code>، وتُخزّن البيانات في ملف البيانات مثل أسطر نصية، إذ يشير كل سطر إلى معلومات لاعب معين. يتطلب ذلك إجراء تحويلات الدخل والخرج، لكنها تقنيةٌ مفيدةٌ إذا لم تكلّف هذه التحويلات أداءً إضافيًا.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9451_44" style="">
<span class="com">/*
*
* التصاريح والتعاريف للدوال التي تتلاعب بسجلات اللاعب بناءً على ترتيبهم
*
*/</span><span class="pln">

</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stddef.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;time.h&gt;</span><span class="pln">

</span><span class="com">#define</span><span class="pln"> NAMELEN </span><span class="lit">12</span><span class="pln">              </span><span class="com">/* الطول الأعظمي لاسم اللاعب */</span><span class="pln">

</span><span class="com">#define</span><span class="pln"> LENBUF </span><span class="lit">256</span><span class="pln">              </span><span class="com">/* الطول الأعظمي لذاكرة الدخل المؤقتة */</span><span class="pln">

</span><span class="com">#define</span><span class="pln"> CHALLENGE_RANGE </span><span class="lit">3</span><span class="pln">       </span><span class="com">// عدد اللاعبين الأعلى تصنيفًا الذين من الممكن للاعب أن يتحداهم ليزيد تصنيفه                                </span><span class="pln">

</span><span class="kwd">extern</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">OptArg</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">typedef</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">char</span><span class="pln">    name</span><span class="pun">[</span><span class="pln">NAMELEN</span><span class="pun">+</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
        </span><span class="typ">int</span><span class="pln">     rank</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">int</span><span class="pln">     wins</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">int</span><span class="pln">     losses</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">time_t</span><span class="pln">  last_game</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> player</span><span class="pun">;</span><span class="pln">

</span><span class="com">#define</span><span class="pln"> NULLPLAYER </span><span class="pun">(</span><span class="pln">player </span><span class="pun">*)</span><span class="lit">0</span><span class="pln">

</span><span class="kwd">extern</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">LadderFile</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">extern</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">WrFmt</span><span class="pun">;</span><span class="pln">       </span><span class="com">/* يُستخدم عند كتابة السجلات */</span><span class="pln">
</span><span class="kwd">extern</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">RdFmt</span><span class="pun">;</span><span class="pln">       </span><span class="com">/* يُستخدم عند قراءة السجلات */</span><span class="pln">

</span><span class="com">/*
تصاريح البرامج التي تُستخدم للتلاعب بسجلات اللاعب وملف لائحة التصنيف المعرفة في ملف‫ player.c
*/</span><span class="pln">

</span><span class="typ">int</span><span class="pln">     valid_records</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*);</span><span class="pln">
</span><span class="typ">int</span><span class="pln">     read_records</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">,</span><span class="pln"> player </span><span class="pun">*);</span><span class="pln">
</span><span class="typ">int</span><span class="pln">     write_records</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*,</span><span class="pln"> player </span><span class="pun">*,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">);</span><span class="pln">
player </span><span class="pun">*</span><span class="pln">find_by_name</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*,</span><span class="pln"> player </span><span class="pun">*,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">);</span><span class="pln">
player </span><span class="pun">*</span><span class="pln">find_by_rank</span><span class="pun">(</span><span class="typ">int</span><span class="pun">,</span><span class="pln"> player </span><span class="pun">*,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">void</span><span class="pln">    push_down</span><span class="pun">(</span><span class="pln">player </span><span class="pun">*,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln">     print_records</span><span class="pun">(</span><span class="pln">player </span><span class="pun">*,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">void</span><span class="pln">    copy_player</span><span class="pun">(</span><span class="pln">player </span><span class="pun">*,</span><span class="pln"> player </span><span class="pun">*);</span><span class="pln">
</span><span class="typ">int</span><span class="pln">     compare_name</span><span class="pun">(</span><span class="pln">player </span><span class="pun">*,</span><span class="pln"> player </span><span class="pun">*);</span><span class="pln">
</span><span class="typ">int</span><span class="pln">     compare_rank</span><span class="pun">(</span><span class="pln">player </span><span class="pun">*,</span><span class="pln"> player </span><span class="pun">*);</span><span class="pln">
</span><span class="kwd">void</span><span class="pln">    sort_players</span><span class="pun">(</span><span class="pln">player </span><span class="pun">*,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">);</span></pre>

<p style="text-align: center;">
	[مثال 4]
</p>

<p>
	إليك شيفرة ملف <code>player.c</code> الذي يستخدم بعض الدوال العامة للتلاعب بسجلات اللاعبين وملف البيانات، ويمكن أن تُستخدم هذه الدوال مع برامج أخرى محدد لتشكيل ثلاثة برامج تتعامل مع لائحة النتائج.
</p>

<p>
	لاحظ أنه يجب على كل برنامج أن يقرأ كامل البيانات من الملف إلى مصفوفة ديناميكية حتى نستطيع التلاعب بسجلات اللاعبين، ومن المفترض أن تكون جميع السجلات المحتواة داخل المصفوفة مُرتبةً حسب التصنيف قبل كتابتها مجددًا إلى ملف البيانات، وستولّد الدالة <code>push_down</code> بعض النتائج المثيرة للاهتمام إن لم تكن السجلات مرتبة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9451_46" style="">
<span class="com">/*
* الدوال الاعتيادية المستخدمة للتلاعب ببيانات ملف لائحة النتائج وسجلات اللاعبين
*/</span><span class="pln">

</span><span class="com">#include</span><span class="pln"> </span><span class="str">"player.h"</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">LadderFile</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"ladder"</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">WrFmt</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"%s %d      %d      %d      %ld\n"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">RdFmt</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"%s %d      %d      %d      %ld"</span><span class="pun">;</span><span class="pln">


</span><span class="com">/* تنبيه المستخدم بخصوص ضمّ السلاسل النصية */</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">HeaderLine</span><span class="pln"> </span><span class="pun">=</span><span class="pln">
        </span><span class="str">"Player Rank Won Lost Last Game\n"</span><span class="pln">
        </span><span class="str">"===============================================\n"</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">PrtFmt</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"%-12s%4d %4d %4d %s\n"</span><span class="pun">;</span><span class="pln">

</span><span class="com">/*إعادة رقم السجلات الموجودة في الملف */</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> valid_records</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fp</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">long</span><span class="pln"> plrs </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0L</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">long</span><span class="pln"> tmp </span><span class="pun">=</span><span class="pln"> ftell</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">char</span><span class="pln"> buf</span><span class="pun">[</span><span class="pln">LENBUF</span><span class="pun">];</span><span class="pln">

        fseek</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0L</span><span class="pun">,</span><span class="pln"> SEEK_SET</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> fgets</span><span class="pun">(</span><span class="pln">buf</span><span class="pun">,</span><span class="pln"> LENBUF</span><span class="pun">,</span><span class="pln"> fp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> NULL </span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln">
                </span><span class="pun">;</span><span class="pln">

        </span><span class="com">/* استعادة مؤشر الملف إلى حالته الأصلية*/</span><span class="pln">

        fseek</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">,</span><span class="pln"> tmp</span><span class="pun">,</span><span class="pln"> SEEK_SET</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">return</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// ‫قراءة القيمة num من سجل اللاعب من الملف fp إلى المصفوفة them</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> read_records</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fp</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> num</span><span class="pun">,</span><span class="pln"> player </span><span class="pun">*</span><span class="pln">them</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">long</span><span class="pln"> tmp </span><span class="pun">=</span><span class="pln"> ftell</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">num </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

        fseek</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0L</span><span class="pun">,</span><span class="pln"> SEEK_SET</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> num </span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">fscanf</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">,</span><span class="pln"> </span><span class="typ">RdFmt</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">them</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]).</span><span class="pln">name</span><span class="pun">,</span><span class="pln">
                                </span><span class="pun">&amp;((</span><span class="pln">them</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]).</span><span class="pln">rank</span><span class="pun">),</span><span class="pln">
                                </span><span class="pun">&amp;((</span><span class="pln">them</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]).</span><span class="pln">wins</span><span class="pun">),</span><span class="pln">
                                </span><span class="pun">&amp;((</span><span class="pln">them</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]).</span><span class="pln">losses</span><span class="pun">),</span><span class="pln">
                                </span><span class="pun">&amp;((</span><span class="pln">them</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]).</span><span class="pln">last_game</span><span class="pun">))</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">)</span><span class="pln">
                        </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">          </span><span class="com">// خطأ عند‫ fscanf</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        fseek</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">,</span><span class="pln"> tmp</span><span class="pun">,</span><span class="pln"> SEEK_SET</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// كتابة‫ num الخاص بسجل اللاعب إلى الملف fp من المصفوفة them </span><span class="pln">

</span><span class="typ">int</span><span class="pln"> write_records</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fp</span><span class="pun">,</span><span class="pln"> player </span><span class="pun">*</span><span class="pln">them</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> num</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

        fseek</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0L</span><span class="pun">,</span><span class="pln"> SEEK_SET</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> num </span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">fprintf</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">,</span><span class="pln"> </span><span class="typ">WrFmt</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">them</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]).</span><span class="pln">name</span><span class="pun">,</span><span class="pln">
                                </span><span class="pun">(</span><span class="pln">them</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]).</span><span class="pln">rank</span><span class="pun">,</span><span class="pln">
                                </span><span class="pun">(</span><span class="pln">them</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]).</span><span class="pln">wins</span><span class="pun">,</span><span class="pln">
                                </span><span class="pun">(</span><span class="pln">them</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]).</span><span class="pln">losses</span><span class="pun">,</span><span class="pln">
                                </span><span class="pun">(</span><span class="pln">them</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]).</span><span class="pln">last_game</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
                        </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">          </span><span class="com">// ‫خطأ عند fprintf </span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">return</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/*
إعادة مؤشر يشير إلى اللاعب في المصفوفة‫ them ذو اسم مطابق للقيمة name
*/</span><span class="pln">

player </span><span class="pun">*</span><span class="pln">find_by_name</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> player </span><span class="pun">*</span><span class="pln">them</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> num</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        player </span><span class="pun">*</span><span class="pln">pp </span><span class="pun">=</span><span class="pln"> them</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> num</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++,</span><span class="pln"> pp</span><span class="pun">++)</span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">strcmp</span><span class="pun">(</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> pp</span><span class="pun">-&gt;</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
                        </span><span class="kwd">return</span><span class="pln"> pp</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">return</span><span class="pln"> NULLPLAYER</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/*
إعادة مؤشر يشير إلى لاعب في مصفوفة‫ them تُطابق رتبته القيمة rank
*/</span><span class="pln">

player </span><span class="pun">*</span><span class="pln">find_by_rank</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> rank</span><span class="pun">,</span><span class="pln"> player </span><span class="pun">*</span><span class="pln">them</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> num</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        player </span><span class="pun">*</span><span class="pln">pp </span><span class="pun">=</span><span class="pln"> them</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> num</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++,</span><span class="pln"> pp</span><span class="pun">++)</span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">rank </span><span class="pun">==</span><span class="pln"> pp</span><span class="pun">-&gt;</span><span class="pln">rank</span><span class="pun">)</span><span class="pln">
                        </span><span class="kwd">return</span><span class="pln"> pp</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">return</span><span class="pln"> NULLPLAYER</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/*
‫خفّض رتبة جميع اللاعبين في مصفوفة them إذا كانت رتبتهم بين start و end
*/</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> push_down</span><span class="pun">(</span><span class="pln">player </span><span class="pun">*</span><span class="pln">them</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> start</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> end</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
        player </span><span class="pun">*</span><span class="pln">pp</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i </span><span class="pun">=</span><span class="pln"> end</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&gt;=</span><span class="pln"> start</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">--){</span><span class="pln">
        </span><span class="kwd">if</span><span class="pun">((</span><span class="pln">pp </span><span class="pun">=</span><span class="pln"> find_by_rank</span><span class="pun">(</span><span class="pln">i</span><span class="pun">,</span><span class="pln"> them</span><span class="pun">,</span><span class="pln"> number</span><span class="pun">))</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> NULLPLAYER</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln">
                        </span><span class="str">"error: could not find player ranked %d\n"</span><span class="pun">,</span><span class="pln"> i</span><span class="pun">);</span><span class="pln">
                free</span><span class="pun">(</span><span class="pln">them</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln">
                </span><span class="pun">(</span><span class="pln">pp</span><span class="pun">-&gt;</span><span class="pln">rank</span><span class="pun">)++;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// ‫طباعة سجل اللاعب num بصورةٍ مُنسّقة من المصفوفة them </span><span class="pln">

</span><span class="typ">int</span><span class="pln"> print_records</span><span class="pun">(</span><span class="pln">player </span><span class="pun">*</span><span class="pln">them</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> num</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

        printf</span><span class="pun">(</span><span class="typ">HeaderLine</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> num </span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">printf</span><span class="pun">(</span><span class="typ">PrtFmt</span><span class="pun">,</span><span class="pln">
                        </span><span class="pun">(</span><span class="pln">them</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]).</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">them</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]).</span><span class="pln">rank</span><span class="pun">,</span><span class="pln">
                        </span><span class="pun">(</span><span class="pln">them</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]).</span><span class="pln">wins</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">them</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]).</span><span class="pln">losses</span><span class="pun">,</span><span class="pln">
                        asctime</span><span class="pun">(</span><span class="pln">localtime</span><span class="pun">(&amp;(</span><span class="pln">them</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]).</span><span class="pln">last_game</span><span class="pun">)))</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
                        </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">          </span><span class="com">/* error on printf! */</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">return</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/* نسخ القيم من لاعب إلى آخر */</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> copy_player</span><span class="pun">(</span><span class="pln">player </span><span class="pun">*</span><span class="pln">to</span><span class="pun">,</span><span class="pln"> player </span><span class="pun">*</span><span class="pln">from</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pun">((</span><span class="pln">to </span><span class="pun">==</span><span class="pln"> NULLPLAYER</span><span class="pun">)</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> </span><span class="pun">(</span><span class="pln">from </span><span class="pun">==</span><span class="pln"> NULLPLAYER</span><span class="pun">))</span><span class="pln">
                </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">

        </span><span class="pun">*</span><span class="pln">to </span><span class="pun">=</span><span class="pln"> </span><span class="pun">*</span><span class="pln">from</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/* مقارنة اسم اللاعب الأول مع اسم اللاعب الثاني */</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> compare_name</span><span class="pun">(</span><span class="pln">player </span><span class="pun">*</span><span class="pln">first</span><span class="pun">,</span><span class="pln"> player </span><span class="pun">*</span><span class="pln">second</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> strcmp</span><span class="pun">(</span><span class="pln">first</span><span class="pun">-&gt;</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> second</span><span class="pun">-&gt;</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/* مقارنة رتبة اللاعب الأول مع رتبة اللاعب الثاني */</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> compare_rank</span><span class="pun">(</span><span class="pln">player </span><span class="pun">*</span><span class="pln">first</span><span class="pun">,</span><span class="pln"> player </span><span class="pun">*</span><span class="pln">second</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">first</span><span class="pun">-&gt;</span><span class="pln">rank </span><span class="pun">-</span><span class="pln"> second</span><span class="pun">-&gt;</span><span class="pln">rank</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// ترتيب‫ num الذي يدل على سجل اللاعب في المصفوفة them</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> sort_players</span><span class="pun">(</span><span class="pln">player </span><span class="pun">*</span><span class="pln">them</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> num</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        qsort</span><span class="pun">(</span><span class="pln">them</span><span class="pun">,</span><span class="pln"> num</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">sizeof</span><span class="pun">(</span><span class="pln">player</span><span class="pun">),</span><span class="pln"> compare_rank</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 5]
</p>

<p>
	<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%A3%D9%88%D9%84-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%AA%D8%B5%D8%B1%D9%8A%D9%81-compilation-%D9%81%D9%8A-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r976/" rel="">صُرّفت</a> الشيفرة السابقة عند تجربتها إلى كائن ملف object file، الذي كان مربوطًا (مع كائن ملف يحتوي على الشيفرة البرمجية الخاصة بالدالة <code>options</code>) بواحدٍ من البرامج الثلاثة الخاصة بالتعامل مع لائحة النتائج.
</p>

<p>
	إليك الشيفرة البرمجية لواحدة من أبسط البرامج هذه، ألا وهو "showlddr"، الذي تحتوي على الملف "showlddr.c". يأخذ هذا البرنامج خيارًا واحدًا وهو <code>‎-f</code> وقد تلاحظ أن هذا الخيار يأخذ وسيطًا اختياريًا أيضًا، والهدف من هذا الوسيط هو السماح بطباعة ملف بيانات لائحة التصنيف باستخدام اسم مغاير للاسم الافتراضي <code>ladder</code>.
</p>

<p>
	يجب أن تُخزّن سجلات اللاعب في ملف البيانات قبل ترتيبها، إلا أن <code>showddlr</code> يرتبها قبل أن يطبعها فقط بهدف التأكُّد.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9451_48" style="">
<span class="com">/*
برنامج يطبع حالة لائحة النتائج الحالية
*/</span><span class="pln">

</span><span class="com">#include</span><span class="pln"> </span><span class="str">"player.h"</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">ValidOpts</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"f:"</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">Usage</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"usage: showlddr [-f ladder_file]\n"</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">OtherFile</span><span class="pun">;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> argc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">argv</span><span class="pun">[])</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> number</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">char</span><span class="pln"> ch</span><span class="pun">;</span><span class="pln">
        player </span><span class="pun">*</span><span class="pln">them</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fname</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fp</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">argc </span><span class="pun">==</span><span class="pln"> </span><span class="lit">3</span><span class="pun">){</span><span class="pln">
                </span><span class="kwd">while</span><span class="pun">((</span><span class="pln">ch </span><span class="pun">=</span><span class="pln"> options</span><span class="pun">(</span><span class="pln">argc</span><span class="pun">,</span><span class="pln"> argv</span><span class="pun">,</span><span class="pln"> </span><span class="typ">ValidOpts</span><span class="pun">))</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">){</span><span class="pln">
                        </span><span class="kwd">switch</span><span class="pun">(</span><span class="pln">ch</span><span class="pun">){</span><span class="pln">
                                </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'f'</span><span class="pun">:</span><span class="pln">
                                        </span><span class="typ">OtherFile</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">OptArg</span><span class="pun">;</span><span class="pln">
                                        </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
                                </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'?'</span><span class="pun">:</span><span class="pln">
                                        fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Usage</span><span class="pun">);</span><span class="pln">
                                        </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
                        </span><span class="pun">}</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">argc </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">1</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Usage</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        fname </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">OtherFile</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)?</span><span class="pln"> </span><span class="typ">LadderFile</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">OtherFile</span><span class="pun">;</span><span class="pln">
        fp </span><span class="pun">=</span><span class="pln"> fopen</span><span class="pun">(</span><span class="pln">fname</span><span class="pun">,</span><span class="pln"> </span><span class="str">"r+"</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">fp </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">){</span><span class="pln">
                perror</span><span class="pun">(</span><span class="str">"showlddr"</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        number </span><span class="pun">=</span><span class="pln"> valid_records </span><span class="pun">(</span><span class="pln">fp</span><span class="pun">);</span><span class="pln">

        them </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">player </span><span class="pun">*)</span><span class="pln">malloc</span><span class="pun">((</span><span class="kwd">sizeof</span><span class="pun">(</span><span class="pln">player</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> number</span><span class="pun">));</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">them </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="str">"showlddr: out of memory\n"</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">read_records</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">,</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> them</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> number</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln"> </span><span class="str">"showlddr: error while reading"</span><span class="pln">
                                        </span><span class="str">" player records\n"</span><span class="pun">);</span><span class="pln">
                free</span><span class="pun">(</span><span class="pln">them</span><span class="pun">);</span><span class="pln">
                fclose</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        fclose</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">);</span><span class="pln">

        sort_players</span><span class="pun">(</span><span class="pln">them</span><span class="pun">,</span><span class="pln"> number</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">print_records</span><span class="pun">(</span><span class="pln">them</span><span class="pun">,</span><span class="pln"> number</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> number</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln"> </span><span class="str">"showlddr: error while printing"</span><span class="pln">
                                        </span><span class="str">" player records\n"</span><span class="pun">);</span><span class="pln">
                free</span><span class="pun">(</span><span class="pln">them</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        free</span><span class="pun">(</span><span class="pln">them</span><span class="pun">);</span><span class="pln">
        exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 6]
</p>

<p>
	يعمل البرنامج "showlddr" فقط إذا كان هناك ملف بيانات يحتوي على سجلات اللاعب بالتنسيق الصحيح، ويُنشئ البرنامج "newplyr" ملفًا إذا لم يكن هناك أي ملف مُسبقًا ومن ثم يضيف بيانات اللاعب الجديد بالتنسيق الصحيح إلى ذلك الملف.
</p>

<p>
	يُدرج اللاعبون الجُدد عادةً أسفل التصنيف إلا أن هناك بعض الحالات الاستثنائية التي يسمح فيها "newplyr" بإدراج اللاعبين وسط التصنيف.
</p>

<p>
	يجب أن يظهر اللاعب مرةً واحدةً في التصنيف (إلا إذا تشابهت أسماء اللاعبين المستعارة) ويجب أن يكون لكل تصنيف لاعب واحد فقط، ولذا فإن البرنامج يفحص الإدخالات المتكررة وإذا كان من الواجب إدخال اللاعب الجديد إلى تصنيف مجاور لتصنيف اللاعبين الآخرين، يُزاح اللاعبون بعيدًا عن تصنيف اللاعب الجديد.
</p>

<p>
	يتعرّف البرنامج "newplyr" على الخيار <code>‎-f</code> بصورةٍ مشابهة للبرنامج "showlddr"، ويفسره على أنه طلب إضافة اللاعب الجديد إلى ملف يُسمى باستخدام وسيط الخيار بدلًا من اسم الملف الافتراضي ألا وهو "ladder". يتطلب البرنامج "newplyr" أيضًا خيارين إضافيين ألا وهما <code>n-</code> و <code>r-</code> ويحدد كل وسيط خيار اسم اللاعب الجديد وتصنيفه الأوّلي بالترتيب.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9451_50" style="">
<span class="com">/*
برنامج يُضيف لاعب جديد إلى لائحة التصنيفات، ويفترض أن تُسنِد رتبةً بقيمة واقعية إلى اللاعب
*/</span><span class="pln">

</span><span class="com">#include</span><span class="pln"> </span><span class="str">"player.h"</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">ValidOpts</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"n:r:f:"</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">OtherFile</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">Usage</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"usage: newplyr -r rank -n name [-f file]\n"</span><span class="pun">;</span><span class="pln">

</span><span class="com">/* تصاريح مسبقة للدوال المعرفة في هذا الملف*/</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> record</span><span class="pun">(</span><span class="pln">player </span><span class="pun">*</span><span class="pln">extra</span><span class="pun">);</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> argc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">argv</span><span class="pun">[])</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">char</span><span class="pln"> ch</span><span class="pun">;</span><span class="pln">
        player dummy</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="kwd">new</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">dummy</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">argc </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">5</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Usage</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">while</span><span class="pun">((</span><span class="pln">ch </span><span class="pun">=</span><span class="pln"> options</span><span class="pun">(</span><span class="pln">argc</span><span class="pun">,</span><span class="pln"> argv</span><span class="pun">,</span><span class="pln"> </span><span class="typ">ValidOpts</span><span class="pun">))</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">){</span><span class="pln">
                </span><span class="kwd">switch</span><span class="pun">(</span><span class="pln">ch</span><span class="pun">){</span><span class="pln">
                </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'f'</span><span class="pun">:</span><span class="pln">
                        </span><span class="typ">OtherFile</span><span class="pun">=</span><span class="typ">OptArg</span><span class="pun">;</span><span class="pln">
                        </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
                </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'n'</span><span class="pun">:</span><span class="pln">
                        strncpy</span><span class="pun">(</span><span class="kwd">new</span><span class="pun">-&gt;</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> </span><span class="typ">OptArg</span><span class="pun">,</span><span class="pln"> NAMELEN</span><span class="pun">);</span><span class="pln">
                        </span><span class="kwd">new</span><span class="pun">-&gt;</span><span class="pln">name</span><span class="pun">[</span><span class="pln">NAMELEN</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
                        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">strcmp</span><span class="pun">(</span><span class="kwd">new</span><span class="pun">-&gt;</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> </span><span class="typ">OptArg</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
                                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln">
                                        </span><span class="str">"Warning: name truncated to %s\n"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">new</span><span class="pun">-&gt;</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
                        </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
                </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'r'</span><span class="pun">:</span><span class="pln">
                        </span><span class="kwd">if</span><span class="pun">((</span><span class="kwd">new</span><span class="pun">-&gt;</span><span class="pln">rank </span><span class="pun">=</span><span class="pln"> atoi</span><span class="pun">(</span><span class="typ">OptArg</span><span class="pun">))</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">){</span><span class="pln">
                                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Usage</span><span class="pun">);</span><span class="pln">
                        exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
                        </span><span class="pun">}</span><span class="pln">
                        </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
                </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'?'</span><span class="pun">:</span><span class="pln">
                        fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Usage</span><span class="pun">);</span><span class="pln">
                        </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">((</span><span class="kwd">new</span><span class="pun">-&gt;</span><span class="pln">rank </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln"> </span><span class="str">"newplyr: bad value for rank\n"</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">strlen</span><span class="pun">(</span><span class="kwd">new</span><span class="pun">-&gt;</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln">
                        </span><span class="str">"newplyr: needs a valid name for new player\n"</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">new</span><span class="pun">-&gt;</span><span class="pln">wins </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pun">-&gt;</span><span class="pln">losses </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
        time</span><span class="pun">(&amp;</span><span class="pln"> </span><span class="kwd">new</span><span class="pun">-&gt;</span><span class="pln">last_game</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫أسند الوقت الحالي إلى last_game</span><span class="pln">

        record</span><span class="pun">(</span><span class="kwd">new</span><span class="pun">);</span><span class="pln">

        exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> record</span><span class="pun">(</span><span class="pln">player </span><span class="pun">*</span><span class="pln">extra</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> new_number</span><span class="pun">,</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
        player </span><span class="pun">*</span><span class="pln">them</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fname </span><span class="pun">=(</span><span class="typ">OtherFile</span><span class="pun">==</span><span class="lit">0</span><span class="pun">)?</span><span class="typ">LadderFile</span><span class="pun">:</span><span class="typ">OtherFile</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fp</span><span class="pun">;</span><span class="pln">

        fp </span><span class="pun">=</span><span class="pln"> fopen</span><span class="pun">(</span><span class="pln">fname</span><span class="pun">,</span><span class="pln"> </span><span class="str">"r+"</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">fp </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">){</span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">((</span><span class="pln">fp </span><span class="pun">=</span><span class="pln"> fopen</span><span class="pun">(</span><span class="pln">fname</span><span class="pun">,</span><span class="pln"> </span><span class="str">"w"</span><span class="pun">))</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">){</span><span class="pln">
                        perror</span><span class="pun">(</span><span class="str">"newplyr"</span><span class="pun">);</span><span class="pln">
                        exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        number </span><span class="pun">=</span><span class="pln"> valid_records </span><span class="pun">(</span><span class="pln">fp</span><span class="pun">);</span><span class="pln">
        new_number </span><span class="pun">=</span><span class="pln"> number </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">((</span><span class="pln">extra</span><span class="pun">-&gt;</span><span class="pln">rank </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> </span><span class="pun">(</span><span class="pln">extra</span><span class="pun">-&gt;</span><span class="pln">rank </span><span class="pun">&gt;</span><span class="pln"> new_number</span><span class="pun">)){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln">
                        </span><span class="str">"newplyr: rank must be between 1 and %d\n"</span><span class="pun">,</span><span class="pln">
                        new_number</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        them </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">player </span><span class="pun">*)</span><span class="pln">malloc</span><span class="pun">((</span><span class="kwd">sizeof</span><span class="pun">(</span><span class="pln">player</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> new_number</span><span class="pun">));</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">them </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="str">"newplyr: out of memory\n"</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">read_records</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">,</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> them</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> number</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln">
                        </span><span class="str">"newplyr: error while reading player records\n"</span><span class="pun">);</span><span class="pln">
                free</span><span class="pun">(</span><span class="pln">them</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">find_by_name</span><span class="pun">(</span><span class="pln">extra</span><span class="pun">-&gt;</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> them</span><span class="pun">,</span><span class="pln"> number</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> NULLPLAYER</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln">
                        </span><span class="str">"newplyr: %s is already on the ladder\n"</span><span class="pun">,</span><span class="pln">
                        extra</span><span class="pun">-&gt;</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
                free</span><span class="pun">(</span><span class="pln">them</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        copy_player</span><span class="pun">(&amp;</span><span class="pln">them</span><span class="pun">[</span><span class="pln">number</span><span class="pun">],</span><span class="pln"> extra</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">extra</span><span class="pun">-&gt;</span><span class="pln">rank </span><span class="pun">!=</span><span class="pln"> new_number</span><span class="pun">)</span><span class="pln">
                push_down</span><span class="pun">(</span><span class="pln">them</span><span class="pun">,</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> extra</span><span class="pun">-&gt;</span><span class="pln">rank</span><span class="pun">,</span><span class="pln"> number</span><span class="pun">);</span><span class="pln">

        sort_players</span><span class="pun">(</span><span class="pln">them</span><span class="pun">,</span><span class="pln"> new_number</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">((</span><span class="pln">fp </span><span class="pun">=</span><span class="pln"> freopen</span><span class="pun">(</span><span class="pln">fname</span><span class="pun">,</span><span class="pln"> </span><span class="str">"w+"</span><span class="pun">,</span><span class="pln"> fp</span><span class="pun">))</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">){</span><span class="pln">
                perror</span><span class="pun">(</span><span class="str">"newplyr"</span><span class="pun">);</span><span class="pln">
                free</span><span class="pun">(</span><span class="pln">them</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">write_records</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">,</span><span class="pln"> them</span><span class="pun">,</span><span class="pln"> new_number</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> new_number</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln">
                        </span><span class="str">"newplyr: error while writing player records\n"</span><span class="pun">);</span><span class="pln">
                fclose</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">);</span><span class="pln">
                free</span><span class="pun">(</span><span class="pln">them</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        fclose</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">);</span><span class="pln">
        free</span><span class="pun">(</span><span class="pln">them</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 7]
</p>

<p>
	البرنامج الأخير المطلوب هو البرنامج الذي يسجل نتائج الألعاب، ألا وهو برنامج "result".
</p>

<p>
	يقبل "result" خيار <code>‎-f</code> كما هو الحال في البرنامجين الآخرين، مصحوبًا باسم الملف لتحديد بديل عن اسم ملف اللاعب الافتراضي.
</p>

<p>
	يعرض برنامج "result" عملية الإدخال للاعبين الخاسرين والرابحين على نحوٍ تفاعلي على عكس برنامج "newplyr"، ويصرّ على أن الأسماء يجب أن تكون للاعبين موجودين مسبقًا. يجري التحقّق من الأسماء بعد إعطاء اسمين صالحين، وذلك فيما إذا كان الخاسر أعلى تصنيفًا من الفائز، أو أن الفائز ذو تصنيف مقارب مما يسمح له بتغيير تصنيفه؛ وإذا لزُم تغيير للتصنيف، يأخذ المنتصر رتبة الخاسر ويُخفّض الخاسر رتبةً واحدةً (إضافةً إلى أي لاعب من تصنيف متداخل).
</p>

<p>
	إليك الشيفرة البرمجية الخاصة ببرنامج <code>result</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9451_52" style="">
<span class="com">/*
* برنامج يسجل النتائج
*
*/</span><span class="pln">

</span><span class="com">#include</span><span class="pln"> </span><span class="str">"player.h"</span><span class="pln">

</span><span class="com">/* تصريحات استباقية للدوال المعرفة في هذا الملف   */</span><span class="pln">

</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">read_name</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*);</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> move_winner</span><span class="pun">(</span><span class="pln">player </span><span class="pun">*,</span><span class="pln"> player </span><span class="pun">*,</span><span class="pln"> player </span><span class="pun">*,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">ValidOpts</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"f:"</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">Usage</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"usage: result [-f file]\n"</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="typ">OtherFile</span><span class="pun">;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> argc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">argv</span><span class="pun">[])</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        player </span><span class="pun">*</span><span class="pln">winner</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">loser</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">them</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> number</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fp</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fname</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">char</span><span class="pln"> buf</span><span class="pun">[</span><span class="pln">LENBUF</span><span class="pun">],</span><span class="pln"> ch</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">argc </span><span class="pun">==</span><span class="pln"> </span><span class="lit">3</span><span class="pun">){</span><span class="pln">
                </span><span class="kwd">while</span><span class="pun">((</span><span class="pln">ch </span><span class="pun">=</span><span class="pln"> options</span><span class="pun">(</span><span class="pln">argc</span><span class="pun">,</span><span class="pln"> argv</span><span class="pun">,</span><span class="pln"> </span><span class="typ">ValidOpts</span><span class="pun">))</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">){</span><span class="pln">
                        </span><span class="kwd">switch</span><span class="pun">(</span><span class="pln">ch</span><span class="pun">){</span><span class="pln">
                                </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'f'</span><span class="pun">:</span><span class="pln">
                                        </span><span class="typ">OtherFile</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">OptArg</span><span class="pun">;</span><span class="pln">
                                        </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
                                </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'?'</span><span class="pun">:</span><span class="pln">
                                        fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Usage</span><span class="pun">);</span><span class="pln">
                                        </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
                        </span><span class="pun">}</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">argc </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">1</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Usage</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        fname </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">OtherFile</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)?</span><span class="pln"> </span><span class="typ">LadderFile</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">OtherFile</span><span class="pun">;</span><span class="pln">
        fp </span><span class="pun">=</span><span class="pln"> fopen</span><span class="pun">(</span><span class="pln">fname</span><span class="pun">,</span><span class="pln"> </span><span class="str">"r+"</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">fp </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">){</span><span class="pln">
                perror</span><span class="pun">(</span><span class="str">"result"</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        number </span><span class="pun">=</span><span class="pln"> valid_records </span><span class="pun">(</span><span class="pln">fp</span><span class="pun">);</span><span class="pln">

        them </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">player </span><span class="pun">*)</span><span class="pln">malloc</span><span class="pun">((</span><span class="kwd">sizeof</span><span class="pun">(</span><span class="pln">player</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> number</span><span class="pun">));</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">them </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="str">"result: out of memory\n"</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">read_records</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">,</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> them</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> number</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln">
                        </span><span class="str">"result: error while reading player records\n"</span><span class="pun">);</span><span class="pln">
                fclose</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">);</span><span class="pln">
                free</span><span class="pun">(</span><span class="pln">them</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        fclose</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">((</span><span class="pln">winner </span><span class="pun">=</span><span class="pln"> find_by_name</span><span class="pun">(</span><span class="pln">read_name</span><span class="pun">(</span><span class="pln">buf</span><span class="pun">,</span><span class="pln"> </span><span class="str">"winner"</span><span class="pun">),</span><span class="pln"> them</span><span class="pun">,</span><span class="pln"> number</span><span class="pun">))</span><span class="pln">
                </span><span class="pun">==</span><span class="pln"> NULLPLAYER</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="str">"result: no such player %s\n"</span><span class="pun">,</span><span class="pln">buf</span><span class="pun">);</span><span class="pln">
                free</span><span class="pun">(</span><span class="pln">them</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">((</span><span class="pln">loser </span><span class="pun">=</span><span class="pln"> find_by_name</span><span class="pun">(</span><span class="pln">read_name</span><span class="pun">(</span><span class="pln">buf</span><span class="pun">,</span><span class="pln"> </span><span class="str">"loser"</span><span class="pun">),</span><span class="pln"> them</span><span class="pun">,</span><span class="pln"> number</span><span class="pun">))</span><span class="pln">
                </span><span class="pun">==</span><span class="pln"> NULLPLAYER</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="str">"result: no such player %s\n"</span><span class="pun">,</span><span class="pln">buf</span><span class="pun">);</span><span class="pln">
                free</span><span class="pun">(</span><span class="pln">them</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        winner</span><span class="pun">-&gt;</span><span class="pln">wins</span><span class="pun">++;</span><span class="pln">
        loser</span><span class="pun">-&gt;</span><span class="pln">losses</span><span class="pun">++;</span><span class="pln">

        winner</span><span class="pun">-&gt;</span><span class="pln">last_game </span><span class="pun">=</span><span class="pln"> loser</span><span class="pun">-&gt;</span><span class="pln">last_game </span><span class="pun">=</span><span class="pln"> time</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">loser</span><span class="pun">-&gt;</span><span class="pln">rank </span><span class="pun">&lt;</span><span class="pln"> winner</span><span class="pun">-&gt;</span><span class="pln">rank</span><span class="pun">)</span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">((</span><span class="pln">winner</span><span class="pun">-&gt;</span><span class="pln">rank </span><span class="pun">-</span><span class="pln"> loser</span><span class="pun">-&gt;</span><span class="pln">rank</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;=</span><span class="pln"> CHALLENGE_RANGE</span><span class="pun">)</span><span class="pln">
                        move_winner</span><span class="pun">(</span><span class="pln">winner</span><span class="pun">,</span><span class="pln"> loser</span><span class="pun">,</span><span class="pln"> them</span><span class="pun">,</span><span class="pln"> number</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">((</span><span class="pln">fp </span><span class="pun">=</span><span class="pln"> freopen</span><span class="pun">(</span><span class="pln">fname</span><span class="pun">,</span><span class="pln"> </span><span class="str">"w+"</span><span class="pun">,</span><span class="pln"> fp</span><span class="pun">))</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">){</span><span class="pln">
                perror</span><span class="pun">(</span><span class="str">"result"</span><span class="pun">);</span><span class="pln">
                free</span><span class="pun">(</span><span class="pln">them</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">write_records</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">,</span><span class="pln"> them</span><span class="pun">,</span><span class="pln"> number</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> number</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="str">"result: error while writing player records\n"</span><span class="pun">);</span><span class="pln">
                free</span><span class="pun">(</span><span class="pln">them</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        fclose</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">);</span><span class="pln">
        free</span><span class="pun">(</span><span class="pln">them</span><span class="pun">);</span><span class="pln">
        exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> move_winner</span><span class="pun">(</span><span class="pln">player </span><span class="pun">*</span><span class="pln">ww</span><span class="pun">,</span><span class="pln"> player </span><span class="pun">*</span><span class="pln">ll</span><span class="pun">,</span><span class="pln"> player </span><span class="pun">*</span><span class="pln">them</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> number</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> loser_rank </span><span class="pun">=</span><span class="pln"> ll</span><span class="pun">-&gt;</span><span class="pln">rank</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">((</span><span class="pln">ll</span><span class="pun">-&gt;</span><span class="pln">rank </span><span class="pun">-</span><span class="pln"> ww</span><span class="pun">-&gt;</span><span class="pln">rank</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln">
                </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">

        push_down</span><span class="pun">(</span><span class="pln">them</span><span class="pun">,</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> ll</span><span class="pun">-&gt;</span><span class="pln">rank</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">ww</span><span class="pun">-&gt;</span><span class="pln">rank </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">));</span><span class="pln">
        ww</span><span class="pun">-&gt;</span><span class="pln">rank </span><span class="pun">=</span><span class="pln"> loser_rank</span><span class="pun">;</span><span class="pln">
        sort_players</span><span class="pun">(</span><span class="pln">them</span><span class="pun">,</span><span class="pln"> number</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">read_name</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">buf</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">whom</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">for</span><span class="pun">(;;){</span><span class="pln">
                </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">cp</span><span class="pun">;</span><span class="pln">
                printf</span><span class="pun">(</span><span class="str">"Enter name of %s : "</span><span class="pun">,</span><span class="pln">whom</span><span class="pun">);</span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">fgets</span><span class="pun">(</span><span class="pln">buf</span><span class="pun">,</span><span class="pln"> LENBUF</span><span class="pun">,</span><span class="pln"> stdin</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">)</span><span class="pln">
                        </span><span class="kwd">continue</span><span class="pun">;</span><span class="pln">
                </span><span class="com">/* حذف السطر الجديد */</span><span class="pln">
                cp </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">buf</span><span class="pun">[</span><span class="pln">strlen</span><span class="pun">(</span><span class="pln">buf</span><span class="pun">)-</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">(*</span><span class="pln">cp </span><span class="pun">==</span><span class="pln"> </span><span class="str">'\n'</span><span class="pun">)</span><span class="pln">
                        </span><span class="pun">*</span><span class="pln">cp </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
                </span><span class="com">/* محرف واحد على الأقل */</span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">cp </span><span class="pun">!=</span><span class="pln"> buf</span><span class="pun">)</span><span class="pln">
                        </span><span class="kwd">return</span><span class="pln"> buf</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 8]
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter10/" rel="external nofollow">Complete Programs in C</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/c/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D9%88%D9%82%D8%AA-%D9%88%D8%A7%D9%84%D8%AA%D8%A7%D8%B1%D9%8A%D8%AE-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1830/" rel="">دوال التعامل مع السلاسل النصية والوقت والتاريخ في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A8%D8%B9%D8%B6-%D8%A7%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A7%D9%84%D8%A8%D8%B3%D9%8A%D8%B7%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D8%B3%D8%A7%D8%A8%D9%8A%D8%A9-r1608/" rel="">.بعض البرامج البسيطة بلغة سي C: المصفوفات والعمليات الحسابية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%B9%D9%88%D8%A7%D9%85%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1613/" rel="">العوامل في لغة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1831</guid><pubDate>Sat, 17 Dec 2022 11:19:27 +0000</pubDate></item><item><title>&#x62F;&#x648;&#x627;&#x644; &#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x633;&#x644;&#x627;&#x633;&#x644; &#x627;&#x644;&#x646;&#x635;&#x64A;&#x629; &#x648;&#x627;&#x644;&#x648;&#x642;&#x62A; &#x648;&#x627;&#x644;&#x62A;&#x627;&#x631;&#x64A;&#x62E; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D9%88%D9%82%D8%AA-%D9%88%D8%A7%D9%84%D8%AA%D8%A7%D8%B1%D9%8A%D8%AE-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1830/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_12/639d92b01fd65_----------C-.png.346a64cfa58e6aeccca753aedf2a66b6.png" /></p>
<p>
	نتطرّق في هذا المقال إلى طرق مختلفة في التعامل مع <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">السلاسل النصية</a> والتلاعب بها، وذلك عن طريق دوال مكتبة string.h، ومن ثمّ ننتقل إلى دوال الوقت والتاريخ المحتواة في مكتبة time.h.
</p>

<h2>
	التعامل مع السلاسل النصية
</h2>

<p>
	هناك العديد من الدوال التي تسمح لنا بالتعامل مع السلاسل النصية، إذ تكون السلسلة النصية في لغة سي مؤلفةً من <a href="https://academy.hsoub.com/programming/c/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1675/" rel="">مصفوفة</a> من المحارف تنتهي بمحرف فارغ null، وتتوقع الدوال في جميع الحالات تمرير مؤشر يشير إلى المحرف الأول ضمن السلسلة النصية، ويعرّف ملف الترويسة <code>&lt;string.h&gt;</code> هذا النوع من الدوال.
</p>

<h3>
	النسخ
</h3>

<p>
	يضم هذا التصنيف الدوال التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6439_8" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;string.h&gt;</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">memcpy</span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s2</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> n</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">memmove </span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s2</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> n</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">strcpy</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s2</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">strncpy</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s2</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> n</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">strcat</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s2</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">strncat</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s2</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> n</span><span class="pun">);</span></pre>

<ul>
	<li>
		دالة <code>memcpy</code>: تنسخ هذه الدالة <code>n</code> بايت من المكان الذي يشير إليه المؤشر <code>s2</code> إلى المكان الذي يشير إليه المؤشر <code>s1</code>، ونحصل على سلوك غير محدد إذا كان الكائنان متداخلان overlapping objects. تعيد الدالة <code>s1</code>.
	</li>
	<li>
		دالة <code>memmove</code>: هذه الدالة مطابقة لعمل دالة <code>memcpy</code> إلا أنها تعمل على الكائنات المتداخلة، إلا أنها قد تكون أبطأ.
	</li>
	<li>
		دالتَي <code>strcpy</code> و<code>strncpy</code>: تنسخ كلا الدالتين السلسلة النصية التي يشير إليها المؤشر <code>s2</code> إلى سلسلة نصية يشير المؤشر <code>s1</code> إليها متضمنًا ذلك المحرف الفارغ في نهاية السلسلة. تنسخ <code>strncpy</code> سلسلةً نصيةً بطول <code>n</code> بايت على الأكثر، وتحشو ما تبقى بمحارف فارغة إذا كانت <code>s2</code> أقصر من <code>n</code> محرف، ونحصل على سلوك غير معرّف، إذا كانت السلسلتان متقاطعتين، وتُعيد كلا الدالتين <code>s1</code>.
	</li>
	<li>
		الدالتان <code>strcat</code> و <code>strncat</code>: تُضيف كلا الدالتين السلسلة النصية <code>s2</code> إلى السلسلة <code>s1</code> بالكتابة فوق overwrite المحرف الفارغ في نهاية السلسلة <code>s1</code>، بينما يُضاف المحرف الفارغ دائمًا إلى نهاية السلسلة. يمكن إضافة <code>n</code> محرف على الأكثر من السلسلة <code>s2</code> باستخدام الدالة <code>strncat</code> مما يعني أن السلسلة النصية الهدف (أي <code>s1</code>) يجب أن تحتوي على مساحة لطولها الأصلي (دون احتساب المحرف الفارغ) زائد <code>n+1</code> محرف للتنفيذ الآمن. تعيد الدالتين <code>s1</code>.
	</li>
</ul>

<h3>
	مقارنة السلسلة النصية والبايت
</h3>

<p>
	تُستخدم هذه الدوال في مقارنة مصفوفات من البايتات، وهذا يتضمن طبعًا السلاسل النصية في لغة سي إذ أنها سلسلةٌ من المحارف <code>char</code> (أي البايتات) بمحرف فارغ في نهايتها. تعمل جميع هذه <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1646/" rel="">الدوال</a> التي سنذكرها على مقارنة بايت تلو الآخر وتتوقف فقط في حالة اختلف بايت مع بايت آخر (في هذه الحالة تُعيد الدالة إشارة الفرق بين البايت والآخر) أو عندما تكون المصفوفتان متساويتين (أي لم يُعثر على أي فرق بينهما وكان طولهما مساوٍ إلى الطول المحدد أو -في حالة المقارنة بين السلاسل النصية- وُجد المحرف الفارغ في النهاية).
</p>

<p>
	القيمة المُعادة في جميع الدوال عدا الدالة <code>strxfrm</code> هي أصغر من الصفر، أو تساوي الصفر، أو أكبر من الصفر وذلك في حالة كان الكائن الأول أصغر من الكائن الثاني، أو يساوي الكائن الثاني، أو أكبر من الكائن الثاني على الترتيب.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6439_11" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;string.h&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> memcmp</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s2</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> n</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> strcmp</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s2</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> strncmp</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s2</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> n</span><span class="pun">);</span><span class="pln">
</span><span class="typ">size_t</span><span class="pln"> strxfrm</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">to</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">from</span><span class="pun">,</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> strcoll</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s2</span><span class="pun">);</span></pre>

<ul>
	<li>
		دالة <code>memcmp</code>: تُقارن أول <code>n</code> محرف في الكائن الذي يشير إليه المؤشر <code>s1</code> و<code>s2</code>، إلا أن مقارنة الهياكل بهذه الطريقة ليست مثالية، إذ قد تحتوي الاتحادات unions أو "الثقوب holes" المُسببة بواسطة محاذاة ذاكرة التخزين على بيانات غير صالحة.
	</li>
	<li>
		دالة <code>strcmp</code>: تُقارن سلسلتين نصيتين وهي إحدى أكثر الدوال استخدامًا عند التعامل مع السلاسل النصية.
	</li>
	<li>
		الدالة <code>strncmp</code>: تُطابق عمل الدالة <code>strcmp</code> إلا أنها تقارن <code>n</code> محرف على الأكثر.
	</li>
	<li>
		الدالة <code>strxfrm</code>: تُحوّل السلسلة النصية المُمرّرة إليها (بصورةٍ خاصة ومميزة)، وتُخزّن إلى موضع المؤشر، ويُكتب <code>maxsize</code> محرف على الأكثر إلى موضع المؤشر (متضمنًا المحرف الفارغ في النهاية)، وتضمن طريقة التحويل أننا سنحصل على نتيجة المقارنة ذاتها لسلسلتين نصيتين محوّلتين ضمن إعدادات المستخدم المحلية عند استخدام الدالة <code>strcmp</code> بعد تطبيق الدالة <code>strcoll</code> على السلسلتين النصيتين الأساسيتين.
	</li>
</ul>

<p>
	نحصل على طول السلسلة النصية الناتجة مثل قيمة مُعادة في جميع الحالات (باستثناء المحرف الفارغ في نهاية السلسلة)، وتكون محتويات المؤشر <code>to</code> غير معرّفة إذا كانت القيمة مساوية أو أكبر من <code>maxsize</code>، وقد تكون <code>s1</code> مؤشرًا فارغًا إذا كانت <code>maxsize</code> صفر، ونحصل على سلوك غير معرّف إذا كان الكائنان متقاطعين.
</p>

<ul>
	<li>
		دالة <code>strcoll</code> تُقارن هذه الدالة سلسلتين نصيتين بحسب سلسلة الترتيب collating sequence المحدد في إعدادات اللغة المحلية.
	</li>
</ul>

<h3>
	دوال بحث المحارف والسلاسل النصية
</h3>

<p>
	يتضمن التصنيف الدوال التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6439_13" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;string.h&gt;</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">memchr</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> c</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> n</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">strchr</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> c</span><span class="pun">);</span><span class="pln">
</span><span class="typ">size_t</span><span class="pln"> strcspn</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s2</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">strpbrk</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s2</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">strrchr</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> c</span><span class="pun">);</span><span class="pln">
</span><span class="typ">size_t</span><span class="pln"> strspn</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s2</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">strstr</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s2</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">strtok</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s2</span><span class="pun">);</span></pre>

<ul>
	<li>
		دالة <code>memchr</code>: تُعيد مؤشرًا يشير إلى أول ظهور ضمن أول <code>n</code> محرف من <code>s*</code> للمحرف <code>c</code> (من نوع <code>unsigned char</code>)، وتُعيد فراغًا null إن لم يُعثر على أي تطابق.
	</li>
	<li>
		دالة <code>strchr</code>: تُعيد مؤشرًا يشير إلى أول ظهور للمحرف <code>c</code> ضمن <code>s*</code> ويتضمن البحث المحرف الفارغ، وتُعيد فراغًا null إذا لم يُعثر على أي تطابق.
	</li>
	<li>
		دالة <code>strcspn</code>: تُعيد طول الجزء الأولي للسلسلة النصية <code>s1</code> الذي لا يحتوي أيًّا من محارف السلسلة <code>s2</code>، ولا يؤخذ المحرف الفارغ في نهاية السلسلة <code>s2</code> بالحسبان.
	</li>
	<li>
		دالة <code>strpbrk</code>: تُعيد مؤشرًا إلى أول محرف ضمن <code>s1</code> يطابق أي محرف من محارف السلسلة <code>s2</code> أو تُعيد فراغًا إن لم يُعثر على أي تطابق.
	</li>
	<li>
		دالة <code>strrchr</code>: تُعيد مؤشرًا إلى آخر محرف ضمن <code>s1</code> يطابق المحرف <code>c</code> آخذة بالحسبان المحرف الفارغ على أنه جزء من السلسلة <code>s1</code> وتُعيد فراغ إن لم يُعثر على تطابق.
	</li>
	<li>
		دالة <code>strspn</code>: تُعيد طول الجزء الأولي ضمن السلسلة <code>s1</code> الذي يتألف كاملًا من محارف السلسلة <code>s1</code>.
	</li>
	<li>
		دالة <code>strstr</code>: تُعيد مؤشرًا إلى أول تطابق للسلسلة <code>s2</code> ضمن السلسلة <code>s1</code> أو تُعيد فراغ إن لم يُعثر على تطابق.
	</li>
	<li>
		دالة <code>strtok</code>: تقسّم السلسلة النصية <code>s1</code> إلى "رموز tokens" يُحدّد كل منها بمحرف من محارف السلسلة <code>s2</code> وتُعيد مؤشرًا يشير إلى الرمز الأول أو فراغًا إن لم يوجد أي رموز. تُعيد استدعاءات لاحقة للدالة باستخدام <code>‎(char *)0</code> قيمةً للوسيط <code>s1</code> الرمز التالي ضمن السلسلة، إلا أن <code>s2</code> (المُحدِّد) قد يختلف عند كل استدعاء، ويُعاد مؤشر فارغ إذا لم يبق أي رمز.
	</li>
</ul>

<h3>
	دوال متنوعة أخرى
</h3>

<p>
	هناك بعض الدوال الأخرى التي لا تنتمي لأي من التصنيفات السابقة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6439_15" style=""><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">memset</span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> c</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> n</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">strerror</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> errnum</span><span class="pun">);</span><span class="pln">
</span><span class="typ">size_t</span><span class="pln"> strlen</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">);</span></pre>

<ul>
	<li>
		دالة <code>memset</code>: تضبط <code>n</code> بايت يشير إليها المؤشر <code>s</code> إلى قيمة المحرف <code>c</code> وهو من النوع <code>unsigned char</code>، وتُعيد الدالة المؤشر <code>s</code>.
	</li>
	<li>
		دالة <code>strlen</code>: تُعيد طول السلسلة النصية <code>s</code> دون احتساب المحرف الفارغ في نهاية السلسلة، وهي دالة شائعة الاستخدام.
	</li>
	<li>
		دالة <code>strerror</code>: تُعيد مؤشرًا يشير إلى سلسلة نصية تصف الخطأ رقم <code>errnum</code>، وقد تُعدَّل هذه السلسلة النصية عن طريق استدعاءات لاحقة للدالة <code>sterror</code>، وتعدّ هذه الدالة مفيدةٌ لمعرفة معنى قيم <code>errno</code>.
	</li>
</ul>

<h2>
	التاريخ والوقت
</h2>

<p>
	تتعامل هذه الدوال إما مع الوقت المُنقضي elapsed time، أو وقت التقويم calendar time ويحتوي ملف الترويسة <code>&lt;time.h&gt;</code> على تصريح كلا النوعين من الدوال بالاعتماد على التالي:
</p>

<ul>
	<li>
		القيمة <code>CLOCKS_PER_SEC</code>: عدد الدقات ticks في الثانية المُعادة من الدالة <code>clock</code>.
	</li>
	<li>
		النوعين <code>clock_t</code> و <code>time_t</code>: أنواع حسابية تُستخدم لتمثيل تنسيقات مختلفة من الوقت.
	</li>
	<li>
		الهيكل <code>struct tm</code>: يُستخدم لتخزين القيمة المُمثّلة لأوقات التقويم، ويحتوي على الأعضاء التالية:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6439_17" style=""><span class="kwd">int</span><span class="pln"> tm_sec      </span><span class="com">// الثواني بعد الدقيقة من 0 إلى 61، وتسمح 61 بثانيتين كبيستين‫ leap-second</span><span class="pln">
</span><span class="kwd">int</span><span class="pln"> tm_min      </span><span class="com">// الدقائق بعد الساعة من 0 إلى 59</span><span class="pln">
</span><span class="kwd">int</span><span class="pln"> tm_hour     </span><span class="com">// الساعات بعد منتصف الليل من 0 إلى 23</span><span class="pln">
</span><span class="kwd">int</span><span class="pln"> tm_mday     </span><span class="com">//اليوم في الشهر من 1 إلى 31</span><span class="pln">
</span><span class="kwd">int</span><span class="pln"> tm_mon      </span><span class="com">// الشهر في السنة من 0 إلى 11</span><span class="pln">
</span><span class="kwd">int</span><span class="pln"> tm_year     </span><span class="com">// السنة الحالية من 1900</span><span class="pln">
</span><span class="kwd">int</span><span class="pln"> tm_wday     </span><span class="com">// الأيام منذ يوم الأحد من 0 إلى 6</span><span class="pln">
</span><span class="kwd">int</span><span class="pln"> tm_yday     </span><span class="com">// الأيام منذ الأول من يناير من 0 إلى 365</span><span class="pln">
</span><span class="kwd">int</span><span class="pln"> tm_isdst    </span><span class="com">// مؤشر التوقيت الصيفي </span></pre>

<p>
	يكون العنصر <code>tm_isdst</code> موجبًا إذا كان التوقيت الصيفي daylight savings فعّالًا، وصفر إن لم يكن كذلك، وسالبًا إن لم تكن هذه المعلومة متوفرة.
</p>

<p>
	إليك دوال التلاعب بالوقت:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6439_19" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;time.h&gt;</span><span class="pln">

</span><span class="typ">clock_t</span><span class="pln"> clock</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">double</span><span class="pln"> difftime</span><span class="pun">(</span><span class="typ">time_t</span><span class="pln"> time1</span><span class="pun">,</span><span class="pln"> </span><span class="typ">time_t</span><span class="pln"> time2</span><span class="pun">);</span><span class="pln">
</span><span class="typ">time_t</span><span class="pln"> mktime</span><span class="pun">(</span><span class="kwd">struct</span><span class="pln"> tm </span><span class="pun">*</span><span class="pln">timeptr</span><span class="pun">);</span><span class="pln">
</span><span class="typ">time_t</span><span class="pln"> time</span><span class="pun">(</span><span class="typ">time_t</span><span class="pln"> </span><span class="pun">*</span><span class="pln">timer</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">asctime</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> tm </span><span class="pun">*</span><span class="pln">timeptr</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ctime</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">time_t</span><span class="pln"> </span><span class="pun">*</span><span class="pln">timer</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> tm </span><span class="pun">*</span><span class="pln">gmtime</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">time_t</span><span class="pln"> </span><span class="pun">*</span><span class="pln">timer</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> tm </span><span class="pun">*</span><span class="pln">localtime</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">time_t</span><span class="pln"> </span><span class="pun">*</span><span class="pln">timer</span><span class="pun">);</span><span class="pln">
</span><span class="typ">size_t</span><span class="pln"> strftime</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> maxsize</span><span class="pun">,</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">format</span><span class="pun">,</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> tm </span><span class="pun">*</span><span class="pln">timeptr</span><span class="pun">);</span></pre>

<p>
	تتشارك كل من الدوال <code>asctime</code> و <code>ctime</code> و <code>gmtime</code> و <code>localtime</code> و <code>strftime</code> بهياكل بيانات ساكنة static من نوع <code>struct tm</code> أو من نوع <code>char []‎</code>، وقد يتسبب استدعاء أحد منها بعملية الكتابة فوق البيانات المخزنة بسبب استدعاء سابق لإحدى الدوال الأخرى، ولذلك يجب على مستخدم الدالة نسخ المعلومات إذا كان هذا سيسبب أية مشاكل.
</p>

<ul>
	<li>
		الدالة <code>clock</code>: تُعيد أفضل تقريب للوقت الذي انقضى منذ تشغيل البرنامج مقدرًا بدقات الساعة ticks، وتُعاد القيمة <code>‎(clock_t)-1</code> إذا لم يُعثر على أي قيمة. من الضروري العثور على الفرق بين وقت بداية تشغيل البرنامج والوقت الحالي إذا أردنا إيجاد الوقت المُنقضي اللازم لتشغيل البرنامج، وهناك ثابتٌ معرفٌ حسب التنفيذ يعدل على القيمة المُعادة من <code>clock</code>. يجب تقسيم القيمة على <code>CLOCKS_PER_SEC</code> لتحديد الوقت بالثواني.
	</li>
	<li>
		الدالة <code>difftime</code>: تُعيد الفرق بين وقت تقويم ووقت تقويم آخر بالثواني.
	</li>
	<li>
		الدالة <code>mktime</code>: تُعيد وقت تقويم يوافق القيم الموجودة في هيكل يشير إلى المؤشر <code>timeptr</code>، أو تُعيد القيمة <code>‎(time_t)-1</code> إذا لم يكن من الممكن تمثيل القيمة. يُتجاهل العضوان <code>tm_wday</code> و <code>tm_yday</code>، ولا تُقيَّد باقي الأعضاء بقيمهم الاعتيادية، إذ يُضبط أعضاء الهيكل إلى قيم مناسبة ضمن النطاق الاعتيادي عند التحويل الناجح، وهذه الدالة مفيدة للعثور على التاريخ والوقت الموافق لقيمة من نوع <code>time_t</code>.
	</li>
	<li>
		الدالة <code>time</code>: تُعيد أفضل تقريب لوقت التقويم الحالي باستخدام ترميز غير محدد unspecified encoding، وتُعيد القيمة <code>‎(time_t)-1</code> إذا كان الوقت غير متوفر.
	</li>
	<li>
		الدالة <code>asctime</code>: تُحول الوقت ضمن هيكل يشير إليه المؤشر <code>timptr</code> إلى سلسلة نصية بالتنسيق التالي:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6439_21" style=""><span class="typ">Sun</span><span class="pln"> </span><span class="typ">Sep</span><span class="pln"> </span><span class="lit">16</span><span class="pln"> </span><span class="lit">01</span><span class="pun">:</span><span class="lit">03</span><span class="pun">:</span><span class="lit">52</span><span class="pln"> </span><span class="lit">1973</span><span class="pln">\n\0</span></pre>

<p>
	المثال السابق مأخوذٌ من المعيار، إذ يعرِّف المعيار <a href="https://wiki.hsoub.com/Algorithms" rel="external">الخوارزمية</a> المستخدمة أيضًا، إلا أنه من المهم ملاحظة أن جميع الحقول ضمن السلسلة النصية ذات عرض ثابت وينطبق استخدامها على المجتمعات التي تتحدث باللغة الإنجليزية فقط. تُخزّن السلسلة النصية في هيكل ساكن static structure ويُمكن إعادة كتابته عن طريق استدعاءات لاحقة لأحد دوال التلاعب بالوقت (المذكورة أعلاه).
</p>

<ul>
	<li>
		الدالة <code>ctime</code>: تكافئ عمل <code>asctime(localtime(timer))‎</code>. اقرأ عن الدالة <code>asctime</code> لمعرفة القيمة المُعادة.
	</li>
	<li>
		الدالة <code>gmtime</code>: تُعيد مؤشرًا إلى <code>struct tm</code>، إذ يُضبط هذا المؤشر إلى وقت التقويم الذي يشير إليه المؤشر <code>timer</code>، ويُمثل الوقت بحسب شروط التوقيت العالمي المُنسّق Coordinated Universal Time -أو اختصارًا UTC-، أو المسمى سابقًا بتوقيت جرينتش Greenwich Mean Time، ونحصل على مؤشر فارغ إذا كان توقيت UTC غير مُتاح.
	</li>
	<li>
		الدالة <code>localtime</code>: تحوّل الوقت الذي يشير إليه المؤشر <code>timer</code> إلى التوقيت المحلي وتُخزن النتيجة في <code>struct tm</code> وتُعيد مؤشرًا يشير إلى ذلك الهيكل.
	</li>
	<li>
		الدالة <code>strftime</code>: تملأ مصفوفة المحارف التي يشير إليها المؤشر <code>s</code> بـمقدار <code>maxsize</code> محرف على الأكثر، وتُستخدم السلسلة النصية <code>format</code> لتنسيق الوقت المُمثّل في الهيكل الذي يشير إليه المؤشر <code>timeptr</code>، تُنسخ المحارف الموجودة في سلسلة التنسيق النصية (متضمنة المحرف الفارغ في نهاية السلسلة) دون أي تغيير إلى المصفوفة إلا إن كان وجِد توجيه تنسيق من التوجيهات التالية، فعندها تُسنخ القيمة المُحددة ضمن الجدول إلى المصفوفة الهدف بما يوافق الإعدادات المحلية.
	</li>
</ul>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
	<tbody>
		<tr>
			<td>
				<code>‎%a</code>
			</td>
			<td>
				اسم يوم الأسبوع باختصار
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%A</code>
			</td>
			<td>
				اسم يوم الأسبوع كاملًا
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%b</code>
			</td>
			<td>
				اسم الشهر باختصار
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%B</code>
			</td>
			<td>
				اسم الشهر كاملًا
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%c</code>
			</td>
			<td>
				تمثيل التاريخ والوقت
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%d</code>
			</td>
			<td>
				تمثيل يوم الشهر عشريًا من 01 إلى 31
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%H</code>
			</td>
			<td>
				الساعة من 00 إلى 23 (تنسيق 24 ساعة)
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%I</code>
			</td>
			<td>
				الساعة من 01 إلى 12 (تنسيق 12 ساعة)
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%j</code>
			</td>
			<td>
				يوم السنة من 001 إلى 366
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%m</code>
			</td>
			<td>
				الشهر من 01 إلى 12
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%M</code>
			</td>
			<td>
				الدقيقة من 00 إلى 59
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%p</code>
			</td>
			<td>
				مكافئة PM أو AM المحلي
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%S</code>
			</td>
			<td>
				الثانية من 00 إلى 61
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%U</code>
			</td>
			<td>
				ترتيب الأسبوع ضمن السنة من 00 إلى 53 (الأحد هو اليوم الأول)
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%w</code>
			</td>
			<td>
				يوم الأسبوع من 0 إلى 6 (الأحد مُمثّل بالرقم 0)
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%W</code>
			</td>
			<td>
				ترتيب الأسبوع ضمن السنة من 00 إلى 53 (الاثنين هو اليوم الأول)
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%x</code>
			</td>
			<td>
				تمثيل التاريخ محليًا
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%X</code>
			</td>
			<td>
				تمثيل الوقت محليًا
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%y</code>
			</td>
			<td>
				السنة دون سابقة القرن من 00 إلى 99
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%Y</code>
			</td>
			<td>
				السنة مع سابقة القرن
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%Z</code>
			</td>
			<td>
				اسم المنطقة الزمنية، لا نحصل على محارف إن لم يكن هناك أي منطقة زمنية
			</td>
		</tr>
		<tr>
			<td>
				<code>‎%%</code>
			</td>
			<td>
				محرف <code>%</code>
			</td>
		</tr>
	</tbody>
</table>

<p>
	يُعاد عدد المحارف الكلية المنسوخة إلى <code>s*</code> باستثناء محرف الفراغ في نهاية السلسلة، وتُعاد القيمة صفر إذا لم يكن هناك أي مساحة (بحسب قيمة <code>maxsize</code>) للمحرف الفارغ في النهاية.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter9/" rel="external nofollow">Libraries</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/c/%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1831/" rel="">تطبيقات عملية في لغة سي C</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/c/%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-stdlib-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1797/" rel="">أدوات مكتبة stdlib في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A8%D9%86%D9%8A%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1607/" rel="">بنية برنامج لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1646/" rel="">الدوال في لغة C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%82%D9%8A%D9%85-%D8%A7%D9%84%D8%AD%D8%AF%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%B1%D9%8A%D8%A7%D8%B6%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1793/" rel="">القيم الحدية والدوال الرياضية في لغة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1830</guid><pubDate>Tue, 03 Jan 2023 16:08:00 +0000</pubDate></item><item><title>&#x623;&#x62F;&#x648;&#x627;&#x62A; &#x645;&#x643;&#x62A;&#x628;&#x629; stdlib &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-stdlib-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1797/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_11/6376178fe4966_------stdlib---C-.png.78ab674520091f8dc5474182659c92a8.png" /></p>
<p>
	نستعرض هنا الأدوات الموجودة في ملف الترويسة <code>&lt;stdlib.h&gt;</code> الذي يصرح عن عدد من الأنواع و<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-macro-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC-%D8%A7%D9%84%D9%85%D8%B3%D8%A8%D9%82-preprocessor-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1750/" rel="">الماكرو</a> وعدة دوال للاستخدام العام، تتضمن الأنواع والماكرو التالي:
</p>

<ul>
	<li>
		النوع <code>size_t</code>: تكلمنا عنه سابقًا.
	</li>
	<li>
		النوع <code>div_t</code>: نوع من الهياكل التي تعيدها الدالة <code>div</code>.
	</li>
	<li>
		النوع <code>ldiv_t</code>: نوع من الهياكل التي تعيدها الدالة <code>ldiv</code>.
	</li>
	<li>
		النوع <code>NULL</code>: تكلمنا عنه سابقًا.
	</li>
	<li>
		القيمة <code>EXIT_FAILURE</code> و <code>EXIT_SUCCESS</code>: يمكن استخدامهما مثل وسيط للدالة <code>exit</code>.
	</li>
	<li>
		القيمة <code>MB_CUR_MAX</code>: العدد الأعظمي للبايتات في محرف متعدد البايتات multibyte character من مجموعة المحارف الإضافية والمحددة حسب إعدادت اللغة المحلية locale.
	</li>
	<li>
		القيمة <code>RAND_MAX</code>: القيمة العظمى المُعادة من استدعاء دالة <code>rand</code>.
	</li>
</ul>

<h2>
	دوال تحويل السلسلة النصية
</h2>

<p>
	هناك ثلاث دوال تقبل سلسلة نصية وسيطًا لها وتحوّلها إلى عدد من نوع معين كما هو موضح هنا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1210_7" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="kwd">double</span><span class="pln"> atof</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">nptr</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">long</span><span class="pln"> atol</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">nptr</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> atoi</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">nptr</span><span class="pun">);</span></pre>

<p>
	نحصل على عدد مُحوّل مُعاد لكل من <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1646/" rel="">الدوال</a> الثلاث السابقة، ولا تضمن لك أيٌ من الدوال أن تضبط القيمة <code>errno</code> (إلا أن الأمر محقّق في بعض التنفيذات)، وتكون النتائج التي نحصل عليها من تحويلات تتسبب بحدوث طفحان overflow ولا يمكننا تمثيلها غير معرّفة.
</p>

<p>
	هناك بعض الدوال أكثر تعقيدًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1210_9" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="kwd">double</span><span class="pln"> strtod</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">nptr</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">**</span><span class="pln">endptr</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">long</span><span class="pln"> strtol</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">nptr</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">**</span><span class="pln">endptr</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> base</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> strtoul</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">nptr</span><span class="pun">,</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">**</span><span class="pln">endptr</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> base</span><span class="pun">);</span></pre>

<p>
	تعمل الدوال الثلاث السابقة بطريقة مشابهة، إذ يجري تجاهل أي مسافات فارغة بادئة ومن ثم يُعثر على الثابت المناسب <code>subject sequence</code> متبوعًا بسلسلة <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">محارف</a> غير معترف عليها، ويكون المحرف الفارغ في نهاية <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">السلسلة النصية</a> غير مُعترف عليه دائمًا. تُحدد السلسلة المذكورة حسب التالي:
</p>

<ul>
	<li>
		في الدالة <code>strtod</code>: إشارة "+" أو "-" اختيارية في البداية متبوعة بسلسلة أرقام تحتوي على محرف الفاصلة العشرية الاختياري وأس exponent اختياري أيضًا. لا يُعترف على أي لاحقة عائمة (ما بعد الفاصلة العشرية)، وتُعدّ الفاصلة العشرية تابعةً لسلسلة الأرقام إذا وُجدت.
	</li>
	<li>
		في الدالة <code>strtol</code>: إشارة "+" أو "-" اختيارية في البداية متبوعة بسلسلة أرقام، وتُؤخذ هذه الأرقام من الخانات العشرية أو من أحرف صغيرة lower case أو كبيرة upper case ضمن النطاق a إلى z في الأبجدية الإنجليزية ويُعطى لكل من هذه الأحرف القيم ضمن النطاق 10 إلى 35 بالترتيب. يحدِّد الوسيط <code>base</code> القيم المسموحة ويمكن أن تكون قيمة الوسيط صفر أو ضمن النطاق 2 إلى 36. يجري التعرُّف على الخانات التي تبلغ قيمتها أقل من الوسيط <code>base</code>، إذ تسمح القيمة 16 للوسيط <code>base</code> على سبيل المثال للمحارف <code>0x</code> أو <code>0X</code> أن تتبع الإشارة الاختيارية، بينما يسمح <code>base</code> بقيمة صفر أن تكون المحارف المدخلة على هيئة أعداد صحيحة ثابتة في سي، ولا يُعترف على أي لاحقة عدد صحيح.
	</li>
	<li>
		في الدالة <code>strtoul</code>: مطابقة للدالة <code>strtol</code> إلا أنها لا تسمح بوجود إشارة.
	</li>
</ul>

<p>
	يُخزّن عنوان أول محرف غير مُعترف عليه في الكائن الذي يشير إليه <code>endptr</code> في حال لم يكُن فارغًا، وتكون هذه قيمة <code>nptr</code> إذا كانت السلسلة فارغة أو ذات تنسيق خاطئ.
</p>

<p>
	تحوّل الدالة الرقم وتُعيده مع الأخذ بالحسبان كون وجود الإشارة البادئة مسموحًا أو لا، وذلك إذا كان إجراء التحويل ممكنًا، وإلا فإنها تعيد القيمة صفر. عند حدوث الطفحان أو حصول خطأ تُجرى العمليات التالية:
</p>

<ul>
	<li>
		في الدالة <code>strtod</code>: تُعيد عند الطفحان القيمة <code>HUGE_VAL±</code> وتعتمد الإشارة على إشارة النتيجة، وتُعيد القيمة صفر عند طفحان الحد الأدنى underflow ويُضبط <code>errno</code> في الحالتين إلى القيمة <code>ERANGE</code>.
	</li>
	<li>
		في الدالة <code>strtol</code>: تُعيد القيمة <code>LONG_MAX</code> أو <code>LONG_MIN</code> عند الطفحان بحسب إشارة النتيحة، ويُضبط <code>errno</code> في كلا الحالتين إلى القيمة <code>ERANGE</code>.
	</li>
	<li>
		في الدالة <code>strtoul</code>: تُعيد القيمة <code>ULONG_MAX</code> عند الطفحان، ويُضبط <code>errno</code> إلى القيمة <code>ERANGE</code>.
	</li>
</ul>

<p>
	قد يكون هناك سلاسل أخرى من الممكن التعرُّف عليها في بعض التنفيذات إذا لم تكن الإعدادات المحلية هي إعدادات سي التقليدية.
</p>

<h2>
	توليد الأرقام العشوائية
</h2>

<p>
	تقدِّم الدوال التالية طريقةً لتوليد الأرقام العشوائية الزائفة pseudo-random:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1210_14" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> rand</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> srand</span><span class="pun">(</span><span class="kwd">unsigned</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> seed</span><span class="pun">);</span></pre>

<p>
	تُعيد الدالة <code>rand</code> رقمًا عشوائيًا زائفًا ضمن النطاق من "0" إلى "RAND_MAX"، وهو ثابت قيمته على الأقل "32767".
</p>

<p>
	تسمح الدالة <code>srand</code> بتحديد نقطة بداية للنطاق المُختار طبقًا لقيمة الوسيط <code>seed</code>، وهي ذات قيمة "1" افتراضيًا إذا لم تُستدعى <code>srand</code> قبل <code>rand</code>، ونحصل على سلسلة قيم مطابقة من الدالة <code>rand</code> إذا استخدمنا قيمة <code>seed</code> ذاتها.
</p>

<p>
	يصف المعيار الخوارزمية المستخدمة في دالتي <code>rand</code> و <code>srand</code>، وتستخدم معظم التنفيذات هذه الخوارزمية عادةً.
</p>

<h2>
	حجز المساحة
</h2>

<p>
	تُستخدم هذه الدوال لحجز وتحرير المساحة، إذ يُضمن للمساحة التي حصلنا عليها أن تكون كبيرةً كفاية لتخزين كائن من نوع معين ومُحاذاة ضمن الذاكرة بحيث لا تتسبب بتفعيل استثناءات العنوان addressing exceptions، ولا يجب افتراض أي شيء آخر بخصوص عملها.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1210_17" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">malloc</span><span class="pun">(</span><span class="typ">size_t</span><span class="pln"> size</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">calloc</span><span class="pun">(</span><span class="typ">size_t</span><span class="pln"> nmemb</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> size</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">realloc</span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ptr</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> size</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">free</span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ptr</span><span class="pun">);</span></pre>

<p>
	تُعيد جميع دوال حجز المساحة مؤشرًا يشير إلى المساحة المحجوزة التي يبلغ حجمها <code>size</code> بايت، وإذا لم يكن هناك أي مساحة فارغة فهي تعيد مؤشرًا فارغًا، إلا أن الفرق بين الدوال هو أن دالة <code>calloc</code> تأخذ وسيطًا يدعى <code>nmemb</code> الذي يحدد عدد العناصر في <a href="https://academy.hsoub.com/programming/c/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1675/" rel="">مصفوفة</a>، وكل عنصر في هذه المصفوفة يبلغ حجمه <code>size</code> بايت، ولذا فهي تحجز مساحةً أكبر من التخزين عادةً من المساحة التي تحجزها <code>malloc</code>، كما أن المساحة المحجوز بواسطة <code>malloc</code> غير مُهيّئة بينما تُهيّأ جميع بتات مساحة التخزين المحجوزة بواسطة <code>calloc</code> إلى الصفر، إلا أن هذا الصفر ليس تمثيلًا مكافئًا للصفر ضمن الفاصلة العائمة floating-point أو مؤشرًا فارغًا null بالضرورة.
</p>

<p>
	تُستخدم الدالة <code>realloc</code> لتغيير حجم الشيء المُشار إليه بواسطة المؤشر <code>ptr</code> مما يتطلب بعض النسخ لإنجاز هذا الأمر ومن ثمّ تُحرّر مساحة التخزين القديمة. لا تُغيّر محتويات الكائن المُشار إليه بواسطة <code>ptr</code> بالنسبة للحجمين القديم والجديد، وتتصرف الدالة تصرفًا مماثلًا لتصرف <code>malloc</code> إذا كان المؤشر <code>ptr</code> مؤشرًا فارغًا وذلك للحجم المخصّص.
</p>

<p>
	تُستخدم الدالة <code>free</code> لتحرير المساحة المحجوزة مسبقًا من إحدى دوال حجز المساحة، ومن المسموح تمرير مؤشر فارغ إلى الدالة <code>free</code> وسيطًا لها، إلا أن الدالة في هذه الحالة لا تنفّذ أي شيء.
</p>

<p>
	إذا حاولت تحرير مساحة لم تُحجز مسبقًا تحصل على سلوك غير محدّد، ويتسبب هذا الأمر في العديد من التنفيذات باستثناء عنوان addressing exception مما يوقف البرنامج، إلا أن هذه ليست بدلالة يمكن الاعتماد عليها.
</p>

<h2>
	التواصل مع البيئة
</h2>

<p>
	سنستعرض مجموعةً من الدوال المتنوعة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1210_20" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> abort</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> atexit</span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">(*</span><span class="pln">func</span><span class="pun">)(</span><span class="kwd">void</span><span class="pun">));</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> exit</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> status</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">getenv</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> system</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">string</span><span class="pun">);</span></pre>

<ul>
	<li>
		دالة <code>abort</code>: تتسبب بإيقاف غير اعتيادي للبرنامج وذلك باستخدام الإشارة <code>SIGABRT</code>، ويمكن منع الإيقاف غير الاعتيادي فقط إذا حصلنا على الإشارة ولم يُعد معالج الإشارة signal handler أي قيمة، وإلا ستُحذف ملفات الخرج وقد يمكن أيضًا إزالة الملفات المؤقتة بحسب تعريف التنفيذ، وتُعاد حالة "إنهاء برنامج غير ناجح unsuccessful termination" إلى البيئة المُستضافة، كما أن هذه الدالة لا يمكن أن تُعيد أي قيمة.
	</li>
	<li>
		دالة <code>atexit</code>: يصبح وسيط الدالة <code>func</code> دالةً يمكن استدعاؤها دون استخدام أي وسطاء عندما يُغلق البرنامج، ويمكن استخدام ما لا يقل عن 32 دالة مشابهة لهذه الدالة وأن تُستدعى عند إغلاق البرنامج بصورةٍ مُعاكسة لتسجيل كلٍّ منها، ونحصل على القيمة المُعادة صفر للدلالة على النجاح وإلا فقيمة غير صفرية للدلالة على الفشل.
	</li>
	<li>
		دالة <code>exit</code>: تُستدعى هذه الدالة عادةً لإنهاء البرنامج على نحوٍ اعتيادي، وتُستدعى عند تنفيذ الدالة جميع الدوال المُسجّلة باستخدام دالة <code>atexit</code>، لكن انتبه، إذ ستُعد الدالة <code>main</code> بحلول هذه النقطة قد أعادت قيمتها ولا يمكن استخدام أي كائنات ذات مدة تخزين تلقائي automatic storage duration على نحوٍ آمن، من ثمّ تُحذف محتويات جميع مجاري <a href="https://academy.hsoub.com/programming/c/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%B9%D9%86-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-io-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1795/" rel="">الخرج</a> output streams وتُغلق وتُزال جميع الملفات التلقائية المُنشأة بواسطة <code>tmpfile</code>، وأخيرًا يُعيد البرنامج التحكم إلى البيئة المستضافة بإعادة حالة نجاح أو فشل محدّدة بحسب التنفيذ، وتعتمد الحالة على إذا ما كان وسيط الدالة <code>exit</code> مساوٍ للقيمة <code>EXITSUCCESS</code> (وهذه حالة النجاح) أو <code>EXITFAILURE</code> (حالة الفشل). للتوافق مع لغة سي القديمة، تُستخدم القيمة صفر بدلًا من <code>EXITFAILURE</code>، بينما يكون لباقي القيم تأثيرات معرّفة بحسب التنفيذ. <strong>لا يمكن</strong> أن تُعاد حالة الخروج.
	</li>
	<li>
		دالة <code>getenv</code>: يجري البحث في <strong>لائحة البيئة environment list</strong> المعرفة بحسب التنفيذ بهدف العثور على عنصر يوافق السلسلة النصية المُشار إليها بواسطة وسيط الاسم <code>name</code>، إذ تُعيد الدالة مؤشرًا إلى العنصر يشير إلى مصفوفة لا يمكن تعديلها من قبل البرنامج ويمكن تعديلها باستدعاء لاحق للدالة <code>getenv</code>، ونحصل على مؤشر فارغ إذا لم يُعثر على عنصر موافق. يعتمد الهدف من لائحة البيئة وتنفيذها على البيئة المُستضافة.
	</li>
	<li>
		دالة <code>system</code>: تُمرّر سلسلة نصية إلى أمر معالج مُعرف حسب التنفيذ، ويتسبب مؤشر فارغ بإعادة القيمة صفر، وقيمة غير صفرية إذا لم يكن الأمر موجودًا، بينما يتسبب مؤشر غير فارغ بمعالجة الأمر. نتيجة الأمر والقيمة المُعادة معرف حسب التنفيذ.
	</li>
</ul>

<h2>
	البحث والترتيب
</h2>

<p>
	هناك دالتان ضمن هذا التصنيف، أولهما دالة للبحث ضمن لائحة مُرتّبة والأخرى لترتيب لائحة غير مرتبة، واستخدام الدالتان عام، إذ يمكن استخدامهما في مصفوفات من أي سعة وعناصرها من أي حجم.
</p>

<p>
	يجب أن يلجأ المستخدم إلى دالة مقارنة إذا أراد مقارنة عنصرين عند استخدام الدوال السابقة، إذ تُستدعى هذه الدالة باستخدام المؤشرين الذين يشيران إلى العنصرين مثل وسطاء الدالة، وتُعيد الدالة قيمةً أقل من الصفر إذا كانت قيمة المؤشر الأول أصغر من قيمة المؤشر الثاني، وقيمة أكبر من الصفر إذا كانت قيمة المؤشر الأول أكبر من المؤشر الثاني، والقيمة صفر إذا كانت قيمتا المؤشرين متساويين.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1210_23" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">bsearch</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">key</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">base</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">size_t</span><span class="pln"> nmemb</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> size</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> </span><span class="pun">(*</span><span class="pln">compar</span><span class="pun">)(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*));</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">qsort</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">base</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> nmemb</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">size_t</span><span class="pln"> size</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> </span><span class="pun">(*</span><span class="pln">compar</span><span class="pun">)(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*));</span></pre>

<p>
	يمثّل الوسيط <code>nmemb</code> في كلٍّ من الدالتين السابقتين عدد العناصر في المصفوفة، ويمثل الوسيط <code>size</code> حجم عنصر المصفوفة الواحد بالبايت و <code>compar</code> هي الدالة التي تُستدعى للمقارنة بين العنصرين، بينما يشير المؤشر <code>base</code> إلى أساس المصفوفة أي بدايتها.
</p>

<p>
	ترتّب الدالة <code>qsort</code> المصفوفة ترتيبًا تصاعديًا.
</p>

<p>
	تفترض الدالة <code>bsearch</code> أن المصفوفة مرتّبة مسبقًا وتُعيد مؤشرًا إلى أي عنصر يتساوى مع العنصر المُشار إليه بالمؤشر <code>key</code>، وتُعيد الدالة مؤشرًا فارغًا إذا لم تجد أي عناصر متساوية.
</p>

<h2>
	دوال العمليات الحسابية الصحيحة
</h2>

<p>
	تقدّم هذه الدوال طريقةً لإيجاد القيمة المطلقة لوسيط يمثل عدد صحيح، إضافةً لحاصل القسمة والباقي من العملية لكلٍّ من النوعين <code>int</code> و <code>long</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1210_25" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> abs</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> j</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">long</span><span class="pln"> labs</span><span class="pun">(</span><span class="kwd">long</span><span class="pln"> j</span><span class="pun">);</span><span class="pln">

</span><span class="typ">div_t</span><span class="pln"> div</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> numerator</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> denominator</span><span class="pun">);</span><span class="pln">
</span><span class="typ">ldiv_t</span><span class="pln"> ldiv</span><span class="pun">(</span><span class="kwd">long</span><span class="pln"> numerator</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> denominator</span><span class="pun">);</span></pre>

<ul>
	<li>
		الدالتان <code>abs</code> و <code>labs</code>: تُعيدان القيمة المطلقة لوسيطهما ويجب اختيار الدالة المناسبة بحسب احتياجاتك. نحصل على سلوك غير معرّف إذا لم تكن القيمة ممكنة التمثيل وقد يحدث ذلك في الأنظمة التي تعمل بنظام المتمم الثنائي two's complement systems، إذ لا يوجد لأكثر رقم سلبيّة أي مكافئ إيجابي.
	</li>
	<li>
		الدالتان <code>div</code> و<code>ldiv</code>: تُقسّمان الوسيط <code>numerator</code> على الوسيط <code>denominator</code> وتُعيدان هيكلًا structure للنوع المحدد، وفي أي حالة، سيحتوي الهيكل على عضو يدعى <code>quot</code> يحتوي على حاصل القسمة الصحيحة وعضوًا آخر يدعى <code>rem</code> يحتوي على باقي القسمة، ونوع العضوين هو <code>int</code> في الدالة <code>div</code> و <code>long</code> في الدالة <code>ldiv</code>، ويمكن تمثيل نتيجة العملية على النحو التالي:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1210_27" style=""><span class="pln">quot</span><span class="pun">*</span><span class="pln">denominator</span><span class="pun">+</span><span class="pln">rem </span><span class="pun">==</span><span class="pln"> numerator</span></pre>

<h2>
	الدوال التي تستخدم المحارف متعددة البايت
</h2>

<p>
	يؤثر تصنيف <code>LC_CTYPE</code> ضمن الإعدادات المحلية الحالية على سلوك هذه الدوال، إذ تُضبط كل دالة إلى حالة ابتدائية باستدعاء يكون وسيطها <code>s</code> الذي يمثل مؤشر المحرف فارغًا null، وذلك في حالة الترميز المُعتمد على الحالة state-dependent endcoding، وتُغيَّر حالة الدالة الداخلية وفق الضرورة عن طريق استدعاءات لاحقة عندما لا يكون <code>s</code> مؤشرًا فارغًا. تُعيد الدالة قيمةً غير صفرية إذا كان الترميز معتمدًا على الحالة، وإلا فتعيد القيمة صفر إذا كان المؤشر <code>s</code> فارغًا. تصبح حالة الإزاحة shift state الخاصة بالدوال غير محددة indeterminate إذا حدث تغيير للتصنيف <code>LC_TYPE</code>.
</p>

<p>
	الدوال هي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1210_29" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> mblen</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> n</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> mbtowc</span><span class="pun">(</span><span class="typ">wchar_t</span><span class="pln"> </span><span class="pun">*</span><span class="pln">pwc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> n</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> wctomb</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">,</span><span class="pln"> </span><span class="typ">wchar_t</span><span class="pln"> wchar</span><span class="pun">);</span><span class="pln">
</span><span class="typ">size_t</span><span class="pln"> mbstowcs</span><span class="pun">(</span><span class="typ">wchar_t</span><span class="pln"> </span><span class="pun">*</span><span class="pln">pwcs</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> n</span><span class="pun">);</span><span class="pln">
</span><span class="typ">size_t</span><span class="pln"> wcstombs</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="typ">wchar_t</span><span class="pln"> </span><span class="pun">*</span><span class="pln">pwcs</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> n</span><span class="pun">);</span></pre>

<ul>
	<li>
		الدالة <code>mblen</code>: تُعيد عدد البايتات المُحتواة بداخل محرف متعدد البايتات multibyte character المُشار إليه بواسطة المؤشر <code>s</code> أو تُعيد القيمة ‎-1 إذا كان أول <code>n</code> بايت لا يشكّل محرف متعدد البايتات صالحًا، أو تُعيد القيمة صفر إذا كان المؤشر يشير إلى محرف فارغ.
	</li>
	<li>
		الدالة <code>mbtowc</code>: تُحوِّل محرف متعدد البايتات يُشير إليه المؤشر <code>s</code> إلى الرمز الموافق له من النوع <code>wchar_t</code> وتُخزّن النتيجة في الكائن المُشار إليه بالمؤشر <code>pwc</code>، إلا إن كان <code>pwc</code> مؤشرًا فارغًا، وتُعيد الدالة عدد البايتات المُحوّلة بنجاح، أو ‎-1 إذا لم تشكّل أول <code>n</code> بايت محرفًا متعدد البايت صالحًا، ولا يُفحص أكثر من <code>n</code> بايت يُشير إليه المؤشر <code>s</code>، ولن تتعدى القيمة المُعادة قيمة <code>n</code> أو <code>MB_CUR_MAX</code>.
	</li>
	<li>
		دالة <code>wctmob</code>: تُحوِّل رمز القيمة <code>wchar</code> إلى سلسلة من البايتات تمثل محرف متعدد البايتات وتخزن النتيجة في مصفوفة يشير إليها المؤشر <code>s</code> وذلك إن لم يكن <code>s</code> مؤشرًا فارغًا، وتُعيد الدالة عدد البايتات المحتواة داخل محرف متعدد البايتات، أو ‎-1 إذا كانت القيمة المخزنة في <code>wchar</code> لا تمثل محرف متعدد البايات، ومن غير الممكن معالجة عدد بايتات يتجاوز <code>MB_CUR_MAX</code>.
	</li>
	<li>
		دالة <code>mbstowcs</code>: تحوّل سلسلة محارف متعددة البايتات بدءًا من الحالة الأولية للإزاحة initial shift state وذلك ضمن المصفوفة التي يشير إليها المؤشر <code>s</code> إلى سلسلة من الرموز الموافقة ومن ثم تخزّنها في مصفوفة يشير إليها المؤشر <code>pwcs</code>، لا يُخزّن ما يزيد عن <code>n</code> قيمة في <code>pwcs</code>، وتُعيد الدالة 1- إذا صادفت محرفًا متعدد البايت غير صالح، وإلا فإنها تعيد عدد عناصر المصفوفة المُعدّلة باستثناء رمز إنهاء المصفوفة.
	</li>
</ul>

<p>
	نحصل على سلوك غير معرّف إذا وجد كائنين متقاطعين.
</p>

<ul>
	<li>
		الدالة <code>wcstombs</code>: تُحوِّل سلسلة من الرموز المُشار إليها بالمؤشر <code>pwcs</code> إلى سلسلة من المحارف متعددة البايتات بدءًا من الحالة الأولية للإزاحة وتُخزّن فيما بعد في مصفوفة مُشار إليها بالمؤشر <code>s</code>. تتوقف عملية التحويل عند مصادفة رمز فارغ، أو عند كتابة <code>n</code> بايت إلى <code>s</code>، وتُعيد الدالة ‎-1 إذا كان الرمز المُصادف لا يمثل محرفًا متعدد البايتات صالحًا، وإلا فيُعاد عدد البايتات التي كُتبت باستثناء رمز الإنهاء الفارغ.
	</li>
</ul>

<p>
	نحصل على سلوك غير محدد إذا وجد كائنين متقاطعين.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter9/" rel="external nofollow">Libraries</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/c/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D9%88%D9%82%D8%AA-%D9%88%D8%A7%D9%84%D8%AA%D8%A7%D8%B1%D9%8A%D8%AE-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1830/" rel="">دوال التعامل مع السلاسل النصية والوقت والتاريخ في لغة سي C</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-io-%D9%88%D8%AA%D9%86%D8%B3%D9%8A%D9%82%D9%87-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1796/" rel="">التعامل مع الدخل والخرج I/O وتنسيقه في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1794/" rel="">التعامل مع المكتبات في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%B6%D8%A8%D8%B7-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D9%88%D8%B7%D9%8A%D9%86-localization-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1778/" rel="">التعامل مع المحارف وضبط إعدادات التوطين localization في لغة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1797</guid><pubDate>Thu, 29 Dec 2022 16:01:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x62F;&#x62E;&#x644; &#x648;&#x627;&#x644;&#x62E;&#x631;&#x62C; I/O &#x648;&#x62A;&#x646;&#x633;&#x64A;&#x642;&#x647; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-io-%D9%88%D8%AA%D9%86%D8%B3%D9%8A%D9%82%D9%87-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1796/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_11/637612c2ddd88_---IO--Formatted---Unformatted---C.png.8d15230fa8349b7cf0fd6c5a9a9ba226.png" /></p>
<p>
	تكلّمنا سابقًا عن <a href="https://academy.hsoub.com/programming/c/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%B9%D9%86-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-io-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1795/" rel="">الدخل والخرج</a> في لغة سي بالاستعانة بالمكتبات القياسية، وحان الوقت لنتعلم الآن مختلف <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1646/" rel="">الدوال</a> الموجودة في هذه المكتبات التي تضمن لنا أساليب مختلفة في القراءة والكتابة.
</p>

<h2>
	الدخل والخرج المنسق
</h2>

<p>
	هناك عدد من الدوال المُستخدمة لتنسيق الدخل والخرج، وتحدد كلًا من هذه الدوال التنسيق المتبع للدخل والخرج باستخدام <strong>سلسلة التنسيق النصية format string</strong>، وتتألف سلسلة التنسيق النصية في حالة الخرج من نص عادي plain text يمثّل الخرج كما هو، إضافةً إلى <strong>مواصفات التنسيق format specifications</strong> التي تتطلب معالجة خاصة لواحد من الوسطاء المتبقية في الدالة، بينما تتألف سلسلة التنسيق النصية في حالة الخرج من نص عادي يطابق مجرى الدخل وتحدد هنا مواصفات التنسيق معنى الوسطاء المتبقية.
</p>

<p>
	يُشار إلى كل واحدة من مواصفات التنسيق باستخدام <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">المحرف</a> "%" متبوعًا ببقية التوصيف.
</p>

<h3>
	الخرج: دوال printf
</h3>

<p>
	تتخذ مواصفات التنسيق في دوال الخرج الشكل التالي، ونشير إلى الأجزاء الاختيارية بوضعها بين قوسين:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6687_10" style=""><span class="pun">%&lt;</span><span class="pln">flags</span><span class="pun">&gt;&lt;</span><span class="pln">field width</span><span class="pun">&gt;&lt;</span><span class="pln">precision</span><span class="pun">&gt;&lt;</span><span class="pln">length</span><span class="pun">&gt;</span><span class="pln">conversion</span></pre>

<p>
	نشرح معنى كل من الراية flag وعرض الحقل field width والدقة precision والطول length والتحويل conversion أدناه، إلا أنه من الأفضل النظر إلى وصف المعيار إذا أردت وصفًا مطولًا ودقيقًا.
</p>

<h3>
	الرايات
</h3>

<p>
	يمكن ألا تأخذ الرايات أي قيمة أو أن تأخذ أحد القيم التالية:
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
	<thead>
		<tr>
			<th>
				قيمة الراية
			</th>
			<th>
				الشرح
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				-
			</td>
			<td>
				ملئ سطر التحويل من اليسار ضمن حقوله.
			</td>
		</tr>
		<tr>
			<td>
				+
			</td>
			<td>
				يبدأ التحويل ذو الإشارة بإشارة زائد أو ناقص دائمًا.
			</td>
		</tr>
		<tr>
			<td>
				مسافة فارغة space
			</td>
			<td>
				إذا كان المحرف الأول من تحويل ذو إشارة ليس بإشارة، أضف مسافةً فارغة، ويمكن تجاوز الراية باستخدام "+" إذا وجدت.
			</td>
		</tr>
		<tr>
			<td>
				#
			</td>
			<td>
				يُجبر استخدام تنسيق مختلف للخرج، مثل: الخانة الأولى لأي تحويل ثماني لها القيمة "0"، وإضافة "0x" أمام أي تحويل ست عشري لا تساوي قيمته الصفر، ويُجبِر الفاصلة العشرية في جميع تحويلات الفاصلة العائمة حتى إن لم تكن ضرورية، ولا يُزيل أي صفر لاحق من تحويلات g و G.
			</td>
		</tr>
		<tr>
			<td>
				0
			</td>
			<td>
				يُضيف إلى تحويلات d و i و o و u و x و X و e و E و f و F و G أصفارًا إلى يسارها بملئ عرض الحقل، ويمكن تجاوزه باستخدام الراية "-"، وتُتجاهل الراية إذا كان هناك أي دقة محددة لتحويلات d، أو i، أو o، أو u، أو x، أو X، ونحصل على سلوك غير معرف للتعريفات الأخرى.
			</td>
		</tr>
		<tr>
			<td>
				عرض الحقل field width
			</td>
			<td>
				عدد صحيح عشري يحدد عرض حقل الخرج الأدنى ويمكن تجاوزه إن لزم الأمر، يُحوّل الوسيط التالي إلى عدد صحيح ويُستخدم مثل قيمة لعرض الحقل إن استُخدمت علامة النجمة "*"، وتُعامل هذه القيمة إذا كانت سالبة كأنها راية "-" متبوعة بعرض حقل ذي قيمة موجبة. يُملأ الخرج ذو الطول الأقصر من عرض الحقل بالمسافات الفارغة (أو بأصفار إذا كان العدد الصحيح المعبر عن عرض الحقل يبدأ بالصفر)، ويُملأ الخرج من الجهة اليسرى إلا إذا حُدّدَت راية تعديل اليسار left-adjustment.
			</td>
		</tr>
		<tr>
			<td>
				الدقة precision
			</td>
			<td>
				تبدأ قيمة الدقة بالنقطة '.'، وهي تحدد عدد الخانات الدنيا لتحويلات d، أو i، أو o، أو u، أو x، أو X، أو عدد الخانات التي تلي الفاصلة العشرية في تحويلات e، أو E، أو f، أو العدد الأعظمي لخانات تحويلات g وG، أو عدد المحارف المطبوعة من سلسلة نصية في تحويلات s. يتسبب تحديد كمية حشو الحقل padding بتجاهل قيمة <code>field width</code>. يُحوَّل الوسيط التالي في حال استخدامنا لعلامة النجمة "*" إلى عدد صحيح ويُستخدم بمثابة قيمة لعرض الحقل، وتعامل القيمة كأنها مفقودة إذا كانت سالبة، وتكون الدقة صفر إذا وجدت النقطة فقط.
			</td>
		</tr>
		<tr>
			<td>
				الطول length
			</td>
			<td>
				وهي <code>h</code> تسبق محدد specifier لطباعة نوع عدد صحيح integral ويتسبب ذلك في معاملتها وكأنها من النوع "short" (لاحظ أن الأنواع المختلفة القصيرة shorts تُرقّى إلى واحدة من أنواع القيم الصحيحة int عندما تُمرّر مثل وسيط). تعمل <code>l</code> مثل عمل <code>h</code> إلا أنها تُطبّق على وسيط عدد صحيح من نوع "long"، وتُستخدم <code>L</code> للدلالة على أنه يجب طباعة وسيط من نوع "long double"، ويطبَّق ذلك فقط على محددات الفاصلة العائمة. يتسبب استخدام هذا في سلوك غير معرف إذا كانت باستخدام النوع الخاطئ من التحويلات.
			</td>
		</tr>
	</tbody>
</table>

<p>
	يوضح الجدول التالي أنواع التحويلات:
</p>

<table>
	<thead>
		<tr>
			<th>
				المحدد
			</th>
			<th>
				التأثير
			</th>
			<th>
				الدقة الافتراضية
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				d
			</td>
			<td>
				عدد عشري ذو إشارة
			</td>
			<td>
				1
			</td>
		</tr>
		<tr>
			<td>
				i
			</td>
			<td>
				عدد عشري ذو إشارة
			</td>
			<td>
				1
			</td>
		</tr>
		<tr>
			<td>
				u
			</td>
			<td>
				عدد عشري عديم الإشارة
			</td>
			<td>
				1
			</td>
		</tr>
		<tr>
			<td>
				o
			</td>
			<td>
				عدد ثماني عديم الإشارة
			</td>
			<td>
				1
			</td>
		</tr>
		<tr>
			<td>
				x
			</td>
			<td>
				عدد ست عشري عديم الإشارة من <code>0</code> إلى <code>f</code>
			</td>
			<td>
				1
			</td>
		</tr>
		<tr>
			<td>
				X
			</td>
			<td>
				عدد ست عشري عديم الإشارة من <code>0</code> إلى <code>F</code>
			</td>
			<td>
				1
			</td>
		</tr>
		<tr>
			<td>
				 
			</td>
			<td>
				تحدد <strong>الدقة</strong> عدد خانات الأدنى المُستبدل بأصفار إن لزم الأمر، ونحصل على خرج دون أي محارف عند استخدام الدقة صفر لطباعة القيمة صفر
			</td>
			<td>
				 
			</td>
		</tr>
		<tr>
			<td>
				f
			</td>
			<td>
				يطبع قيمة من النوع <code>double</code> بعدد خانات الدقة (المقربة) بعد الفاصلة العشرية. استخدم دقة بقيمة صفر للحد من الفاصلة العشرية، وإلا فستظهر خانة واحدة على الأقل بعد الفاصلة العشرية
			</td>
			<td>
				6
			</td>
		</tr>
		<tr>
			<td>
				e, E
			</td>
			<td>
				يطبع قيمة من نوع double بالتنسيق الأسي مُقرّبًا بخانة واحدة قبل الفاصلة العشرية، وعدد من الخانات يبلغ الدقة المحددة بعده، وتُلغى الفاصلة العشرية عند استخدام الدقة صفر، وللأس خانتان على الأقل تطبع بالشكل <code>1.23e15</code> في تنسيق e أو 1.23E15 في حالة التنسيق E
			</td>
			<td>
				6
			</td>
		</tr>
		<tr>
			<td>
				g, G
			</td>
			<td>
				تستخدم أسلوب التنسيق f، أو e (E مع G) بحسب الأس، ولا يُستخدم التنسيق f إذا كان الأس أصغر من "‎-4" أو أكبر أو يساوي الدقة. تُحدّ الأصفار التي تتبع القيمة وتُطبع الفاصلة العشرية فقط في حال وجود خانات تابعة.
			</td>
			<td>
				غير محدد
			</td>
		</tr>
		<tr>
			<td>
				c
			</td>
			<td>
				يُحوّل الوسيط من نوع عدد صحيح إلى محرف عديم الإشارة ويُطبع المحرف الناتج عن التحويل
			</td>
			<td>
				 
			</td>
		</tr>
		<tr>
			<td>
				s
			</td>
			<td>
				تُطبع سلسلة نصية بطول خانات الدقة، ويجب إنهاء السلسلة النصية باستخدام <code>NUL</code> إذا لم تُحدّد الدقة أو كانت أكبر من طول السلسلة النصية
			</td>
			<td>
				لا نهائي
			</td>
		</tr>
		<tr>
			<td>
				p
			</td>
			<td>
				إظهار قيمة مؤشر من نوع (void *‎) بطريقة تعتمد على النظام
			</td>
			<td>
				 
			</td>
		</tr>
		<tr>
			<td>
				n
			</td>
			<td>
				يجب أن يكون الوسيط مؤشرًا يشير إلى عدد صحيح، ويكون عدد محارف الخرج باستخدام هذا الاستدعاء مُسندًا إلى العدد الصحيح
			</td>
			<td>
				 
			</td>
		</tr>
		<tr>
			<td>
				%
			</td>
			<td>
				علامة "%"
			</td>
			<td>
				_
			</td>
		</tr>
	</tbody>
</table>

<p style="text-align: center;">
	[جدول 1 التحويلات]
</p>

<p>
	تجد وصف الدوال التي تستخدم هذه التنسيقات في الجدول التالي، وجميع الدوال المُستخدمة مُضمّنة في المكتبة <code>&lt;stdio.h&gt;</code>، إليك تصاريح هذه الدوال:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6687_12" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> fprintf</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">format</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> printf</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">format</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> sprintf</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">format</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...);</span><span class="pln">

</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdarg.h&gt;</span><span class="pln">     </span><span class="com">// بالإضافة إلى‫ stdio.h</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> vfprintf</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">format</span><span class="pun">,</span><span class="pln"> va </span><span class="typ">list</span><span class="pln"> arg</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> vprintf</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">format</span><span class="pun">,</span><span class="pln"> va </span><span class="typ">list</span><span class="pln"> arg</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> vsprintf</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">format</span><span class="pun">,</span><span class="pln"> va </span><span class="typ">list</span><span class="pln"> arg</span><span class="pun">);</span></pre>

<table>
	<thead>
		<tr>
			<th>
				الاسم
			</th>
			<th>
				الاستخدام
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<code>fprintf</code>
			</td>
			<td>
				نحصل على الخرج المنسق العام بواسطتها كما وصفنا سابقًا، ويكتب الخرج إلى الملف المُحدد باستخدام المجرى <code>stream</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>printf</code>
			</td>
			<td>
				دالة مُطابقة لعمل الدالة <code>fprintf</code> إلا أن وسيطها الأول هو <code>stdout</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>sprintf</code>
			</td>
			<td>
				دالة مُطابقة لعمل الدالة <code>fprintf</code> باستثناء أن خرجها لا يُكتب إلى ملف، بل يُكتب إلى مصفوفة محارف يُشار إليها باستخدام المؤشر <code>s</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>vfprintf</code>
			</td>
			<td>
				خرج مُنسَّق مشابه لخرج الدالة <code>fprintf</code> إلا أن لائحة الوسطاء المتغيرة تُستبدل بالوسيط <code>arg</code> الذي يجب أن يُهيَّأ باستخدام <code>va_start</code>، ولا تُستدعى <code>va_end</code> باستخدام هذه الدالة
			</td>
		</tr>
		<tr>
			<td>
				<code>vprintf</code>
			</td>
			<td>
				مطابقة للدالة <code>vfprintf</code> باستثناء أن الوسيط الأول يساوي إلى <code>stdout</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>vsprintf</code>
			</td>
			<td>
				خرج مُنسَّق مشابه لخرج الدالة <code>sprintf</code> إلا أن لائحة الوسطاء المتغيرة تُستبدل بالوسيط <code>arg</code> الذي يجب أن يُهيَّأ باستخدام <code>va_start</code>، ولا تُستدعى <code>va_end</code> باستخدام هذه الدالة
			</td>
		</tr>
	</tbody>
</table>

<p style="text-align: center;">
	[جدول 2 الدوال التي تطبع خرجًا مُنسَّقًا]
</p>

<p>
	تُعيد كل الدوال السابقة عدد المحارف المطبوعة أو قيمة سالبة للدلالة على حصول خطأ، ولا يُحسب المحرف الفارغ الزائد بواسطة دالة <code>sprintf</code> و <code>vsprintf</code>.
</p>

<p>
	يجب أن تسمح التنفيذات بالحصول على 509 محارف على الأقل عند استخدام أي تحويل.
</p>

<h3>
	الدخل: دوال scanf
</h3>

<p>
	هناك عدة دوال مشابهة لمجموعة دوال <code>printf</code> بهدف الحصول على الدخل، والفارق الواضح بين مجموعتي الدوال هذه هو أن مجموعة دوال <code>scanf</code> تأخذ وسيطًا متمثلًا بمؤشر حتى تُسند القيم المقروءة إلى وجهتها المناسبة، ويُعد نسيان تمرير المؤشر خطأ شائع الحدوث ولا يمكن للمصرّف أن يكتشف حدوثه، إذ يمنع استخدام لائحة وسطاء متغيرة من ذلك.
</p>

<p>
	تُستخدم سلسلة التنسيق النصية للتحكم بتفسير المجرى للبيانات المُدخلة التي تحتوي غالبًا على قيم تُسند إلى كائنات يُشار إليها باستخدام وسطاء دالة <code>scanf</code> المتبقية، وقد تتألف سلسلة التنسيق النصية من:
</p>

<ul>
	<li>
		مساحة فارغة white space: تتسبب بقراءة مجرى الدخل إلى المحرف التالي الذي لا يمثّل محرف مسافة فارغة.
	</li>
	<li>
		محرف اعتيادي ordinary character: ويمثل المحرف أي محرف عدا محارف السلسلة الفارغة أو "%"، ويجب أن يطابق المحرف التالي في مجرى الدخل هذا المحرف المُحدّد.
	</li>
	<li>
		توصيف التحويل conversion specification: وهو محرف "%" متبوع بمحرف "*" اختياري (الذي يكبح التحويل)، ويُتبع بعدد عشري صحيح لا يساوي الصفر يحدد عرض الحقل الأعظمي، ومحرف "h"، أو "l"، أو "L" اختياري للتحكم بطول التحويل، وأخيرًا محدد تحويل إجباري. لاحظ أن استخدام "h"، أو "l"، أو "L" سيؤثر على على نوع المؤشر الواجب استخدامه.
	</li>
</ul>

<p>
	حقل الدخل -باستثناء المحددات "c" و "n" و "]"- هو سلسلة من المحارف التي لا تمثل مسافة فارغة وتبدأ من أول محرف في الدخل (بشرط ألا يكون المحرف مسافة فارغة)، وتُنهى السلسلة عند أول محرف متعارض أو عند الوصول إلى عرض الحقل المُحدّد.
</p>

<p>
	تُسند النتيجة إلى الشيء الذي يُشير إليه الوسيط إلا إذا كان الإسناد مكبوحًا باستخدام "*" المذكورة سابقًا، ويمكن استخدام محددات التحويل التالية:
</p>

<ul>
	<li>
		المحددات <code>d i o u x</code>: تُحوِّل <code>d</code> عدد صحيح ذو إشارة، وتحوّل <code>i</code> عدد صحيح ذو إشارة وتنسيق ملائم لـ<code>strtol</code>، وتحوِّل <code>o</code> عدد صحيح ثماني، وتحوّل <code>u</code> عدد صحيح عديم الإشارة، وتحول <code>x</code> عدد صحيح ست عشري.
	</li>
	<li>
		المحددات <code>e f g</code>: تحوِّل قيمة من نوع <code>float</code> (وليس <code>double</code>).
	</li>
	<li>
		المحدد <code>s</code>: يقرأ سلسلة نصية ويُضيف محرف فارغ في نهايته، وتُنهى السلسلة النصية باستخدام مسافة فارغة عند الدخل (ولا تُقرأ هذه المسافة الفارغة على أنها جزء من الدخل).
	</li>
	<li>
		المحدد <code>]</code>: يقرأ سلسلة نصية، وتتبع <code>]</code> لائحة من المحارف تُدعى <strong>مجموعة المسح scan set</strong>، ويُنهي المحرف <code>[</code> هذه اللائحة. تُقرأ المحارف إلى (غير متضمنةً) المحرف الأول غير الموجود ضمن مجموعة المسح؛ فإذا كان المحرف الأول في اللائحة هو "^" فهذا يعني أن مجموعة القراءة تحتوي على أي محرف غير موجود في هذه القائمة، وإذا كانت السلسلة الأولية هي "[^]" أو "[]" فهذا يعني أن <code>[</code> ليس محدّدًا بل جزءًا من السلسلة ويجب إضافة محرف "[" آخر لإنهاء اللائحة. إذا وجدت علامة ناقص "-" في اللائحة، يجب أن يكون موقعها المحرف الأول أو الأخير، وإلا فإن معناها معرف بحسب التنفيذ.
	</li>
	<li>
		المحدد <code>c</code>: يقرأ محرفًا واحدًا متضمنًا محارف المسافات الفارغة، ولقراءة المحرف الأول باستثناء محارف المسافات الفارغة استخدم <code>‎%1s</code>، ويحدد عرض الحقل مصفوفة المحارف التي يجب قراءتها.
	</li>
	<li>
		المحدد <code>p</code>: يقرأ مؤشرًا من النوع (void *‎) والمكتوب سابقًا باستخدام المحدد <code>‎%p</code> ضمن استدعاء سابق لمجموعة دوال <code>printf</code>.
	</li>
	<li>
		المحدد <code>%</code>: المحرف "%" متوقّع في الدخل ولا يُجرى أي إسناد.
	</li>
	<li>
		المحدد <code>n</code>: يُعاد عددًا صحيح يمثل عدد المحارف المقروءة باستخدام هذا الاستدعاء.
	</li>
</ul>

<p>
	يوضح الجدول التالي تأثير محددات الحجم size specifiers:
</p>

<table>
	<thead>
		<tr>
			<th>
				المحدد
			</th>
			<th>
				يُحدِّد
			</th>
			<th>
				يُحوِّل
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<code>l</code>
			</td>
			<td>
				<code>d i o u x</code>
			</td>
			<td>
				عدد صحيح كبير long int
			</td>
		</tr>
		<tr>
			<td>
				<code>h</code>
			</td>
			<td>
				<code>d i o u x</code>
			</td>
			<td>
				عدد صحيح صغير short int
			</td>
		</tr>
		<tr>
			<td>
				<code>l</code>
			</td>
			<td>
				<code>e f</code>
			</td>
			<td>
				عدد عشري مضاعف double
			</td>
		</tr>
		<tr>
			<td>
				<code>L</code>
			</td>
			<td>
				<code>e f</code>
			</td>
			<td>
				عدد عشري مضاعف كبير long double
			</td>
		</tr>
	</tbody>
</table>

<p style="text-align: center;">
	[جدول 3 محددات الحجم]
</p>

<p>
	إليك وصف دوال مجموعة <code>scanf</code> مع تصاريحها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6687_14" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> fscanf</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">format</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> sscanf</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">format</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> scanf</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">format</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...);</span></pre>

<p>
	تأخذ الدالة <code>fscanf</code> دخلها من المجرى المُحدد، وتُطابق الدالة <code>scanf</code> الدالة <code>fscanf</code> مع اختلاف أن الوسيط الأول هو المجرى <code>stdin</code>، بينما تأخذ <code>sscanf</code> دخلها من <a href="https://academy.hsoub.com/programming/c/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1675/" rel="">مصفوفة</a> محارف مُحدّدة.
</p>

<p>
	نحصل على القيمة <code>EOF</code> المُعادة في حال حدوث خطأ دخل قبل أي تحويلات، وإلا فنحصل على عدد التحويلات الناجحة الحاصلة وقد يكون هذا العدد صفر إن لم تُجرى أي تحويلات، ونحصل على خطأ دخل إذا قرأنا <code>EOF</code>، أو بوصولنا إلى نهاية سلسلة الدخل النصية، بينما نحصل على خطأ تحويل إذا فشل العثور على نمط مناسب يوافق التحويل المحدّد.
</p>

<h2>
	عمليات الإدخال والإخراج على المحارف
</h2>

<p>
	هناك عدد من الدوال التي تسمح لنا بإجراء عمليات الدخل والخرج على المحارف بصورةٍ منفردة، إليك تصاريحها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6687_17" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">/* دخل المحرف */</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> fgetc</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> getc</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> getchar</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> ungetc</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> c</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">);</span><span class="pln">

</span><span class="com">/* خرج المحرف */</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> fputc</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> c</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> putc</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> c</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> putchar</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> c</span><span class="pun">);</span><span class="pln">

</span><span class="com">/* دخل السلسلة النصية */</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fgets</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> n</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">gets</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">);</span><span class="pln">

</span><span class="com">/* خرج السلسلة النصية */</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> fputs</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> puts</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">);</span></pre>

<p>
	لنستعرض سويًّا كلًّا منها.
</p>

<h3>
	دخل المحرف
</h3>

<p>
	تقرأ مجموعة الدوال التي تنفذ هذه المهمة المحرف مثل قيمة من نوع "unsigned char" من مجرى الدخل المحدد أو من <code>stdin</code>، ونحصل على المحرف الذي يليه في كل حالة من مجرى الدخل. يُعامل المحرف مثل قيمة "unsigned char" ويُحوّل إلى "int" وهي القيمة المُعادة من الدالة. نحصل على الثابت <code>EOF</code> عند الوصول إلى نهاية الملف، ويُضبط مؤشر نهاية الملف end-of-file indicator إلى المجرى المحدد، كما نحصل على <code>EOF</code> في حالة الخطأ ويُضبط مؤشر الخطأ إلى المجرى المحدّد. نستطيع الحصول على المحارف بصورةٍ تتابعية باستدعاء الدالة تباعًا. قد نحصل على وسيط المجرى <code>stream</code> أكثر من مرة في حال استخدام هذه الدوال على أنها ماكرو، لذا لا تستخدم الآثار الجانبية هنا.
</p>

<p>
	هناك برنامج "ungetc" الداعم أيضًا، الذي يُستخدم لإعادة محرف إلى المجرى مما يجعله المحرف التالي الذي سيُقرأ، إلا أن هذه ليست بعملية خرج ولن تتسبب بتغيير محتوى الملف، ولذا تتسبب عمليات <code>fflush</code> و <code>fseek</code> و <code>rewind</code> على المجرى بين عملية إعادة المحرف وقراءته بتجاهل هذا المحرف، ويمكن إعادة محرف واحد فقط وأي محاولات لإعادة <code>EOF</code> تُتجاهل، ولا يُعدّل على موضع مؤشر الملف في جميع حالات إعادة قراءة مجموعة من المحارف وإعادة قرائتها أو تجاهلها. يتسبب استدعاء <code>ungetc</code> الناجح على مجرى ثنائي بتناقص موضع مؤشر الملف إلا أن ذلك غير محدد عند استخدام مجرى نصي، أو مجرى ثنائي موجود في بداية الملف.
</p>

<h3>
	خرج المحرف
</h3>

<p>
	هذه الدوال مطابقة لدوال الدخل الموصوفة سابقًا إلا أنها تجري عمليات الخرج، إذ تعيد المحرف المكتوب أو <code>EOF</code> عند حدوث خطأ ما، ولا يوجد ما يعادل نهاية الملف End Of File في ملف الخرج.
</p>

<h3>
	خرج السلسلة النصية
</h3>

<p>
	تكتب هذه الدوال <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">سلاسلًا نصيةً</a> إلى ملف الخرج باستخدام المجرى <code>stream</code> إن ذُكر وإلا فإلى المجرى <code>stdout</code>، ولا يُكتب محرف الإنهاء الفارغ. نحصل على قيمة لا تساوي الصفر عند حدوث خطأ وإلا فالقيمة صفر.
</p>

<p>
	<strong>تحذير</strong>: تضيف <code>puts</code> سطرًا جديدًا إلى سلسلة الخرج النصية بينما لا تفعل <code>fputs</code> ذلك.
</p>

<h3>
	دخل السلسلة النصية
</h3>

<p>
	تقرأ الدالة <code>fgets</code> السلسلة النصية إلى مصفوفة يُشار إليها باستخدام المؤشر <code>s</code> من المجرى <code>stream</code>، وتتوقف عن القراءة في حال الوصول إلى <code>EOF</code> أو عند أول سطر جديد (وتقرأ محرف السطر الجديد)، وتضيف محرفًا فارغًا null في النهاية. يُقرأ n-1 محرف على الأكثر (لترك حيز للمحرف الفارغ).
</p>

<p>
	تعمل الدالة <code>gets</code> على نحوٍ مشابه لمجرى <code>stdin</code> إلا أنها تتجاهل محرف السطر الجديد.
</p>

<p>
	تعيد كلا الدالتين <code>s</code> في حال نجاحهما وإلا فمؤشر فارغ، إذ نحصل على مؤشر فارغ عندما نصادف <code>EOF</code> قبل قراءة أي محرف ولا يطرأ أي تغيير على المصفوفة، بينما تصبح محتويات المصفوفة غير معرفة إذا ما واجهنا خطأ قراءة وسط السلسلة النصية بالإضافة إلى إعادة مؤشر فارغ.
</p>

<h2>
	الدخل والخرج غير المنسق
</h2>

<p>
	هذا الجزء بسيط، إذا هناك فقط دالتان تقدمان هذه الخاصية، واحدة منهما للقراءة والأخرى للكتابة ويصرَّح عنهما على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6687_20" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">

</span><span class="typ">size_t</span><span class="pln"> fread</span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ptr</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> size</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> nelem</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">);</span><span class="pln">
</span><span class="typ">size_t</span><span class="pln"> fwrite</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ptr</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> size</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> nelem</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">);</span></pre>

<p>
	تُجرى عملية القراءة أو الكتابة المناسبة للبيانات المُشار إليها بواسطة المؤشر <code>ptr</code> وذلك على <code>nelem</code> عنصر، وبحجم <code>size</code>، ويفشل نقل ذلك المقدار الكامل من العناصر فقط عند الكتابة، إذ قد تعيق نهاية الملف دخل العناصر بأكملها، وتُعيد الدالة عدد العناصر التي نُقلت فعليًّا. نستخدم <code>feof</code> أو <code>ferror</code> للتمييز بين نهاية الملف عند الدخل أو للإشارة على خطأ.
</p>

<p>
	تُعيد الدالة القيمة صفر دون أي فعل إذا كانت قيمة <code>size</code> أو <code>nelem</code> تساوي إلى الصفر.
</p>

<p>
	قد يساعدنا المثال الآتي في توضيح عمل الدالتين المذكورتين:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6687_22" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> xx</span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> xx_int</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">float</span><span class="pln"> xx_float</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">ar</span><span class="pun">[</span><span class="lit">20</span><span class="pun">];</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">

        </span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fp </span><span class="pun">=</span><span class="pln"> fopen</span><span class="pun">(</span><span class="str">"testfile"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"w"</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">fwrite</span><span class="pun">((</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">ar</span><span class="pun">,</span><span class="pln">
                </span><span class="kwd">sizeof</span><span class="pun">(</span><span class="pln">ar</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]),</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> fp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">){</span><span class="pln">

                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="str">"Error writing\n"</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        rewind</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">fread</span><span class="pun">((</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*)&amp;</span><span class="pln">ar</span><span class="pun">[</span><span class="lit">10</span><span class="pun">],</span><span class="pln">
                </span><span class="kwd">sizeof</span><span class="pun">(</span><span class="pln">ar</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]),</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> fp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">){</span><span class="pln">

                </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">ferror</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">)){</span><span class="pln">
                        fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="str">"Error reading\n"</span><span class="pun">);</span><span class="pln">
                        exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">feof</span><span class="pun">(</span><span class="pln">fp</span><span class="pun">)){</span><span class="pln">
                        fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="str">"End of File\n"</span><span class="pun">);</span><span class="pln">
                        exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 1]
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter9/" rel="external nofollow">Libraries</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/c/%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-stdlib-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1797/" rel="">أدوات مكتبة stdlib في لغة سي C</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/c/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%B9%D9%86-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-io-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1795/" rel="">مقدمة عن التعامل مع الدخل والخرج I/O في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">التعامل مع المحارف والسلاسل النصية في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%B6%D8%A8%D8%B7-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D9%88%D8%B7%D9%8A%D9%86-localization-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1778/" rel="">التعامل مع المحارف وضبط إعدادات التوطين localization في لغة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1796</guid><pubDate>Fri, 23 Dec 2022 16:09:00 +0000</pubDate></item><item><title>&#x645;&#x642;&#x62F;&#x645;&#x629; &#x639;&#x646; &#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x62F;&#x62E;&#x644; &#x648;&#x627;&#x644;&#x62E;&#x631;&#x62C; I/O &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%B9%D9%86-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-io-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1795/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_11/63760d41e590e_-------IO---C.png.6dbb5f3039ad7a62311ad237ba64d35c.png" /></p>
<p>
	يعدّ افتقار <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة</a> لدعمها للدخل والخرج إحدى أبرز الأسباب التي منعت التبني واسع النطاق واستخدامها في البرمجة العملية، وهو الموضوع الذي لم يرد أي مصمّم لغة أن يتعامل معه، إلا أن لغة سي تفادت هذه المشكلة، بعدم تضمينها لأي دعم للدخل والخرج، إذ كان سلوك سي هو أن تترك التعامل مع الدخل والخرج لدوال المكتبات، مما عنى أنه بالإمكان لمصمّمي الأنظمة استخدام طرق دخل وخرج مخصصة بدلًا من إجبارهم على تغيير اللغة بذات نفسها.
</p>

<p>
	تطوّرت حزمة مكتبات عُرفت باسم "مكتبة الدخل والخرج القياسي Standard I/O Library" -أو اختصارًا stdio- في الوقت ذاته الذي كانت لغة سي تتطوّر، وقد أثبتت هذه المكتبة مرونتها وقابلية نقلها وأصبحت الآن جزءًا من المعيار.
</p>

<p>
	اعتمدت حزمة الدخل والخرج القياسي القديمة كثيرًا على نظام يونيكس UNIX للوصول إلى الملفات وبالأخص الافتراض أنه لا يوجد أي فرق بين ملفات ثنائية غير مُهيكلة وملفات أخرى تحتوي على نص مقروء، إلا أن العديد من <a href="https://academy.hsoub.com/files/24-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/" rel="">أنظمة التشغيل</a> تفصل ما بين الاثنين وعُدّلت الحزمة فيما بعد لضمان قابلية نقل برامج لغة سي بين نوعَي نظام الملفات. هناك بعض التغييرات في هذا المجال التي تتسبب بالضرر لكثيرٍ من البرامج المكتوبة مسبقًا على الرغم من الجهود التي تحاول أن تحدّ من هذا الضرر.
</p>

<p>
	من المفترض أن تعمل برامج لغة سي القديمة بنجاح دون تعديل في بيئة يونيكس.
</p>

<h2>
	نموذج الدخل والخرج
</h2>

<p>
	لا يُميّز نموذج الدخل والخرج بين أنواع الأجهزة المادية التي تدعم الدخل والخرج، إذ يُعامل كل مصدر أو حوض من البيانات بالطريقة ذاتها ويُنظر إليه على أنه مجرًى من البايتات stream of bytes. بما أن الكائن الأصغر الذي يمكن تمثيله في لغة سي هو المحرف، فالوصول إلى الملف مسموحٌ باستخدام حدود أي محرف، وبالتالي يمكن قراءة أو كتابة أي عدد من المحارف انطلاقًا من نقطة متحركة تُعرف باسم مؤشر الموضع position indicator، وتُكتب أو تُقرأ المحارف تباعًا بدءًا من هذه النقطة ويُحرّك مؤشر الموضع خلال ذلك. يُضبط مؤشر الموضع مبدئيًا إلى بداية الملف عند فتحه، لكن من الممكن تحريكه باستخدام طلبات تحديد الموقع، ويُتجاهل مؤشر موضع الملف في حال كان الوصول العشوائي إلى الملف غير ممكن. لفتح الملف في نمط الإضافة append تأثيرات على مجرى موضع المؤشر في الملف معرفة بحسب التنفيذ.
</p>

<p>
	الفكرة العامة هي تقديم إمكانية القراءة أو الكتابة بصورةٍ تتابعية، باستثناء حالة فتح المجرى باستخدام نمط الإضافة، أو إذا حُرّك مؤشر موضع الملف مباشرةً.
</p>

<p>
	هناك نوعان من أنواع الملفات، هما: <strong>الملفات النصية text files</strong> و<strong>الملفات الثنائية binary files</strong> التي يمكن التعامل معها داخل البرنامج على أنها <strong>مجاري نصية text streams</strong> أو <strong>مجاري ثنائية binary streams</strong> بعد فتحها لعمليات الإدخال والإخراج. لا تسمح حزمة stdio بالعمليات على محتوى الملف مباشرةً، بل بالتعديل على المجرى الذي يحتوي على بيانات الملف.
</p>

<h3>
	المجاري النصية
</h3>

<p>
	يحدّد المعيار المجرى النصي text stream، الذي يمثّل ملفًا يحتوي على أسطر نصية ويتألف السطر من صفر محرف أو أكثر ينتهي بمحرف نهاية السطر، ومن الممكن أن يكون تمثيل الأسطر الفعلي في البيئة الخارجية مختلفًا عن تمثيله هنا، كما من الممكن إجراء تحويلات على مجرى البيانات عند دخولها إلى أو خروجها من البرنامج، وأكثر المتطلبات شيوعًا هو ترجمة المحرف الذي ينهي السطر "'‎\n'" إلى السلسلة "'‎\r\n'" عند الخرج وإجراء عكس العملية عند الدخل، ومن الممكن تواجد بعض الترجمات الضرورية الأخرى.
</p>

<p>
	يُضمن للبيانات التي تُقرأ من المجرى النصي أن تكون مساويةً إلى البيانات المكتوبة سابقًا إلى الملف، وذلك إذا كانت هذه البيانات مؤلفةً من أسطر مكتملة تحتوي على محارف يمكن طباعتها، وكانت محارف التحكم control characters ومحارف مسافة الجدولة الأفقية horizontal-tab ومحارف الأسطر الجديدة newline فقط، ولم يُتبع أي محرف سطر جديد بمحرف مسافة فارغة space مباشرةً، وكان المحرف الأخير في المجرى هو محرف سطر جديد.
</p>

<p>
	كما أن هناك ضمان بأن المحرف الأخير المكتوب إلى الملف النصي هو محرف سطر جديد، ومن الممكن قراءة الملف مجددًا بمحتوياته المماثلة التي كُتبت إليه سابقًا.
</p>

<p>
	إلحاق المحرف الأخير المكتوب إلى الملف بمحرف سطر جديد معرفٌ بحسب التنفيذ، وذلك لأن الملفات النصية والملفات الثنائية تُعامل نفس المعاملة في بعض التنفيذات.
</p>

<p>
	قد تُجرّد بعض التنفيذات المسافة الفارغة البادئة من الأسطر التي تتألف من مسافات فارغة فقط متبوعةً بسطر جديد، أو تُجرّد المسافة الفارغة في نهاية السطر.
</p>

<p>
	يجب أن يدعم التنفيذ الملفات النصية التي تحتوي سطورها على 254 محرفًا على الأقل، ويتضمن ذلك محرف السطر الجديد الذي يُنهي السطر.
</p>

<p>
	قد نحصل على مجرى ثنائي عند فتح مجرى نصي بنمط التحديث update mode في بعض التنفيذات.
</p>

<p>
	قد تتسبب الكتابة على مجرًى نصي باقتطاع الملف عند نقطة الكتابة في بعض التنفيذات، أي ستُهمل جميع البيانات التي تتبع البايت الأخير المكتوب.
</p>

<h3>
	المجاري الثنائية
</h3>

<p>
	يمثل المجرى الثنائي سلسلةً من المحارف التي يمكن استخدامها لتسجيل البيانات الداخلية لبرنامج ما، مثل محتويات الهياكل structures، أو المصفوفات وذلك بالشكل الثنائي، إذ تكون البيانات المقروءة من المجاري الثنائية مساويةً للبيانات المكتوبة إلى المجرى ذاته سابقًا ضمن نفس التنفيذ، وقد يُضاف عددٌ من المحارف الفارغة "NULL" في بعض الظروف إلى نهاية المجرى الثنائي، ويكون عدد المحارف معرفًا بحسب التنفيذ.
</p>

<p>
	تعتمد بيانات الملفات الثنائية على الآلة التي تعمل عليها لأبعد حد، وهي غير قابلة للنقل عمومًا.
</p>

<h3>
	المجاري الأخرى
</h3>

<p>
	قد تتوفر بعض أنواع المجاري الأخرى، إلا أنها معرفة بحسب التنفيذ.
</p>

<h2>
	ملف الترويسة <code>&lt;stdio.h&gt;</code>
</h2>

<p>
	هناك عدد من <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1646/" rel="">الدوال</a> و<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-macro-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC-%D8%A7%D9%84%D9%85%D8%B3%D8%A8%D9%82-preprocessor-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1750/" rel="">الماكرو</a> الموجودة لتقديم الدعم لمختلف أنواع المجاري، ويحتوي ملف الترويسة <code>&lt;stdio.h&gt;</code> العديد من التصريحات المهمة لهذه الدوال، إضافةً إلى الماكرو التالية وتصاريح الأنواع:
</p>

<ul>
	<li>
		النوع <code>FILE</code>: نوع الكائن المُستخدم لاحتواء معلومات التحكم بالمجرى، ولا يحتاج مستخدمو مكتبة "stdio" لمعرفة محتويات هذه الكائنات، إذ يكفي التعامل مع المؤشرات التي تشير إليهم. لا يُعد نسخ الكائنات هذه ضمن البرنامج آمنًا، إذ أن عناوينهم قد تكون في بعض الأحيان معقدة.
	</li>
	<li>
		النوع <code>fpos_t</code>: نوع الكائن الذي يُستخدم لتسجيل القيم الفريدة من نوعها التي تنتمي إلى مجرى مؤشر موضع الملف.
	</li>
	<li>
		القيم <code>IOFBF_</code> و <code>IOLBF_</code> و <code>IONBF_</code>: وهب قيم تُستخدم للتحكم بالتخزين المؤقت buffering للمجرى بالاستعانة بالدالة <code>setvbuf</code>.
	</li>
	<li>
		القيمة <code>BUFSIZ</code>: حجم التخزين المؤقت المُستخدم بواسطة الدالة <code>setbuf</code>، وهو تعبيرٌ رقم صحيح integral ثابت constant تكون قيمته 256 على الأقل.
	</li>
	<li>
		القيمة <code>EOF</code>: تعبير رقم صحيح سالب ثابت يحدد نهاية الملف end-of-file ضمن مجرى، أي عند الوصول إلى نهاية الدخل.
	</li>
	<li>
		القيمة <code>FILENAME_MAX</code>: الطول الأعظمي الذي يمكن لاسم ملف أن يكون إذا كان هناك قيد على ذلك، وإلا فهو الحجم الذي يُنصح به لمصفوفة تحمل اسم ملف.
	</li>
	<li>
		القيمة <code>FOPEN_MAX</code>: العدد الأدنى من الملفات التي يضمن التنفيذ فتحها في وقت آني، وهو ثمانية ملفات. لاحظ أنه من الممكن إغلاق ثلاث مجاري مُعرفة مسبقًا إذا احتاج البرنامج فتح أكثر من خمسة ملفات مباشرةً.
	</li>
	<li>
		القيمة <code>L_tmpnam</code>: الطول الأعظمي المضمون لسلسلة نصية في <code>tmpnam</code>، وهو تعبير رقم صحيح ثابت.
	</li>
	<li>
		القيم <code>SEEK_CUR</code> و <code>SEEK_END</code> و <code>SEEK_SET</code>: تعابير رقم صحيح ثابتة تُستخدم للتحكم بأفعال <code>fseek</code>.
	</li>
	<li>
		القيمة <code>TMP_MAX</code>: العدد الأدنى من أسماء الملفات الفريدة من نوعها المولدة من قبل <code>tmpnam</code>، وهو تعبير رقم صحيح ثابت بقيمة لا تقل عن 25.
	</li>
	<li>
		الكائنات <code>stdin</code> و <code>stdout</code> و <code>stderr</code>: وهي كائنات معرفة مسبقًا من النوع "* FILE" وتشير إلى مجرى الدخل القياسي ومجرى الخرج القياسي ومجرى الخطأ بالترتيب، وتُفتح هذه المجاري تلقائيًا عند بداية تنفيذ البرنامج.
	</li>
</ul>

<h2>
	العمليات على المجاري
</h2>

<p>
	بعد أن تعرفنا على أنواع المجاري وطبيعتها والقيم المرتبطة بها، نستعرض الآن العمليات الأساسية عليها ألا وهي فتح المجرى وإغلاقه، إضافةً إلى التخزين المؤقت.
</p>

<h3>
	فتح المجرى
</h3>

<p>
	يتصل المجرى بالملف عن طريق دالة <code>fopen</code>، أو <code>freopen</code>، أو <code>tmpfile</code>، إذ تعيد هذه الدوال -إذا كان استدعاؤها ناجحًا- مؤشرًا يشير إلى كائن من نوع <code>FILE</code>.
</p>

<p>
	هناك ثلاث أنواع من المجاري المتاحة افتراضيًا دون أي جهد إضافي مطلوب منك، وتتصل هذه المجاري عادةً بالجهاز المادي المُرتبط بالبرنامج المُنفّذ ألا وهو الطرفية Terminal عادةً، ويشار إلى هذه المجاري بالأسماء:
</p>

<ul>
	<li>
		<code>stdin</code>: وهو مجرى <strong>الدخل القياسي standard input</strong>.
	</li>
	<li>
		<code>stdout</code>: وهو مجرى <strong>الخرج القياسي standard output</strong>.
	</li>
	<li>
		<code>stderr</code>: وهو مجرى <strong>الخطأ القياسي standard error</strong>.
	</li>
</ul>

<p>
	ويكون دخل لوحة المفاتيح في الحالة الطبيعية من المجرى <code>stdin</code> وخرج الطرفية هو <code>stdout</code>، بينما تُوجّه رسائل الأخطاء إلى المجرى <code>stderr</code>. الهدف من فصل رسائل الأخطاء عن رسائل الخرج العادية هو السماح بربط مجرى <code>stdout</code> إلى شيءٍ آخر مغاير لجهاز للطرفية مثل ملف ما والحصول على رسائل الخطأ بنفس الوقت على الشاشة أمامك عوضًا عن توجيه الأخطاء إلى الملف، وتخزّن كامل الملفات مؤقتًا إذا لم توجّه إلى أجهزة تفاعلية.
</p>

<p>
	كما ذكرنا سابقًا، قد يكون مؤشر موضع الملف قابلًا للتحريك أو غير قابل للتحريك بحسب الجهاز المُستخدم، إذ يكون مؤشر موضع الملف غير قابل للتحريك ضمن مجرى <code>stdin</code> على سبيل المثال إذا كان متصلًا إلى الطرفية (الحالة الاعتيادية له).
</p>

<p>
	جميع الملفات غير المؤقتة تمتلك اسمًا filename وهو سلسلةٌ نصية، والقوانين التي تحدد اسم الملف الصالح معرفةٌ حسب التنفيذ، وينطبق الأمر ذاته على إمكانية فتح الملف لعدة مرات بصورةٍ آنية. قد يتسبب فتح ملف جديد بإنشاء هذا الملف، وتتسبب إعادة إنشاء ملف موجود مسبقًا بإهمال محتوياته السابقة.
</p>

<h3>
	إغلاق المجرى
</h3>

<p>
	تُغلق المجاري عند استدعاء <code>fclose</code> أو <code>exit</code> بصورةٍ صريحة، أو عندما يعود البرنامج إلى الدالة <code>main</code>، وتُمسح جميع البيانات المخزنة مؤقتًا عند إغلاق المجرى. تصبح حالة الملفات المفتوحة غير معروفة إذا توقف البرنامج لسببٍ ما دون استخدام الطرق السابقة لإغلاقه.
</p>

<h3>
	التخزين المؤقت للمجرى
</h3>

<p>
	هناك ثلاث أنواع للتخزين المؤقت:
</p>

<ol>
	<li>
		دون تخزين مؤقت unbuffered: تُستخدم مساحة التخزين بأقل ما يمكن من قبل <code>stdio</code> بهدف إرسال أو تلقي البيانات أسرع ما يمكن.
	</li>
	<li>
		تخزين مؤقت خطي line buffered: تُعالج المحارف سطرًا تلو سطر، ويُستخدم هذا النوع من التخزين المؤقت كثيرًا في البيئات التفاعلية، وتُمسح محتويات الذواكر المؤقتة الداخلية internal buffers فقط عندما تمتلئ أو عندما يُعالج سطر جديد.
	</li>
	<li>
		التخزين المؤقت الكامل fully buffered: تُسمح الذواكر المؤقتة الداخلية فقط عندما تمتلئ.
	</li>
</ol>

<p>
	يُمكن مسح محتوى الذاكرة الداخلية المرتبطة بمجرى ما عن طريق استخدام <code>fflush</code> مباشرةً. يُعرَّف الدعم لأنواع التخزين المؤقت المختلفة بحسب التنفيذ، ويمكن التحكم به ضمن الحدود المعرفة باستخدام <code>setbuf</code> و <code>setvbuf</code>.
</p>

<h2>
	التلاعب بمحتويات الملف مباشرة
</h2>

<p>
	هناك عدة دوال تسمح لنا بالتعامل مع الملف مباشرةً.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_34_11" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> remove</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">filename</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> rename</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">old</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="kwd">new</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">tmpnam</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">);</span><span class="pln">
</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">tmpfile</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span></pre>

<ul>
	<li>
		الدالة <code>remove</code>: تتسبب بإزالة الملف، وستفشل محاولات فتح هذا الملف لاحقًا إلا في حال إنشاء الملف مجددًا. يكون سلوك الدالة <code>remove</code> عندما يكون الملف مفتوحًا معرفًا بحسب التنفيذ، وتعيد الدالة القيمة صفر للدلالة على النجاح، بينما تدل أي قيمة أخرى على فشل عملها.
	</li>
	<li>
		الدالة <code>rename</code>: تُغيّر اسم الملف المعرف بالكلمة <code>old</code> في مثالنا السابق إلى <code>new</code>، وستفشل محاولات فتح الملف باستخدام اسمه القديم، إلا إذا أنشئ ملفٌ جديد يحمل الاسم القديم ذاته، وكما هو الحال في <code>remove</code> فإن الدالة <code>rename</code> تُعيد القيمة صفر للدلالة على نجاح العملية وأي قيمة مغايرة لذلك تدل على حصول خطأ. السلوك معرف حسب التنفيذ إذا حاولنا تسمية الملف باسم جديد باستخدام <code>rename</code> وكان هناك ملف بالاسم ذاته مسبقًا. لن يُعدّل على الملف إذا فشلت الدالة <code>rename</code> لأي سببٍ كان.
	</li>
	<li>
		الدالة <code>tmpnam</code>: تولّد سلسلة نصية لتُستخدم اسمًا لملف، ويضمن لهذه السلسلة النصية أن تكون فريدةً من نوعها بالنسبة لأي اسم ملف آخر موجود، ويمكن أن تُستدعى بصورةٍ متتالية للحصول على اسم جديد كل مرة. يُستخدم الثابت <code>TMP_MAX</code> لتحديد عدد مرات استدعاء الدالة <code>tmpnam</code> قبل أن يتعذر عليه العثور على اسماء فريدة، وقيمته 25 على الأقل، ونحصل على سلوك غير معرّف من قبل المعيار في حال استدعاء الدالة <code>tmpnam</code> عدد مرات يتجاوز هذا الثابت إلا أن الكثير من التنفيذات تقدم حدًّا لا نهائيًا. تستخدم <code>tmpnam</code> ذاكرة مؤقتة داخلية لبناء الاسم وتُعيد مؤشرًا يشير إليه وذلك إذا ضُبط الوسيط <code>s</code> إلى القيمة <code>NULL</code>، وقد تغيّر الاستدعاءات اللاحقة للدالة الذاكرة المؤقتة الداخلية ذاتها. يمكن استخدام مؤشر يشير إلى مصفوفة مثل وسيط بدلًا من السابق، بحيث تحتوي <a href="https://academy.hsoub.com/programming/c/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1675/" rel="">المصفوفة</a> على <code>L_tmpnam</code> محرف على الأقل، وفي هذه الحالة سُيملأ الاسم إلى الذاكرة المؤقتة المزوّدة (المصفوفة)، ويمكن فيما بعد إنشاء ملف بهذا الاسم واستخدامه ملفًا مؤقتًا. لن يكون اسم الملف مفيدًا ضمن سياقات أخرى غالبًا، بالنظر إلى توليده من قبل الدالة. لا تُزال الملفات المؤقتة من هذا النوع إلا إن استدعيت دالة الحذف، وغالبًا ما تُستخدم هذه الملفات لتمرير البيانات المؤقتة بين برنامجين منفصلين.
	</li>
	<li>
		الدالة <code>tmpfile</code>: تُنشئ ملف ثنائي مؤقت يُمكن التعديل على محتوياته، وتعيد الدالة مؤشرًا يشير إلى مجرى الملف، ويُزال هذا الملف فيما بعد عند إغلاق مجراه، وتُعيد الدالة <code>tmpfile</code> مؤشرًا فارغًا null إذا لم ينجح فتح الملف.
	</li>
</ul>

<h2>
	فتح الملفات بالاسم
</h2>

<p>
	يمكن فتح الملفات الموجودة بالاسم عن طريق استدعاء الدالة <code>fopen</code> المصرّح عنها على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_34_14" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fopen</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">pathname</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">mode</span><span class="pun">);</span></pre>

<p>
	يمثل الوسيط <code>pathname</code> اسم الملف الذي تريد فتحه، مثل الاسم الذي تعيده الدالة <code>tmpnam</code> أو أي اسم ملف معين آخر.
</p>

<p>
	يمكن فتح الملفات باستخدام عدة <strong>أنماط modes</strong>، مثل نمط <strong>القراءة read</strong> لقراءة البيانات، ونمط <strong>الكتابة write</strong> لكتابة البيانات وهكذا.
</p>

<p>
	لاحظ أن الدالة <code>fopen</code> ستُنشئ ملفًا إذا أردت كتابة البيانات على ملف، أو أنها ستتخلص من محتويات الملف إذا وُجد ليصبح طوله صفر (أي أنك ستخسر محتويات الملف السابقة).
</p>

<p>
	يوضح الجدول التالي جميع الأنماط الموجودة في المعيار، إلا أن التنفيذ قد يسمح بأنماط أخرى بإضافة محارف إضافية في نهاية كل من الأنماط.
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
	<thead>
		<tr>
			<th>
				النمط
			</th>
			<th>
				نوع الملف
			</th>
			<th>
				القراءة
			</th>
			<th>
				الكتابة
			</th>
			<th>
				إنشاء جديد
			</th>
			<th>
				حذف القيمة السابقة
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				"r"
			</td>
			<td>
				نصي
			</td>
			<td>
				نعم
			</td>
			<td>
				لا
			</td>
			<td>
				لا
			</td>
			<td>
				لا
			</td>
		</tr>
		<tr>
			<td>
				"rb"
			</td>
			<td>
				ثنائي
			</td>
			<td>
				نعم
			</td>
			<td>
				لا
			</td>
			<td>
				لا
			</td>
			<td>
				لا
			</td>
		</tr>
		<tr>
			<td>
				"r+‎"
			</td>
			<td>
				نصي
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
			<td>
				لا
			</td>
			<td>
				لا
			</td>
		</tr>
		<tr>
			<td>
				"r+b"
			</td>
			<td>
				ثنائي
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
			<td>
				لا
			</td>
			<td>
				لا
			</td>
		</tr>
		<tr>
			<td>
				"rb+‎"
			</td>
			<td>
				ثنائي
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
			<td>
				لا
			</td>
			<td>
				لا
			</td>
		</tr>
		<tr>
			<td>
				"w"
			</td>
			<td>
				نصي
			</td>
			<td>
				لا
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
		</tr>
		<tr>
			<td>
				"wb"
			</td>
			<td>
				ثنائي
			</td>
			<td>
				لا
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
		</tr>
		<tr>
			<td>
				"w+‎"
			</td>
			<td>
				نصي
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
		</tr>
		<tr>
			<td>
				"w+b"
			</td>
			<td>
				ثنائي
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
		</tr>
		<tr>
			<td>
				"wb+‎"
			</td>
			<td>
				ثنائي
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
		</tr>
		<tr>
			<td>
				"a"
			</td>
			<td>
				نصي
			</td>
			<td>
				لا
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
			<td>
				لا
			</td>
		</tr>
		<tr>
			<td>
				"ab"
			</td>
			<td>
				ثنائي
			</td>
			<td>
				لا
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
			<td>
				لا
			</td>
		</tr>
		<tr>
			<td>
				"a+‎"
			</td>
			<td>
				نصي
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
			<td>
				لا
			</td>
		</tr>
		<tr>
			<td>
				"a+b"
			</td>
			<td>
				ثنائي
			</td>
			<td>
				لا
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
			<td>
				لا
			</td>
		</tr>
		<tr>
			<td>
				"ab+‎"
			</td>
			<td>
				ثنائي
			</td>
			<td>
				لا
			</td>
			<td>
				نعم
			</td>
			<td>
				نعم
			</td>
			<td>
				لا
			</td>
		</tr>
	</tbody>
</table>

<p>
	انتبه من بعض التنفيذات التي تضيف إلى النمط الأخير محارف <code>NULL</code> في حالة الملفات الثنائية، إذ قد يتسبب فتح هذه الملفات بالنمط <code>ab</code> أو <code>+ab</code> أو <code>a+b</code> بوضع مؤشر الملف خارج نطاق آخر البيانات المكتوبة.
</p>

<p>
	تُكتب جميع البيانات في نهاية الملف إذا فُتح باستخدام نمط الإضافة append، بغض النظر عن محاولة تغيير موضع المؤشر باستخدام الدالة <code>fseek</code>، ويكون موضع مؤشر الملف المبدئي معرف بحسب التنفيذ.
</p>

<p>
	تفشل محاولات فتح الملف بنمط القراءة (النمط 'r')، إذا لم يكن الملف موجودًا أو لم يمكن قراءته.
</p>

<p>
	يمكن القراءة من والكتابة إلى الملفات المفتوحة بنمط التحديث update (باستخدام <code>'+'</code> مثل المحرف الثاني أو الثالث ضمن النمط) إلا أنه من غير الممكن إلحاق القراءة بالكتابة مباشرةً أو الكتابة بالقراءة دون استدعاء بينهما لدالة واحدة (أو أكثر) من الدوال: <code>fflush</code> أو <code>fseek</code> أو <code>fsetpos</code> أو <code>rewind</code>، والاستثناء الوحيد هنا هو جواز إلحاق الكتابة مباشرةً بعد القراءة إذا قُرأ المحرف <code>EOF</code> (نهاية الملف).
</p>

<p>
	من الممكن أيضًا في بعض التنفيذات أن يُتخلى عن <code>b</code> في أنماط فتح الملفات الثنائية واستخدام الأنماط ذاتها الخاصة بالملفات النصية.
</p>

<p>
	تُخزّن المجاري المفتوحة باستخدام <code>fopen</code> تخزينًا مؤقتًا بالكامل إذا لم تكن متصلة إلى جهاز تفاعلي، ويضمن ذلك التعامل مع الأسئلة prompts والطلبات responses على النحو الصحيح.
</p>

<p>
	تعيد الدالة <code>fopen</code> مؤشرًا فارغًا null إذا فشلت بفتح الملف، وإلا فتعيد مؤشرًا يشير إلى الكائن الذي يتحكم بالمجرى. كائنات المجاري <code>stdin</code> و <code>stdout</code> و <code>stderr</code> غير قابلة للتعديل بالضرورة ومن الممكن عدم وجود إمكانية استخدام القيمة المُعادة من الدالة <code>fopen</code> لإسنادها إلى واحدة من هذه الكائنات، بدلًا من ذلك نستخدم <code>freopen</code> لهذا الغرض.
</p>

<h2>
	الدالة freopen
</h2>

<p>
	تُستخدم الدالة <code>freopne</code> لأخذ مؤشر يشير إلى مجرى وربطه مع اسم ملف آخر، وتصرَّح الدالة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_34_16" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">freopen</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">pathname</span><span class="pun">,</span><span class="pln">
              </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">mode</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">);</span></pre>

<p>
	وسيط <code>mode</code> مشابه لمثيله في دالة <code>fopen</code>. يُغلق المجرى <code>stream</code> أولًا ويحدث تجاهل أي أخطاء متولدة عن ذلك، ونحصل على قيمة <code>NULL</code> في حالة حدوث خطأ عند تنفيذ الدالة، وإلا فإننا نحصل على القيمة الجديدة للمجرى <code>stream</code>.
</p>

<h2>
	إغلاق الملفات
</h2>

<p>
	يمكننا إغلاق ملف مفتوح باستخدام الدالة <code>close</code> والمصرح عنها كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_34_18" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> fclose</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">);</span></pre>

<p>
	يُتخلّص من أي بيانات موجودة على الذاكرة المؤقتة لم تُكتب على الملف الخاص بالمجرى <code>stream</code> إضافةً إلى أي بيانات أخرى لم تُقرأ، وتُحرّر الذاكرة المؤقتة المرتبطة بالمجرى إذا رُبطت به تلقائيًا، وأخيرًا يُغلق الملف.
</p>

<p>
	نحصل على القيمة صفر للدلالة على نجاح العملية، وإلا فالقيمة <code>EOF</code> للدلالة على الخطأ.
</p>

<h2>
	الدالتان setbuf و setvbuf
</h2>

<p>
	تُستخدم الدالتان للتعديل على استراتيجية التخزين المؤقتة لمجرى معين مفتوح، ويُصرّح عن الدالتين كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_34_20" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> setvbuf</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">buf</span><span class="pun">,</span><span class="pln">
              </span><span class="typ">int</span><span class="pln"> type</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> size</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> setbuf</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">buf</span><span class="pun">);</span></pre>

<p>
	يجب استخدام الدالتين <strong>قبل</strong> قراءة الملف أو الكتابة إليه، ويعرف الوسيط <code>type</code> نوع التخزين المؤقت للمجرى <code>stream</code>، ويوضح الجدول التالي أنواع التخزين المؤقت.
</p>

<table>
	<thead>
		<tr>
			<th>
				القيمة
			</th>
			<th>
				التأثير
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<code>‎_IONBF</code>
			</td>
			<td>
				لا تخزّن الدخل والخرج مؤقتًا
			</td>
		</tr>
		<tr>
			<td>
				<code>‎_IOFBF</code>
			</td>
			<td>
				خزِّن الدخل والخرج مؤقتًا
			</td>
		</tr>
		<tr>
			<td>
				<code>‎_IOLBF</code>
			</td>
			<td>
				تخزين مؤقت خطي: تخلص من محتويات الذاكرة المؤقتة عندما تمتلئ، أو عند كتابة سطر جديد، أو عند طلب القراءة
			</td>
		</tr>
	</tbody>
</table>

<p>
	يمكن للوسيط <code>buf</code> أن يكون مؤشرًا فارغًا، وفي هذه الحالة تُنشأ مصفوفة تلقائيًا لتخزين البيانات مؤقتًا، ويمكن بخلاف ذلك للمستخدم توفير ذاكرة مؤقتة لكن يجب التأكد من استمرارية الذاكرة المؤقتة بقدر مساوٍ (أو أكثر) لاستمرارية التدفق <code>stream</code>. يُعد استخدام مساحة التخزين المحجوزة تلقائيًا ضمن تعليمة مركبة compound statement من الأخطاء الشائعة، إذ أن الحصول على المساحة التخزينية على النحو الصحيح في هذه الحالة يجري عن طريق الدالة <code>malloc</code> عوضًا عن ذلك. يُحدد حجم الذاكرة المؤقتة باستخدام الوسيط <code>size</code>.
</p>

<p>
	يشابه استدعاء الدالة <code>setbuf</code> استدعاء الدالة <code>setvbuf</code> إذا استخدمنا <code>‎_IOFBF</code> قيمةً للوسيط <code>type</code> والقيمة <code>BUFSIZ</code> للوسيط <code>size</code>، وتُستخدم القيمة <code>‎_IONBF</code> للوسيط <code>type</code> إذا كان <code>buf</code> مؤشرًا فارغًا.
</p>

<p>
	لا تُعاد أي قيمة بواسطة الدالة <code>setbuf</code>، بينما تُعيد الدالة <code>setvbuf</code> القيمة صفر للدلالة على نجاح الاستدعاء، وإلا فقيمة غير صفرية إذا كانت قيم <code>type</code>، أو <code>size</code> غير صالحة، أو كان الطلب غير ممكن التنفيذ.
</p>

<h2>
	دالة fflush
</h2>

<p>
	يُصرّح عن الدالة <code>fflush</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_34_22" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> fflush</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">);</span></pre>

<p>
	إذا أشار المجرى <code>stream</code> إلى ملف مفتوح للخرج أو بنمط التحديث، وكان هناك أي بيانات غير مكتوبة فإنها تُكتب خارجًا، وهذا يعني أنه لا يمكن لدالة داخل بيئة مستضافة hosted environment، أو ضمن لغة سي أن تضمن -على سبيل المثال- أن البيانات تصل مباشرةً إلى سطح قرص يدعم الملف. تُهمَل أي عملية <code>ungetc</code> سابقة إذا كان المجرى مرتبطًا بالملف المفتوح بهدف الخرج أو التحديث.
</p>

<p>
	يجب أن تكون آخر عملية على المجرى عملية خرج، وإلا فسنحصل على سلوك غير معرّف.
</p>

<p>
	يتخلص استدعاء <code>fflush</code> الذي يحتوي على وسيط قيمته صفر من جميع مجاري الدخل والخرج، ويجب هنا الانتباه إلى المجاري التي لم تكن عمليتها الأخيرة عملية خرج، أي تفادي حصول السلوك غير المعرّف الذي ذكرناه سابقًا.
</p>

<p>
	تُعيد الدالة القيمة <code>EOF</code> للدلالة على الخطأ، وإلا فالقيمة صفر للدلالة على النجاح.
</p>

<h2>
	الدوال عشوائية الوصول Random access functions
</h2>

<p>
	تعمل جميع دوال دخل وخرج الملفات بصورةٍ مشابهة بين بعضها، إذ أن الملفات ستُقرأ أو يُكتب إليها بصورةٍ متتابعة إلا إذا اتخذ المستخدم خطوات مقصودة لتغيير موضع مؤشر الملف. ستتسبب عملية قراءة متبوعة بكتابة متبوعة بقراءة ببدء عملية القراءة الثانية بعد نهاية عملية كتابة البيانات فورًا، وذلك بفرض أن الملف مفتوح باستخدام نمط يسمح بهذا النوع من العمليات، كما يجب أن تتذكر أن المجرى <code>stdio</code> يُصرّ على إدخال المستخدم لعملية <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%B3%D8%A7%D8%AF%D8%B3-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-memory-management-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r994/" rel="">تحرير ذاكرة </a>مؤقتة بين كل عنصر من عناصر دورة قراءة- كتابة- قراءة، وللتحكم بذلك، تسمح دالة الوصول العشوائي random access function بالتحكم بموضع الكتابة والقراءة ضمن الملف، إذ يُحرّك موضع مؤشر الملف دون الحاجة لقراءة أو كتابة ويشير إلى البايت الذي سيخضع لعملية القراءة أو الكتابة التالية.
</p>

<p>
	هناك ثلاثة أنواع من الدوال التي تسمح بفحص موضع مؤشر الملف أو تغييره، إليك تصاريح كل منهم:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_34_25" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">

</span><span class="com">/* إعادة موضع مؤشر الملف */</span><span class="pln">
</span><span class="kwd">long</span><span class="pln"> ftell</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> fgetpos</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">,</span><span class="pln"> </span><span class="typ">fpos_t</span><span class="pln"> </span><span class="pun">*</span><span class="pln">pos</span><span class="pun">);</span><span class="pln">

</span><span class="com">/* ضبط موضع مؤشر الملف إلى الصفر */</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> rewind</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">);</span><span class="pln">

</span><span class="com">/* ضبط موضع مؤشر الملف */</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> fseek</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> offset</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> ptrname</span><span class="pun">);</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> fsetpos</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="typ">fpos_t</span><span class="pln"> </span><span class="pun">*</span><span class="pln">pos</span><span class="pun">);</span></pre>

<p>
	تُعيد الدالة <code>ftell</code> القيمة الحالية لموضع مؤشر الملف (المُقاسة بعدد المحارف)، إذا كان المجرى <code>stream</code> يشير إلى ملف ثنائي، وإلا فإنها تعيد رقمًا مميزًا في حالة الملف النصي، ويمكن استخدام هذه القيمة فقط عند استدعاءات لاحقة لدالة <code>fseek</code> لإعادة ضبط موضع مؤشر الملف الحالة. نحصل على القيمة <code>‎-1L</code> في حالة الخطأ ويُضبط <code>errno</code>.
</p>

<p>
	تضبط الدالة <code>rewind</code> موضع مؤشر الملف الحالي إلى بداية الملف المُشار إليه بالمجرى <code>stream</code>، ويُعاد ضبط مؤشر خطأ الملف باستدعاء الدالة <code>rewind</code> ولا تُعيد الدالة أي قيمة.
</p>

<p>
	تسمح الدالة <code>fseek</code> لموضع مؤشر الملف ضمن المجرى أن يُضبط لقيمة عشوائية (للملفات الثنائية)، أو إلى الموضع الذي نحصل عليه من <code>ftell</code> فقط بالنسبة للملفات النصية، وتتبع الدالة القوانين التالية:
</p>

<ul>
	<li>
		يُضبط موضع مؤشر الملف في الحالة الاعتيادية بفارق معين من البايتات (المحارف) عن نقطة الملف المُحددة بالقيمة <code>prtname</code>، وقد يكون الفارق <code>offset</code> سالبًا. قد يأخذ <code>ptrname</code> القيمة <code>SEEK_SET</code> التي تضبط موضع مؤشر الملف نسبيًا إلى بداية الملف، أو القيمة <code>SEEK_CUR</code> التي تضبط موضع مؤشر الملف نسبيًا إلى قيمتها الحالية، أو القيمة <code>SEEK_END</code> التي تضبط موضع مؤشر الملف نسبيًا إلى نهاية الملف، إلا أنه من غير المضمون أن تعمل القيمة الأخيرة بنجاح في المجاري الثنائية.
	</li>
	<li>
		يجب أن تكون قيمة <code>offset</code> في الملفات النصية إما صفر أو قيمة مُعادة بواسطة استدعاء سابق للدالة <code>ftell</code> على المجرى ذاته، ويجب أن تكون قيمة <code>ptrnmae</code> مساويةً إلى <code>SEEK_SET</code>.
	</li>
	<li>
		يُفرغ <code>fseek</code> مؤشر نهاية الملف للمجرى المُحدد ويحذف بيانات أي استدعاء لعملية <code>ungetc</code>، ويعمل ذلك لكلٍّ من الدخل والخرج.
	</li>
	<li>
		تُعاد القيمة صفر للدلالة على النجاح وأي قيمة غير صفرية تدل على طلب ممنوع للدالة.
	</li>
</ul>

<p>
	لاحظ أنه يمكن لكلٍ من <code>ftell</code> و <code>fseek</code> ترميز قيمة موضع مؤشر الملف إلى قيمة من نوع <code>long</code>، وقد لا يحدث هذا بنجاح في حالة استخدامه على الملفات الطويلة جدًا؛ لذلك، يقدم المعيار كلًا من <code>fgetpos</code> و <code>fsetpos</code> للتغلُّب على هذه المشكلة.
</p>

<p>
	تخزِّن الدالة <code>fgetpos</code> موضع مؤشر الملف الحالي ضمن المجرى للكائن المُشار إليه باستخدام المؤشر <code>pos</code>، والقيمة المخزنة هي قيمة مميزة تُستخدم فقط للعودة إلى الموضع المحدد ضمن المجرى ذاته باستخدام الدالة <code>fsetpos</code>. تعمل الدالة <code>fsetpos</code> كما وضحنا سابقًا، كما أنها تُفرغ مؤشر نهاية الملف للمجرى وتُزيل أي تأثير لعمليات <code>ungetc</code> سابقة.
</p>

<p>
	نحصل على القيمة صفر في حالة النجاح لكلا الدالتين، بينما نحصل على قيمة غير صفرية في حالة الخطأ ويُضبط <code>errno</code>.
</p>

<h2>
	التعامل مع الأخطاء
</h2>

<p>
	تحافظ دوال الدخل والخرج القياسية على مؤشرين لكل مجرى مفتوح للدلالة على نهاية الملف وحالة الخطأ ضمنه، ويمكن الحصول على قيم هذه المؤشرات وضبطها عن طريق الدوال التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_34_27" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> clearerr</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">);</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> feof</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">);</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> ferror</span><span class="pun">(</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stream</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> perror</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">);</span></pre>

<ul>
	<li>
		تُفرّغ الدالة <code>clearerr</code> كلًا من مؤشري الخطأ ونهاية الملف EOF للمجرى <code>stream</code>.
	</li>
	<li>
		تُعيد الدالة <code>feof</code> قيمةً غير صفرية إذا كان لمؤشر نهاية الملف الخاص بالمجرى <code>stream</code> قيمة، وإلا فإنها تعيد القيمة صفر.
	</li>
	<li>
		تُعيد الدالة <code>ferror</code> قيمة غير صفرية إذا كان لمؤشر الخطأ الخاص بالمجرى <code>stream</code> قيمة، وإلا فإنها تعيد القيمة صفر.
	</li>
	<li>
		تطبع الدالة <code>perror</code> سطرًا واحدًا يحتوي على رسالة خطأ على خرج البرنامج القياسي مسبوقًا بالسلسلة النصية المُشار إليها بواسطة المؤشر <code>s</code> مع إضافة مسافة فارغة ونقطتين ":". تُحدد رسالة الخطأ بحسب قيمة <code>errno</code> وتُعطي شرحًا بسيطًا عن سبب الخطأ، على سبيل المثال يتسبب البرنامج التالي برسالة خطأ:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_34_29" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">

        fclose</span><span class="pun">(</span><span class="pln">stdout</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">fgetc</span><span class="pun">(</span><span class="pln">stdout</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">){</span><span class="pln">
                fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln"> </span><span class="str">"What - no error!\n"</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        perror</span><span class="pun">(</span><span class="str">"fgetc"</span><span class="pun">);</span><span class="pln">
        exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/* رسالة الخطأ */</span><span class="pln">
fgetc</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Bad</span><span class="pln"> file number</span></pre>

<p style="text-align: center;">
	[مثال 2]
</p>

<p>
	لم نقُل أن الرسالة التي سنحصل عليها ستكون واضحة.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter9/" rel="external nofollow">Libraries</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-io-%D9%88%D8%AA%D9%86%D8%B3%D9%8A%D9%82%D9%87-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1796/" rel="">التعامل مع الدخل والخرج I/O وتنسيقه في لغة سي C</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1794/" rel="">التعامل مع المكتبات في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">التعامل مع المحارف والسلاسل النصية في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-pointers-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1682/" rel="">التعامل مع المؤشرات Pointers في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%B6%D8%A8%D8%B7-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D9%88%D8%B7%D9%8A%D9%86-localization-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1778/" rel="">التعامل مع المحارف وضبط إعدادات التوطين localization في لغة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1795</guid><pubDate>Sun, 18 Dec 2022 16:03:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x645;&#x643;&#x62A;&#x628;&#x627;&#x62A; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1794/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_11/6376089aa8960_------C.png.48ba75f932ae856f4006940d3ee8213b.png" /></p>
<p>
	نتحدث في هذا المقال عن مجموعة من الخصائص غير المرتبطة مع بعضها بعضًا مباشرةً ولكنها تصب في موضوع المكتبات والتعامل معها في <a href="https://academy.hsoub.com/programming/c/%D8%A8%D9%86%D9%8A%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1607/" rel="">لغة سي</a>، وهي القفزات اللا محلية والتعامل مع الإشارات والدوال ذات العدد المتغير من الوسطاء، ونستعرض <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1646/" rel="">الدوال</a> والأنواع و<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-macro-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC-%D8%A7%D9%84%D9%85%D8%B3%D8%A8%D9%82-preprocessor-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1750/" rel="">الماكرو</a> الموجودة بداخل ملفات الترويسة الموافقة لكل منها.
</p>

<h2>
	القفزات اللا محلية
</h2>

<p>
	تقدم القفزات اللا محلية non-local jumps طريقةً مشابهة لطريقة <code>goto</code> بالانتقال من دالة إلى أخرى. نلجأ إلى استخدام الماكرو <code>setjmp</code> والدالة <code>longjmp</code> لأن الأمر غير ممكن الحدوث باستخدام <code>goto</code> والعناوين labels إذ أن للعناوين نطاق scope داخل الدالة فقط، وتُعرف هذه الطريقة باسم goto اللا محلية أو القفزة اللا محلية.
</p>

<p>
	يصرح ملف الترويسة <code>&lt;setjmp.h&gt;</code> شيئًا يدعى <code>jmp_buf</code>، وهو اسم مستخدم في الماكرو والدالة لتخزين المعلومات الضرورية لإجراء القفزة، وتُكتب التصاريح على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4763_10" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;setjmp.h&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> setjmp</span><span class="pun">(</span><span class="pln">jmp_buf env</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> longjmp</span><span class="pun">(</span><span class="pln">jmp_buf env</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> val</span><span class="pun">);</span></pre>

<p>
	يُستخدم الماكرو <code>setjmp</code> لتهيئة قيمة <code>jmp_buf</code> ويُعيد القيمة صفر عند استدعائه الأولي، إلا أن الأمر غير الاعتيادي هنا، هو أنه يُعيد <strong>مجددًا</strong> قيمة غير صفرية لاحقًا عند استدعاء الدالة <code>longjmp</code>، وتكون القيمة غير الصفرية هذه مساويةً للقيمة المُمرّرة للدالة <code>longjmp</code>. لعل الأمر سيتضح لك بوضوح بعد المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4763_12" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;setjmp.h&gt;</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> func</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span><span class="pln">
jmp_buf place</span><span class="pun">;</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> retval</span><span class="pun">;</span><span class="pln">

        </span><span class="com">/*
    ‫يُعيد الاستدعاء الأول القيمة 0، ويعيد استدعاء آخر للدالة longjmp قيمة غير صفرية
         */</span><span class="pln">
        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">setjmp</span><span class="pun">(</span><span class="pln">place</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">){</span><span class="pln">
                printf</span><span class="pun">(</span><span class="str">"Returned using longjmp\n"</span><span class="pun">);</span><span class="pln">
                exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="com">/*
    لن يُعيد الاستدعاء التالي أي قيمة لأنه يقفز مجددًا إلى الأعلى
         */</span><span class="pln">
        func</span><span class="pun">();</span><span class="pln">
        printf</span><span class="pun">(</span><span class="str">"What! func returned!\n"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">void</span><span class="pln">
func</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">){</span><span class="pln">
      </span><span class="com">/*
    العودة إلى‫ main، ويبدو أن الاستدعاء الثاني لدالة setjmp يعيد القيمة 4
       */</span><span class="pln">
      longjmp</span><span class="pun">(</span><span class="pln">place</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">);</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"What! longjmp returned!\n"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 1]
</p>

<p>
	يمثل وسيط الدالة <code>longjmp</code> المسمى <code>val</code> القيمة المُعادة من الاستدعاء الثاني اللاحق لتعليمة الإعادة <code>return</code> ضمن الدالة <code>setjmp</code>، ويجب أن تكون هذه القيمة قيمة غير صفرية عادةً، وستغيّر القيمة إلى 1 إذا حاولت إعادة القيمة صفر باستخدام <code>longjmp</code>، وبذلك يمكننا معرفة فيما إذا كان استدعاء الدالة <code>setjmp</code> مباشرةً أو عن طريق استدعاء الدالة <code>longjmp</code>.
</p>

<p>
	تأثير الدالة <code>setjmp</code> غير محدد إذا لم يكن هناك أي استدعاء لها قبل استدعاء <code>longjmp</code>، وسيتسبب ذلك غالبًا بتوقف البرنامج. لا يُتوقّع من الدالة <code>longjmp</code> أن تُعيد قيمة بعد استدعائها مباشرةً. يكون لجميع الكائنات الممكن الوصول إليها من تعليمة الإعادة <code>return</code> داخل الدالة <code>setjmp</code> القيم السابقة المخزنة عند استدعاء <code>longjmp</code> عدا الكائنات ذات صنف التخزين التلقائي automatic storage class التي لا تحتوي على نوع "volatile"، وتكون قيمها غير محددة إذا تغيرت هذه الكائنات بين استدعاء <code>setjmp</code> واستدعاء <code>longjmp</code>.
</p>

<p>
	تُنفّذ الدالة <code>longjmp</code>على نحوٍ صحيح بخصوص المقاطعات interrupts والإشارات وأي دوال أخرى مرتبطة، ونحصل على سلوك غير معرف إذا حصل استدعاء <code>longjmp</code> باستخدام دالة نتج استدعائها عن إشارة وصلت بينما تُعالج إشارة أخرى.
</p>

<p>
	يُعدّ القفز إلى دالة غير فعالة باستخدام <code>longjmp</code> خطئًا فادحًا (ويُقصد بدالة غير فعالة أنها أعادت قيمة للتو، أو أن استدعاء <code>longjmp</code> آخر تحوّل إلى <code>setjmp</code> ضمن مجموعة من الاستدعاءات المترابطة nested calls).
</p>

<p>
	يصرّ المعيار على أن <code>setjmp</code> يجب أن تستخدم فقط مثل تعبير للتحكم في تعليمات <code>if</code> و <code>switch</code> و <code>do</code> و <code>while</code> و <code>for</code> (إضافةً إلى كونها التعليمة الوحيدة الموجودة في تعليمة تعبير)، وامتدادًا لهذه القاعدة، يمكن لاستدعاء <code>setjmp</code> (طالما يشكّل تعبير التحكم بأكمله كما ذكرنا سابقًا) أن يخضع للعامل <code>!</code>، أو أن يُقارن مباشرةً مع تعبير ثابت ذي قيمة عدد صحيح باستخدام إحدى العوامل العلاقيّة أو عوامل المساواة، ولا يجب استخدام أي تعابير معقدة أكثر من ذلك. إليك الأمثلة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4763_14" style=""><span class="pln">setjmp</span><span class="pun">(</span><span class="pln">place</span><span class="pun">);</span><span class="pln">                    </span><span class="com">/* تعليمة تعبير */</span><span class="pln">
</span><span class="kwd">if</span><span class="pun">(</span><span class="pln">setjmp</span><span class="pun">(</span><span class="pln">place</span><span class="pun">))</span><span class="pln"> </span><span class="pun">...</span><span class="pln">             </span><span class="com">/* تعبير تحكم كامل */</span><span class="pln">
</span><span class="kwd">if</span><span class="pun">(!</span><span class="pln">setjmp</span><span class="pun">(</span><span class="pln">place</span><span class="pun">))</span><span class="pln"> </span><span class="pun">...</span><span class="pln">            </span><span class="com">/* تعبير تحكم كامل */</span><span class="pln">
</span><span class="kwd">if</span><span class="pun">(</span><span class="pln">setjmp</span><span class="pun">(</span><span class="pln">place</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">4</span><span class="pun">)</span><span class="pln"> </span><span class="pun">...</span><span class="pln">         </span><span class="com">/* تعبير تحكم كامل */</span><span class="pln">
</span><span class="kwd">if</span><span class="pun">(</span><span class="pln">setjmp</span><span class="pun">(</span><span class="pln">place</span><span class="pun">)&lt;;</span><span class="lit">4</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="lit">1</span><span class="pun">!=</span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">...</span><span class="pln">  </span><span class="com">/* ممنوع */</span></pre>

<h2>
	التعامل مع الإشارة
</h2>

<p>
	تقدّم لنا دالتان إمكانية التعامل مع الأحداث غير المتزامنة؛ وتُعرف الإشارة signal بأنها شرط قد يحدث خلال تنفيذ البرنامج ويمكن تجاهله أو التعامل معه بصورةٍ خاصة أو استخدامه لإنهاء البرنامج كما هي الحالة الاعتيادية. تُرسل إحدى الدوال الإشارة بينما تُستخدم الأخرى لتحديد كيفية معالجة الإشارة، وقد تولّد الكثير من الإشارات من العتاد الصلب أو نظام التشغيل إضافةً إلى دوال إرسال الإشارات <code>raise</code>.
</p>

<p>
	الإشارات المُعرّفة في ملف الترويسة <code>&lt;signal.h&gt;</code>، هي:
</p>

<ul>
	<li>
		الإشارة <code>SIGABRT</code>: إنهاء غير اعتيادي للبرنامج، مثل الإنهاء الحاصل باستخدام الدالة <code>abort</code> (إبطال).
	</li>
	<li>
		الإشارة <code>SIGFPE</code>: عملية حسابية خاطئة، مثل التقسيم على الصفر أو الطفحان overflow (استثناء الفاصلة والأرقام العشرية Floating point exception).
	</li>
	<li>
		الإشارة <code>SIGILL</code>: العثور على "كائن برنامج غير صالح"، وهذا يعني غالبًا أن هناك تعليمات غير صالحة في البرنامج. (تعليمة غير صالحة Illegal instruction).
	</li>
	<li>
		الإشارة <code>SIGINT</code>: إشارة تفاعلية للفت الانتباه، وتولد هذه الإشارة على الأنظمة التفاعلية عادةً بكتابة مفتاح الهروب break-in في الطرفية terminal (مقاطعة Interrupt).
	</li>
	<li>
		الإشارة <code>SIGSEGV</code>: محاولة غير صالحة للوصول إلى مساحة تخزين، وتُسبب غالبًا بمحاولة تخزين قيمة في كائن مُشار إليه بمؤشر خاطئ. (انتهاك جزء segment violation).
	</li>
	<li>
		الإشارة <code>SIGTERM</code>: طلب إنهاء للبرنامج. (إنهاء Terminate).
	</li>
</ul>

<p>
	قد تمتلك بعض التنفيذات implementations بعض الإشارات الإضافية الزائدة عن الإشارات السابقة المعرفة في المعيار، وستبدأ أسماء الإشارات بالأحرف <code>SIG</code> وستمتلك قيمًا مميزة مختلفة عن القيم السابقة.
</p>

<p>
	تسمح لك الدالة <code>signal</code> بتحديد الفعل الذي تريد اتخاذه عند تلقي إشارة، وتُصطحب بحالة إشارة من الإشارات المذكورة سابقًا ومؤشر يشير إلى دالة تُنفّذ للتعامل مع الإشارة، وذلك بتغيير المؤشر وإعادة القيمة الأصلية، إذًا، نعرف الدالة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4763_16" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;signal.h&gt;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">(*</span><span class="pln">signal </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> sig</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">(*</span><span class="pln">func</span><span class="pun">)(</span><span class="typ">int</span><span class="pun">)))(</span><span class="typ">int</span><span class="pun">);</span></pre>

<p>
	يدل ما سبق على أن الدالة <code>signal</code> تُعيد مؤشرًا يشير إلى دالة أخرى وتأخذ الدالة الثانية وسيطًا واحدًا من نوع عدد صحيح وتُعيد <code>void</code>؛ بينما يكون الوسيط الثاني للدالة <code>signal</code> مؤشرًا يشير إلى دالة تعيد <code>void</code> بصورةٍ مشابهة، وتأخذ <code>int</code> وسيطًا لها.
</p>

<p>
	يمكن استخدام قيمتين مميزتين لوسيط لدالة <code>func</code> (دالة التعامل مع الإشارة)، ألا وهما <code>SIG_DFL</code> وهي معالج الإشارة الافتراضي الأولي و <code>SIG_IGN</code> الذي يُستخدم لتجاهل الإشارة، ويضبط التنفيذ حالة جميع الإشارات إلى واحدة من هذه القيمتين في بداية البرنامج.
</p>

<p>
	تُعاد قيمة <code>func</code> السابقة للإشارة إذ استدعيت <code>signal</code> بنجاح، وإلا فتُعاد <code>SIG_ERR</code> ويُضبط <code>errno</code> إلى قيمة.
</p>

<p>
	عند حصول حدث إشارة غير مُتجاهل، يُنفّذ أول <code>signal(sig, SIG_DFL)‎</code> يطابق الحالة وذلك إذا كانت الدالة <code>func</code> المترافقة تمثّل مؤشرًا يشير إلى دالة، وتتسبب تلك العملية بإعادة تشغيل معالج الإشارة إلى الإجراء الافتراضي ألا وهو إنهاء البرنامج، وإذا كانت الإشارة هي <code>SIGILL</code> فسيكون إعادة التشغيل معرفًا حسب التنفيذ، إذ قد تختار بعض التنفيذات حجب أي حالات أخرى من الإشارة عوضًا عن إعادة التشغيل.
</p>

<p>
	بعد ذلك، يُجرى استدعاء لدالة معالجة الإشارة، وسيعاود البرنامج عمله من نقطة حصول الحدث في معظم الحالات وذلك إذا أعادت الدالة قيمة بنجاح، إلا أننا نحصل على سلوك غير معرف إذا كانت قيمة <code>sig</code> مساويةً إلى <code>SIGFPE</code> (استثناء الفاصلة العائمة) أو أي استثناء حسابي معرف بحسب التنفيذ، والحل الأكثر استخدامًا لمعالج <code>SIGFPE</code> هو استدعاء إحدى الدوال: <code>abort</code>، أو <code>exit</code>، أو <code>longjmp</code>.
</p>

<p>
	يستعرض الجزء التالي استخدام الإشارة لتحقيق خروج أنيق من البرنامج عند تلقي مقاطعة أو إشارة "الانتباه التفاعلي interactive attention".
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4763_18" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;signal.h&gt;</span><span class="pln">


</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">temp_file</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> leave</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> sig</span><span class="pun">);</span><span class="pln">

main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln"> signal</span><span class="pun">(</span><span class="pln">SIGINT</span><span class="pun">,</span><span class="pln">leave</span><span class="pun">);</span><span class="pln">
        temp_file </span><span class="pun">=</span><span class="pln"> fopen</span><span class="pun">(</span><span class="str">"tmp"</span><span class="pun">,</span><span class="str">"w"</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">for</span><span class="pun">(;;)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="com">/*
                 افعل بعض الأشياء هنا
                 */</span><span class="pln">
                printf</span><span class="pun">(</span><span class="str">"Ready...\n"</span><span class="pun">);</span><span class="pln">
                </span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln">getchar</span><span class="pun">();</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="com">/* لا يمكننا الوصول إلى هذه النقطة */</span><span class="pln">
        exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/*
أغلق الملف‫ tmp عند الحصول على SIGINT، لكن انتبه
لأن استدعاء دوال المكتبات من معالج الإشارة غير مضمون العمل في جميع التنفيذات 
وهذا ليس ببرنامج متجاوب مع جميع التنفيذات بالضرورة
 */</span><span class="pln">

</span><span class="kwd">void</span><span class="pln">
leave</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> sig</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        fprintf</span><span class="pun">(</span><span class="pln">temp_file</span><span class="pun">,</span><span class="str">"\nInterrupted..\n"</span><span class="pun">);</span><span class="pln">
        fclose</span><span class="pun">(</span><span class="pln">temp_file</span><span class="pun">);</span><span class="pln">
        exit</span><span class="pun">(</span><span class="pln">sig</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 2]
</p>

<p>
	من الممكن للبرنامج أن يرسل إشارات إلى نفسه باستخدام دالة <code>raise</code> وهذا معرّف على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4763_20" style=""><span class="pln">include </span><span class="str">&lt;signal.h&gt;</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> raise </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> sig</span><span class="pun">);</span></pre>

<p>
	تُرسل الإشارة <code>sig</code> في هذه الحالة إلى البرنامج.
</p>

<p>
	تُعيد التعليمة <code>raise</code> القيمة صفر في حال النجاح، وقيمة غير صفرية عدا ذلك، تُنفّذ دالة <code>abort</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4763_22" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;signal.h&gt;</span><span class="pln">

</span><span class="kwd">void</span><span class="pln">
abort</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  raise</span><span class="pun">(</span><span class="pln">SIGABRT</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا حصلنا على إشارة لأي سبب كان -باستثناء استدعاء <code>abort</code> أو <code>raise</code>- فمن الممكن للدالة أن تستدعي فقط الإشارة أو أن تُسند قيمةً إلى كائن ساكن static متطاير volatile (مؤهل باستخدام <code>volatile</code>) من النوع <code>sig_atomic_t</code>، وهذا النوع مصرّحٌ في ملف الترويسة <code>&lt;signal.h&gt;</code>، وهو النوع الوحيد من الكائنات الممكن تعديله بأمان مثل كيان ذري atomic entity حتى مع وجود المقاطعات اللا متزامنة، وهذا قيدٌ مرهق مفروض من المعيار، الذي على سبيل المثال، يُبطل الدالة <code>leave</code> في مثالنا أعلاه، وعلى الرغم من أن الدالة ستعمل بصورةٍ صحيحة في بعض البيئات إلى أنها لا تتبع القوانين الصارمة الخاصة بالمعيار.
</p>

<h2>
	أعداد متغيرة من الوسطاء
</h2>

<p>
	غالبًا ما يكون تنفيذ دالة تأخذ عددًا غير معروفًا أو غير ثابت من الوسطاء محبّذًا عند كتابة الدالة، نذكر دالة <code>printf</code> على سبيل المثال التي سنتكلم عنها لاحقًا. يوضح المثال التالي تصريح دالة مشابهة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4763_24" style=""><span class="typ">int</span><span class="pln"> f</span><span class="pun">(</span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...</span><span class="pln"> </span><span class="pun">);</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> f</span><span class="pun">(</span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> g</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        f</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="lit">2</span><span class="pun">,</span><span class="lit">3</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 3]
</p>

<p>
	علينا تضمين الدوال المصرح عنها ضمن ملف الترويسة <code>&lt;stdarg.h&gt;</code> لكي نستطيع الوصول إلى الوسطاء الموجودة بداخل الدالة المُستدعاة، ونحصل نتيجةً لذلك على نوع جديد يدعى <code>va_list</code> وثلاثة دوال تتعامل مع كائنات من هذا النوع وتدعى <code>va_start</code> و <code>va_arg</code> و <code>va_end</code>.
</p>

<p>
	علينا استدعاء <code>va_start</code> قبل محاولة الوصول إلى لائحة الوسطاء المتغيرة، وهي معرفة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4763_26" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdarg.h&gt;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> va_start</span><span class="pun">(</span><span class="pln">va_list ap</span><span class="pun">,</span><span class="pln"> parmN</span><span class="pun">);</span></pre>

<p>
	يُهيّئ الماكرو <code>va_start</code> الوسيط <code>ap</code> بهدف الاستخدام اللاحق من قبل الدالتين <code>va_arg</code> و <code>va_end</code>، بينما يكون الوسيط الثاني للدالة <code>va_start</code> المسمّى <code>parmN</code> المعرّف identifier الذي يسمّي المعامل الذي يقع أقصى اليمين في لائحة المعاملات المتغيرة (أي المعامل الذي يقع قبل "…,")، ولا يجب التصريح عن المعرف <code>parmN</code> باستخدام صنف التخزين storage class من النوع <code>register</code> أو على أنه دالة أو نوع <a href="https://academy.hsoub.com/programming/c/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1675/" rel="">مصفوفة</a>.
</p>

<p>
	يمكن الوصول إلى الوسطاء على نحوٍ تتابعي بعد التهيئة وذلك باستخدام الماكرو <code>va_arg</code>، وهذا غير مألوف لأن النوع المُعاد يحدّد باستخدام وسيط للماكرو. لاحظ أن ذلك مستحيل التنفيذ في دالة فعلية، ويمكن تنفيذه فقط باستخدام الماكرو، وهو معرّف على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4763_29" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdarg.h&gt;</span><span class="pln">
type va_arg</span><span class="pun">(</span><span class="pln">va_list ap</span><span class="pun">,</span><span class="pln"> type</span><span class="pun">);</span></pre>

<p>
	سيتسبب كل استدعاء للماكرو السابق بالحصول على الوسيط التالي من لائحة الوسطاء بقيمة من النوع المُحدّد، ويجب للوسيط <code>va_list</code> أن يُهيّأ باستخدام <code>va_start</code>، ونحصل على سلوك غير معرّف إذا لم يكن الوسيط التالي من النوع المُحدّد. احذر من المشاكل التي قد تنتج من التحويلات الحسابية وتفاداها، إذ أن استخدام النوع <code>char</code> أو عدد صغير short وسيطًا ثانيًا للدالة <code>va_arg</code> خطأٌ واضح؛ لأن هذه الأنواع تُرقّى دائمًا إلى <code>signed int</code> أو <code>unsigned int</code> ويُحوّل <code>float</code> إلى <code>double</code>.
</p>

<p>
	لاحظ أن ترقية الكائنات المصرّحة عنها من الأنواع <code>char</code> و <code>unsigned char</code> و <code>unsigned short</code> وحقول البت عديمة الإشارة unsigned bitfields إلى النوع <code>unsigned int</code> الذي سيعقّد أكثر استخدام الدالة <code>va_arg</code> هو معرّف بحسب التنفيذ، وقد يكون ذلك هو السبب في الحصول على بعض المشاكل الخفية غير المتوقعة.
</p>

<p>
	نحصل على سلوك غير معرف أيضًا إذا استُدعيت الدالة <code>va_arg</code> ولم يكن هناك مزيدًا من الوسطاء.
</p>

<p>
	يجب أن يكون الوسيط <code>type</code> -في تعريفنا السابق لدالة <code>va_arg</code>- ممثلًا لاسم نوع يمكن تحويله إلى مؤشر يشير إلى كائن بإضافة المحرف "*" ببساطة (حتى يعمل الماكرو)، وذلك محقق للأنواع البسيطة مثل <code>char</code> (لأن <code>char *‎</code> يمثّل نوع مؤشر يشير إلى محرف)، لكن لن تعمل مصفوفة المحارف (لا يتحول النوع <code>char []‎</code> إلى مؤشر يشير إلى مصفوفة محارف بإضافة "*" إليه). يمكن لحسن الحظ معالجة المصفوفات إذا ما تذكرنا أن اسم المصفوفة الذي يُستخدم وسيطًا فعليًا لاستدعاء الدالة يُحوّل إلى مؤشر، وبذلك فإن النوع الصحيح لوسيط من النوع "مصفوفة من المحارف" هو <code>char *‎</code>.
</p>

<p>
	تُستدعى الدالة <code>va_end</code> بعد معالجة جميع الوسطاء، وهذا سيمنع اللائحة <code>va_list</code> من أن تُستخدم بعد ذلك، ونحصل على سلوك غير معرف إذا لم تُستخدم الدالة <code>va_end</code>.
</p>

<p>
	يمكن إعادة قراءة كامل لائحة الوسطاء باستدعاء الدالة <code>va_start</code> مجددًا بعد استدعاء <code>va_end</code>، وتُصرّح الدالة <code>va_end</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4763_31" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdarg.h&gt;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> va_end</span><span class="pun">(</span><span class="pln">va </span><span class="typ">list</span><span class="pln"> ap</span><span class="pun">);</span></pre>

<p>
	يوضح المثال التالي كيفية استخدام كل من <code>va_start</code> و <code>va_arg</code> و <code>va_end</code> ضمن دالة تُعيد أكبر قيم وسطائها التي تكون من نوع عدد صحيح.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4763_33" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdarg.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> maxof</span><span class="pun">(</span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...)</span><span class="pln"> </span><span class="pun">;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> f</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
        f</span><span class="pun">();</span><span class="pln">
        exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> maxof</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> n_args</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...){</span><span class="pln">
        </span><span class="kwd">register</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> max</span><span class="pun">,</span><span class="pln"> a</span><span class="pun">;</span><span class="pln">
        va_list ap</span><span class="pun">;</span><span class="pln">

        va_start</span><span class="pun">(</span><span class="pln">ap</span><span class="pun">,</span><span class="pln"> n_args</span><span class="pun">);</span><span class="pln">
        max </span><span class="pun">=</span><span class="pln"> va_arg</span><span class="pun">(</span><span class="pln">ap</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> n_args</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">((</span><span class="pln">a </span><span class="pun">=</span><span class="pln"> va_arg</span><span class="pun">(</span><span class="pln">ap</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">))</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> max</span><span class="pun">)</span><span class="pln">
                        max </span><span class="pun">=</span><span class="pln"> a</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        va_end</span><span class="pun">(</span><span class="pln">ap</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> max</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> f</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> j</span><span class="pun">[</span><span class="lit">256</span><span class="pun">];</span><span class="pln">
        j</span><span class="pun">[</span><span class="lit">42</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">24</span><span class="pun">;</span><span class="pln">
        printf</span><span class="pun">(</span><span class="str">"%d\n"</span><span class="pun">,</span><span class="pln">maxof</span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> i</span><span class="pun">,</span><span class="pln"> j</span><span class="pun">[</span><span class="lit">42</span><span class="pun">],</span><span class="pln"> </span><span class="lit">0</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 4]
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter9/" rel="external nofollow">Libraries</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/c/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%B9%D9%86-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-io-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1795/" rel="">مقدمة عن التعامل مع الدخل والخرج I/O في لغة سي C</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%82%D9%8A%D9%85-%D8%A7%D9%84%D8%AD%D8%AF%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%B1%D9%8A%D8%A7%D8%B6%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1793/" rel="">القيم الحدية والدوال الرياضية في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">التعامل مع المحارف والسلاسل النصية في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-pointers-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1682/" rel="">التعامل مع المؤشرات Pointers في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%B6%D8%A8%D8%B7-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D9%88%D8%B7%D9%8A%D9%86-localization-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1778/" rel="">التعامل مع المحارف وضبط إعدادات التوطين localization في لغة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1794</guid><pubDate>Sun, 11 Dec 2022 16:06:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x642;&#x64A;&#x645; &#x627;&#x644;&#x62D;&#x62F;&#x64A;&#x629; &#x648;&#x627;&#x644;&#x62F;&#x648;&#x627;&#x644; &#x627;&#x644;&#x631;&#x64A;&#x627;&#x636;&#x64A;&#x629; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%82%D9%8A%D9%85-%D8%A7%D9%84%D8%AD%D8%AF%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%B1%D9%8A%D8%A7%D8%B6%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1793/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_11/6376003b7b219_-------C.png.3edce82b41527cda2fa825b8c6ef4341.png" /></p>
<p>
	نتطرق في هذا المقال إلى كل من ملفات الترويسة الخاصة بالقيم الحدّية Limits والدوال الرياضية في <a href="https://academy.hsoub.com/programming/c/%D8%A8%D9%86%D9%8A%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1607/" rel="">لغة C</a>، كما نقدّم شرحًا موجزًا عن الأسماء المعرفة بداخل كل من الملفات، ويمكنك الاحتفاظ بهذا القسم كمرجع سريع بخصوص هذا الأمر.
</p>

<h2>
	القيم الحدية
</h2>

<p>
	يعرّف ملفا الترويسة <code>&lt;float.h&gt;</code> و <code>&lt;limits.h&gt;</code> عدة قيم حدية معرفة حسب التطبيق.
</p>

<h3>
	ملف الترويسة <code>&lt;limits.h&gt;</code>
</h3>

<p>
	يوضح الجدول 1 الأسماء المُصرح عنها في هذا الملف وقيمها المسموحة، إضافةً إلى وصف موجز عن وظيفتها، إذ يوضح وصف <code>SHRT_MIN</code> مثلًا أن قيمة الاسم في بعض التطبيقات يجب أن تكون أقل من أو تساوي القيمة ‎-32767، وهذا يعني أن البرنامج لا يستطيع الاعتماد على متغيرات صغيرة short لتخزين قيم سالبة تتعدى 32767- إذا أردنا قابلية نقل أكبر للبرنامج. قد يدعم التطبيق في بعض الأحيان القيم السالبة الأكبر إلا أن الحد الأدنى الذي يجب أن يدعمه التطبيق هو 32767-.
</p>
<style type="text/css">
table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; }</style>
<table>
	<thead>
		<tr>
			<th>
				الاسم
			</th>
			<th>
				القيم المسموحة
			</th>
			<th>
				الوصف
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<code>CHAR_BIT</code>
			</td>
			<td>
				(≥8)
			</td>
			<td>
				بتات في قيمة من نوع <code>char</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>CHAR_MAX</code>
			</td>
			<td>
				اقرأ الملاحظة
			</td>
			<td>
				القيمة العظمى لنوع <code>char</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>CHAR_MIN</code>
			</td>
			<td>
				اقرأ الملاحظة
			</td>
			<td>
				القيمة الدنيا لنوع <code>char</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>INT_MAX</code>
			</td>
			<td>
				(≥‎+32767)
			</td>
			<td>
				القيمة العظمى لنوع <code>int</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>INT_MIN</code>
			</td>
			<td>
				(≤‎−32767)
			</td>
			<td>
				القيمة الدنيا لنوع <code>int</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>LONG_MAX</code>
			</td>
			<td>
				(≥‎+2147483647)
			</td>
			<td>
				القيمة العظمى لنوع <code>long</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>LONG_MIN</code>
			</td>
			<td>
				(≤‎−2147483647)
			</td>
			<td>
				القيمة الدنيا لنوع <code>long</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>MB_LEN_MAX</code>
			</td>
			<td>
				(≥1)
			</td>
			<td>
				عدد البتات الأعظمي في محرف متعدد البتات multibyte character
			</td>
		</tr>
		<tr>
			<td>
				<code>SCHAR_MAX</code>
			</td>
			<td>
				(≥‎+127)
			</td>
			<td>
				القيمة العظمى لنوع <code>signed char</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>SCHAR_MIN</code>
			</td>
			<td>
				(≤‎−127)
			</td>
			<td>
				القيمة الدنيا لنوع <code>signed char</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>SHRT_MAX</code>
			</td>
			<td>
				(≥‎+32767)
			</td>
			<td>
				القيمة العظمى لنوع <code>short</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>SHRT_MIN</code>
			</td>
			<td>
				(≤‎−32767)
			</td>
			<td>
				القيمة الدنيا لنوع <code>short</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>UCHAR_MAX</code>
			</td>
			<td>
				(≥255U)
			</td>
			<td>
				القيمة العظمى لنوع <code>unsigned char</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>UINT_MAX</code>
			</td>
			<td>
				(≥65535U)
			</td>
			<td>
				القيمة الدنيا لنوع <code>unsigned int</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>ULONG_MAX</code>
			</td>
			<td>
				(≥4294967295U)
			</td>
			<td>
				القيمة العظمى لنوع <code>unsigned long</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>USHRT_MAX</code>
			</td>
			<td>
				(≥65535U)
			</td>
			<td>
				القيمة الدنيا لنوع <code>unsigned short</code>
			</td>
		</tr>
	</tbody>
</table>

<p style="text-align: center;">
	[جدول 1 أسماء ملف الترويسة <code>&lt;limits.h&gt;</code>]
</p>

<p>
	ملاحظة: إذا كان التطبيق يعامل <code>char</code> على أنه من نوع ذو إشارة فقيمة <code>CHAR_MAX</code> و<code>CHAR_MIN</code> مماثلة لقيمة <code>SCHAR</code> الموافق لها، وإلا فقيمة <code>CHAR_MIN</code> هي صفر وقيمة <code>CHAR_MAX</code> هي مساوية لقيمة <code>UCHAR_MAX</code>.
</p>

<h3>
	ملف الترويسة <code>&lt;float.h&gt;</code>
</h3>

<p>
	يتضمن ملف الترويسة <code>&lt;float.h&gt;</code> قيمًا دنيا للأرقام ذات الفاصلة العائمة floating point بصورةٍ مشابهة لما سبق، ويمكن الافتراض عند عدم وجود قيمة دنيا لنوع ما أن هذا النوع لا يمتلك قيمة دنيا أو أن القيمة مرتبطة بقيمة أخرى.
</p>

<table>
	<thead>
		<tr>
			<th>
				الاسم
			</th>
			<th>
				القيم المسموحة
			</th>
			<th>
				الوصف
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<code>FLT_RADIX</code>
			</td>
			<td>
				(≥2)
			</td>
			<td>
				تمثيل أساس الأس
			</td>
		</tr>
		<tr>
			<td>
				<code>DBL_DIG</code>
			</td>
			<td>
				(≥10)
			</td>
			<td>
				عدد خانات الدقة في نوع <code>double</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>DBL_EPSILON</code>
			</td>
			<td>
				(≤1E−9)
			</td>
			<td>
				العدد الموجب الأدنى الذي يحقق 1.0‎ + x ≠ 1.0
			</td>
		</tr>
		<tr>
			<td>
				<code>DBL_MANT_DIG</code>
			</td>
			<td>
				(—)
			</td>
			<td>
				عدد خانات أساس <code>FLT_RADIX</code> في الجزء العشري من النوع <code>double</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>DBL_MAX</code>
			</td>
			<td>
				(≥1E+37)
			</td>
			<td>
				القيمة العظمى لنوع <code>double</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>DBL_MAX_10_EXP</code>
			</td>
			<td>
				(≥‎+37)
			</td>
			<td>
				القيمة العظمى لأس (أساسه 10) من نوع <code>double</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>DBL_MAX_EXP</code>
			</td>
			<td>
				(—)
			</td>
			<td>
				القيمة العظمى لأس (أساسه <code>FLT_RADIX</code>) من نوع <code>double</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>DBL_MIN</code>
			</td>
			<td>
				(≤1E−37)
			</td>
			<td>
				القيمة الدنيا للنوع <code>double</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>DBL_MIN_10_EXP</code>
			</td>
			<td>
				(≤37)
			</td>
			<td>
				القيمة الدنيا لأس (أساسه 10) من نوع <code>double</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>DBL_MIN_EXP</code>
			</td>
			<td>
				(—)
			</td>
			<td>
				القيمة الدنيا لأس (أساسه <code>FLT_RADIX</code>) من نوع <code>double</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>FLT_DIG</code>
			</td>
			<td>
				(≥6)
			</td>
			<td>
				عدد خانات الدقة في نوع <code>float</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>FLT_EPSILON</code>
			</td>
			<td>
				(≤1E−5)
			</td>
			<td>
				العدد الموجب الأدنى الذي يحقق 1.0‎ + x ≠ 1.0
			</td>
		</tr>
		<tr>
			<td>
				<code>FLT_MANT_DIG</code>
			</td>
			<td>
				(—)
			</td>
			<td>
				عدد خانات أساس <code>FLT_RADIX</code> في الجزء العشري من النوع <code>float</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>FLT_MAX</code>
			</td>
			<td>
				(≥1E+37)
			</td>
			<td>
				القيمة العظمى للنوع <code>float</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>FLT_MAX_10_EXP</code>
			</td>
			<td>
				(≥‎+37)
			</td>
			<td>
				القيمة العظمى لأس (أساسه 10) من نوع <code>float</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>FLT_MAX_EXP</code>
			</td>
			<td>
				(—)
			</td>
			<td>
				القيمة العظمة لأس (أساسه <code>FLT_RADIX</code>) من نوع <code>float</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>FLT_MIN</code>
			</td>
			<td>
				(≤1E−37)
			</td>
			<td>
				القيمة الدنيا للنوع <code>float</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>FLT_MIN_10_EXP</code>
			</td>
			<td>
				(≤‎−37)
			</td>
			<td>
				القيمة الدنيا لأس (أساسه 10) من نوع <code>float</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>FLT_MIN_EXP</code>
			</td>
			<td>
				(—)
			</td>
			<td>
				القيمة الدنيا لأس (أساسه <code>FLT_RADIX</code>) من نوع <code>float</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>FLT_ROUNDS</code>
			</td>
			<td>
				(0)
			</td>
			<td>
				يحدد التقريب للفاصلة العائمة، غير مُحدّد لقيمة 1-، تقريب باتجاه الصفر لقيمة 0، تقريب للقيمة الأقرب لقيمة 1، تقريب إلى اللا نهاية الموجبة لقيمة 2، تقريب إلى اللا نهاية السالبة لقيمة 3. أي قيمة أخرى تكون محددة بحسب التطبيق
			</td>
		</tr>
		<tr>
			<td>
				<code>LDBL_DIG</code>
			</td>
			<td>
				(≥10)
			</td>
			<td>
				عدد خانات الدقة في نوع <code>long double</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>LDBL_EPSILON</code>
			</td>
			<td>
				(≤1E−9)
			</td>
			<td>
				العدد الموجب الأدنى الذي يحقق ‎1.0 + x ≠ 1.0
			</td>
		</tr>
		<tr>
			<td>
				<code>LDBL_MANT_DIG</code>
			</td>
			<td>
				(—)
			</td>
			<td>
				عدد خانات أساس <code>FLT_RADIX</code> في الجزء العشري من النوع <code>long double</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>LDBL_MAX</code>
			</td>
			<td>
				(≥1E+37)
			</td>
			<td>
				القيمة العظمى للنوع <code>long double</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>LDBL_MAX_10_EXP</code>
			</td>
			<td>
				(≥‎+37)
			</td>
			<td>
				القيمة العظمى لأس (أساسه 10) من نوع <code>long double</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>LDBL_MAX_EXP</code>
			</td>
			<td>
				(—)
			</td>
			<td>
				القيمة العظمى لأس (أساسه <code>FLT_RADIX</code>) من نوع <code>long double</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>LDBL_MIN</code>
			</td>
			<td>
				(≤1E−37)
			</td>
			<td>
				القيمة الدنيا للنوع <code>long double</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>LDBL_MIN_10_EXP</code>
			</td>
			<td>
				(≤‎−37)
			</td>
			<td>
				القيمة الدنيا لأس (أساسه 10) من نوع <code>long double</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>LDBL_MIN_EXP</code>
			</td>
			<td>
				(—)
			</td>
			<td>
				القيمة الدنيا لأس (أساسه <code>FLT_RADIX</code>) من نوع <code>long double</code>
			</td>
		</tr>
	</tbody>
</table>

<p style="text-align: center;">
	[جدول 2 أسماء ملف الترويسة <code>&lt;float.h&gt;</code>]
</p>

<h2>
	الدوال الرياضية
</h2>

<p>
	إذا كنت تكتب برامجًا رياضيّة تجري عمليات على الفاصلة العائمة وما شابه، فهذا يعني أنك تحتاج الوصول إلى مكتبات الدوال الرياضية دون أدنى شك، ويأخذ هذا النوع من <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1646/" rel="">الدوال</a> وسطاءً من النوع <code>double</code> ويعيد نتيجةً من النوع ذاته أيضًا. تُعرَّف الدوال و<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-macro-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC-%D8%A7%D9%84%D9%85%D8%B3%D8%A8%D9%82-preprocessor-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1750/" rel="">الماكرو</a> المرتبطة بها في ملف الترويسة <code>&lt;math.h&gt;</code>.
</p>

<p>
	يُستبدل الماكرو <code>HUGE_VAL</code> المُعرف إلى تعبير ذي قيمة موجبة من نوع عدد عشري مضاعف الدقة "double"، ولا يمكن تمثيله بالضرورة باستخدام النوع <code>float</code>.
</p>

<p>
	نحصل على <strong>خطأ نطاق domain error</strong> في جميع الدوال إذا كانت قيمة الوسيط المُدخل خارج النطاق المُعرّف للدالة، مثل محاولة الحصول على جذر تربيعي لعدد سالب، وإذا حصل هذا الخطأ يُضبط <code>errno</code> إلى الثابت <code>EDOM</code>، وتُعيد الدالة قيمة معرّفة بحسب التطبيق.
</p>

<p>
	نحصل على <strong>خطأ مجال range error</strong> إذا لم يكن من الممكن تمثيل نتيجة الدالة بقيمة عدد عشري مضاعف الدقة، تُعيد الدالة القيمة <code>‎±HUGE_VAL</code> إذا كانت قيمة النتيجة كبيرة جدًا (الإشارة موافقة للقيمة) وتُضبط <code>errno</code> إلى <code>ERANGE</code> إذا كانت القيمة صغيرة جدًا وتُعاد القيمة <code>0.0</code> وتُعتمد قيمة <code>errno</code> على تعريف التطبيق.
</p>

<p>
	تصف اللائحة التالية كلًا من الدوال المتاحة باختصار:
</p>

<ul>
	<li>
		الدالة <code>double acos(double x);‎</code>: تُعيد القيمة الرئيسة Principal value لقوس جيب التمام Arc cosine للوسيط <code>x</code> في النطاق من 0 إلى π راديان، ونحصل على الخطأ <code>EDOM</code> إذا كان <code>x</code> خارج النطاق ‎-1 إلى 1.
	</li>
	<li>
		الدالة <code>double asin(double x);‎</code>: تُعيد القيمة الرئيسة لقوس الجيب Arc sin للوسيط <code>x</code> في النطاق من ‎-π/2 إلى ‎+π/2 راديان، ونحصل على الخطأ <code>EDOM</code> إذا كان <code>x</code>خارج النطاق ‎-1 إلى 1.
	</li>
	<li>
		الدالة <code>double atan(double x);‎</code>: تُعيد القيمة الرئيسة لقوس الظل Arc tan للوسيط <code>x</code> في النطاق من ‎-π/2 إلى ‎+π/2 راديان.
	</li>
	<li>
		الدالة <code>double atan2(double y, double x);‎</code>: تُعيد القيمة الرئيسة لقوس الظل للقيمة y/x في النطاق من ‎-π إلى ‎+π راديان، وتستخدم إشارتي الوسيطين <code>x</code> و <code>y</code> لتحديد الربع الذي تقع فيه قيمة الإجابة، ونحصل على الخطأ <code>EDOM</code> في حال كان <code>x</code> و <code>y</code> مساويين إلى الصفر.
	</li>
	<li>
		الدالة <code>double cos(double x);‎</code>: تُعيد جيب تمام قيمة الوسيط <code>x</code> (تُقاس x بالراديان).
	</li>
	<li>
		الدالة <code>double sin(double x);‎</code>: تُعيد جيب قيمة الوسيط <code>x</code> (تُقاس x بالراديان).
	</li>
	<li>
		الدالة <code>double tan(double x);‎</code>: تُعيد ظل قيمة الوسيط <code>x</code> (تُقاس x بالراديان)، وتكون إشارة <code>HUGE_VAL</code> غير مضمونة الصحّة إذا حدث خطأ مجال.
	</li>
	<li>
		الدالة <code>double cosh(double x);‎</code>: تُعيد جيب التمام القطعي Hyperbolic للقيمة <code>x</code>، ونحصل على الخطأ <code>ERANGE</code> إذا كان مقدار <code>x</code> كبيرًا جدًا.
	</li>
	<li>
		الدالة <code>double sinh(double x);‎</code>: تُعيد الجيب القطعي للقيمة <code>x</code>، ونحصل على الخطأ <code>ERANGE</code> إذا كان مقدار <code>x</code> كبيرًا للغاية.
	</li>
	<li>
		الدالة <code>double tanh(double x);‎</code>: تُعيد الظل القطعي للقيمة <code>x</code>.
	</li>
	<li>
		الدالة <code>double exp(double x);‎</code>: دالة أسية للقيمة <code>x</code>، ونحصل على الخطأ <code>ERANGE</code> إذا كان مقدار <code>x</code>كبيرًا جدًا.
	</li>
	<li>
		الدالة <code>double frexp(double value, int *exp);‎</code>: تجزئة عدد ذو فاصلة عائمة إلى كسر طبيعي وأُس عدد صحيح من الأساس 2، ويخزن هذا العدد الصحيح في الغرض المُشار إليه بواسطة المؤشر <code>exp</code>.
	</li>
	<li>
		الدالة <code>double ldexp(double x, int exp);‎</code>: ضرب <code>x</code> بمقدار 2 إلى الأُس <code>exp</code>، وقد نحصل على الخطأ <code>ERANGE</code>.
	</li>
	<li>
		الدالة <code>double log(double x);‎</code>: اللوغاريتم الطبيعي للقيمة <code>x</code>، وقد نحصل على الخطأ <code>EDOM</code> إذا كانت القيمة <code>x</code> سالبة، و <code>ERANGE</code> إذا كانت <code>x</code> تساوي إلى الصفر.
	</li>
	<li>
		الدالة <code>double log10(double x);‎</code>: اللوغاريتم ذو الأساس 10 للقيمة <code>x</code>، ونحصل على الخطأ <code>EDOM</code> إذا كانت <code>x</code> سالبة، و <code>ERANGE</code> إذا كانت <code>x</code> تساوي إلى الصفر.
	</li>
	<li>
		الدالة <code>double modf(double value, double *iptr);‎</code>: تجزئة قيمة الوسيط <code>value</code> إلى جزء عدد صحيح وجزء كسري، ويحمل كل جزء إشارة الوسيط ذاتها، وتُخزن قيمة العدد الصحيح على أنها قيمة من نوع <code>double</code> في الكائن المُشار إليه بواسطة المؤشر <code>iptr</code> وتُعيد الدالة الجزء الكسري.
	</li>
	<li>
		الدالة <code>double pow(double x, double y);‎</code>: تحسب x إلى الأس y، ونحصل على الخطأ <code>EDOM</code> إذا كانت القيمة <code>x</code> سالبة و <code>y</code> عدد غير صحيح، أو <code>ERANGE</code> إذا لم يكن من الممكن تمثيل النتيجة في حال كانت <code>x</code> تساوي إلى الصفر و <code>y</code> موجبة أو تساوي الصفر.
	</li>
	<li>
		الدالة <code>double sqrt(double x);‎</code>: تحسب مربع القيمة <code>x</code>، ونحصل على الخطأ <code>EDOM</code> إذا كانت <code>x</code> سالبة.
	</li>
	<li>
		الدالة <code>double ceil(double x);‎</code>: أصغر عدد صحيح لا يكون أصغر من <code>x</code>.
	</li>
	<li>
		الدالة <code>double fabs(double x);‎</code>: القيمة المطلقة للوسيط <code>x</code>.
	</li>
	<li>
		الدالة <code>double floor(double x);‎</code>: أكبر عدد صحيح لا يكون أكبر من <code>x</code>.
	</li>
	<li>
		الدالة <code>double fmod(double x, double y);‎</code>: الباقي العشري من عملية القسمة x/y، ويعتمد الأمر على تعريف التطبيق فيما إذا كانت <code>fmod</code> تُعيد صفرًا أو خطأ نطاق في حال كانت <code>y</code> تساوي إلى الصفر.
	</li>
</ul>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter9/" rel="external nofollow">Libraries</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1794/" rel="">تعلم لغة سي التعامل مع المكتبات في لغة سي C</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%B6%D8%A8%D8%B7-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D9%88%D8%B7%D9%8A%D9%86-localization-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1778/" rel="">التعامل مع المحارف وضبط إعدادات التوطين localization في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1646/" rel="">الدوال في لغة C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%88%D8%AF-recursion-%D9%88%D8%AA%D9%85%D8%B1%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D8%B3%D8%B7%D8%A7%D8%A1-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1673/" rel="">مفهوم التعاود Recursion وتمرير الوسطاء إلى الدوال في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D9%86%D8%B7%D8%A7%D9%82-scope-%D9%88%D8%A7%D9%84%D8%B1%D8%A8%D8%B7-linkage-%D8%B9%D9%84%D9%89-%D9%85%D8%B3%D8%AA%D9%88%D9%89-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1674/" rel="">مفهوم النطاق Scope والربط Linkage على مستوى الدوال في لغة C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1793</guid><pubDate>Sun, 04 Dec 2022 16:01:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x645;&#x62D;&#x627;&#x631;&#x641; &#x648;&#x636;&#x628;&#x637; &#x625;&#x639;&#x62F;&#x627;&#x62F;&#x627;&#x62A; &#x627;&#x644;&#x62A;&#x648;&#x637;&#x64A;&#x646; localization &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%B6%D8%A8%D8%B7-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D9%88%D8%B7%D9%8A%D9%86-localization-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1778/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_11/63679af12cb6e_-----localization---C.png.22c78c862b804a57baa93a4233273912.png" /></p>
<p>
	نستعرض في هذا المقال كيفية التعامل مع المحارف في لغة سي باستخدام دوال <a href="https://academy.hsoub.com/programming/c/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%85%D9%83%D8%AA%D8%A8%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1777/" rel="">المكتبات</a> القياسية، إضافةً إلى التوطين وإعدادات اللغة المحلية locale.
</p>

<h2>
	التعامل مع المحارف
</h2>

<p>
	هناك مجموعةٌ متنوعةٌ من <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1646/" rel="">الدوال</a> تهدف لفحص وربط mapping المحارف، إذ تسمح لك دوال الفحص test functions -التي سنناقشها أولًا- بفحص فيما إذا كان <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">المحرف</a> من نوع معين، مثل حرف أبجدي، أو حرف صغير أم كبير، أو محرف رقمي، أو محرف تحكّم control character، أو إشارة ترقيم، أو محرف قابل للطباعة أو لا، وهكذا. تُعيد دوال فحص المحرف قيمة عدد صحيح integer تساوي الصفر إذا لم يكن المحرف المُحدّد منتميًا إلى التصنيف المذكور، أو قيمة غير صفرية عدا ذلك، ويأخذ هذا النوع من الدوال وسيطًا ذا قيمة عدد صحيح تُمثّل قيمته من نوع "unsigned char"، أو عدد صحيح ثابت قيمته "EOF" مثل تلك القيمة المُعادة من دوال مشابهة، مثل <code>getchar()‎</code>، ونحصل على سلوك غير معرّف خارج هذه الحالات.
</p>

<p>
	تعتمد هذه الدوال على إعدادات البرنامج المحلية:
</p>

<p>
	<strong>محرف الطباعة printing character</strong> هو عضو من مجموعة المحارف المعرّفة بحسب التطبيق، ويشغل كل محرف طباعة موقع طباعة واحد، و<strong>محرف التحكم control character</strong> هو عضو من مجموعة المحارف المعرفة بحسب التطبيق أيضًا إلا أن كل محرف منها ليس بمحرف طباعة. إذا استخدمنا مجموعة محارف معيار ASCII ‏‎7-bit، ستكون محارف الطباعة بين الفراغ <code>(0x20)</code> وتيلدا tilde‏ <code>(0x7e)‏</code>‎، بينما تكون محارف التحكم بين <code>NUL (0x0‎)‎</code> و <code>US (0x1f)‎</code> والمحرف <code>DEL (0x7f)‎</code>.
</p>

<p>
	تجد أدناه ملخصًا يحتوي على جميع دوال فحص المحرف، ويجب تضمين ملف الترويسة <code>&lt;ctype.h&gt;</code> قبل استخدام أيّ منها.
</p>

<ul>
	<li>
		دالة <code>isalnum(int c)‎</code>: تُعيد القيمة "True" إذا كان <code>c</code> محرفًا أبجديًا أو رقمًا؛ أي <code>(isalpha(c)||isdigit(c)‎)</code>.
	</li>
	<li>
		دالة <code>isalpha(int c)‎</code>: تُعيد القيمة "True" إذا كان هذا الشرط <code>(isupper(c)||islower(c)‎)</code> محققًا، كما أنها تُعيد القيمة True لمجموعة المحارف المُعرفة بحسب التطبيق التي لا تعيد القيمة True عند تمريرها على الدالة <code>iscntrl</code> أو <code>isdigit</code> أو <code>ispunct</code> أو <code>isspace</code> وتكون مجموعة المحارف الإضافية هذه فارغة في لغة سي المحلية.
	</li>
	<li>
		دالة <code>iscntrl(int c)‎</code>: تُعيد القيمة True إذا كان المحرف محرف تحكم.
	</li>
	<li>
		دالة <code>isdigit(int c)‎</code>: تُعيدالقيمة True إذا كان المحرف رقمًا عشريًا decimal.
	</li>
	<li>
		دالة <code>isgraph(int c)‎</code>: تُعيد القيمة True إذا كان المحرف هو محرف طباعة عدا محرف المسافة الفارغة.
	</li>
	<li>
		دالة <code>islower(int c)‎</code>: تُعيد القيمة True إذا كان المحرف محرفًا أبجديًا صغيرًا lower case، كما أنها محققة لمجموعة محارف معرفة حسب التطبيق لا تُعيد القيمة True لأي من الدالة <code>iscntrl</code> أو <code>isdigit</code> أو <code>ispunct</code> أو <code>isspace</code>، وتكون مجموعة المحارف الإضافية هذه فارغة في C المحلية.
	</li>
	<li>
		دالة <code>isprint(int c)‎</code>: تُعيد القيمة True إذا كان المحرف محرف طباعة (متضمّنًا محرف المسافة الفارغة).
	</li>
	<li>
		دالة <code>ispunct(int c)‎</code>: تُعيد القيمة True إذا كان المحرف محرف طباعة عدا محرف المسافة الفارغة أو المحارف التي تُعيد القيمة True في دالة <code>isalnum</code>.
	</li>
	<li>
		دالة <code>isspace(int c)‎</code>: تُعيد القيمة True إذا كان المحرف محرف مسافة بيضاء (المحرف <code>' '</code> أو <code>‎\f</code> أو <code>‎\n</code> أو <code>‎\r</code> أو <code>‎\t</code> أو <code>‎\v</code>)
	</li>
	<li>
		دالة <code>isupper(int c)‎</code>: تُعيد القيمة True إذا كان المحرف محرف أبجديًا كبيرًا upper case، كما أنها محققة لمجموعة محارف معرفة حسب التطبيق لا تُعيد القيمة True لأي من الدالة <code>iscntrl</code> أو <code>isdigit</code> أو <code>ispunct</code> أو <code>isspace</code>، وتكون مجموعة المحارف الإضافية هذه فارغة في لغة سي المحلية.
	</li>
	<li>
		دالة <code>isxdigit(int c)‎</code>: تُعيد القيمة True إذا كان المحرف رقم ستّ عشري صالح.
	</li>
</ul>

<p>
	هناك دالتان إضافيتان تربطان المحارف من مجموعةٍ إلى أخرى، إذ تُعيد الدالة <code>tolower</code> محرفًا صغيرًا موافقًا لمحرف كبير مُرِّر لها، على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5741_12" style=""><span class="pln">tolower</span><span class="pun">(</span><span class="str">'A'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="str">'a'</span></pre>

<p>
	تُعيد الدالة <code>tolower</code> المحرف ذاته، إذا تلقّت أي محرف مُغاير للمحارف الأبجدية الكبيرة.
</p>

<p>
	تربط الدالة <code>toupper</code> المعاكسة للدالة السابقة في عملها المحرف المُمرّر لها إلى مكافئه الكبير.
</p>

<p>
	تُجرى عملية الربط في الدالتين السابقتين فقط في حال وجود محرف موافق للمحرف المُمرّر لها، إذ لا تمتلك بعض اللغات محرفًا كبيرًا موافق لمحرف صغير والعكس صحيح.
</p>

<h2>
	التوطين Localization
</h2>

<p>
	نستطيع التحكم بالإعدادات المحليّة للبرنامج من هنا، ويصرح ملف الترويسة <code>&lt;locale.h&gt;</code> دوال <code>setlocale</code> و <code>localeconv</code> وعددًا من <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-macro-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC-%D8%A7%D9%84%D9%85%D8%B3%D8%A8%D9%82-preprocessor-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1750/" rel="">الماكرو</a>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5741_10" style=""><span class="pln">LC_ALL
LC_COLLATE
LC_CTYPE
LC_MONETARY
LC_NUMERIC
LC_TIME</span></pre>

<p>
	تُستبدل جميع الماكرو بتعبير <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AB%D9%88%D8%A7%D8%A8%D8%AA-%D9%88%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%87%D8%B1%D9%88%D8%A8-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1614/" rel="">ثابت</a> ذي قيمة عدد صحيح وتُستخدم القيمة الناتجة عن التعبير مكان الوسيط <code>category</code> في الدالة <code>setlocale</code> (يمكن تعريف أسماء أخرى أيضًا، ويجب أن يبدأ كل منها بـ <code>LC_X</code>، إذ يمثّل <code>X</code> المحرف الأبجدي الكبير)، ويُستخدم النوع <code>struct lconv</code> لتخزين المعلومات المتعلقة بتنسيق القيم الرقمية، ويُستخدَم <code>CHAR_MAX</code> للأعضاء من النوع <code>char</code> للدلالة على أن القيمة غير متوافرة في الإعدادات المحلية الحالية.
</p>

<p>
	يحتوي <code>lconv</code> على عضو واحد على الأقل من الأعضاء التالية:
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
	<thead>
		<tr>
			<th>
				العضو
			</th>
			<th>
				الاستخدام
			</th>
			<th>
				تمثيله في إصدارات سي المحلية
			</th>
			<th>
				ملاحظات إضافية
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<code>char *decimal_point</code>
			</td>
			<td>
				يُستخدم المحرف للفاصلة العشرية في القيم المنسقة غير المالية.
			</td>
			<td>
				"."
			</td>
			<td>
				---
			</td>
		</tr>
		<tr>
			<td>
				<code>char *thousands_sep</code>
			</td>
			<td>
				يُستخدم المحرف لفصل مجموعات من الخانات الواقعة على يسار الفاصلة العشرية في القيم المنسقة غير المالية.
			</td>
			<td>
				""
			</td>
			<td>
				---
			</td>
		</tr>
		<tr>
			<td>
				<code>char *grouping</code>
			</td>
			<td>
				يعرّف عدد الخانات في كل مجموعة في القيم المنسقة غير المالية، وتحدد القيمة <code>CHAR_MAX</code> أنه لا يوجد أي تجميع إضافي مطلوب، بينما تحدد القيمة <code>0</code> أنه يجب تكرار العنصر السابق للخانات الرقمية المتبقية، وإذا استُخدمت أي قيمة أخرى فهي تمثل قيمة العدد الصحيح المُمثل لعدد الخانات التي تتألف منها المجموعة الحالية (المحرف اللاحق في السلسلة النصية يُفسَّر قبل التجميع).
			</td>
			<td>
				""
			</td>
			<td>
				يحدد <code>"‎\3"</code> أن الخانات يجب أن تجمع كل ثلاثة في مجموعة ويشير محرف الإنهاء الفارغ terminating null في السلسلة النصية إلى تكرار <code>‎\3</code>.
			</td>
		</tr>
		<tr>
			<td>
				<code>char *int_curr_symbol</code>
			</td>
			<td>
				تُستخدم المحارف الأولى الثلاث لتخزين رمز العملة العالمي الأبجدي لإصدار سي المحلي، بينما يُستخدم المحرف الرابع للفصل بين رمز العملة العالمي والكمية النقدية.
			</td>
			<td>
				""
			</td>
			<td>
				---
			</td>
		</tr>
		<tr>
			<td>
				<code>char *currency_symbol</code>
			</td>
			<td>
				يمثل رمز العملة للإصدار المحلي الحالي.
			</td>
			<td>
				""
			</td>
			<td>
				---
			</td>
		</tr>
		<tr>
			<td>
				<code>char *mon_decimal_point</code>
			</td>
			<td>
				المحرف المُستخدم مثل فاصلة عشرية عند تنسيق القيم النقدية.
			</td>
			<td>
				""
			</td>
			<td>
				---
			</td>
		</tr>
		<tr>
			<td>
				<code>char *mon_thousands_sep</code>
			</td>
			<td>
				يمثل فاصل مجموعات خانات الأرقام ذات القيم المنسقة بتنسيق نقدي.
			</td>
			<td>
				""
			</td>
			<td>
				---
			</td>
		</tr>
		<tr>
			<td>
				<code>char *mon_grouping</code>
			</td>
			<td>
				يعرف عدد الخانات في كل مجموعة عند تنسيق قيم نقدية، وتُفسّر عناصره على أنها جزء من التجميع
			</td>
			<td>
				""
			</td>
			<td>
				---
			</td>
		</tr>
		<tr>
			<td>
				<code>char *positive_sign</code>
			</td>
			<td>
				السلسلة النصية المُستخدمة للدلالة على قيمة نقدية غير سالبة.
			</td>
			<td>
				""
			</td>
			<td>
				---
			</td>
		</tr>
		<tr>
			<td>
				<code>char *negative_sign</code>
			</td>
			<td>
				السلسلة النصية المُستخدمة للدلالة على قيمة نقدية سالبة.
			</td>
			<td>
				""
			</td>
			<td>
				---
			</td>
		</tr>
		<tr>
			<td>
				<code>char int_frac_digits</code>
			</td>
			<td>
				عدد الخانات التي تُعرض بعد الفاصلة العشرية في قيمة نقدية منسقة عالميًا.
			</td>
			<td>
				<code>CHAR_MAX</code>
			</td>
			<td>
				---
			</td>
		</tr>
		<tr>
			<td>
				<code>char frac_digits</code>
			</td>
			<td>
				عدد الخانات التي تُعرض بعد الفاصلة العشرية في قيمة نقدية غير منسقة عالميًا.
			</td>
			<td>
				<code>CHAR_MAX</code>
			</td>
			<td>
				---
			</td>
		</tr>
		<tr>
			<td>
				<code>char p_cs_precedes</code>
			</td>
			<td>
				قيمة <code>1</code> تدل على وجوب إتباع <code>currency_symbol</code> بالقيمة عند تنسيق قيمة غير سالبة نقدية، بينما تدل القيمة <code>0</code> على إسباق <code>currency_symbol</code> بالقيمة.
			</td>
			<td>
				<code>CHAR_MAX</code>
			</td>
			<td>
				---
			</td>
		</tr>
		<tr>
			<td>
				<code>char p_sep_by_space</code>
			</td>
			<td>
				قيمة <code>1</code> تدل على تفريق رمز العملة من القيمة بمسافة فارغة عند تنسيق قيمة غير سالبة نقدية، بينما تدل قيمة <code>0</code> على عدم وجود أي مسافة فارغة.
			</td>
			<td>
				<code>CHAR_MAX</code>
			</td>
			<td>
				---
			</td>
		</tr>
		<tr>
			<td>
				<code>char n_cs_precedes</code>
			</td>
			<td>
				تشابه <code>p_cs_precedes</code> ولكن للقيم النقدية السالبة.
			</td>
			<td>
				<code>CHAR_MAX</code>
			</td>
			<td>
				---
			</td>
		</tr>
		<tr>
			<td>
				<code>char n_sep_by_space</code>
			</td>
			<td>
				تشابه <code>p_sep_by_space</code> ولكن للقيم النقدية السالبة.
			</td>
			<td>
				<code>CHAR_MAX</code>
			</td>
			<td>
				---
			</td>
		</tr>
		<tr>
			<td>
				<code>char n_sign_posn</code>
			</td>
			<td>
				يشابه <code>p_sign_posn</code> ولكن للقيم النقدية السالبة.
			</td>
			<td>
				<code>CHAR_MAX</code>
			</td>
			<td>
				---
			</td>
		</tr>
		<tr>
			<td>
				<code>char p_sign_posn</code>
			</td>
			<td>
				يمثل موقع <code>positive_sign</code> للقيم النقدية المنسقة غير السالبة.
			</td>
			<td>
				<code>CHAR_MAX</code>
			</td>
			<td>
				يتبع الشروط التالية: تُحيط الأقواس القيمة النقدية و<code>currency_symbol</code>. تسبق السلسلة النصية كل من القيمة النقدية و <code>currency_symbol</code>. تتبع السلسلة النصية القيمة النقدية و <code>currency_symbol</code>. تسبق السلسلة النصية القيمة <code>currency_symbol</code>. تتبع السلسلة النصية القيمة <code>currency_symbol</code>
			</td>
		</tr>
	</tbody>
</table>

<h3>
	دالة setlocale لضبط الإعدادات المحلية
</h3>

<p>
	يكون تعريف دالة <code>setlocale</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5741_14" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;locale.h&gt;</span><span class="pln">

</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">setlocale</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> category</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">locale</span><span class="pun">);</span></pre>

<p>
	تسمح هذه الدالة بضبط إعدادات البرنامج المحلية، ويمكن ضبط جميع أجزاء الإصدار المحلي باختيار القيم المناسبة لوسيط التصنيف <code>category</code> كما يلي:
</p>

<ul>
	<li>
		القيمة <code>LC_ALL</code>: تضبط كامل الإصدار المحلي.
	</li>
	<li>
		القيمة <code>LC_COLLATE</code>: تعديل سلوك <code>strcoll</code> و <code>strxfrm</code>.
	</li>
	<li>
		القيمة <code>LC_CTYPE</code>: تعديل سلوك دوال التعامل مع المحارف character-handling.
	</li>
	<li>
		القيمة <code>LC_MONETARY</code>: تعديل تنسيق القيم النقدية المُعادة من دالة <code>localeconv</code>.
	</li>
	<li>
		القيمة <code>LC_NUMERIC</code>: تعديل محرف الفاصلة العشرية لتنسيق الدخل والخرج وبرامج تحويل <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">السلاسل النصية</a>.
	</li>
	<li>
		القيمة <code>LC_TIME</code>: تعديل سلوك <code>strftime</code>.
	</li>
</ul>

<p>
	يمكن ضبط قيم الإعدادات المحلية إلى:
</p>

<table>
	<tbody>
		<tr>
			<td>
				"C"
			</td>
			<td>
				تحديد البيئة ذات المتطلبات الدنيا لترجمة سي C
			</td>
		</tr>
		<tr>
			<td>
				""
			</td>
			<td>
				تحديد البيئة الأصيلة المعرفة حسب التطبيق
			</td>
		</tr>
		<tr>
			<td>
				<strong>قيمة معرفة بحسب التنفيذ</strong>
			</td>
			<td>
				تحديد البيئة الموافقة لهذه القيمة
			</td>
		</tr>
	</tbody>
</table>

<p>
	البيئة الافتراضية عند بداية البرنامج موافقة للبيئة التي نحصل عليها عند تنفيذ التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5741_19" style=""><span class="pln">setlocale</span><span class="pun">(</span><span class="pln">LC_ALL</span><span class="pun">,</span><span class="pln"> </span><span class="str">"C"</span><span class="pun">);</span></pre>

<p>
	يمكن فحص السلسلة النصية الحالية المترافقة مع تصنيف ما بتمرير مؤشر فارغ null قيمةً للوسيط <code>locale</code>؛ نحصل على السلسلة النصية المترافقة مع التصنيف <code>category</code> المحدد للتوطين الجديد إذا كان من الممكن حصول التصنيف المحدد، وتُستخدم هذه السلسلة النصية في استدعاء لاحق للدالة <code>setlocale</code> مع تصنيفها المترافق لاستعادة الجزء الموافق من إعدادات البرنامج المحلية، وإذا كان التحديد غير ممكن الحصول نحصل على مؤشر فراغ دون تغيير الإعدادات المحلية.
</p>

<h3>
	دالة localeconv
</h3>

<p>
	يكون تصريح الدالة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5741_21" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;locale.h&gt;</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> lconv </span><span class="pun">*</span><span class="pln">localeconv</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span></pre>

<p>
	تُعيد هذه الدالة مؤشرًا يشير إلى هيكل من النوع <code>struct lconv</code>، ويُضبط هذا <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-pointers-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1679/" rel="">المؤشر</a> طبقًا للإعدادات المحلية الحالية ويمكن تغييره باستدعاء لاحق للدالة <code>localconv</code> أو <code>setlocale</code>، ويجب ألّا يكون الهيكل قابلًا للتعديل بأي طريقة أخرى.
</p>

<p>
	على سبيل المثال، إذا كانت إعدادات القيم النقدية المحلية الحالية مُمثّلةً حسب الإعدادات التالية:
</p>

<table>
	<tbody>
		<tr>
			<td>
				<code>IR£1,234.56</code>
			</td>
			<td>
				تنسيق القيم الموجبة
			</td>
		</tr>
		<tr>
			<td>
				<code>(IR£1,234.56)</code>
			</td>
			<td>
				تنسيق القيم السالبة
			</td>
		</tr>
		<tr>
			<td>
				<code>IRP 1,234.56</code>
			</td>
			<td>
				التنسيق العالمي
			</td>
		</tr>
	</tbody>
</table>

<p>
	يجب أن تحمل الأعضاء التي تمثّل القيم النقدية في <code>lconv</code> القيم التالية:
</p>

<table>
	<tbody>
		<tr>
			<td>
				<code>int_curr_symbol</code>
			</td>
			<td>
				"IRP "
			</td>
		</tr>
		<tr>
			<td>
				<code>currency_symbol</code>
			</td>
			<td>
				"IR£"
			</td>
		</tr>
		<tr>
			<td>
				<code>mon_decimal_point</code>
			</td>
			<td>
				"."
			</td>
		</tr>
		<tr>
			<td>
				<code>mon_thousands_sep</code>
			</td>
			<td>
				","
			</td>
		</tr>
		<tr>
			<td>
				<code>mon_grouping</code>
			</td>
			<td>
				"\3"
			</td>
		</tr>
		<tr>
			<td>
				<code>postive_sign</code>
			</td>
			<td>
				""
			</td>
		</tr>
		<tr>
			<td>
				<code>negative_sign</code>
			</td>
			<td>
				""
			</td>
		</tr>
		<tr>
			<td>
				<code>int_frac_digits</code>
			</td>
			<td>
				2
			</td>
		</tr>
		<tr>
			<td>
				<code>frac_digits</code>
			</td>
			<td>
				2
			</td>
		</tr>
		<tr>
			<td>
				<code>p_cs_precedes</code>
			</td>
			<td>
				1
			</td>
		</tr>
		<tr>
			<td>
				<code>p_sep_by_space</code>
			</td>
			<td>
				0
			</td>
		</tr>
		<tr>
			<td>
				<code>n_cs_precedes</code>
			</td>
			<td>
				1`
			</td>
		</tr>
		<tr>
			<td>
				<code>n_sep_by_space</code>
			</td>
			<td>
				0
			</td>
		</tr>
		<tr>
			<td>
				<code>p_sign_posn</code>
			</td>
			<td>
				CHAR_MAX
			</td>
		</tr>
		<tr>
			<td>
				<code>n_sign_posn</code>
			</td>
			<td>
				0
			</td>
		</tr>
	</tbody>
</table>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter9/" rel="external nofollow">Libraries</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%82%D9%8A%D9%85-%D8%A7%D9%84%D8%AD%D8%AF%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%B1%D9%8A%D8%A7%D8%B6%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1793/" rel="">القيم الحدية والدوال الرياضية في لغة سي C</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/c/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%85%D9%83%D8%AA%D8%A8%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1777/" rel="">مقدمة إلى مكتبات لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">التعامل مع المحارف والسلاسل النصية في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1675/" rel="">مدخل إلى المصفوفات في لغة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1778</guid><pubDate>Sat, 26 Nov 2022 16:07:00 +0000</pubDate></item><item><title>&#x645;&#x642;&#x62F;&#x645;&#x629; &#x625;&#x644;&#x649; &#x645;&#x643;&#x62A;&#x628;&#x627;&#x62A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%85%D9%83%D8%AA%D8%A8%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1777/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_11/636792a0b6d03_-----C.png.91191bef2b22b5e5c0cd2d8faf509a9a.png" /></p>
<p>
	سيساهم قرار لجنة لغة سي المعيارية بتعريف إجراءات Routines عدد من المكتبات بما يعود بالنفع الكبير لجميع مستخدمي لغة سي دون أي شك، إذ لم يكن هناك أي معيار مُتّفق عليه يعرف إجراءات المكتبات ويقدم دعمًا للغة، وانعكس ذلك سلبًا على قابلية نقل البرامج Portability كثيرًا.
</p>

<p>
	ليس من المطلوب أن تتواجد إجراءات المكتبة داخل البرنامج، إذ تتواجد فقط في البيئات المُستضافة Hosted environment وتنطبق هذه الحالة غالبًا على مبرمجي التطبيقات، بينما لن تكون المكتبات موجودةً في حالة مبرمجي النظم المُدمجة ومبرمجي البيئات المُستضافة؛ إذ يستخدم هذا النوع من المبرمجين لغة سي لوحدها ضمن بيئة مستقلة Freestanding environment، وبالتالي لن يكون هذا المقال مهمًّا لهم.
</p>

<p>
	لن تكون المواضيع التي ستتبع هذه المقدمة مكتوبةً بهدف قراءتها بالتسلسل، ويمكنك قراءتها أجزاء منفصلة، إذ نهدف هنا إلى توفير محتوى يُستخدم كمرجع بسيط للمعلومات وليس درس تعليمي شامل، وإلا فسيتطلب الأمر كتابًا مخصصًا لنستطيع تغطية جميع المكتبات.
</p>

<h2>
	ملفات الترويسات والأنواع القياسية
</h2>

<p>
	تُستخدم عدّة أنواع types و<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-macro-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC-%D8%A7%D9%84%D9%85%D8%B3%D8%A8%D9%82-preprocessor-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1750/" rel="">ماكرو macro</a> على نحوٍ واسع في دوال المكتبات، وتُعرّف في ملف <code>‎#include</code> الموافق للدالة. كما سيصرِّح ملف الترويسة Header عن الأنواع والنماذج الأولية المناسبة لدوالّ المكتبة، وعلينا أن نذكر عدّة نقاط مهمة بهذا الخصوص:
</p>

<ul>
	<li>
		تُحجز جميع المعرّفات الخارجية External identifiers وأسماء الماكرو المُصرّح عنها في أي ملف ترويسة لمكتبة، بحيث لا يُمكن استخدامها أو إعادة تعريفها لأي استعمال آخر. قد تحمل الأسماء في بعض الأحيان أثرًا "سحريًّا" عندما تكون معروفةً للمصرف ويتسبب ذلك باستخدام بعض الأساليب الخاصة لتطبيقها.
	</li>
	<li>
		جميع المعرفات التي تبدأ <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">بمحرف</a> الشرطة السفلية underscore <code>_</code> محجوزة.
	</li>
	<li>
		يمكن تضمين ملفات الترويسة بأي ترتيب كان ولأكثر من مرة، إلا أن تضمينها يجب أن يحدث خارج أي <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B5%D8%A7%D8%B1%D9%8A%D8%AD-declarations-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%B1%D9%8A%D9%81-definitions-%D9%88%D8%A5%D9%85%D9%83%D8%A7%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-accessibility-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1762/" rel="">تصريح</a> داخلي أو تعريف وقبل أي استخدام <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1646/" rel="">للدوال</a> والماكرو المعرّفة بداخلها.
	</li>
	<li>
		نحصل على سلوك غير معرّف إن مرّرنا <strong>قيمة غير صالحة</strong> لدالة، مثل مؤشر فارغ، أو قيمة خارج نطاق القيم التي تقبلها الدالة.
	</li>
</ul>

<p>
	لا يُحدّد المعيار النوع ذاته من القيود الموضحة أعلاه بخصوص المعرّفات، وقد يتبادر إلى ذهنك المغامرة والاستفادة من هذه الثغرات، إلا أننا ننصحك بالالتزام بالطرق الآمنة.
</p>

<p>
	ملفات الترويسة المعيارية هي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6200_8" style=""><span class="str">&lt;assert.h&gt;</span><span class="pln">   </span><span class="str">&lt;locale.h&gt;</span><span class="pln">   </span><span class="str">&lt;stddef.h&gt;</span><span class="pln">
</span><span class="str">&lt;ctype.h&gt;</span><span class="pln">    </span><span class="str">&lt;math.h&gt;</span><span class="pln">     </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="str">&lt;errno.h&gt;</span><span class="pln">    </span><span class="str">&lt;setjmp.h&gt;</span><span class="pln">   </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">
</span><span class="str">&lt;float.h&gt;</span><span class="pln">    </span><span class="str">&lt;signal.h&gt;</span><span class="pln">   </span><span class="str">&lt;string.h&gt;</span><span class="pln">
</span><span class="str">&lt;limits.h&gt;</span><span class="pln">   </span><span class="str">&lt;stdarg.h&gt;</span><span class="pln">   </span><span class="str">&lt;time.h&gt;</span></pre>

<p>
	معلومة عامّة أخيرة: تُنفّذ العديد من إجراءات المكتبات على أنها ماكرو في عملها، شرط ألا يتسبب ذلك في أي مشاكل ناتجة عن الآثار الجانبية لهذا الاستخدام (كما وضّح <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-macro-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC-%D8%A7%D9%84%D9%85%D8%B3%D8%A8%D9%82-preprocessor-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1750/" rel="">الفصل السابع</a>). يضمن المعيار وجود دالة اعتيادية إذا كان هناك دالة تُستخدم عادةً مثل ماكرو موافقة لها، بحيث تُنجز الدالتان العمل ذاته، وحتى تستخدم الدالة الاعتيادية عليك أن تُلغي تعريف الماكرو باستخدام التوجيه <code>‎#undef</code>، أو أن تكتب اسم الماكرو داخل قوسين، مما يضمن أنه لن يُعامل معاملة الماكرو:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6200_10" style=""><span class="pln">some function</span><span class="pun">(</span><span class="str">"Might be a macro\n"</span><span class="pun">);</span><span class="pln"> </span><span class="com">//قد يمثل هذا ماكرو</span><span class="pln">
</span><span class="pun">(</span><span class="pln">some function</span><span class="pun">)(</span><span class="str">"Can't be a macro\n"</span><span class="pun">);</span><span class="pln"> </span><span class="com">//من غير الممكن أن يكون هذا ماكرو</span></pre>

<h2>
	مجموعات المحارف والاختلافات اللغوية
</h2>

<p>
	قدّمت لجنة المعيار بعض المزايا الموجهة لاستخدام سي في البيئات التي لا تستخدم مجموعة محارف معيار US ASCII، والاختلافات اللغوية الأخرى التي تستخدم الفاصلة أو النقطة للدلالة على الفاصلة العشرية. قُدّمت التسهيلات (ألقِ نظرةً على القسم) بفكرة برنامج يتحكم بسلوك دوال المكتبات ليوافق الاختلافات اللغوية.
</p>

<p>
	تُعد مهمة تقديم دعم متكامل لمختلف اللغات والتقاليد مهمّة صعبة، وغالبًا ما يُساء فهمها، والتسهيلات المُزوّدة بمكتبات لغة سي هي الخطوة الأولى في هذا المشوار الطويل للوصول إلى الحل الكامل.
</p>

<p>
	الحل الوحيد المُعرّف من المعيار هو ما يدعى بلغة C المحلية locale، ويقدّم هذا دعمًا فعالًا على نحوٍ مشابه لعمل لغة سي القديمة، بينما تقدم الإعدادات المحلية الأخرى سلوكًا مختلفًا بحسب تعريف التطبيق.
</p>

<h2>
	ملف ترويسة <code>&lt;stddef.h&gt;</code>
</h2>

<p>
	هناك عددٌ صغير من الأنواع والماكرو الموجودة في <code>&lt;stddef.h&gt;</code> والمُستخدمة كثيرًا في ملفات الترويسة الأخرى، الذين سنتكلم عنها لاحقًا.
</p>

<p>
	تعطينا عملية طرح <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-pointers-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1682/" rel="">مؤشر</a> من آخر نتيجةً من نوع مختلف بحسب التطبيق، وللسماح بالاستخدام الآمن في حال الاختلاف، يعرّف ملف الترويسة <code>&lt;stddef.h&gt;</code> النوع <code>ptrdiff_t</code>، كما يمكنك استخدام النوع <code>size_t</code> لتخزين نتيجة <a href="https://academy.hsoub.com/programming/c/%D8%B9%D8%A7%D9%85%D9%84-sizeof-%D9%88%D8%AD%D8%AC%D8%B2-%D9%85%D8%B3%D8%A7%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1681/" rel="">العامل sizeof</a> بصورةٍ مشابهة.
</p>

<p>
	لأسباب لا تزال مخفية عنّا للوقت الحالي، هناك "مؤشر ثابت فارغ معرّف بحسب التنفيذ" معرّف في <code>&lt;stddef.h&gt;</code> باسم <code>NULL</code>، قد يبدو ذلك غير ضروريًا بالنظر إلى أن لغة سي تعرّف <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AB%D9%88%D8%A7%D8%A8%D8%AA-%D9%88%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%87%D8%B1%D9%88%D8%A8-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1614/" rel="">ثابت</a> الرقم الصحيح 0 إلى القيمة التي يمكن إسنادها إلى مؤشر فارغ ومقارنتها معه، إلا أن الممارسة التالية <strong>شائعة جدًا</strong> وسط مبرمجي لغة سي المتمرسين:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6200_12" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stddef.h&gt;</span><span class="pln">
</span><span class="typ">FILE</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fp</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">if</span><span class="pun">((</span><span class="pln">fp </span><span class="pun">=</span><span class="pln"> fopen</span><span class="pun">(</span><span class="str">"somefile"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"r"</span><span class="pun">))</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> NULL</span><span class="pun">){</span><span class="pln">
        </span><span class="com">/* وهلمّ جرًّا  */</span></pre>

<p>
	هناك ماكرو باسم <code>offsetof</code> مهمته إيجاد مقدار الإزاحة offset بالبايت لعضو هيكل ما؛ إذ أن مقدار الإزاحة هو المسافة بين العضو وبداية الهيكل، إليك مثالًا عن ذلك:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6200_14" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stddef.h&gt;</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
        </span><span class="typ">size_t</span><span class="pln"> distance</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">struct</span><span class="pln"> x</span><span class="pun">{</span><span class="pln">
                </span><span class="typ">int</span><span class="pln"> a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">,</span><span class="pln"> c</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">s_tr</span><span class="pun">;</span><span class="pln">

        distance </span><span class="pun">=</span><span class="pln"> offsetof</span><span class="pun">(</span><span class="pln">s_tr</span><span class="pun">,</span><span class="pln"> c</span><span class="pun">);</span><span class="pln">
        printf</span><span class="pun">(</span><span class="str">"Offset of x.c is %lu bytes\n"</span><span class="pun">,</span><span class="pln">
                </span><span class="pun">(</span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">long</span><span class="pun">)</span><span class="pln">distance</span><span class="pun">);</span><span class="pln">

        exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 1]
</p>

<p>
	يجب أن يكون التعبير <code>s_tr.c</code> قادرًا على التقييم مثل عنوانٍ لثابت (انظر <a href="https://academy.hsoub.com/programming/c/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%82%D9%88%D8%A7%D8%A6%D9%85-%D8%A7%D9%84%D9%85%D8%AA%D8%B1%D8%A7%D8%A8%D8%B7%D8%A9-linked-lists-%D9%88%D8%A7%D9%84%D8%A3%D8%B4%D8%AC%D8%A7%D8%B1-trees-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1747/" rel="">مقال هياكل البيانات: القوائم المترابطة Linked lists والأشجار Trees في لغة سي C</a>)، فإذا كان العضو الذي تبحث عن مقدار إزاحته هو حقل بتات bitfield فستحصل على سلوك غير معرّف في هذه الحالة.
</p>

<p>
	لاحظ طريقة <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%AD%D9%88%D9%8A%D9%84%D8%A7%D8%AA-%D9%85%D8%A7-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D9%81%D9%8A-%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1612/" rel="">تحويل الأنواع</a> في <code>size_t</code> التي نحوّل فيها لأطول نوع ممكن عديم الإشارة للتأكد من أن وسيط <code>printf</code> هو من النوع المناسب (<code>‎%ul</code> هو رمز التنسيق الخاص بطباعة النوع <code>unsigned long</code>) مع المحافظة على دقة القيمة، وهذا بسبب أن نوع <code>size_t</code> مجهول للمبرمج.
</p>

<p>
	العنصر الأخير المُصرّح عنه في <code>&lt;stddef.h&gt;</code> هو <code>wchar_t</code> وهو قيمة عدد صحيح كبيرة يُمكن تخزين محرف عريض wide character فيها ينتمي إلى أي مجموعة محارف موسّعة extended character set.
</p>

<h2>
	ملف الترويسة <error.h></error.h>
</h2>

<p>
	يعرّف ملف الترويسة ما يُدعى <code>errno</code> الذي يُستبدل بتعبير ثابت ذو قيمة صحيحة لا تساوي الصفر، ويُضمن أن يكون هذا التعبير مقبولًا في موجّهات <code>‎#if</code>، ويعرّف أيضًا الماكرو <code>EDOM</code> والماكرو <code>ERANGE</code> اللذان يُستخدمان في الدوال الرياضية للدلالة على نوع الخطأ الحاصل وسنشرحهما بتوسعٍ أكبر لاحقًا.
</p>

<p>
	يُستخدم <code>errno</code> للدلالة على خطأ مُكتشف من دوال المكتبات، وهو ليس متغير خارجي بالضرورة -كما كان سابقًا- بل هو قيمةٌ متغيرةٌ من نوع <code>int</code>، إذ تُسند القيمة صفر إليه عند بداية تشغيل البرنامج، ولا يُعاد ضبط قيمته من تلك النقطة فصاعدًا إلا إذا جرى ذلك مباشرةً؛ أي بكلمات أخرى، لا تحاول إجراءات المكتبات إعادة ضبطه إطلاقًا، وإذا حدث أي خطأ في إجراء المكتبة فإن قيمة <code>errno</code> تتغير إلى قيمة معينة تشير إلى نوع الخطأ الحاصل ويُعيد الإجراء هذه القيمة (غالبًا ‎-1) للدلالة على الخطأ، إليك تطبيقًا عمليًا عن ذلك:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6200_17" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stddef.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;errno.h&gt;</span><span class="pln">

errno </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">if</span><span class="pun">(</span><span class="pln">some_library_function</span><span class="pun">(</span><span class="pln">arguments</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">){</span><span class="pln">
        </span><span class="com">// خطأ في معالجة الشيفرة المصدرية </span><span class="pln">
        </span><span class="com">// ‫قد يستخدم قيمة errno مباشرةً </span></pre>

<p>
	تطبيق <code>errno</code> غير معروف بالنسبة للمبرمج، فلا تحاول فعل أي شيء على هذه القيمة عدا إعادة ضبطها أو فحصها، فعلى سبيل المثال، من غير المضمون أن يكون لهذه القيمة عنوانًا على الذاكرة. يجب أن تتفقّد قيمة <code>errno</code> فقط في حال كانت دالة المكتبة المُستخدمة توثّق تأثيرها على <code>errno</code>، إذ يمكن لدوال المكتبات الأخرى أن تضبطها إلى قيمة عشوائية بعد استدعائها إلا إذا كان وصف الدالة يحدد ما الذي تفعله الدالة بالقيمة بصورةٍ صريحة.
</p>

<h2>
	تشخيص الأخطاء
</h2>

<p>
	من المفيد عندما تبحث عن الأخطاء في برنامجك أن تكون قادرًا على فحص قيمة تعبير ما والتأكد من أن قيمته هي ما تتوقّعها فعلًا، وهذا ما تقدمه لك دالة <code>assert</code>.
</p>

<p>
	يجب عليك أن تُضمّن ملف الترويسة <code>&lt;assert.h&gt;</code> أولًا حتى تتمكن من استخدام الدالة <code>assert</code>، وهذه الدالة معرفةٌ على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6200_19" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;assert.h&gt;</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> assert</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> expression</span><span class="pun">)</span></pre>

<p>
	إذا كانت قيمة التعبير صفر (أي "خطأ false")، فستطبع الدالة <code>assert</code> رسالةً تدل على التعبير الفاشل، وتتضمن الرسالة اسم ملف الشيفرة المصدرية والسطر الذي يحتوي على التوكيد assertion والتعبير، ومن ثم تُستدعى دالة <code>abort</code> التي تقطع عمل البرنامج.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6200_21" style=""><span class="pln">assert</span><span class="pun">(</span><span class="lit">1</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span><span class="pln">

</span><span class="com">/* قد يتسبب ما سبق بالتالي */</span><span class="pln">

</span><span class="typ">Assertion</span><span class="pln"> failed</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> file silly</span><span class="pun">.</span><span class="pln">c</span><span class="pun">,</span><span class="pln"> line </span><span class="lit">15</span></pre>

<p>
	في حقيقة الأمر الكلمة <code>Assert</code> معرّفة مثل ماكرو، وليس مثل دالة حقيقية. لتعطيل التوكيدات في برنامج يستوفي شروط عمله دون مشاكل، نعرّف الاسم <code>NDEBUG</code> <strong>قبل</strong> تضمين <code>&lt;assert.h&gt;</code>، وسيعطّل هذا جميع التوكيدات الموجودة في كامل البرنامج. عليك أن تعرف الآثار الجانبية التي يتسبب بها هذا للتعبير، فلن يُقيّم التعبير تعطيل التوكيدات باستخدام <code>NDEBUG</code>، وبذلك سيسلك المثال التالي سلوكًا غير مُتوقع عند إلغاء التوكيدات باستخدام <code>‎#define NDEBUG</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6200_23" style=""><span class="com">#define</span><span class="pln"> NDEBUG
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;assert.h&gt;</span><span class="pln">

</span><span class="kwd">void</span><span class="pln">
func</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> c</span><span class="pun">;</span><span class="pln">
        assert</span><span class="pun">((</span><span class="pln">c </span><span class="pun">=</span><span class="pln"> getchar</span><span class="pun">())</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> EOF</span><span class="pun">);</span><span class="pln">
        putchar</span><span class="pun">(</span><span class="pln">c</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 2]
</p>

<p>
	لاحظ أن الدالة <code>assert</code> لا تُعيد أي قيمة.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter9/" rel="external nofollow">Libraries</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%B6%D8%A8%D8%B7-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D9%88%D8%B7%D9%8A%D9%86-localization-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1778/" rel="">التعامل مع المحارف وضبط إعدادات التوطين localization في لغة سي C</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/c/%D9%85%D8%B9%D8%B1%D9%81%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D9%88%D8%B9-typedef-%D9%88%D8%A7%D9%84%D9%85%D8%A4%D9%87%D9%84%D8%A7%D8%AA-qualifiers-%D9%88%D9%86%D9%82%D8%A7%D8%B7-%D8%A7%D9%84%D8%AA%D8%B3%D9%84%D8%B3%D9%84-sequence-points-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1763/" rel="">معرفات النوع typedef والمؤهلات qualifiers ونقاط التسلسل sequence points في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A8%D9%86%D9%8A%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1607/" rel="">بنية برنامج لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1609/" rel="">المحارف المستخدمة في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1675/" rel="">مدخل إلى المصفوفات في لغة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1777</guid><pubDate>Sat, 19 Nov 2022 16:02:00 +0000</pubDate></item><item><title>&#x645;&#x639;&#x631;&#x641;&#x627;&#x62A; &#x627;&#x644;&#x646;&#x648;&#x639; typedef &#x648;&#x627;&#x644;&#x645;&#x624;&#x647;&#x644;&#x627;&#x62A; qualifiers &#x648;&#x646;&#x642;&#x627;&#x637; &#x627;&#x644;&#x62A;&#x633;&#x644;&#x633;&#x644; sequence points &#x641;&#x64A; &#x644;&#x63A;&#x629; C</title><link>https://academy.hsoub.com/programming/c/%D9%85%D8%B9%D8%B1%D9%81%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D9%88%D8%B9-typedef-%D9%88%D8%A7%D9%84%D9%85%D8%A4%D9%87%D9%84%D8%A7%D8%AA-qualifiers-%D9%88%D9%86%D9%82%D8%A7%D8%B7-%D8%A7%D9%84%D8%AA%D8%B3%D9%84%D8%B3%D9%84-sequence-points-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1763/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_11/636351a0c5eea_---typedef--qualifiers---sequenc-points---C-.png.b93d9e7a4da8eae6063a0c656f3f5c1c.png" /></p>
<p>
	 استعرضنا في المقالات السابقة مبادئ اللغة وغطّينا معظم الجوانب المعرّفة من المعيار تقريبًا، إلا أن هناك بعض التفاصيل المتفرقة التي لا تنطوي تحت أي عنوان محدد، وتأتي هذه المقالة لجمع هذه التفاصيل المتفرقة العويصة من لغة C. استعدّ لما هو قادم ولا تتردد بتدوين الملاحظات التي تعتقد أنها ستهمّك، واقرأها من وقت إلى وقت آخر، فالأمر الذي تجده للمرة الأولى غير مثير للاهتمام وصعب الفهم سيصبح ضروريًّا ومفيدًا لك بعد أن تكتسب الخبرة الكافية لتوظيفه.
</p>

<h2>
	معرف النوع typedef
</h2>

<p>
	يسود الاعتقاد بأن معرّف النوع هو صنف تخزين، إلا أن هذا الاعتقاد خاطئ، إذ يسمح لك هذا المعرّف باختيار مرادفات لأنواع أخرى، والتي من الممكن أن يُصرّح عنها بطريقة مختلفة، ويصبح الاسم الجديد المرادف مكافئًا للنوع الذي تريده، كما سيوضح المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_7" style=""><span class="kwd">typedef</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> aaa</span><span class="pun">,</span><span class="pln"> bbb</span><span class="pun">,</span><span class="pln"> ccc</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">typedef</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> ar</span><span class="pun">[</span><span class="lit">15</span><span class="pun">],</span><span class="pln"> arr</span><span class="pun">[</span><span class="lit">9</span><span class="pun">][</span><span class="lit">6</span><span class="pun">];</span><span class="pln">
</span><span class="kwd">typedef</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> c</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">cp</span><span class="pun">,</span><span class="pln"> carr</span><span class="pun">[</span><span class="lit">100</span><span class="pun">];</span><span class="pln">

</span><span class="com">/* صرّح عن بعض الكائنات */</span><span class="pln">

</span><span class="com">/* جميع الأعداد الصحيحة */</span><span class="pln">
aaa     </span><span class="typ">int1</span><span class="pun">;</span><span class="pln">
bbb     </span><span class="typ">int2</span><span class="pun">;</span><span class="pln">
ccc     </span><span class="typ">int3</span><span class="pun">;</span><span class="pln">

ar      yyy</span><span class="pun">;</span><span class="pln">    </span><span class="com">/* مصفوفة من 15 عدد صحيح */</span><span class="pln">
arr     xxx</span><span class="pun">;</span><span class="pln">    </span><span class="com">/* مصفوفة من 9×6 عدد صحيح */</span><span class="pln">

c       ch</span><span class="pun">;</span><span class="pln">     </span><span class="com">/* محرف */</span><span class="pln">
cp      pnt</span><span class="pun">;</span><span class="pln">    </span><span class="com">/* مؤشر يشير لمحرف */</span><span class="pln">
carr    chry</span><span class="pun">;</span><span class="pln">   </span><span class="com">/* مصفوفة من 100 محرف */</span></pre>

<p>
	تنص القاعدة العامة في استخدام معرف النوع على كتابة التصريح وكأنك تصرح عن <a href="https://academy.hsoub.com/programming/c/%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1749/" rel="">متغيرات</a> من الأنواع التي تريدها، فعندما يتضمن التصريح الاسم مع نوعه المحدد، فإن إسباق ذلك بمعرّف النوع يعني أنك تصرح عن اسم جديد لنوع ما بدلًا من التصريح عن متغيرات، ويمكن بعدها استخدام أسماء النوع الجديد مثل سابقة prefix لتصريح متغير من النوع الجديد.
</p>

<p>
	لا يُعد استخدام الكلمة المفتاحية <code>typedef</code> شائعًا في معظم البرامج، ويُستخدم غالبًا في ملفات الترويسة ومن النادر أن تجده في الممارسة اليومية الاعتيادية.
</p>

<p>
	تُعرَّف الأنواع الجديدة للمتغيرات الأساسية في البرامج التي تتطلب قابلية نقل كبيرة غالبًا، وتُستخدم تعليمات <code>typedef</code> المناسبة لكتابة البرنامج بصورة مُخصصة للآلة الهدف، إلا أن استخدامها الزائد قد يتسبب ببعض اللبس لمبرمجي اللغة إذا كانوا يستخدمون بيئة مغايرة، يوضح المثال التالي ما نقصده:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_9" style=""><span class="pln"> </span><span class="com">// ‫ملف 'mytype.h' </span><span class="pln">
</span><span class="kwd">typedef</span><span class="pln"> </span><span class="kwd">short</span><span class="pln">   SMALLINT        </span><span class="com">/* النطاق *******30000 */</span><span class="pln">
</span><span class="kwd">typedef</span><span class="pln"> </span><span class="typ">int</span><span class="pln">     BIGINT          </span><span class="com">// النطاق ******* 2‫E9 </span><span class="pln">

</span><span class="com">/* البرنامج*/</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">"mytype.h"</span><span class="pln">

SMALLINT        i</span><span class="pun">;</span><span class="pln">
BIGINT          loop_count</span><span class="pun">;</span></pre>

<p>
	لا يتسع نطاق العدد <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%AD%D9%82%D9%8A%D9%82%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%B5%D8%AD%D9%8A%D8%AD%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1611/" rel="">الصحيح</a> في <code>BIGINT</code> في بعض الآلات، ونتيجةً لذلك يجب إعادة تعريف النوع ليصبح <code>long</code>.
</p>

<p>
	لإعادة استخدام الاسم المُصرّح مثل تعريف نوع <code>typedef</code>، يجب أن يحتوي تصريحه محدد نوع واحد على الأقل، وهذا من شأنه أن يبعد أي لبس:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_11" style=""><span class="kwd">typedef</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> new_thing</span><span class="pun">;</span><span class="pln">
func</span><span class="pun">(</span><span class="pln">new_thing x</span><span class="pun">){</span><span class="pln">
        </span><span class="typ">float</span><span class="pln"> new_thing</span><span class="pun">;</span><span class="pln">
        new_thing </span><span class="pun">=</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نحذّر هنا أن الكلمة المفتاحية <code>typedef</code> يمكن استخدامها فقط للتصريح عن نوع القيمة المُعادة من <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1646/" rel="">دالة</a> وليس نوع الدالة الكامل، ونقصد بنوع الدالة الكامل معلومات حول معاملات الدالة ونوع القيمة المُعادة أيضًا.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_14" style=""><span class="com">/*
‫صرّح عن func باستخدام typedef لتكون من النوع الذي يأخذ وسيطين من نوع عدد صحيح ويعيد قيمة عدد صحيح
*/</span><span class="pln">
</span><span class="kwd">typedef</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> func</span><span class="pun">(</span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">);</span><span class="pln">

</span><span class="com">/* خطأ‏ */</span><span class="pln">
func func_name</span><span class="pun">{</span><span class="pln"> </span><span class="com">/*....*/</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

</span><span class="com">// ‫مثال صالح، يعيد مؤشر إلى النوع func </span><span class="pln">
func </span><span class="pun">*</span><span class="pln">func_name</span><span class="pun">(){</span><span class="pln"> </span><span class="com">/*....*/</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

</span><span class="com">/*
مثال صالح إذا كانت الدوال تعيد دوالًا، لكن هذا غير ممكن في لغة سي‫ 
*/</span><span class="pln">
func func_name</span><span class="pun">(){</span><span class="pln"> </span><span class="com">/*....*/</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	لا يمكن استخدام معرّف مثل معامل صوري في دالة إذا كان ذلك المعرّف مرتبطًا بمعرف نوع <code>typedef</code> معين ضمن النطاق، إذ سيسبب التصريح من الشكل التالي بمشكلة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_16" style=""><span class="kwd">typedef</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="typ">i1_t</span><span class="pun">,</span><span class="pln"> </span><span class="typ">i2_t</span><span class="pun">,</span><span class="pln"> </span><span class="typ">i3_t</span><span class="pun">,</span><span class="pln"> </span><span class="typ">i4_t</span><span class="pun">;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> f</span><span class="pun">(</span><span class="typ">i1_t</span><span class="pun">,</span><span class="pln"> </span><span class="typ">i2_t</span><span class="pun">,</span><span class="pln"> </span><span class="typ">i3_t</span><span class="pun">,</span><span class="pln"> </span><span class="typ">i4_t</span><span class="pun">)</span><span class="pln"> </span><span class="com">//هنا النقطة‫ X</span></pre>

<p>
	يصل المصرف إلى النقطة "X" عند قراءة تصريح الدالة، ولا يعرف فيما إذا كان يقرأ تصريحًا عن دالة، وهذه الحالة مشابهة إلى:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_18" style=""><span class="typ">int</span><span class="pln"> f</span><span class="pun">(</span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">)</span><span class="pln"> </span><span class="com">/* نموذج أولي */</span></pre>

<p>
	أو
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_20" style=""><span class="typ">int</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">,</span><span class="pln"> c</span><span class="pun">,</span><span class="pln"> d</span><span class="pun">)</span><span class="pln"> </span><span class="com">/* ليس نموذجًا أوليًا */</span></pre>

<p>
	يمكن حل المشكلة السابقة (في أسوأ الحالات) بالنظر إلى ما يتبع النقطة "X"؛ فإذا كانت فاصلة منقوطة فهذا تصريح؛ أما إذا كان <code>}</code> فهذا تعريف. تعني القاعدة التي تمنع أسماء تعريف النوع من أن تكون لمعامل صوري أن <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%A3%D9%88%D9%84-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%AA%D8%B5%D8%B1%D9%8A%D9%81-compilation-%D9%81%D9%8A-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r976/" rel="">المصرف</a> يمكنه دائمًا أن يخبر فيما إذا كان يعالج تصريحًا أو تعريفًا بالنظر إلى المعرف الأول الذي يتبع اسم الدالة.
</p>

<p>
	استخدام معرف النوع مفيد عندما تريد التصريح عن أشياء ذات صياغة معقدة، مثل "مصفوفة مؤلفة من عشرة <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-pointers-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1682/" rel="">مؤشرات</a> تشير إلى مصفوفة تتألف من خمسة أعداد صحيحة"، وهي صيغة معقدة للكتابة حتى لأمهر المبرمجين. يمكنك كتابتها لمرة واحدة فقط باستخدام معرف النوع أو تجزئتها إلى قطع مقبولة التعقيد:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_22" style=""><span class="kwd">typedef</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="pun">(*</span><span class="pln">a10ptoa5i</span><span class="pun">[</span><span class="lit">10</span><span class="pun">])[</span><span class="lit">5</span><span class="pun">];</span><span class="pln">
</span><span class="com">/* أو */</span><span class="pln">
</span><span class="kwd">typedef</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> a5i</span><span class="pun">[</span><span class="lit">5</span><span class="pun">];</span><span class="pln">
</span><span class="kwd">typedef</span><span class="pln"> a5i </span><span class="pun">*</span><span class="pln">atenptoa5i</span><span class="pun">[</span><span class="lit">10</span><span class="pun">];</span></pre>

<p>
	جرّبها بنفسك.
</p>

<h2>
	المؤهلان const و volatile
</h2>

<p>
	تُعد المؤهلات qualifiers من الأشياء الجديدة التي أتت مع لغة سي C المعيارية، على الرغم من أن فكرة <code>const</code> كانت من <a href="https://academy.hsoub.com/programming/cpp/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-c-r802/" rel="">لغة ++C</a> أصلًا. دعنا أولًا نوضح لك شيئًا، وهو أن مفهومي const و volatile مستقلان كليًا عن بعضهما بعضًا، إذ يسود الاعتقاد الخاطئ أن const تؤدي عكس غرض volatile، إلا أن المفهومين غير مرتبطين ويجب أن تبقي ذلك ببالك.
</p>

<p>
	بما أن تصاريح const هي الأبسط فسنبدأ بها، إلا أننا سنستعرض الحالات التي تُستخدم فيها كلا النوعين من المؤهلات. إليك لائحةً بالكلمات المفتاحية المرتبطة بهذا الشأن:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_24" style=""><span class="kwd">char</span><span class="pln">      </span><span class="kwd">long</span><span class="pln">      </span><span class="typ">float</span><span class="pln">     </span><span class="kwd">volatile</span><span class="pln">
</span><span class="kwd">short</span><span class="pln">     </span><span class="kwd">signed</span><span class="pln">    </span><span class="kwd">double</span><span class="pln">    </span><span class="kwd">void</span><span class="pln">
</span><span class="typ">int</span><span class="pln">       </span><span class="kwd">unsigned</span><span class="pln">  </span><span class="kwd">const</span></pre>

<p>
	يمثل كل من <code>const</code> و <code>volatile</code> في اللائحة السابقة أنواع مؤهلات، والكلمات المفتاحية المتبقية هي محددات نوع type specifiers، ويُسمح باستخدام عدة محددات أنواع بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_27" style=""><span class="kwd">char</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">signed</span><span class="pln"> </span><span class="kwd">char</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">char</span><span class="pln">
</span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">signed</span><span class="pln"> </span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="typ">int</span><span class="pln">
</span><span class="kwd">short</span><span class="pln"> </span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">signed</span><span class="pln"> </span><span class="kwd">short</span><span class="pln"> </span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">short</span><span class="pln"> </span><span class="typ">int</span><span class="pln">
</span><span class="kwd">long</span><span class="pln"> </span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">signed</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> </span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> </span><span class="typ">int</span><span class="pln">
</span><span class="typ">float</span><span class="pln">
</span><span class="kwd">double</span><span class="pln">
</span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">double</span></pre>

<p>
	هناك بعض النقاط التي يجب أن ننوه إليها، وهي أن جميع التصاريح التي تحتوي على <code>int</code> ستكون ذات إشارة <code>signed</code> افتراضيًا، وبذلك فالكلمة المفتاحية <code>signed</code> هي تكرار لا لزوم له في هذا السياق، ويمكن التخلص من <code>int</code> إذا وُجد أي محدد نوع أو مؤهل لأن <code>int</code> هو النوع الافتراضي.
</p>

<p>
	يمكن تطبيق الكلمتين المفتاحيتين <code>const</code> و <code>volatile</code> لأي تصريح، متضمّنًا تصريح الهياكل والاتحادات وأنواع المعدّدات أو أسماء <code>typedef</code>، ونقول عن تصريح يحتوي هذه الكلمتين المفتاحيتين بأنه مؤهّل، وهذا السبب في تسمية <code>const</code> و <code>volatile</code> بالمؤهّلات عوضًا عن تسميتهما بمحددات النوع، إليك بعض الأمثلة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_29" style=""><span class="kwd">volatile</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">volatile</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> j</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> q</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">volatile</span><span class="pln"> </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> rt_clk</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">struct</span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> li</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">signed</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> sc</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="kwd">volatile</span><span class="pln"> vs</span><span class="pun">;</span></pre>

<p>
	لا تشعر بالضيق من الأمثلة السابقة، فبعضها معقّد وسنشرح معناها لاحقًا، لكن تذكر أنه من الممكن زيادة التعقيد باستخدام محددات صنف التخزين أيضًا، ويوضح المثال التالي استخدامًا واقعيًا ضمن بعض أنوية نظام تشغيل في الوقت الحقيقي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_31" style=""><span class="kwd">extern</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">volatile</span><span class="pln"> </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> rt_clk</span><span class="pun">;</span></pre>

<h3>
	المؤهل const
</h3>

<p>
	لنلقي نظرةً على كيفية استخدام المؤهل const، والأمر بسيطٌ جدًا، إذ تعني const أن الشيء ليس قابلًا للتعديل، فإذا صُرّح عن كائن بيانات باستخدام الكلمة المفتاحية <code>const</code> مثل جزءٍ من توصيف نوعه فهذا يعني أنه من غير الممكن إسناد أي قيمة إليه خلال تشغيل البرنامج، ويحتوي التصريح عن الكائن غالبًا على قيمة أولية (وإلا فمتى سيحصل على قيمة إن لم يكن الإسناد إليه مسموحًا؟) إلا أنها ليست الحالة دائمًا؛ فعلى سبيل المثال، إذا أردت الوصول إلى منفذ port للعتاد الصلب ضمن عنوان ذاكرة محدد واحتجت للقراءة منه فقط، فسيُصرّح عنه أنه مؤهل <code>const</code> لكنه لن يُهيأ.
</p>

<p>
	يُعدّ أخذُ عنوان كائن بيانات ذي نوع ليس <code>const</code> ووضعه في مؤشر يشير إلى إصدار مؤهل باستخدام <code>const</code> للنوع نفسه طريقةً آمنةً ومسموحة، إذ سيمكنك ذلك من استخدام <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-pointers-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1679/" rel="">المؤشر</a> للنظر إلى الكائن دون إمكانية التعديل عليه، بينما يُعدّ وضع عنوان نوع ثابت إلى مؤشر يشير إلى النوع غير المؤهل طريقةً خطرةً وبالتالي فهي محظورة، إلا أنه يمكنك تجاوز ذلك باستخدام تحويل الأنواع cast. إليك مثالًا عن ذلك:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_33" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">const</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> ci </span><span class="pun">=</span><span class="pln"> </span><span class="lit">123</span><span class="pun">;</span><span class="pln">

        </span><span class="com">/* تصريح عن مؤشر يشير إلى ثابت */</span><span class="pln">
        </span><span class="kwd">const</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">cpi</span><span class="pun">;</span><span class="pln">

        </span><span class="com">/* مؤشر اعتيادي يشير إلى كائن غير ثابت */</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ncpi</span><span class="pun">;</span><span class="pln">

        cpi </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ci</span><span class="pun">;</span><span class="pln">
        ncpi </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">i</span><span class="pun">;</span><span class="pln">

        </span><span class="com">/*
         التعليمة التالية صالحة
         */</span><span class="pln">
        cpi </span><span class="pun">=</span><span class="pln"> ncpi</span><span class="pun">;</span><span class="pln">

        </span><span class="com">/*
    يتطلب الأمر في هذه الحالة تحويل للأنواع لأنه خطأ كبير، انظر لما يلي لمعرفة السماحيات
         */</span><span class="pln">
        ncpi </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">cpi</span><span class="pun">;</span><span class="pln">

        </span><span class="com">/*
    عدّل على ثابت من خلال المؤشر للحصول على سلوك غير محدد
         */</span><span class="pln">
        </span><span class="pun">*</span><span class="pln">ncpi </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

        exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 1]
</p>

<p>
	كما يوضح المثال السابق، فمن الممكن أخذ عنوان كائن ثابت وإنشاء مؤشر يشير إلى كائن غير ثابت ومن ثم استخدام المؤشر، وسيولد ذلك خطأً في برنامجك ويؤدي إلى سلوك غير محدد.
</p>

<p>
	الهدف الرئيسي من استخدام الكائنات الثابتة هو وضعها في حالة قراءة فقط، والسماح للمصرف بإجراء بعض التفقد الإضافي لها ضمن البرنامج، وسيكون المصرف قادرًا على التحقق من أن كائنات <code>const</code> لم تُعدّل قسريًا من قبل المستخدم إلا إذا كنت قادرًا على تجاوز ذلك باستخدام المؤشرات.
</p>

<p>
	إليك ميزةً إضافية. ما الذي يعنيه التالي؟
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_35" style=""><span class="kwd">char</span><span class="pln"> c</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="kwd">const</span><span class="pln"> cp </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">c</span><span class="pun">;</span></pre>

<p>
	الأمر بسيط جدًا، إذ أن <code>cp</code> مؤشر يشير إلى <code>char</code> وهي الحالة الاعتيادية إن لم تتواجد الكلمة المفتاحية <code>const</code>، وتعني الكلمة المفتاحية <code>const</code> أن <code>cp</code> لا يمكن التعديل عليه، إلا أنه من الممكن تعديل الشيء المُشار إليه بواسطة المؤشر، فالمؤشر هو الثابت وليس الشيء الذي يشير إليه. المثال المُعاكس لما سبق هو:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_37" style=""><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">cp</span><span class="pun">;</span></pre>

<p>
	الذي يعني أن <code>cp</code> هو مؤشر اعتيادي يمكن التعديل عليه، إلا أن الشيء الذي يشير إليه يجب عدم تعديله، إذًا من الممكن اختيار كون المؤشر أو الشيء الذي يشير إليه قابلًا للتعديل أو لا باستخدام التصريح المناسب بحسب التطبيق الذي تحتاجه.
</p>

<h3>
	المؤهل volatile
</h3>

<p>
	ننتقل إلى volatile بعد تحدثنا عن const. يعود السبب في استخدامنا لهذا النوع من المؤهلات إلى معالجة المشاكل الناتجة عند وقت التشغيل أو الأنظمة المُدمجة المُبرمجة باستخدام لغة سي.
</p>

<p>
	تخيّل كتابة شيفرة برمجية تتحكم بجهاز عتاد صلب بوضع قيم مناسبة في مسجّلات الجهاز في عناوين معروفة، ودعنا نتخيل أيضًا أن للجهاز مسجّلين، كل مسجل بطول 16 بت، بعنوان ذاكرة تصاعدي، والمسجل الأول هو مسجل التحكم والحالة control and status register -أو اختصارًا csr-، والمسجل الثاني هو منفذ البيانات، ونستطيع الوصول إلى جهاز مماثل بالطريقة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_40" style=""><span class="com">// ‫مثال بلغة C المعيارية دون استخدام const أو volatile</span><span class="pln">

</span><span class="com">/* صرّح عن مسجلات الجهاز، واستخدام العدد الصحيح أو العدد الصحيح القصير معرّف بحسب التطبيق */</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> devregs</span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">short</span><span class="pln">  csr</span><span class="pun">;</span><span class="pln">    </span><span class="com">/* مسجل التحكم والحالة */</span><span class="pln">
        </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">short</span><span class="pln">  data</span><span class="pun">;</span><span class="pln">   </span><span class="com">/* منفذ البيانات */</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="com">// أنماط البتات في‫ csr </span><span class="pln">
</span><span class="com">#define</span><span class="pln"> ERROR   </span><span class="lit">0x1</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> READY   </span><span class="lit">0x2</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> RESET   </span><span class="lit">0x4</span><span class="pln">

</span><span class="com">/* العنوان المطلق للجهاز */</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> DEVADDR </span><span class="pun">((</span><span class="kwd">struct</span><span class="pln"> devregs </span><span class="pun">*)</span><span class="lit">0xffff0004</span><span class="pun">)</span><span class="pln">

</span><span class="com">/* عدد الأجهزة في النظام */</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> NDEVS   </span><span class="lit">4</span><span class="pln">

</span><span class="com">/*
انتظر الدالة حتى تقرأ بت من الجهاز‫ n، ثم تحقق من نطاق رقم الجهاز
انتظر حتى‫ READY أو ERROR، ‫واقرأ البايت إن لم يحدث أي خطأ وأعد قيمته
‫وإلا فأعد ضبط الخطأ وأعد القيمة 0xffff
*/</span><span class="pln">
</span><span class="kwd">unsigned</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> read_dev</span><span class="pun">(</span><span class="kwd">unsigned</span><span class="pln"> devno</span><span class="pun">){</span><span class="pln">

        </span><span class="kwd">struct</span><span class="pln"> devregs </span><span class="pun">*</span><span class="pln">dvp </span><span class="pun">=</span><span class="pln"> DEVADDR </span><span class="pun">+</span><span class="pln"> devno</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">devno </span><span class="pun">&gt;=</span><span class="pln"> NDEVS</span><span class="pun">)</span><span class="pln">
                </span><span class="kwd">return</span><span class="pun">(</span><span class="lit">0xffff</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">while</span><span class="pun">((</span><span class="pln">dvp</span><span class="pun">-&gt;</span><span class="pln">csr </span><span class="pun">&amp;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">READY </span><span class="pun">|</span><span class="pln"> ERROR</span><span class="pun">))</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
                </span><span class="pun">;</span><span class="pln"> </span><span class="com">/* فراغ. انتظر حتى الانتهاء من الحلقة */</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">dvp</span><span class="pun">-&gt;</span><span class="pln">csr </span><span class="pun">&amp;</span><span class="pln"> ERROR</span><span class="pun">){</span><span class="pln">
                dvp</span><span class="pun">-&gt;</span><span class="pln">csr </span><span class="pun">=</span><span class="pln"> RESET</span><span class="pun">;</span><span class="pln">
                </span><span class="kwd">return</span><span class="pun">(</span><span class="lit">0xffff</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">return</span><span class="pun">((</span><span class="pln">dvp</span><span class="pun">-&gt;</span><span class="pln">data</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln"> </span><span class="lit">0xff</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 2]
</p>

<p>
	تُعد الطريقة المتبعة في استخدام تصريح الهيكل لوصف تخطيط مسجل الجهاز واسمه ممارسةً شائعة، لاحظ أنه لا يوجد أي كائنات معرفة من هذا النوع، إذ يحدّد التصريح ببساطة الهيكل دون استخدام أي مساحة.
</p>

<p>
	نستخدم تحويل أنواع ثابت للوصول إلى مسجلات الجهاز وكأنها تشير إلى هيكل، إلا أنها تشير في هذه الحالة إلى عنوان الذاكرة بدلًا من ذلك.
</p>

<p>
	إلا أن هناك مشكلةً كبيرةً في مصرّفات لغة سي السابقة، وهي متعلقة بحلقة while التكرارية التي تختبر المسجل الأول (مسجل الحالة) وتنتظر البت <code>ERROR</code> أو <code>READY</code> ليظهر، وسيلاحظ أي مصرّف جيد أن الحلقة تفحص عنوان ذاكرة مماثل بصورةٍ متكررة، وسيحاول المصرف أن يشير إلى الذاكرة مرةً واحدةً وأن ينسخ القيمة إلى المسجل لتسريع العملية؛ وهذا ما لا نريده، إذ أن هذه الحالة من الحالات التي يجب علينا التحقق من المكان الذي يشير إليه المؤشر كل دورة ضمن الحلقة.
</p>

<p>
	نتيجةً للمشكلة السابقة، لم تكن معظم مصرفات لغة سي قادرةً سابقًا على تحسين أداء البرنامج، وللتخلص من هذه المشكلة (ومشاكل أخرى مشابهة مرتبطة بمتى يمكن الكتابة على شيء يشير إليه المؤشر) قُدِّمت الكلمة المفتاحية volatile، التي تخبر المصرف أن الكائن عرضةٌ للتغير المفاجئ بسبب أسباب لا يمكن التنبؤ بها من خلال النظر إلى البرنامج ذات نفسه، وتُجبر كل مرجع refernece للكائن بأن يصبح مرجعًا فعليًّا (وليس عن طريق مؤشر).
</p>

<p>
	إليك البرنامج السابق (مثال 2) مكتوبًا باستخدام <code>volatile</code> و <code>const</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_42" style=""><span class="com">/*
صرّح عن مسجلات الجهاز، واستخدام العدد الصحيح أو العدد الصحيح القصير معرّف بحسب التطبيق
*/</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> devregs</span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">short</span><span class="pln"> </span><span class="kwd">volatile</span><span class="pln"> csr</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">short</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">volatile</span><span class="pln"> data</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="com">// أنماط البتات في‫ csr  </span><span class="pln">
</span><span class="com">#define</span><span class="pln"> ERROR   </span><span class="lit">0x1</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> READY   </span><span class="lit">0x2</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> RESET   </span><span class="lit">0x4</span><span class="pln">

</span><span class="com">/* العنوان المطلق للجهاز */</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> DEVADDR </span><span class="pun">((</span><span class="kwd">struct</span><span class="pln"> devregs </span><span class="pun">*)</span><span class="lit">0xffff0004</span><span class="pun">)</span><span class="pln">

</span><span class="com">/* عدد الأجهزة في النظام */</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> NDEVS   </span><span class="lit">4</span><span class="pln">

</span><span class="com">/*
‫انتظر الدالة حتى تقرأ بت من الجهاز n، ثم تحقق من نطاق رقم الجهاز
انتظر حتى‫ READY أو ERROR، واقرأ البايت إن لم يحدث أي خطأ وأعد قيمته
‫وإلا فأعد ضبط الخطأ وأعد القيمة 0xffff
*/</span><span class="pln">
</span><span class="kwd">unsigned</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> read_dev</span><span class="pun">(</span><span class="kwd">unsigned</span><span class="pln"> devno</span><span class="pun">){</span><span class="pln">

        </span><span class="kwd">struct</span><span class="pln"> devregs </span><span class="pun">*</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> dvp </span><span class="pun">=</span><span class="pln"> DEVADDR </span><span class="pun">+</span><span class="pln"> devno</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">devno </span><span class="pun">&gt;=</span><span class="pln"> NDEVS</span><span class="pun">)</span><span class="pln">
                </span><span class="kwd">return</span><span class="pun">(</span><span class="lit">0xffff</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">while</span><span class="pun">((</span><span class="pln">dvp</span><span class="pun">-&gt;</span><span class="pln">csr </span><span class="pun">&amp;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">READY </span><span class="pun">|</span><span class="pln"> ERROR</span><span class="pun">))</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
                </span><span class="pun">;</span><span class="pln"> </span><span class="com">/* فراغ. انتظر حتى الانتهاء من الحلقة */</span><span class="pln">

        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">dvp</span><span class="pun">-&gt;</span><span class="pln">csr </span><span class="pun">&amp;</span><span class="pln"> ERROR</span><span class="pun">){</span><span class="pln">
                dvp</span><span class="pun">-&gt;</span><span class="pln">csr </span><span class="pun">=</span><span class="pln"> RESET</span><span class="pun">;</span><span class="pln">
                </span><span class="kwd">return</span><span class="pun">(</span><span class="lit">0xffff</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">return</span><span class="pun">((</span><span class="pln">dvp</span><span class="pun">-&gt;</span><span class="pln">data</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln"> </span><span class="lit">0xff</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 3]
</p>

<p>
	تطابق القوانين الخاصة بمزج volatile والأنواع الاعتيادية تلك الخاصة بالمؤهل const، إذ من الممكن إسناد مؤشر يشير إلى كائن مؤهّل بالمؤهّل volatile إلى عنوان كائن اعتيادي بأمان دون أي مشاكل، إلا أنه من الخطر (ويجب استخدام تحويل الأنواع في هذه الحالة) أخذ عنوان الكائن المؤهل بالمؤهل volatile ووضعه في مؤشر يشير إلى كائن اعتيادي، واستخدام مؤشر من هذا النوع سيتسبب بسلوك غير معرّف.
</p>

<p>
	إذا صُرّح عن مصفوفة أو هيكل باستخدام إحدى المؤهلين const أو volatile، فمن الممكن لجميع الأعضاء أن تمتلك هذا المؤهل أيضًا، وهذا الأمر المنطقي إذا فكرت به للحظة، فكيف لعضو من هيكل مؤهل بالمؤهل const أن يكون قابلًا للتعديل؟
</p>

<p>
	هذا يعني أن أي محاولة لإعادة كتابة المثال السابق ممكنة، فعوضًا عن تصريح مسجلات الجهاز بكونها volatile في الهيكل، من الممكن للمؤشر أي يُصرّح بأن يشير إلى هيكل volatile عوضًا عن ذلك، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_44" style=""><span class="kwd">struct</span><span class="pln"> devregs</span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">short</span><span class="pln">  csr</span><span class="pun">;</span><span class="pln">    </span><span class="com">/* مسجل التحكم والحالة */</span><span class="pln">
      </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">short</span><span class="pln">  data</span><span class="pun">;</span><span class="pln">   </span><span class="com">/* منفذ البيانات  */</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">volatile</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> devregs </span><span class="pun">*</span><span class="kwd">const</span><span class="pln"> dvp</span><span class="pun">=</span><span class="pln">DEVADDR</span><span class="pun">+</span><span class="pln">devno</span><span class="pun">;</span></pre>

<p>
	بما أن <code>dvp</code> يشير إلى كائن <code>volatile</code>، فمن غير المسموح تحسين المراجع عن طريق المؤشرات، وعلى الرغم من أن ما سبق يعمل، إلا أنه ممارسةٌ سيئة، إذ ينتمي تصريح <code>volatile</code> إلى الهيكل، ومسجلات الجهاز هي <code>volatile</code> وهذا المكان الذي يجب أن يبقى فيه التصريح، لتسهيل قراءة الشيفرة البرمجية.
</p>

<p>
	إذًا، مؤهل النوع <code>volatile</code> مهم جدًا لأي كائن سيتعرض لتغييرات، سواءٌ كانت التغييرات بواسطة العتاد الصلب أو برامج خدمات المقاطعة الغير المتزامنة asynchronous interrupt service routines.
</p>

<p>
	وما إن اعتقدت أنّك فهمت كلّ ما سبق بصورة مثالية، حتى يأتي التصريح التالي الذي سيغير من رأيك:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_46" style=""><span class="kwd">volatile</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> devregs</span><span class="pun">{</span><span class="pln">
      </span><span class="com">/* محتوى */</span><span class="pln">
</span><span class="pun">}</span><span class="pln">v_decl</span><span class="pun">;</span></pre>

<p>
	الذي يصرح عن النوع <code>struct devregs</code> إضافةً إلى كائن مؤهل باستخدام <code>volatile</code> من ذلك النوع باسم <code>v_decl</code>. ويتبعه التصريح التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_48" style=""><span class="kwd">struct</span><span class="pln"> devregs nv_decl</span><span class="pun">;</span></pre>

<p>
	الذي يصرح عن <code>nv_decl</code> وهو <strong>ليس</strong> مؤهّلًا باستخدام <code>volatile</code>؛ فالتأهيل ليس جزءًا من النوع <code>struct devregs</code> وإنما ينطبق فقط على تصريح <code>v_decl</code>. لننظر للأمر من زاوية أخرى لعلّ الأمر يتضح لك بعض الشيء (التصريحان متماثلان في نتيجتهما):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_50" style=""><span class="kwd">struct</span><span class="pln"> devregs</span><span class="pun">{</span><span class="pln">
      </span><span class="com">/* محتوى */</span><span class="pln">
</span><span class="pun">}</span><span class="kwd">volatile</span><span class="pln"> v_decl</span><span class="pun">;</span></pre>

<p>
	إذا أردت الطريقة المختصرة لإرفاق مؤهل إلى نوع آخر، فيمكنك استخدام <code>typedef</code> لتحقيق الآتي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_52" style=""><span class="kwd">struct</span><span class="pln"> x</span><span class="pun">{</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> a</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">typedef</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> x csx</span><span class="pun">;</span><span class="pln">

csx const_sx</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> x non_const_sx </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="lit">1</span><span class="pun">};</span><span class="pln">

const_sx </span><span class="pun">=</span><span class="pln"> non_const_sx</span><span class="pun">;</span><span class="pln">        </span><span class="com">/* التعديل على ثابت بتسبب بخطأ */</span></pre>

<h4>
	العمليات غير القابلة للتجزئة
</h4>

<p>
	سيفهم الذي يتعاملون مع تقنيات مقاطعات العتاد الصلب والجوانب الأخرى للوقت الحقيقي في البرمجة أهمية أنواع volatile، وهناك ضرورةٌ في هذا المجال للتأكد من أن الوصول إلى كائنات البيانات متواصل، إلا أن مناقشة هذا الأمر سيأخذنا في رحلة بعيدة عن موضوعنا هنا، لكن دعنا نتكلم عن بعض المشكلات بخصوص هذا الأمر على الأقل.
</p>

<p>
	لا تخطئ الاعتقاد وتفترض أن جميع العمليات المكتوبة في لغة سي متواصلة، فعلى سبيل المثال قد يكون التصريح التالي عدّادًا يُحدّث عن طريق مقاطعة برنامج ساعة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_54" style=""><span class="kwd">extern</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">volatile</span><span class="pln"> </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> realtimeclock</span><span class="pun">;</span></pre>

<p>
	من المهم هنا أن يتضمن التصريح على المؤهل <code>volatile</code> بسبب التغييرات اللامتزامنة التي تحصل له، ويحتوي على المؤهل <code>const</code> لأنه من غير الممكن التعديل على قيمته سوى عن طريق برنامج المقاطعة، وإن سُمح للبرنامج الوصول إليه بالطريقة التالية سنحصل على مشكلة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_56" style=""><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> time_of_day</span><span class="pun">;</span><span class="pln">

time_of_day </span><span class="pun">=</span><span class="pln"> real_time_clock</span><span class="pun">;</span></pre>

<p>
	ماذا لو استغرقت عملية نسخ <code>long</code> إلى <code>long</code> آخر عدّة تعليمات آلة لنسخ الكلمتين <code>real_time_clock</code> و <code>time_of_day</code>؟ من الممكن حدوث مقاطعة خلال عملية الإسناد وستكون أسوأ حالة لهذه المقاطعة هي عندما تكون الكلمة الأقل ترتيبًا لـ<code>real_time_clock</code> تساوي <code>0xffff</code> والكلمة مرتفعة الترتيب تساوي <code>0x0000</code>، وبهذا ستكون قيمة الكلمة منخفضة الترتيب مساوية إلى <code>0xffff</code>. تعمل المقاطعة عملها وتزيد الكلمة منخفضة الترتيب لـ<code>real_time_clock</code> إلى <code>0x0</code> والكلمة مرتفعة الترتيب إلى <code>0x1</code> ومن ثم تعيد القيمة، ويُستكمل ما تبقى من الإسناد فيما بعد وينتهي الأمر باحتواء <code>time_of_day</code> على القيمة <code>0x0001ffff</code> و <code>real_time_clock</code> على القيمة الصائبة <code>0x00010000</code>.
</p>

<p>
	تعدّ المشكلات المسابقة لما سبق منطقةً خطرة، ويعلم جميع من يعمل في البيئات غير المتزامنة هذا الأمر جيّدًا، ولا تأخذ لغة سي المعيارية أي إجراءات احترازية لتفادي هذا النوع من المشكلات، ويجب تطبيق الطريقة الاعتيادية.
</p>

<p>
	يُصرّح ملف الترويسة <code>signal.h</code> عن نوع باسم <code>sig_atomic_t</code> ومن المضمون إمكانية تعديل هذا النوع بأمان عند التعامل مع الأحداث غير المتزامنة، وهذا يعني أنه من الممكن تعديله عن طريق إسناد قيمة إليه أو زيادة قيمته أو إنقاصها أو أي شيء آخر يعطي قيمة جديدة بحسب القيمة السابقة، وهو ليس آمنًا.
</p>

<h2>
	نقاط التسلسل
</h2>

<p>
	ترتبط نقاط التسلسل sequence points بمشكلات برمجة الوقت الحقيقي، إلا أنها مختلفة عن المشكلات التي ناقشناها، وتعدّ بمثابة محاولة للمعيار في تعريف الحالات التي تسمح -أو لا تسمح- بها طرق معينة من التحسين، على سبيل المثال، ألقِ نظرةً على البرنامج التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_58" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> i_var</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> func</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
        </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">i_var </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">10000</span><span class="pun">){</span><span class="pln">
                func</span><span class="pun">();</span><span class="pln">
                i_var</span><span class="pun">++;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">void</span><span class="pln">
func</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">){</span><span class="pln">
        printf</span><span class="pun">(</span><span class="str">"in func, i_var is %d\n"</span><span class="pun">,</span><span class="pln"> i_var</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 4]
</p>

<p>
	قد يحاول المصرّف تحسين الأداء في الحلقة التكرارية بحيث يخزّن <code>i_var</code> في مسجّل الآلة لزيادة السرعة، إلا أن الدالة تحتاج وصولًا إلى القيمة الصحيحة من <code>i_var</code> حتى يمكنها طباعة القيمة الصحيحة، وهذا يعني أن المسجّل يجب أن يعيد تخزين قيمة <code>i_var</code> عند كل استدعاءٍ للدالة على الأقل، ويصف المعيار الشروط التي تحدد متى وأين يحصل ذلك. تُستكمل التأثيرات الجانبية لكل تعبير في نقطة التسلسل التي سبقتها، وهذا السبب في عدم قدرتنا على الاعتماد على تعابير مشابهة لهذه:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6658_60" style=""><span class="pln">a</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> i</span><span class="pun">++;</span></pre>

<p>
	وذلك بسبب عدم وجود أي نقطة تسلسلية مُحدّدة ضمن الإسناد أو عوامل الزيادة والنقصان، ولا يمكننا معرفة متى سيؤثر عامل الزيادة على <code>i</code> تحديدًا.
</p>

<p>
	يعرّف المعيار نقاط التسلسل على النحو التالي:
</p>

<ul>
	<li>
		نقطة استدعاء دالة، بعد تقييم وسطائها.
	</li>
	<li>
		نهاية المعامل الأول للعامل <code>&amp;&amp;</code>.
	</li>
	<li>
		نهاية المعامل الأول للعامل <code>||</code>.
	</li>
	<li>
		نهاية المعامل الأول للعامل الشرطي <code>:?</code>.
	</li>
	<li>
		نهاية كل من معاملات عامل الفاصلة <code>,</code>.
	</li>
	<li>
		نهاية تقييم تعبير كامل، على النحو التالي:
	</li>
	<li>
		تقييم القيمة الأولية لكائن <code>auto</code>.
	</li>
	<li>
		تعبير اعتيادي، أي متبوع بفاصلة منقوطة.
	</li>
	<li>
		تعابير التحكم في تعليمات <code>do</code> أو <code>while</code> أو <code>if</code> أو <code>switch</code> أو <code>for</code>.
	</li>
	<li>
		التعبيران الآخران في تعليمة حلقة <code>for</code>.
	</li>
	<li>
		التعبير في تعليمة <code>return</code>.
	</li>
</ul>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter8/" rel="external nofollow">Specialized Areas of C</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/c/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%85%D9%83%D8%AA%D8%A8%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1777/" rel="">مقدمة إلى مكتبات لغة سي C</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B5%D8%A7%D8%B1%D9%8A%D8%AD-declarations-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%B1%D9%8A%D9%81-definitions-%D9%88%D8%A5%D9%85%D9%83%D8%A7%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-accessibility-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1762/" rel="">التصاريح declarations والتعاريف definitions وإمكانية الوصول accessibility في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">التعامل مع المحارف والسلاسل النصية في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1749/" rel="">تهيئة المتغيرات وأنواع البيانات في لغة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1763</guid><pubDate>Sat, 12 Nov 2022 16:08:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x635;&#x627;&#x631;&#x64A;&#x62D; declarations &#x648;&#x627;&#x644;&#x62A;&#x639;&#x627;&#x631;&#x64A;&#x641; definitions &#x648;&#x625;&#x645;&#x643;&#x627;&#x646;&#x64A;&#x629; &#x627;&#x644;&#x648;&#x635;&#x648;&#x644; accessibility &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B5%D8%A7%D8%B1%D9%8A%D8%AD-declarations-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%B1%D9%8A%D9%81-definitions-%D9%88%D8%A5%D9%85%D9%83%D8%A7%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-accessibility-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1762/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_11/636346ed539b3_--declarations--definitions---accessibility---C.png.1a334321b232c8da0848479e635e4403.png" /></p>
<p>
	قدمنا سابقًا مفهوم <a href="https://academy.hsoub.com/programming/c/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D9%86%D8%B7%D8%A7%D9%82-scope-%D9%88%D8%A7%D9%84%D8%B1%D8%A8%D8%B7-linkage-%D8%B9%D9%84%D9%89-%D9%85%D8%B3%D8%AA%D9%88%D9%89-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1674/" rel="">النطاق scope والربط linkage</a>، ووضحنا كيف يمكن استخدامهما سويًّا للتحكم بقابلية الوصول لأجزاء معينة ضمن البرنامج، وعمدنا إلى إعطاء وصف غامض لما يحدّد التعريف Definition لأن شرح ذلك سيتسبب بتشويشك في تلك المرحلة ولن يكون مثمرًا لرحلة تعلمك، إلا أنه علينا تحديد هذا الأمر في مرحلة من المراحل وهذا ما سنفعله في هذ الجزئية من السلسلة، كما سنتكلم عن صنف التخزين Storage class لجعل الأمر أكثر إثارة للاهتمام.
</p>

<p>
	لعلك ستجد مزج هذه المفاهيم واستخدامها سويًّا معقدًا ومربكًا، وهذا مبرّر، إلا أننا سنحاول إزالة الغموض بالتكلم عن بعض القوانين المفيدة لاحقًا، لكنك بحاجة لقراءة بعض التفاصيل قبل ذلك على الأقل مرةً واحدةً لتفهم هذه القوانين.
</p>

<p>
	حتى تفهم القوانين جيدًا، عليك أن تفهم ثلاثة مفاهيم مختلفة -ولكن مرتبطة- وهي كما يدعوها المعيار:
</p>

<ul>
	<li>
		المدة الزمنية duration.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D9%86%D8%B7%D8%A7%D9%82-scope-%D9%88%D8%A7%D9%84%D8%B1%D8%A8%D8%B7-linkage-%D8%B9%D9%84%D9%89-%D9%85%D8%B3%D8%AA%D9%88%D9%89-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1674/" rel="">النطاق scope</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D9%86%D8%B7%D8%A7%D9%82-scope-%D9%88%D8%A7%D9%84%D8%B1%D8%A8%D8%B7-linkage-%D8%B9%D9%84%D9%89-%D9%85%D8%B3%D8%AA%D9%88%D9%89-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1674/" rel="">الربط linkage</a>.
	</li>
</ul>

<p>
	ويشرح المعيار هذه المصطلحات، كما أننا ناقشنا النطاق والربط في المقالة سابقة الذكر لكننا سنعيد ذكرهما بصورةٍ مقتضبة.
</p>

<h2>
	محددات صنف التخزين
</h2>

<p>
	تندرج خمس كلمات مفتاحية تحت تصنيف محدّدات صنف التخزين storage class specifiers، أحدها هو <code>typedef</code>، وبالرغم من أنه أشبه باختصار عن شبهه بخاصيّة، إلا أننا سنناقش هذه الكلمة المفتاحية بالتفصيل لاحقًا. يتبقى لدينا الكلمات المفتاحية <code>auto</code> و <code>extern</code> و <code>register</code> و <code>static</code>.
</p>

<p>
	تساعدك محددات صنف التخزين على تحديد نوع التخزين المستخدم لكائنات البيانات، وهناك صنف تخزين واحد مسموح به عند التصريح، وهذا الأمر منطقي لأن هناك طريقةً واحدةً لتخزين الأشياء. يُطبق المحدّد الافتراضي على عملية التصريح إذا أُهمل محدد صف التخزين، ويعتمد اختيار المحدد الافتراضي على نوع التصريح إذا كان خارج دالة (تصريح خارجي) أو داخل الدالة (تصريح داخلي)، إذ أن محدد التخزين الافتراضي للتصريح الخارجي هو <code>extern</code> بينما <code>auto</code> هو محدد التخزين الافتراضي للتصريح الداخلي، والاستثناء الوحيد لهذه القاعدة هو تصريح <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1646/" rel="">الدوال</a>، إذ إن قيمة المحدد <strong>الافتراضي</strong> لها هو <code>extern</code> دائمًا.
</p>

<p>
	يمكن أن يؤثر مكان التصريح ومحددات صنف التخزين المستخدمة (أو الافتراضية في حال عدم وجودها) على ربط الاسم، بالإضافة إلى تصريحات الاسم ذاته التي تلي التصريح الأولي، ولحسن الحظ فإن ذلك لا يؤثر على النطاق أو المدة الزمنية. سنستعرض المفاهيم الأسهل أوّلًا.
</p>

<h3>
	المدة الزمنية
</h3>

<p>
	تصف المدة الزمنية لكائن ما طبيعة تخزينه، وذلك إذا كان حجز المساحة يصبح مرةً واحدةً عند تشغيل البرنامج أو أنه من طبيعة عابرة بمعنى أن مساحته تُحجز وتحرّر عند الضرورة. هناك نوعان فقط من المدة الزمنية، هما: <strong>المدة الساكنة static duration</strong> و<strong>المدة التلقائية automatic duration</strong>؛ إذ تعني المدة الساكنة أن مساحة التخزين المحجوزة للكائن دائمة؛ بينما تعني المدة التلقائية أن مساحة التخزين المحجوزة للكائن تُحرّر وتُحجز عند الضرورة، ومن السهل معرفة أي من المدتين ستحصل عليهما، لأنك ستحصل على المدة التلقائية فقط في حالة:
</p>

<ul>
	<li>
		إذا كان التصريح داخل دالة،
	</li>
	<li>
		<strong>و</strong>لم يكن التصريح يحتوي على أي من الكلمتين المفتاحيتين <code>static</code> أو <code>extern</code>.
	</li>
	<li>
		<strong>و</strong>ليس التصريح تصريحًا لدالة.
	</li>
</ul>

<p>
	ستجد أن معاملات الدالة الصورية تطابق هذه القوانين الثلاث دائمًا، وبذلك فهي ذات مدة تلقائية. مع أن تواجد الكلمة المفتاحية <code>static</code> ضمن التصريح يعني أن الكائن ذو مدة ساكنة دون أي شك، إلا أنها ليست الطريقة الوحيدة لإنجاز ذلك، ويسبب هذا بعض اللبس لدى الكثير، وعلينا تقبُّل هذا الأمر.
</p>

<p>
	تُمنح كائنات البيانات المصرحة داخل الدوال محدد صنف التخزين الافتراضي <code>auto</code>، إلا إذا استُخدم محدد آخر لصنف التخزين. لن تحتاج الوصول إلى هذه الكائنات من خارج الدالة في معظم الحالات، لذا ستريد أن تكون <strong>عديمة الربط no linkage</strong>، وفي هذه الحالة نستخدم الحالة الافتراضية <code>auto</code> أو محدّد صنف التخزين <code>register storage</code>، إذ سيعطينا هذا كائنًا عديم الربط وذا مدة تلقائية. لا يمكن تطبيق الكلمة المفتاحية <code>auto</code> أو <code>register</code> ضمن تصريح يقع خارج دالةٍ ما.
</p>

<p>
	يُعد صنف التخزين <code>register</code> مثيرًا للاهتمام، فعلى الرغم من قلة استخدامه هذه الأيام إلا أنه يقترح على المصرف تخزين الكائن في مسجل register واحد أو أكثر في الذاكرة والذي ينعكس بالإيجاب على سرعة التنفيذ. لا ينفذ المصرف الأوامر بناءً على هذا الأمر إلا أن متغيرات <code>register</code> لا تمتلك أي عنوان ( يُمنع استخدام العامل <code>&amp;</code> معها) لتسهيل الأمر وذلك لأن بعض الحواسيب لا تدعم المسجلات ذات العناوين. قد يتسبب التصريح عن عدة كائنات <code>register</code> بتأثير عكسي فيبطئ البرنامج بدلًا من تسريعه، وذلك لأن المصرف سوف يضطر إلى حجز مزيدٍ من المسجلات عند الدخول إلى (تنفيذ) الدالة وهي عملية بطيئة، أو لن يكون هناك العدد الكافي من المسجلات المتبقية لتُستخدم في العمليات الحسابية الوسيطة.
</p>

<p>
	يعود الاختيار في استخدام المسجلات إلى الآلة المُستخدمة، ويجب استخدام هذه الطريقة فقط عندما توضح الحسابات أن هذا النوع ضروري لتسريع تنفيذ دالة ما بعينها، وعندها يتوجب عليك فحص البرنامج وتجربته. يجب ألّا تصرح عن متغيرات المسجلات أبدًا خلال عملية تطوير البرنامج برأينا، بل تأكد من أن البرنامج يعمل ومن ثم أجرِ بعض القياسات، وعندها قرّر استخدام هذا النوع من المتغيرات حسب النتائج فيما إذا كان يتسبب استخدامها بتحسن ملحوظ في الأداء، ولكن عليك تكرار العملية ذاتها إذا اتبعت هذه الطريقة في كل نوع من <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-macro-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC-%D8%A7%D9%84%D9%85%D8%B3%D8%A8%D9%82-preprocessor-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1750/" rel="">المعالجات المسبقة</a> التي تنقل برنامجك إليها، إذ يحتوي كل نوع من المعالجات المسبقة على خصائص مختلفة.
</p>

<p>
	ملاحظة أخيرة بخصوص متغيرات <code>register</code>: يُعد محدد صنف التخزين هذا المحدد الوحيد الممكن استخدامه ضمن نموذج دالة أولي function prototype أو تعريف دالة، ويجري تجاهل محدد صنف التخزين في حالة نموذج الدالة الأولي، بينما يشير تعريف الدالة على أن المعامل الفعلي مخزّن في مسجّل إذا كان الأمر ممكنًا. إليك مثالًا يوضح كيفية يمكن توظيف ذلك:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7866_8" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> func</span><span class="pun">(</span><span class="kwd">register</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> arg1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> arg2</span><span class="pun">);</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      func</span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/*
توضح الدالة أنه يمكن التصريح عن المعاملات الصورية باستخدام صنف تخزين مسجّل
*/</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> func</span><span class="pun">(</span><span class="kwd">register</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> arg1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> arg2</span><span class="pun">){</span><span class="pln">

      </span><span class="com">/*
هذا الاستخدام لأهداف توضيحية، فلا أحد يكتب ذلك في هذا السياق
‪لا يمكن أخذ عنوان ‫arg1 حتى لو أردت ذلك
       */</span><span class="pln">
      </span><span class="kwd">double</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fp </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">arg2</span><span class="pun">;</span><span class="pln">

      </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">arg1</span><span class="pun">){</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"res = %f\n"</span><span class="pun">,</span><span class="pln"> arg1 </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(*</span><span class="pln">fp</span><span class="pun">));</span><span class="pln">
              arg1</span><span class="pun">--;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 1]
</p>

<p>
	تعتمد إذًا المدة الزمنية لكائن على محدد صنف التخزين المستخدَم -بغض النظر عن كون الكائن كائن بيانات أو دالة-، كما تعتمد على مكان التصريح (في كتلة داخلية أو على كامل نطاق الملف؟). يعتمد الربط أيضًا على محدد صنف التخزين، إضافةً إلى نوع الكائن ونطاق التصريح عنه. يوضح الجدول 1 والجدول 2 التاليين مدة التخزين الناتجة والربط لكل من الحالات الممكنة عند استخدام محددات صنف التخزين الممكنة، وموضع التصريح. يُعد ربط الكائنات باستخدام المدة الساكنة أكثر تعقيدًا، لذا ننصحك باستخدام هذه الجداول لتوجيهك في الحالات البسيطة وانتظر قليلًا حتى نصل إلى الجزء الذي نتكلم فيه عن التعريفات.
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
	<thead>
		<tr>
			<th>
				محدد صنف التخزين
			</th>
			<th>
				دالة أو كائن بيانات
			</th>
			<th>
				الربط
			</th>
			<th>
				المدة الزمنية
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<code>static</code>
			</td>
			<td>
				أحدهما
			</td>
			<td>
				داخلي
			</td>
			<td>
				ساكنة
			</td>
		</tr>
		<tr>
			<td>
				<code>extern</code>
			</td>
			<td>
				أحدهما
			</td>
			<td>
				خارجي غالبًا
			</td>
			<td>
				ساكنة
			</td>
		</tr>
		<tr>
			<td>
				لا يوجد
			</td>
			<td>
				دالة
			</td>
			<td>
				خارجي غالبًا
			</td>
			<td>
				ساكنة
			</td>
		</tr>
		<tr>
			<td>
				لا يوجد
			</td>
			<td>
				كائن بيانات
			</td>
			<td>
				خارجي
			</td>
			<td>
				ساكنة
			</td>
		</tr>
	</tbody>
</table>

<p>
	أهملنا في الجدول السابق المحددين <code>register</code> و <code>auto</code> لأنه من غير المسموح استخدامهما على نطاق التصريحات الخارجية (على نطاق الملف).
</p>

<table>
	<thead>
		<tr>
			<th>
				محدد صنف التخزين
			</th>
			<th>
				دالة أو كائن بيانات
			</th>
			<th>
				الربط
			</th>
			<th>
				المدة الزمنية
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<code>register</code>
			</td>
			<td>
				كائن بيانات فقط
			</td>
			<td>
				لا يوجد
			</td>
			<td>
				تلقائية
			</td>
		</tr>
		<tr>
			<td>
				<code>auto</code>
			</td>
			<td>
				كائن بيانات فقط
			</td>
			<td>
				لا يوجد
			</td>
			<td>
				تلقائية
			</td>
		</tr>
		<tr>
			<td>
				<code>static</code>
			</td>
			<td>
				كائن بيانات فقط
			</td>
			<td>
				لا يوجد
			</td>
			<td>
				ساكنة
			</td>
		</tr>
		<tr>
			<td>
				<code>extern</code>
			</td>
			<td>
				كلاهما
			</td>
			<td>
				خارجي غالبًا
			</td>
			<td>
				ساكنة
			</td>
		</tr>
		<tr>
			<td>
				لا يوجد none
			</td>
			<td>
				كائن بيانات
			</td>
			<td>
				لا يوجد
			</td>
			<td>
				تلقائية
			</td>
		</tr>
		<tr>
			<td>
				لا يوجد none
			</td>
			<td>
				دالة
			</td>
			<td>
				خارجي غالبًا
			</td>
			<td>
				ساكنة
			</td>
		</tr>
	</tbody>
</table>

<p>
	تحتفظ المتغيرات الساكنة <code>static</code> الداخلية بقيمها بين استدعاءات الدوال التي تحتويها، وهذا شيءٌ مفيدٌ جدًا في بعض الحالات.
</p>

<h2>
	النطاق
</h2>

<p>
	علينا الآن إلقاء نظرة على نطاق أسماء الكائنات مجددًا، والذي يعرف أين ومتى يكون لاسمٍ ما معنًى محدد. هناك أنواعٌ مختلفة للنطاق هي:
</p>

<ul>
	<li>
		نطاق دالة.
	</li>
	<li>
		نطاق ملف.
	</li>
	<li>
		نطاق كتلة.
	</li>
	<li>
		نطاق نموذج دالة أولي.
	</li>
</ul>

<p>
	النطاق الأسهل هو نطاق الدالة، إذ ينطبق ذلك على عناوين labels الأسماء المرئية ضمن دالة ما صُرّح عنها بداخلها، ولا يمكن لعنوانين داخل نفس الدالة أن يمتلكان الاسم ذاته، لكن يمكن للعنوان استخدام الاسم ذاته إذا كان ضمن أي دالةٍ أخرى بحكم أن النطاق هو نطاق دالة. <strong>ليست</strong> العناوين كائنات فهي لا تمتلك أي مساحة تخزينية ولا يعني مفهوم الربط والمدة الزمنية أي معنًى في عالمها.
</p>

<p>
	يمتلك أي اسم مُصرّح عنه خارج الدالة نطاق ملف، وهذا يعني أن الاسم قابلٌ للاستخدام ضمن أي نقطة من البرنامج من لحظة التصريح عنه إلى نهاية ملف الشيفرة المصدرية الذي يحتوي ذلك التصريح، ومن الممكن طبعًا لهذه الأسماء أن تصبح مخفية مؤقتًا باستخدام تصريحات ضمن تعليمات مركبة، لأنه كما نعلم، ينبغي على تعريفات الدالة أن تكون خارج الدوال الأخرى حتى يكون اسم الدالة في التعريف ذو نطاق ملف.
</p>

<p>
	يمتلك الاسم المُصرّح عنه بداخل تعليمة مركبة أو معامل دالة صوري نطاق كتلة ومن الممكن استخدامه ضمن الكتلة حتى الوصول للقوس <code>{</code> الذي يغلِق التعليمة المركبة، ويُخفي أي تصريح لاسم ضمن تعليمة مركبة أي تصريح خارجي آخر للاسم ذاته حتى نهاية التعليمة المركبة.
</p>

<p>
	يعدّ نطاق النموذج الأولي للدالة مثالًا مميزًا وبسيطًا من النطاقات، إذ يمتد تصريح الاسم حتى نهاية النموذج الأولي للدالة فقط، وهذا يعني أن ما يلي خاطئ (باستخدام نفس الاسم مرتين):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7866_10" style=""><span class="kwd">void</span><span class="pln"> func</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">);</span></pre>

<p>
	وهذا هو الاستعمال الصحيح:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7866_12" style=""><span class="kwd">void</span><span class="pln"> func</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> j</span><span class="pun">);</span></pre>

<p>
	وتختفي الأسماء المُصرّح عنها بداخل الأقواس خارجها. نطاق الاسم مستقل تمامًا عن أي محدد صنف تخزين مُستخدم في التصريح.
</p>

<h2>
	الربط
</h2>

<p>
	سنذكّر بمصطلح الربط Linkage بصورةٍ مقتضبة هنا أيضًا، إذ يُستخدم الربط لتحديد ما الذي يجعل الاسم المُعلن عنه في نطاقات مختلفة يشير إلى الشيء ذاته، إذ يمتلك الكائن اسمًا واحدًا فقط، ولكننا في بعض الحالات نحتاج للإشارة إلى هذا الكائن على مستوى نطاقات مختلفة، ويُعدّ استدعاء الدالة <code>printf</code> من أماكن متعددة ضمن البرنامج أبسط الأمثلة، حتى لو كانت هذه الأماكن المذكورة لا تنتمي إلى ملف الشيفرة المصدرية ذاته.
</p>

<p>
	يحذّر المعيار أنه يجب على التصاريح التي تشير إلى الشيء ذاته أن تكون من نوع متوافق، وإلا فسنحصل على برنامج ذي سلوك غير محدد، وسنتكلم عن الأنواع المتوافقة بالتفصيل لاحقًا، وسنكتفي حاليًا بقول أن التصاريح يجب أن تكون متطابقةً باستثناء استخدام محددات صنف محدد التخزين، وتحقيق هذا من مسؤوليات المبرمج، إلا أن هناك بعض الأدوات لتساعدك في تحقيق هذا غالبًا.
</p>

<p>
	هناك ثلاثة أنواع مختلفة من الربط:
</p>

<ul>
	<li>
		الربط الخارجي.
	</li>
	<li>
		الربط الداخلي.
	</li>
	<li>
		عديم الربط.
	</li>
</ul>

<p>
	إذا كان اسم الكائن ذو ربط خارجي فهذا يعني أن جميع النسخ instances الموجودة في البرنامج -الذي قد يكون مؤلفًا من عدد من ملفات الشيفرة المصدرية والمكتبات- تعود إلى الكائن ذاته، ويعني الربط الداخلي لكائن ما أن نُسخ هذا الكائن الموجودة في ملف الشيفرة المصدرية نفسه فقط تُشير إلى الشيء ذاته، بينما يعني الكائن عديم الربط أن كل كائن ذي اسم مماثل له هو كائنٌ منفصلٌ عنه.
</p>

<h2>
	الربط والتعاريف
</h2>

<p>
	يجب لكل كائن بيانات أو دالة مُستخدمة في برنامج (عدا معاملات <a href="https://academy.hsoub.com/programming/c/%D8%B9%D8%A7%D9%85%D9%84-sizeof-%D9%88%D8%AD%D8%AC%D8%B2-%D9%85%D8%B3%D8%A7%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1681/" rel="">عامل sizeof</a>) أن تمتلك <strong>تعريفًا واحدًا فقط</strong>، ومع أننا لم نتطرق إلى هذا بعد، إلا أن هذا الأمر مهمٌ جدًا؛ ويعود السبب بعدم تطرقنا لهذا الأمر إلى استخدام جميع أمثلتنا كائنات بيانات ذات مدة تلقائية فقط وكون تصاريحها تعاريفًا، أو دوالًا كنا قد عرّفناها من خلال كتابة متنها.
</p>

<p>
	تعني القاعدة السابقة أنه يجب للكائنات ذات الربط الخارجي أن تحتوي على تعريفٍ واحد فقط ضمن كامل البرنامج، ويجب للكائنات ذات الربط الداخلي (المقيدة داخل ملف شيفرة مصدرية واحد) أن يُعرّف عنها لمرة واحدة فقط ضمن الملف المُصرّح فيه عنها، كما أن للكائنات عديمة الربط تعريفٌ واحد فقط، إذ أن التصريح عنها هو تعريفٌ أيضًا.
</p>

<p>
	لجمع النقاط الآنف ذكرها، نسأل الأسئلة التالية:
</p>

<ol>
	<li>
		كيف أستطيع الحصول على نوع الربط الذي أريده؟
	</li>
	<li>
		ما الشيء الذي يحدد التعريف؟
	</li>
</ol>

<p>
	علينا أن ننظر إلى الربط أولًا ومن ثم التعريف.
</p>

<p>
	إذّا، كيف نحصل على التعريف المناسب لاسمٍ ما؟ القوانين معقدة بعض الشيء.
</p>

<ol>
	<li>
		ينتج التصريح خارج دالة (نطاق ملف) تحتوي على محدد صنف تخزين ساكن ربطًا داخليًا لهذا الاسم، ويحدد المعيار وجوب وجود تصاريح الدالة التي تحتوي على الكلمة المفتاحية <code>static</code> على مستوى الملف وخارج أي كتلة برمجية.
	</li>
	<li>
		إذا احتوى التصريح على محدد صنف التخزين <code>extern</code>، أو إذا كان تصريح الدالة لا يحتوي على محدد صنف التخزين، أو كلا الحالتين، فهذا يعني:
		<ul>
			<li>
				إذا وجِد تصريح للمعرف ذاته بنطاق ملف، فهذا يعني أن الربط الناتج مماثلٌ لهذا التصريح السابق المرئي.
			</li>
			<li>
				وإلا، فالنتيجة هي ربط خارجي.
			</li>
		</ul>
	</li>
	<li>
		إذا لم يكن التصريح ذو نطاق الملف تصريحًا لدالة أو لم يحتوي على محدد صنف تخزين واضح، فالنتيجة هي ربط خارجي.
	</li>
	<li>
		أي شكل آخر من التصاريح سيكون عديم الربط.
	</li>
	<li>
		إذا وجِد معرف ذو ربط داخلي وخارجي في ذات الوقت ضمن ملف شيفرة مصدرية ما، فالنتيجة غير محددة.
	</li>
</ol>

<p>
	استُخدمت القوانين السابقة لكتابة جدول الربط السابق (جدول 2) دون تطبيق كامل للقاعدة 2، وهذا السبب في استخدامنا الكلمة "خارجي غالبًا"، وتسمح لك القاعدة 2 بتحديد الربط بدقة في هذه الحالات.
</p>

<p>
	ما الذي يجعل التصريح تعريفًا؟
</p>

<ul>
	<li>
		التصاريح التي تعطينا كائنات عديمة الربط هي تعاريف أيضًا.
	</li>
	<li>
		التصاريح التي تتضمن قيمةً أولية هي تعاريف دائمًا، وهذا يتضمن تهيئة دالة ما بكتابة متن الدالة، ويمكن للتصاريح ذات نطاق الكتلة أن تحتوي على قيم أولية فقط في حال كانت عديمة الربط.
	</li>
	<li>
		وإلا، فالتصريح عن الاسم على نطاق ملف بدون محدد صنف التخزين أو مع محدد صنف التخزين <code>static</code> هو <strong>تعريف مبدئي tentative definition</strong>، وإذا احتوى ملف شيفرة مصدرية على تعريف مبدئي واحد أو أكثر لكائن ما وكان الملف لا يحتوي على أي تعاريف فعلية، يصبح للكائن تعريفٌ افتراضيٌ وهو مشابه لحالة إسناد القيمة الأولية "0" إليه (تُهيّأ عناصر <a href="https://academy.hsoub.com/programming/c/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1675/" rel="">المصفوفات</a> والهياكل جميعها إلى قيمة "0")، ولا يوجد للدوال تعريفٌ مبدئي.
	</li>
</ul>

<p>
	طبقًا لما سبق، لا تتسبب التصريحات التي تحتوي على محدد صنف تخزين خارجي extern بتعريف، إلا إذا ضمّنت قيمةً أوليةً للتصريح.
</p>

<h2>
	الاستخدام العملي لكل من الربط والتعاريف
</h2>

<p>
	تبدو القوانين التي تحدد كل من الربط والتعريف المرتبطة بالتصاريح معقدةً بعض الشيء، إلا أن استخدام هذه القوانين عمليًّا ليس بالأمر الصعب، دعونا نناقش بعض الحالات الاعتيادية.
</p>

<p>
	أنواع إمكانية أو قابلية الوصول الثلاث التي ستريدها لكائنات البيانات أو الدوال هي:
</p>

<ul>
	<li>
		على كامل نطاق البرنامج.
	</li>
	<li>
		مقيّد بنطاق ملف شيفرة مصدرية واحد.
	</li>
	<li>
		مقيّد بنطاق دالة واحدة، أو تعليمة مركبة واحدة.
	</li>
</ul>

<p>
	ستحتاج إلى ربط خارجي وربط داخلي وربط عديم لكل من الحالات الثلاث السابقة بالترتيب، ومن الممارسات المُحبذة بالنسبة للحالة الأولى والثانية هي التصريح عن الأسماء ضمن ملف الشيفرة المصدرية الموافق لها قبل أن تعرّف أي دالة، ويوضح الشكل 1 هيكل ملف الشيفرة المصدرية بهذا الخصوص.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="111195" href="https://academy.hsoub.com/uploads/monthly_2022_11/013Layout_of_a_source_file.png.4042beb5302f13445282114d0b9dfc18.png" rel="" data-fileext="png"><img alt="هيكل ملف الشيفرة المصدرية" class="ipsImage ipsImage_thumbnailed" data-fileid="111195" data-unique="cj05g7475" style="width: 289px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_11/013Layout_of_a_source_file.png.4042beb5302f13445282114d0b9dfc18.png"></a>
</p>

<p style="text-align: center;">
	[شكل 1 هيكل ملف الشيفرة المصدرية]
</p>

<p>
	يمكن أن تُسبق تصاريح الربط الخارجية بالكلمة المفتاحية <code>extern</code> وتصاريح الربط الداخلية بالكلمة المفتاحية <code>static</code>. إليك مثالًا عن ذلك:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7866_15" style=""><span class="com">/* مثال عن هيكل ملف الشيفرة المصدرية */</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">

</span><span class="com">/*
 يمكن الوصول للأشياء ذات الربط الخارجي عبر البرنامج
ما يلي هو تصاريح وليس تعاريف، لذا نفترض أن التعاريف في مكان ما آخر
*/</span><span class="pln">

</span><span class="kwd">extern</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> important_variable</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">extern</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> library_func</span><span class="pun">(</span><span class="kwd">double</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">);</span><span class="pln">

</span><span class="com">/*
تعاريف ذات ربط خارجي
*/</span><span class="pln">
</span><span class="kwd">extern</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> ext_int_def </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">     </span><span class="com">/* تعريف صريح */</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> tent_ext_int_def</span><span class="pun">;</span><span class="pln">           </span><span class="com">/* تعريف مبدئي */</span><span class="pln">

</span><span class="com">/*
* يمكن الوصول للأشياء ذات الربط الداخلي فقط من داخل الملف
* يعني استخدام المحدد الساكن أن التعريفات هي تعريفات مبدئية
*/</span><span class="pln">

</span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> less_important_variable</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">struct</span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> member_1</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> member_2</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">local_struct</span><span class="pun">;</span><span class="pln">

</span><span class="com">/*
 ذات ربط داخلي لكنها ليست بتعريف مبدئي لأنها دالة
*/</span><span class="pln">
</span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> lf</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span><span class="pln">

</span><span class="com">/*
* التعريف مع الربط الداخلي
*/</span><span class="pln">
</span><span class="kwd">static</span><span class="pln"> </span><span class="typ">float</span><span class="pln"> int_link_f_def </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5.3</span><span class="pun">;</span><span class="pln">

</span><span class="com">/*
وأحيرًا إليك تعاريف الدوال ضمن هذا الملف
*/</span><span class="pln">

</span><span class="com">/*
للدالة التالية ربط خارجي ويمكن استدعاؤها من أي مكان ضمن البرنامج
*/</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> f1</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> a</span><span class="pun">){}</span><span class="pln">

</span><span class="com">/*
يمكن استخدام الدالتين التاليتين باسمهما ضمن هذا الملف
*/</span><span class="pln">
</span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> local_function</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> a1</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> a2</span><span class="pun">){</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">(</span><span class="pln">a1 </span><span class="pun">*</span><span class="pln"> a2</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> lf</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">){</span><span class="pln">
        </span><span class="com">/*
    متغير ساكن عديم الربط، لذا يمكن استخدامه فقط ضمن هذه الدالة، 
وهو تعريف بحكم أنه عديم الربط
         */</span><span class="pln">
        </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> count</span><span class="pun">;</span><span class="pln">
        </span><span class="com">/*
    متغير تلقائي عديم الربط ولكنه ذو مُهيّأ بقيمة أولية
         */</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">

        printf</span><span class="pun">(</span><span class="str">"lf called for time no %d\n"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">++</span><span class="pln">count</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">/*
ضُمّنت التعاريف الفعلية لجميع التعاريف المبدئية المتبقية في نهاية الملف
*/</span></pre>

<p style="text-align: center;">
	[مثال 2]
</p>

<p>
	نقترح عليك قراءة الفقرات السابقة مجددًا لملاحظة القوانين التي طُبّقت في المثال 2.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter8/" rel="external nofollow">Specialized Areas of C</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/c/%D9%85%D8%B9%D8%B1%D9%81%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D9%88%D8%B9-typedef-%D9%88%D8%A7%D9%84%D9%85%D8%A4%D9%87%D9%84%D8%A7%D8%AA-qualifiers-%D9%88%D9%86%D9%82%D8%A7%D8%B7-%D8%A7%D9%84%D8%AA%D8%B3%D9%84%D8%B3%D9%84-sequence-points-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1763/" rel="">معرفات النوع typedef والمؤهلات qualifiers ونقاط التسلسل sequence points في لغة C</a>
	</li>
	<li>
		المقال السابق: ا<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-macro-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC-%D8%A7%D9%84%D9%85%D8%B3%D8%A8%D9%82-preprocessor-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1750/" rel="">لماكرو Macro والمعالج المسبق Preprocessor في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">التعامل مع المحارف والسلاسل النصية في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1749/" rel="">تهيئة المتغيرات وأنواع البيانات في لغة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1762</guid><pubDate>Sat, 05 Nov 2022 16:04:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x645;&#x627;&#x643;&#x631;&#x648; Macro &#x648;&#x627;&#x644;&#x645;&#x639;&#x627;&#x644;&#x62C; &#x627;&#x644;&#x645;&#x633;&#x628;&#x642; Preprocessor &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-macro-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC-%D8%A7%D9%84%D9%85%D8%B3%D8%A8%D9%82-preprocessor-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1750/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_10/6343195ae4d7d_--Macro---Preprocessor---C-.png.7182ed15f352448566749d3145dd076f.png" /></p>
<p>
	يتناول المقال مرحلة المعالجة المسبقة للشيفرة المصدرية بما فيها مراحل استبدال الماكرو ومختلف موجهات المعالج المسبق الأخرى.
</p>

<h2>
	أثر المعيار
</h2>

<p>
	ستشعر أن المعالج المُسبق Preprocessor لا ينتمي إلى لغة سي عمومًا، إذ لا يسمح لك وجوده بالتعامل بصورةٍ متكاملة مع اللغة كما أنك لا تستطيع الاستغناء عنه في ذات الوقت، وفي الحقيقة، كان استخدام المعالج المُسبق في أيام سي الأولى اختياريًا واعتاد الناس على كتابة برامج لغة سي C بدونه، ويمكننا أن ننظر إلى كونه جزءًا من لغة سي حاليًا صدفةً إلى حدٍ ما، إذ كان يعالج بعضًا من أوجه القصور في اللغة، مثل تعريف الثوابت وتضمين التعريفات القياسية، وأصبح نتيجةً لذلك جزءًا ضمن حزمة لغة سي ككُل.
</p>

<p>
	لم يكن هناك في تلك الفترة معيارٌ رسميٌ متفقٌ عليه يوحّد ما يفعله المعالج المسبق، وكانت إصدارات مختلفة منه مُطبّقة بصورةٍ مختلف على عدة أنظمة، وأصبحت عملية نقل البرنامج وتصديره إلى أنظمة أخرى مشكلةً كبيرة إذا استخدم ما يزيد عن الخصائص الأساسية للمعالج.
</p>

<p>
	كانت وظيفة المعيار الأساسية هنا هي تعريف سلوك المعالج المُسبق بما يتوافق مع الممارسات الشائعة، وقد سبق حصول ذلك مع لغة سي القديمة، إلا أن المعيار اتخذ إجراءات إضافية وسط الخلاف وحدد مجموعةً من الخصائص الإضافية التي قُدمت مع إصدارات المعالج المُسبق الأكثر شعبية، وعلى الرغم من فائدة هذه الخصائص إلا أن الخلاف كان بخصوص الاتفاق على طريقة تطبيقها. لم يكترث المعيار لمشكلة القابلية مع البرامج القديمة بالنظر إلى أن هذه البرامج تستخدم طرقًا غير قابلة للنقل في المقام الأول، وسيحسِّن تواجد هذه الخصائص المتقدمة ضمن المعيار سهولة نقل برامج لغة سي مستقبلًا بصورةٍ ملحوظة.
</p>

<p>
	يعدّ استخدام المعالج المسبق سهلًا إذا استُخدم لمهمته الأساسية البسيطة في جعل البرامج سهلة القراءة والصيانة، ولكن يُفضّل ترك خصائصه المتقدمة لاستخدام الخبراء. بحكم تجربتنا، يُعد استخدام <code>‎#‎‎‎define</code> ومجموعة تعليمات التصريف الشرطي conditional compilation (أوامر <code>‎#if</code>) مناسبًا للمبتدئين، وإذا ما زلت مبتدئً في لغة سي، فاقرأ هذه المقالة مرةً واحدة لمعرفة إمكانيات المعالج المُسبق واستخدم التمارين للتأكد من فهمك، وإلا فنحن ننصح بخبرة لا تقل عن ستة أشهر في لغة سي حتى تستطيع فهم إمكانيات المعالج المسبق كاملةً، لذا لن نركز على منحك مقدمة سهلة هنا بل سنركز على التفاصيل الدقيقة فورًا.
</p>

<h2>
	كيف يعمل المعالج المسبق؟
</h2>

<p>
	بالرغم من أن المعالج المسبق الموضح في الشكل التالي سينتهي به المطاف غالبًا بكونه جزءًا هامًا من مصرف لغة سي المعيارية، إلا أنه يمكننا التفكير به على أنه برنامجٌ منفصلٌ يحول شيفرة سي المصدرية التي تحتوي على موجهات المعالج المسبق إلى شيفرة مصدرية لا تحتوي على هذه الموجهات.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="109386" href="https://academy.hsoub.com/uploads/monthly_2022_10/012The_preprocessor.png.8a6233b56023e90d7d09ac4ba4917df6.png" rel="" data-fileext="png"><img alt="012The_preprocessor.png" class="ipsImage ipsImage_thumbnailed" data-fileid="109386" data-unique="zam3cfz1h" src="https://academy.hsoub.com/uploads/monthly_2022_10/012The_preprocessor.png.8a6233b56023e90d7d09ac4ba4917df6.png"></a>
</p>

<p style="text-align: center;">
	شكل 1: المعالج المسبق في لغة سي
</p>

<p>
	من المهم هنا أن نتذكر أن المعالج المسبق لا يعمل متبعًا القوانين الخاصة بشيفرة لغة سي ذاتها، وإنما يعمل على أساس كل سطرٍ بسطره، وهذا يعني أن نهاية السطر حدثٌ مميز وليس كما تنظر لغة سي إلى نهاية السطر بكونه مشابهًا لمحرف مسافة أو مسافة جدولة.
</p>

<p>
	لا يعي المعالج المسبق قوانين لغة سي الخاصة بالنطاق Scope، إذ تأخذ موجهات المعالج المسبق (مثل <code>‎#define</code>) تأثيرها فور رؤيتها ويبقى تأثيرها موجودًا حتى الوصول إلى نهاية الملف الذي يحتوي هذه الموجهات، ولا ينطبق هنا هيكل البرنامج المتعلق بالكتل البرمجية. من المحبّذ إذًا استخدام موجهات المعالج المسبق بأقل ما أمكن، فكلما قلّ عدد الأجزاء التي لا تتبع قوانين النطاق "الاعتيادية" كلّما قلت إمكانية ارتكاب الأخطاء، وهذا ما نقصده عندما نقول أن تكامل المعالج المسبق ولغة سي C محدودٌ فيما بينهما.
</p>

<p>
	يصف المعيار بعض القوانين المعقدة بخصوص كتابة موجهات المعالج المسبق، وبالأخص بالنسبة للمفاتيح Tokens، ويجب عليك معرفة القوانين كلها إذا أردت فهم موجهات المعالج المسبق، فالنص الذي يُعالج لا يعدّ سلسلةً من المحارف بل هو مُجزّأٌ إلى مفاتيح ومن ثم معلومات معالجة مُجزأة.
</p>

<p>
	من الأفضل اللجوء إلى المعيار إذا أردت تعريفًا كاملًا بالعملية، إلا أننا سنتطرق إلى شرح بسيط؛ إذ سنشرح كل جزء موجود في القائمة التالية لاحقًا.
</p>

<ol>
	<li>
		اسم ملف الترويسة
	</li>
</ol>

<ul style="margin-right: 40px;">
	<li>
		<code>&gt;</code> يمكن استخدام أي محرف هنا (باستثناء) <code>&lt;</code>.
	</li>
</ul>

<ol start="2">
	<li>
		مفتاح المعالج المسبق
	</li>
</ol>

<ul style="margin-right: 40px;">
	<li>
		اسم ملف الترويسة كما ذُكر سابقًا لكن فقط في حالة ذكره ضمن <code>‎#include</code>
	</li>
	<li>
		أو معرّف identifier مثل معرف لغة C أو كلمة مفتاحية
	</li>
	<li>
		أو ثابت وهو أي عدد صحيح أو طبيعي ثابت
	</li>
	<li>
		أو سلسلة نصية وهو سلسلة نصية سي اعتيادية
	</li>
	<li>
		أو عامل وهو من أحد عوامل لغة سي
	</li>
	<li>
		أو واحد من علامات الترقيم <code>[ ] ( ) { } * , : = ; ... #</code>
	</li>
	<li>
		أو أي محرف غير فارغ (محرف فارغ مثل محرف المسافة) غير مذكور في اللائحة أعلاه
	</li>
</ul>

<p>
	نقصد أي محرف (باستثناء) أي باستثناء المحرفين <code>&lt;</code> أو محرف السطر الجديد.
</p>

<h2>
	الموجهات Directives
</h2>

<p>
	تبدأ موجّهات المعالج المسبق بالمحرف "#" دائمًا، وتُتبع بمحرف مسافة فارغة اختياريًا إلا أن هذا الاستخدام غير شائع، ويوضح الجدول التالي الموجهات المعرّفة في المعيار.
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
	<thead>
		<tr>
			<th>
				الموجّه
			</th>
			<th>
				المعنى
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<code>‎# include</code>
			</td>
			<td>
				تضمين ملف مصدري
			</td>
		</tr>
		<tr>
			<td>
				<code>‎# define</code>
			</td>
			<td>
				تعريف ماكرو
			</td>
		</tr>
		<tr>
			<td>
				<code>‎# undef</code>
			</td>
			<td>
				التراجع عن تعريف ماكرو
			</td>
		</tr>
		<tr>
			<td>
				<code>‎# if</code>
			</td>
			<td>
				تصريف شرطي
			</td>
		</tr>
		<tr>
			<td>
				<code>‎# ifdef</code>
			</td>
			<td>
				تصريف شرطي
			</td>
		</tr>
		<tr>
			<td>
				<code>‎# ifndef</code>
			</td>
			<td>
				تصريف شرطي
			</td>
		</tr>
		<tr>
			<td>
				<code>‎# elif</code>
			</td>
			<td>
				تصريف شرطي
			</td>
		</tr>
		<tr>
			<td>
				<code>‎# else</code>
			</td>
			<td>
				تصريف شرطي
			</td>
		</tr>
		<tr>
			<td>
				<code>‎# endif</code>
			</td>
			<td>
				تصريف شرطي
			</td>
		</tr>
		<tr>
			<td>
				<code>‎# line</code>
			</td>
			<td>
				التحكم بتقارير الأخطاء
			</td>
		</tr>
		<tr>
			<td>
				<code>‎# error</code>
			</td>
			<td>
				عرض رسالة خطأ قسرية
			</td>
		</tr>
		<tr>
			<td>
				<code>‎# pragma</code>
			</td>
			<td>
				تُستخدم للتحكم المعتمد على التنفيذ
			</td>
		</tr>
		<tr>
			<td>
				<code>#</code>
			</td>
			<td>
				موجّه فارغ؛ دون تأثير
			</td>
		</tr>
	</tbody>
</table>

<p style="text-align: center;">
	جدول 1 موجّهات المعالج المُسبق
</p>

<p>
	سنشرح كل من معنى واستخدام الموجّهات بالتفصيل في الفقرات التالية. لاحظ أن المحرف <code>#</code> والكلمة المفتاحية التي تليه عنصرين مستقلين منفصلين، ويمكن إضافة مسافةٍ بيضاء بينهما.
</p>

<h3>
	الموجه الفارغ
</h3>

<p>
	هذا الموجّه بسيط، إذ ليس لإشارة <code>#</code> بمفردها على السطر أي تأثير.
</p>

<h3>
	موجه تعريف الماكرو ‎define
</h3>

<p>
	هناك طريقتان لتعريف الماكرو، أولهما تبدو مثل تابع والأخرى على النقيض، إليك مثالًا باستخدام الطريقتين:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_7" style=""><span class="com">#define</span><span class="pln"> FMAC</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln">b</span><span class="pun">)</span><span class="pln"> a here</span><span class="pun">,</span><span class="pln"> then b

</span><span class="com">#define</span><span class="pln"> NONFMAC some text here</span></pre>

<p>
	يعرّف التصريحان السابقان ماكرو ونصًا بديلًا له، إذ سيستخدم ليُستبدل بالماكرو المذكور ضمن كامل البرنامج، ويمكن استخدامهما بعد التصريح عنهما على النحو التالي مع ملاحظة أثر استبدال الماكرو الموضح في التعليقات:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_9" style=""><span class="pln">NONFMAC
</span><span class="com">/* النص هنا */</span><span class="pln">

FMAC</span><span class="pun">(</span><span class="pln">first text</span><span class="pun">,</span><span class="pln"> some more</span><span class="pun">)</span><span class="pln">
</span><span class="com">/* النص الأول، ومزيدٌ من النص */</span></pre>

<p>
	يُستبدل اسم الماكرو في الحالة التي لا يبدو فيها مثل دالة بالنص البديل ببساطة، وكذلك الأمر بالنسبة لماكرو من نوع دالة، وفي حال كان النص البديل يحتوي على معرّف يطابق اسم معامل من معاملات الماكرو، يُستخدم النص الموجود وسيطًا بدلًا من المعرِّف في النص البديل. يُحدَّد نطاق الأسماء المذكورة في وسطاء الماكرو بالكتلة التي تحتوي الموجه <code>‎#define</code>.
</p>

<p>
	تُهمل أي مسافات فارغة بعد أو قبل النص البديل ضمن تعريف الماكرو، وذلك لكلا الطريقتين على حد سواء.
</p>

<p>
	يتبادر إلى البعض السؤال الفضولي التالي: كيف يمكنني تعريف ماكرو بسيط بحيث يكون النص البديل الخاص به ينتهي بالقوس المفتوح ")"؟ الإجابة بسيطة، إذا احتوى تعريف الماكرو على مسافة أمام القوس ")"، فلن يحتسب الماكرو من نوع ماكرو دالة، بل نص بديل لماكرو بسيط فحسب، إلا أنه لا يوجد قيد مماثل عندما تستخدم الماكرو الشبيه بالدالة.
</p>

<p>
	يسمح المعيار للماكرو بغض النظر عن نوع أن يُعاد تعريفه في أي لحظة باستخدام موجّه <code>‎# define</code> آخر، وذلك بفرض عدم تغيير نوع الماكرو وأن تكون المفاتيح التي تشكل التعريف الأساسي وإعادة التعريف متماثلة بالعدد والترتيب والكتابة بما فيها استخدام المسافة الفارغة، وتُعد المسافات الفارغة في هذا السياق متساوية، وطبقًا لذلك فالتالي صحيح:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_11" style=""><span class="com"># define XXX abc/*تعليق*/def hij</span><span class="pln">
</span><span class="com"># define XXX abc def hij</span></pre>

<p>
	وذلك لأن التعليق شكلٌ من أشكال المسافات الفارغة، وسلسلة المفاتيح للحالتين السابقتين هي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_13" style=""><span class="com"># w-s define w-s XXX w-s abc w-s def w-s hij w-s</span></pre>

<p>
	إذ تعني <code>w-s</code> مفتاح مسافة بيضاء.
</p>

<h4>
	استبدال الماكرو
</h4>

<p>
	أين سيتسبب اسم الماكرو باستبدال النص بالنص البديل؟ يحدث الاستبدال عمليًا في أي مكان يحدث التعرف فيه على المعرّف identifier مثل مفتاح منفصل ضمن البرنامج، عدا المعرف المتبوع بالمحرف "#" الخاص بموجّه المعالج المُسبق. يمكنك كتابة التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_15" style=""><span class="com">#define</span><span class="pln"> define XXX

</span><span class="com">#define</span><span class="pln"> YYY ZZZ</span></pre>

<p>
	ومن المتوقع أن يتسبب استبدال سطر <code>‎#define</code> الثاني بالسطر <code>‎#xxx</code> بخطأ.
</p>

<p>
	يُستبدل المعرف المرتبط بماكرو بسيط عندما يُرى بمفتاح الماكرو البديل، ومن ثم يُعاد مسحه rescanned (سنتكلم عن ذلك لاحقًا) للعثور على أي استبدالات أخرى.
</p>

<p>
	يمكن استخدام ماكرو الدالة مثل أي دالة أخرى اعتيادية، وذلك بوضع مساحات فارغة حول اسم الماكرو ولائحة الوسطاء وغيره، كما قد يحتوي على محرف سطر جديد:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_17" style=""><span class="com">#define</span><span class="pln"> FMAC</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> printf</span><span class="pun">(</span><span class="str">"%s %s\n"</span><span class="pun">,</span><span class="pln"> a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln">

FMAC </span><span class="pun">(</span><span class="str">"hello"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"sailor"</span><span class="pln">
      </span><span class="pun">);</span><span class="pln">
</span><span class="com">/* ينتج ما سبق بالتالي */</span><span class="pln">
printf</span><span class="pun">(</span><span class="str">"%s %s\n"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"hello"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"sailor"</span><span class="pun">)</span></pre>

<p>
	يمكن أن تأخذ وسطاء الماكرو من نوع دالة أي تسلسل عشوائي للمفتاح، وتُستخدم الفاصلة "," لفصل الوسطاء عن بعضهم بعضًا، ولكن يمكن إخفاؤها بوضعها داخل أقواس "( )". توازن الأزواج المتطابقة من الأقواس داخل الوسيط بعضها بعضًا، وبالتالي ينهي القوس "(" استدعاء الماكرو إذا كان القوس ")" هو الذي بدأ باستدعائه.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_25" style=""><span class="com">#define</span><span class="pln"> CALL</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> a b

CALL</span><span class="pun">(</span><span class="pln">printf</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="str">"%d %d %s\n"</span><span class="pun">,</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">24</span><span class="pun">,</span><span class="pln"> </span><span class="str">"urgh"</span><span class="pun">));</span><span class="pln">
</span><span class="com">/* results in */</span><span class="pln">
printf </span><span class="pun">(</span><span class="str">"%d %d %s\n"</span><span class="pun">,</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">24</span><span class="pun">,</span><span class="pln"> </span><span class="str">"urgh"</span><span class="pun">);</span></pre>

<p>
	لاحظ كيف حافظنا على الأقواس حول الوسيط الثاني للدالة <code>CALL</code> عند الاستبدال، ولم تُزال من النص.
</p>

<p>
	إذا أردت استخدام ماكرو مثل <code>printt</code>، لن يساعدك المعيار بهذا الخصوص عندما تختار عدد متغير من الوسطاء، فذلك غير مدعوم.
</p>

<p>
	نحصل على سلوك غير معرف إذا لم يحتوي أحد الوسطاء على مفتاح معالج مسبق، والأمر مماثل إذا احتوت سلسلة مفاتيح المعالج المسبق التي تشكل الوسيط على موجّه معالج مسبق مغاير:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_27" style=""><span class="com">#define</span><span class="pln"> CALL</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> a b

</span><span class="com">/* كل حالة تنتج عن سلوك غير محدد */</span><span class="pln">
CALL</span><span class="pun">(,</span><span class="pln">hello</span><span class="pun">)</span><span class="pln">
CALL</span><span class="pun">(</span><span class="pln">xyz</span><span class="pun">,</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> abc def</span><span class="pun">)</span></pre>

<p>
	إلا أننا نعتقد برأينا أن الاستخدام الثاني الخاطئ للدالة <code>CALL</code> يجب أن ينتج بسلوك معرف، إذ أن أي أحد قادر على كتابة ذلك سيستفيد من انتباه المصرّف.
</p>

<p>
	تتبع معالجة ماكرو الدالة الخطوات التالية:
</p>

<ol>
	<li>
		جميع وسطائها معرفة.
	</li>
	<li>
		إن كان أي من المفاتيح ضمن الوسيط مرشح لاستبدال بواسطة ماكرو، فسيُستبدل حتى الوصول للنقطة التي لا يمكن فيها إجراء المزيد من الاستبدالات، باستثناء الحالات المذكورة في البند الثالث التالي. لا يوجد هناك أي خطر بخصوص امتلاك الماكرو لعدد مختلف من الوسطاء بعد إضافة فاصلة إلى قائمة الوسطاء الأساسية، إذ يُحدد الوسطاء في الخطوة السابقة <strong>فقط</strong>.
	</li>
	<li>
		تُستبدل المعرفات التي تسمّي وسيط الماكرو في نص الاستبدال بسلسلة مفتاح مثل وسيطٍ فعلي، ويُهمل الاستبدال إذا كان المعرف مسبوقًا بإشارة "#" أو اثنتين "##" أو متبوعًا بالإشارتين "##".
	</li>
</ol>

<h4>
	التنصيص
</h4>

<p>
	هناك طريقةٌ خاصة لمعالجة الأماكن التي يسبق فيها وسيط الماكرو الإشارة "#" في نص الماكرو البديل، إذ تًهمل أي مسافة فارغة تسبق أو تلي قائمة الوسطاء الفعلية للمفتاح، ومن ثم تُحوّل قائمة المفتاح والإشارة <code>#</code> إلى سلسلة نصية واحدة، وتُعامل المسافات بين المفاتيح كأنها محارف مسافة في سلسلة نصية؛ ولمنع حدوث أي نتائج مفاجئة، يُسبق أي محرف <code>"</code> أو <code>\</code> في السلسلة النصية الجديدة بالمحرف <code>\</code>.
</p>

<p>
	إليك المثال التالي الذي يوضح الخاصية المذكورة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_29" style=""><span class="com">#define</span><span class="pln"> MESSAGE</span><span class="pun">(</span><span class="pln">x</span><span class="pun">)</span><span class="pln"> printf</span><span class="pun">(</span><span class="str">"Message: %s\n"</span><span class="pun">,</span><span class="pln"> </span><span class="com">#x)</span><span class="pln">

MESSAGE </span><span class="pun">(</span><span class="typ">Text</span><span class="pln"> with </span><span class="str">"quotes"</span><span class="pun">);</span><span class="pln">
</span><span class="com">/*
* النتيجة هي
* printf("Message: %s\n", "Text with \"quotes\"");
*/</span></pre>

<h4>
	لصق المفتاح Token pasting
</h4>

<p>
	قد نجد العامل <code>##</code> في أي مكان ضمن النص البديل للماكرو باستثناء نهايته أو بدايته، وتُستخدم سلسلة مفتاح وسيط الماكرو لاستبدال النص البديل إذا ورد في اسم الوسيط لماكرو دالة مسبوقًا أو متبوعًا بأحد هذه العوامل، وتُدمج المفاتيح المحيطة بالعامل <code>##</code> سويًا سواءٌ كانت ضمن ماكرو دالة أو ماكرو بسيط، ونحصل على سلوك غير معرف إذا شكّل ذلك مفتاح غير صالح، ويُجرى إعادة مسح بعدها.
</p>

<p>
	إليك عملية تحصل على عدة مراحل يُستخدم فيها إعادة المسح لتوضيح لصق المفتاح:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_31" style=""><span class="com">#define</span><span class="pln"> REPLACE some replacement text
</span><span class="com">#define</span><span class="pln"> JOIN</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> a </span><span class="com">## b</span><span class="pln">

JOIN</span><span class="pun">(</span><span class="pln">REP</span><span class="pun">,</span><span class="pln"> LACE</span><span class="pun">)</span><span class="pln">
becomes</span><span class="pun">,</span><span class="pln"> after token pasting</span><span class="pun">,</span><span class="pln">
REPLACE
becomes</span><span class="pun">,</span><span class="pln"> after rescanning
some replacement text</span></pre>

<h4>
	إعادة المسح
</h4>

<p>
	يُمسح النص البديل مضافًا إلى مفاتيح الملف المصدري مجددًا حالما تحصل العملية الموضحة في الفقرة السابقة، وذلك للبحث عن أسماء ماكرو أخرى لاستبدالها، مع استثناء أن اسم الماكرو داخل النص البديل لا يُستبدل. من الممكن أن نضيف ماكرو متداخلة Nested macros وبالتالي يمكن لعدد من الماكرو أن تُعالج لاستبدالها في أي نقطة دفعةً واحدة، ولا يوجد في هذه الحالة أي اسم مرشح لاستبداله ضمن المستوى الداخلي لهذا التداخل، وهذا يسمح لنا بإعادة تعريف الدوال الموجودة سابقًا مثل ماكرو:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_33" style=""><span class="com">#define</span><span class="pln"> exit</span><span class="pun">(</span><span class="pln">x</span><span class="pun">)</span><span class="pln"> exit</span><span class="pun">((</span><span class="pln">x</span><span class="pun">)+</span><span class="lit">1</span><span class="pun">)</span></pre>

<p>
	تصبح أسماء الماكرو التي لم تُستبدل مفاتيحٌ محمية من الاستبدال مستقبلًا، حتى لو وردت أي عمليات تالية لتبديلها، وهذا يدرأ الخطر عن حدوث التعادوية recursion اللانهائية في المعالج المسبق، وتنطبق هذه الحماية فقط في حالة نتج اسم الماكرو عن النص البديل، وليس النص المصدري ضمن البرنامج، إليك ما الذي نقصده:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_35" style=""><span class="com">#define</span><span class="pln"> m</span><span class="pun">(</span><span class="pln">x</span><span class="pun">)</span><span class="pln"> m</span><span class="pun">((</span><span class="pln">x</span><span class="pun">)+</span><span class="lit">1</span><span class="pun">)</span><span class="pln">
</span><span class="com">/* هذا */</span><span class="pln">
m</span><span class="pun">(</span><span class="pln">abc</span><span class="pun">);</span><span class="pln">
</span><span class="com">/* ينتج عن هذا بعد الاستبدال */</span><span class="pln">
m</span><span class="pun">((</span><span class="pln">abc</span><span class="pun">)+</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
</span><span class="com">/*
* على الرغم من أن النتيجة السابقة تبدو مثل ماكرو
* إلا أن القواعد تنص على لزوم عدم استبداله
*/</span><span class="pln">

m</span><span class="pun">(</span><span class="pln">m</span><span class="pun">(</span><span class="pln">abc</span><span class="pun">));</span><span class="pln">
</span><span class="com">/*
* ‏تبدأ‫ m( الخارجية باستدعاء الماكرو,
* لكن تُستبدل الداخلية أولًا
* ‫لتصبح بالشكل m((abc)+1‪) 
* وتُستخدم مثل وسيط، مما يعطينا
*/</span><span class="pln">
m</span><span class="pun">(</span><span class="pln">m</span><span class="pun">((</span><span class="pln">abc</span><span class="pun">+</span><span class="lit">1</span><span class="pun">));</span><span class="pln">
</span><span class="com">/*
* ويصبح بعد الاستبدال على النحو التالي
*/</span><span class="pln">
m</span><span class="pun">((</span><span class="pln">m</span><span class="pun">((</span><span class="pln">abc</span><span class="pun">+</span><span class="lit">1</span><span class="pun">))+</span><span class="lit">1</span><span class="pun">);</span></pre>

<p>
	إذا لم يؤلمك دماغك بقراءة ما سبق، فاذهب واقرأ ما الذي يقوله المعيار عن هذا ونضمن لك أنه سيؤلمك.
</p>

<h4>
	ملاحظات
</h4>

<p>
	هناك مشكلة غير واضحة تحدث عند استخدام وسطاء ماكرو الدالة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_37" style=""><span class="com">/* تحذير: هناك مشكلة في هذا البرنامج */</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> SQR</span><span class="pun">(</span><span class="pln">x</span><span class="pun">)</span><span class="pln">  </span><span class="pun">(</span><span class="pln"> x </span><span class="pun">*</span><span class="pln"> x </span><span class="pun">)</span><span class="pln">
</span><span class="com">/*
* عند ورود المعاملات الصورية في النص البديل، تُستبدل بالمعاملات الفعلية للماكرو
*/</span><span class="pln">
printf</span><span class="pun">(</span><span class="str">"sqr of %d is %d\n"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> SQR</span><span class="pun">(</span><span class="lit">2</span><span class="pun">));</span></pre>

<p>
	المعامل الصوري formal parameter للماكرو <code>SQR</code> هو <code>x</code>، والمعامل الفعلي actual argument هو <code>2</code>، وبالتالي سينتج النص البديل عن:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_39" style=""><span class="pln">printf</span><span class="pun">(</span><span class="str">"sqr of %d is %d\n"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">));</span></pre>

<p>
	لاحظ استخدام الأقواس، فالمثال التالي قد يتسبب بمشكلة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_41" style=""><span class="com">/* مثال سيء */</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> DOUBLE</span><span class="pun">(</span><span class="pln">y</span><span class="pun">)</span><span class="pln"> y</span><span class="pun">+</span><span class="pln">y

printf</span><span class="pun">(</span><span class="str">"twice %d is %d\n"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> DOUBLE</span><span class="pun">(</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
printf</span><span class="pun">(</span><span class="str">"six times %d is %d\n"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">*</span><span class="pln">DOUBLE</span><span class="pun">(</span><span class="lit">2</span><span class="pun">));</span></pre>

<p>
	تكمن المشكلة في أن التعبير الأخير في استدعاء الدالة <code>printf</code> الثاني يُستبدل بالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_43" style=""><span class="lit">3</span><span class="pun">*</span><span class="lit">2</span><span class="pun">+</span><span class="lit">2</span></pre>

<p>
	وهذا ينتج عن <code>8</code> وليس <code>12</code>. تنص القاعدة على أنه يجب عليك الحرص بخصوص الأقواس فهي ضرورية في حالة استخدام الماكرو لبناء تعابير. إليك مثالًا آخر:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_45" style=""><span class="pln">SQR</span><span class="pun">(</span><span class="lit">3</span><span class="pun">+</span><span class="lit">4</span><span class="pun">)</span><span class="pln">

</span><span class="com">/* تصبح بالشكل التالي بعد الاستبدال */</span><span class="pln">

</span><span class="pun">(</span><span class="pln"> </span><span class="lit">3</span><span class="pun">+</span><span class="lit">4</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">3</span><span class="pun">+</span><span class="lit">4</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="com">/*  للأسف، ما زالت خاطئة!‏ */</span></pre>

<p>
	لذا، يجب عليك النظر بحرص إلى الوسطاء الصورية عندما ترِد ضمن نصل بديل. إليك الأمثلة الصحيحة عن الدالتين <code>SQR</code> و <code>DOUBLE</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_47" style=""><span class="com">#define</span><span class="pln"> SQR</span><span class="pun">(</span><span class="pln">x</span><span class="pun">)</span><span class="pln"> </span><span class="pun">((</span><span class="pln">x</span><span class="pun">)*(</span><span class="pln">x</span><span class="pun">))</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> DOUBLE</span><span class="pun">(</span><span class="pln">x</span><span class="pun">)</span><span class="pln"> </span><span class="pun">((</span><span class="pln">x</span><span class="pun">)+(</span><span class="pln">x</span><span class="pun">))</span></pre>

<p>
	في جعبة الماكرو حيلةٌ صغيرة بعد لمفاجئتك، كما سيوضح لك المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_49" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> DOUBLE</span><span class="pun">(</span><span class="pln">x</span><span class="pun">)</span><span class="pln"> </span><span class="pun">((</span><span class="pln">x</span><span class="pun">)+(</span><span class="pln">x</span><span class="pun">))</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> a</span><span class="pun">[</span><span class="lit">20</span><span class="pun">],</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ip</span><span class="pun">;</span><span class="pln">

      ip </span><span class="pun">=</span><span class="pln"> a</span><span class="pun">;</span><span class="pln">
      a</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
      a</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"%d\n"</span><span class="pun">,</span><span class="pln"> DOUBLE</span><span class="pun">(*</span><span class="pln">ip</span><span class="pun">++));</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	مثال 1
</p>

<p>
	لمَ يتسبب المثال السابق بمشاكل؟ لأن نص ماكرو البديل يشير إلى <code>‎*ip++‎</code> مرتين، مما يتسبب بزيادة <code>ip</code> مرتين، لا يجب للماكرو أن يُستخدم مع التعابير التي لها آثار جانبية، إلا إذا تحققت بحرص من أمانها.
</p>

<p>
	بغض النظر عن هذه التحذيرات التي تخص الماكرو، إلا أنها تقدم خصائص مفيدة، وستُستخدم هذه الخصائص كثيرًا من الآن فصاعدًا.
</p>

<h3>
	موجه التراجع عن تعريف ماكرو undef
</h3>

<p>
	يُمكن أن يُهمل (يُنتسى) أي معرّف يعود لموجه <code>‎#define</code> بكتابة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_51" style=""><span class="com">#undef</span><span class="pln">  NAME</span></pre>

<p>
	إذ لا يولد <code>‎#undef</code> خطأً إن لم يكن الاسم <code>NAME</code> معرفًا مسبقًا.
</p>

<p>
	سنستفيد من هذا الموجه كثيرًا عمّا قريب، وسنتكلم لاحقًا عن بعض دوال المكتبات التي هي في الحقيقة ماكرو وليس دالة، وستصبح قادرًا على الوصول إلى الدالة الفعلية عن طريق التراجع عن تعريفها.
</p>

<h3>
	موجه تضمين ملف مصدري include
</h3>

<p>
	يمكن كتابة هذا الموجه بشكلين:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_53" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;filename&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">"filename"</span></pre>

<p>
	ينجم عن استخدام أحد الطريقتين قراءة ملف جديد عند نقطة ذكر الموجّه، وكأننا استبدالنا سطر الموجه بمحتويات الملف المذكور، وإذا احتوى هذا الملف على بعض التعليمات الخاطئة ستظهر لك الأخطاء مع إشارتها إلى الملف التي نجمت عنه مصحوبةً برقم السطر، وهذه مهمة مطوّر المصرف، وينص المعيار على أنه يجب للمصرف دعم ثمانية طبقات من موجّهات <code>‎# include</code> المتداخلة على الأقل.
</p>

<p>
	يمكن الاختلاف بين استخدام <code>&lt;&gt;</code> و <code>" "</code> حول اسم الملف بالمكان الذي سيُبحث فيه عن الملف؛ إذ يتسبب استخدام الأقواس في البحث في عددٍ من الأماكن المعرّفة بحسب التطبيق؛ بينما يتسبب استخدام علامتي التنصيص بحثًا في المكان المرتبط بمكان ملف الشيفرة المصدرية، وستُعلمك ملاحظات تطبيقك بما هو المقصود بكلمة "المكان" والتفاصيل المرتبطة بها، إذا لم تعود عملية البحث عن الملف باستخدام علامتي التنصيص بأي نتيجة، تُعاود عملية البحث من جديد وكأنك استخدمت القوسين.
</p>

<p>
	تُستخدم الأقواس عمومًا عندما تريد تحديد ملفات ترويسة لمكتبة قياسية Standard library، بينما تُستخدم علامتي التنصيص لملفات الترويسة الخاصة بك، التي تكون مخصصة غالبًا لبرنامج واحد.
</p>

<p>
	لا يحدد المعيار كيفية تسمية ملف بصورةٍ صالحة، إلا أنه يحدد وجوب وجود طريقة فريدة معرفة بحسب التطبيق لترجمة اسم الملفات من الشكل <code>xxx.x</code> (يمثّل كل <code>x</code> حرفًا)، إلى أسماء ملفات الشيفرة المصدرية، ويمكن تجاهل الفرق بين الأحرف الكبيرة والصغيرة من قِبل التطبيق، ويمكن أن يختار التطبيق أيضًا ستة محارف ذات أهمية فقط بعد محرف النقطة <code>.</code>.
</p>

<p>
	يمكنك أيضًا الكتابة بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_55" style=""><span class="com"># define NAME &lt;stdio.h&gt;</span><span class="pln">
</span><span class="com"># include NAME</span></pre>

<p>
	للحصول على نتيجة مماثلة لهذا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_57" style=""><span class="com"># include &lt;stdio.h&gt;</span></pre>

<p>
	إلا أن هذه الطريقة تعقيدٌ لا داعي له، وهي معرضةٌ لبعض الأخطاء طبقًا للقواعد المعرفة بحسب التطبيق إذ تحدد هذه القواعد كيف سيُعالج النص بين القوسين <code>&lt; &gt;</code>.
</p>

<p>
	من الأبسط أن يكون النص البديل للماكرو <code>NAME</code> <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">سلسلةً نصية</a>، على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_59" style=""><span class="com">#define</span><span class="pln"> NAME </span><span class="str">"stdio.h"</span><span class="pln">

</span><span class="com">#include</span><span class="pln"> NAME</span></pre>

<p>
	لا يوجد في حالتنا السابقة أي فرصة للأخطاء الناتجة عن التصرف المعرف بحسب التطبيق، إلا أن مسارات البحث مختلفة كما وضحنا سابقًا. سلسلة المفتاح في حالتنا الأولى التي تستبدِل <code>NAME</code> هي على النحو التالي (بحسب القوانين التي ناقشناها سابقًا):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_63" style=""><span class="pun">&lt;</span><span class="pln">
stdio
</span><span class="pun">.</span><span class="pln">
h
</span><span class="pun">&gt;</span></pre>

<p>
	أما في الحالة الثانية فهي من الشكل:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_67" style=""><span class="str">"stdio.h"</span></pre>

<p>
	الحالة الثانية سهلة الفهم، لأنه يوجد لدينا فقط سلسلة نصية وهي مفتاح تقليدي لموجّه <code>‎# include</code>، بينما الحالة الثانية معرفةٌ بحسب التطبيق، وبالتالي يعتمد تشكيل سلسلة المفاتيح لاسم ترويسة صالح على التطبيق.
</p>

<p>
	أخيرًا، المحرف الأخير من الملف المُضمّن داخل موجه <code>include</code> يجب أن يكون سطرًا جديدًا، وإلا سنحصل على خطأ.
</p>

<h2>
	الأسماء مسبقة التعريف
</h2>

<p>
	الأسماء التالية هي أسماء مسبقة التعريف predefined names داخل المعالج المُسبق:
</p>

<ul>
	<li>
		الاسم <code>__LINE__</code>: ثابت عدد صحيح بالنظام العشري، ويشير إلى السطر الحالي ضمن ملف الشيفرة المصدرية.
	</li>
	<li>
		الاسم <code>__FILE__</code>: اسم ملف الشيفرة المصدرية الحالي، وهو سلسلة نصية.
	</li>
	<li>
		الاسم <code>__DATE__</code>: التاريخ الحالي، وهو سلسلة نصية من الشكل:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_69" style=""><span class="typ">Apr</span><span class="pln"> </span><span class="lit">21</span><span class="pln"> </span><span class="lit">1990</span></pre>

<p>
	إذ يظهر اسم الشهر كما هو معرّف في الدالة المكتبية <code>asctime</code> وأول خانة من التاريخ مسافة فارغة إذان كان التاريخ أقل من 10.
</p>

<ul>
	<li>
		الاسم <code>__TIME__</code>: وقت ترجمة الملف، وهو سلسلة نصية موافقة للشكل السابق باستخدام الدالة <code>asctime</code>، أي من الشكل "hh:mm:ss".
	</li>
	<li>
		الاسم <code>__STDC__</code>: عدد صحيح ثابت بقيمة 1، ويُستخدم لاختبار اتباع المصرّف لضوابط المعيار، إذ يمتلك هذا العدد قيمًا مختلفة لإصدارات مختلفة من المعيار.
	</li>
</ul>

<p>
	الطريقة الشائعة في استخدام هذه الأسماء المعرفة مسبقًا هي على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_71" style=""><span class="com">#define</span><span class="pln"> TEST</span><span class="pun">(</span><span class="pln">x</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">if</span><span class="pun">(!(</span><span class="pln">x</span><span class="pun">))</span><span class="pln">\
      printf</span><span class="pun">(</span><span class="str">"test failed, line %d file %s\n"</span><span class="pun">,</span><span class="pln">\
              __LINE__</span><span class="pun">,</span><span class="pln"> __FILE__</span><span class="pun">)</span><span class="pln">

</span><span class="com">/**/</span><span class="pln">

TEST</span><span class="pun">(</span><span class="pln">a </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">23</span><span class="pun">);</span><span class="pln">

</span><span class="com">/**/</span></pre>

<p style="text-align: center;">
	مثال 2
</p>

<p>
	إذا كانت نتيجة <code>TEST</code> في المثال السابق خطأ، فستُطبع الرسالة متضمنةً اسم الملف ورقم السطر في الرسالة. إلا أن استخدام تعليمة <code>if</code> في هذه الحالات قد يتسبب ببعض من اللبس، كما يوضح المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_73" style=""><span class="kwd">if</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">)</span><span class="pln">
      TEST</span><span class="pun">(</span><span class="pln">expr2</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">else</span><span class="pln">
      statement_n</span><span class="pun">;</span></pre>

<p>
	إذ سترتبط تعليمة <code>else</code> بتعليمة <code>if</code> المخفية التي ستُستبدل باستخدام الماكرو <code>TEST</code>، وعلى الرغم من أن حدوث هذا الشيء عند الممارسة مستبعد إلا أنه سيكون خطأً لعينًا صعب الحل والتشخيص إذا حدث لك، ولتفادي ذلك، يُحبّذ استخدام الأقواس وجعل محتوى كل تعليمة تحكم بتدفق البرنامج مثل تعليمة مركّبة بغض النظر عن طولها.
</p>

<p>
	لا يمكننا استعمال أي من الأسماء <code>__LINE__</code> أو <code>__FILE__</code> أو <code>__DATE__</code> أو <code>__TIME__</code> أو <code>__STDC__</code> أو أي من الأسماء المعرفة الأخرى ضمن موجه <code>‎#define</code> أو <code>‎#undef</code>.
</p>

<p>
	ينص المعيار على أن أي اسم محجوز يجب أن يبدأ بشرطةٍ سفلية underscore وحرفٍ كبير، أو شرطتان، وبالتالي يمكنك استخدام أي اسم لاستخدامك الخاص، لكن انتبه من استخدام الأسماء المحجوزة في ملفات الترويسة التي ضمّنتها في برنامجك.
</p>

<h3>
	موجه التحكم بتقارير الأخطاء ‎ line
</h3>

<p>
	يُستخدم هذا الموجه في ضبط القيمة التي يحملها كل من <code>__LINE__</code> و <code>__FILE__</code>، لكن ما المُستفاد من ذلك؟ توّلد العديد من الأدوات في الوقت الحالي شيفرةً بلغة سي C خرجًا لها، ويسمح هذا الموجه لهذه الأدوات بالتحكم برقم السطر الحالي إلا أن استخدامه محدودٌ لمبرمج لغة سي الاعتيادية.
</p>

<p>
	يأتي الموجه بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_75" style=""><span class="com"># line number optional-string-literal newline</span></pre>

<p>
	يضبط الرقم number قيمة <code>__LINE__</code> وتضبط السلسلة النصية الاختيارية إن وُجدت قيمة <code>__FILE__</code>. في الحقيقة، ستُوسّع سلسلة المفاتيح التي تتبع الموجه <code>‎#line</code> باستخدام ماكرو، ومن المفترض أن تشكّل موجهًا صالحًا بعد التوسعة.
</p>

<h3>
	التصريف الشرطي
</h3>

<p>
	يتحكم بالتصريف الشرطي عدد من الموجهات، إذ تسمح هذه الموجهات بتصريف أجزاء معينة من البرنامج اختياريًا أو تجاهلها حسب الشروط، وهذه الشروط هي: <code>‎#if</code> و <code>‎#ifdef</code> و <code>‎#ifndef</code> و <code>‎#elif</code> و <code>‎#else</code> و <code>‎#endif</code> إضافةً إلى عامل المُعالج المسبق الأحادي.
</p>

<p>
	يمكننا استخدام الموجهات على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_77" style=""><span class="com">#ifdef</span><span class="pln">  NAME
</span><span class="com">/* صرّف هذا القسم إذا كان الاسم معرّفًا */</span><span class="pln">
</span><span class="com">#endif</span><span class="pln">
</span><span class="com">#ifndef</span><span class="pln"> NAME
</span><span class="com">/* صرّف هذا القسم إذا كان الاسم غير معرّفًا */</span><span class="pln">
</span><span class="com">#else</span><span class="pln">
</span><span class="com">/* صرّف هذا القسم إذا كان الاسم معرّفًا */</span><span class="pln">
</span><span class="com">#endif</span></pre>

<p>
	يُستخدم كل من <code>‎#ifdef</code> و <code>‎#endif</code> لاختبار تعريف اسم الماكرو، ويمكن استخدام <code>‎#else</code> طبعًا مع <code>‎#ifdef</code> (و <code>‎#if</code> أو <code>‎#elif</code> أيضًا)، لا يوجد التباس حول استخدام <code>‎#else</code>، لأن استخدام <code>‎#endif</code> يحدّد نطاق الموجّه مما يبعد أي مجال للشبهات. ينص المعيار على وجوب دعم ثمان طبقات من الموجهات الشرطية المتداخلة، إلا أنه من المستبعد وجود أي حدّ عمليًّا.
</p>

<p>
	تُستخدم هذه الموجهات لتحديد فقرات صغيرة من برامج سي التي تعتمد على الآلة التي تعمل عليها (عندما لا يكون بالإمكان جعل كامل البرنامج ذا أداء مستقل غير مرتبط بطبيعة الآلة التي يعمل عليها)، أو لتحديد خوارزميات مختلفة تعتمد على بعض المقايضات لتنفيذها.
</p>

<p>
	تأخذ بنية <code>‎#if</code> و <code>‎#elif</code> تعبيرًا ثابتًا وحيدًا ذا قيمةٍ صحيحة، وقيم المعالج المسبق هذه مماثلةٌ للقيم الاعتيادية باستثناء أنها يجب أن تخلو من عوامل تحويل الأنواع casts. تخضع سلسلة المفتاح التي تشكّل التعبير الثابت لعملية استبدال بالماكرو، عدا الأسماء المسبوقة بموجه التعريف فلا تُستبدل. بالاعتماد على ما سبق ذكره، فالتعبير <code>defined NAME</code> أو <code>defined ( NAME )‎</code> يُقيّمان إلى القيمة <code>1</code> إذا كان <code>NAME</code> معرّفًا، وإلى القيمة <code>0</code> إن لم يكن معرّفًا، وتُستبدل جميع المعرفات ضمن التعبير -بما فيها كلمات لغة سي المفتاحية- بالقيمة <code>0</code>، ومن ثم يُقيّم التعبير. يُقصد بالاستبدال (بما فيه استبدال الكلمات المفتاحية) أن <code>sizeof</code> لا يمكن استخدامه في هذه التعابير للحصول على القيمة التي تحصل عليها في الحالة الاعتيادية.
</p>

<p>
	تُستخدم القيمة الصفر -كما هو الحال في تعليمات سي الشرطية- للدلالة على القيمة "خطأ false"، وتدل أي قيمة أخرى على القيمة "صواب true".
</p>

<p>
	يجب استخدام المعالج المسبق العمليات الحسابية وفق النطاقات المحددة في ملف الترويسة <code>&lt;limits.h&gt;</code>، وأن تُعامل التعابير ذات قيمة العدد الصحيح مثل عدد صحيح طويل والعدد الصحيح عديم الإشارة مثل عدد صحيح طويل عديم الإشارة، بينما لا يتوجب على المحارف أن تكون مساوية إلى القيمة ذاتها عند وقت التنفيذ، لذا من الأفضل في البرامج القابلة للنقل أن نتجنب استخدامها في تعابير المعالج المُسبق. تعني القوانين السابقة عمومًا أنه بإمكاننا أن نحصل على نتائج حسابية من المعالج المسبق التي قد تكون مختلفة عن النتائج التي نحصل عليها عند وقت التنفيذ، وذلك بفرض إجراء الترجمة والتنفيذ على آلات مختلفة. إليك مثالًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_79" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;limits.h&gt;</span><span class="pln">

</span><span class="com">#if ULONG_MAX+1 != 0</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"Preprocessor: ULONG_MAX+1 != 0\n"</span><span class="pun">);</span><span class="pln">
</span><span class="com">#endif</span><span class="pln">

      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">ULONG_MAX</span><span class="pun">+</span><span class="lit">1</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"Runtime: ULONG_MAX+1 != 0\n"</span><span class="pun">);</span></pre>

<p style="text-align: center;">
	مثال 3
</p>

<p>
	من الممكن أن يُجري المعالج المسبق بعض العمليات الحسابية بنطاق أكبر من النطاق المُستخدم في البيئة المُستهدفة، ويمكن في هذه الحالة ألّا يطفح تعبير المعالج المسبق <code>ULONG MAX+1</code> ليعطي النتيجة <code>0</code>، بينما يجب أن يحدث لك في بيئة التنفيذ.
</p>

<p>
	يوضح المثال التالي استخدام الثوابت المذكور آنفًا، وتعليمة "وإلّا else" الشرطية <code>‎#elif</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_81" style=""><span class="com">#define</span><span class="pln"> NAME    </span><span class="lit">100</span><span class="pln">

</span><span class="com">#if     ((NAME &gt; 50) &amp;&amp; (defined __STDC__))</span><span class="pln">
</span><span class="com">/* افعل شيئًا */</span><span class="pln">
</span><span class="com">#elif</span><span class="pln">   NAME </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">25</span><span class="pln">
</span><span class="com">/* افعل شيئًا آخر */</span><span class="pln">
</span><span class="com">#elif</span><span class="pln">   NAME </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">10</span><span class="pln">
</span><span class="com">/* افعل شيئًا آخر */</span><span class="pln">
</span><span class="com">#else</span><span class="pln">
</span><span class="com">/* الاحتمال الأخير */</span><span class="pln">
</span><span class="com">#endif</span></pre>

<p>
	يتوجب التنويه هنا على أن موجهات التصريف الشرطية لا تتبع لقوانين النطاق الخاصة بلغة سي، ولهذا فيجب استخدامها بحرص، إلا إذا أردت أن يصبح برنامجك صعب القراءة، إذ من الصعب أن تقرأ برنامج سي C مع وجود هذه الأشياء كل بضعة أسطر، وسيتملكك الغضب تجاه كاتب البرنامج إذا صادفت شيفرة مشابهة لهذه دون أي موجه <code>‎#if</code> واضح بالقرب منها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_83" style=""><span class="com">#else</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
</span><span class="com">#endif</span></pre>

<p>
	لذلك يجب أن تعامل هذه الموجهات معاملة الصلصة الحارّة، استخدامها ضروري في بعض الأحيان، إلا أن الاستخدام الزائد لها سيعقّد الأمور.
</p>

<h3>
	موجه التحكم المعتمد على التنفيذ pragma
</h3>

<p>
	كان هذا الموجه بمثابة محاولة للجنة المعيار بإضافة طريقة للولوج للباب الخلفي، إذ يسمح هذا الموجه بتنفيذ بعض الأشياء المعرفة بحسب التطبيق. إذا لم يعرف التطبيق ما الذي سيفعله بهذا الموجه (أي لم يتعرف على وظيفته) فيستجاهله ببساطة، إليك مثالًا عن استخدامه:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_85" style=""><span class="com">#pragma</span><span class="pln"> byte_align</span></pre>

<p>
	يُستخدم الموجه السابق لإعلام التطبيق بضرورة محاذاة جميع أعضاء الهياكل بالنسبة لعنوان البايت الخاص بهم، إذ يمكن لبعض معماريات المعالجات أن تتعامل مع أعضاء الهياكل بطول الكلمة بمحاذاة عنوان البايت، ولكن مع خسارة السرعة في الوصول إليها.
</p>

<p>
	يمكن أن يُفسّر هذا الأمر بحسب تفسير التطبيق له طبعًا، وإن لم يكن للتطبيق أي تفسير خاص به، فلن يأخذ الموجه أي تأثير، ولن يُعد خطأً، ومن المثير للاهتمام رؤية بعض الاستخدامات الخاصة لهذا النوع من الموجهات.
</p>

<h3>
	موجه عرض رسالة خطأ قسرية ‎ error
</h3>

<p>
	يُتبع هذا الموجه بمفتاح أو أكثر في نهاية السطر الخاص به، وتُشكّل رسالة تشخيصية من قبل المصرف تحتوي على هذه المفاتيح، ولا توجد مزيدٌ من التفاصيل عن ذلك في المعيار. يُمكن استخدام هذا الموجه لإيقاف عملية التصريف على آلة ذات عتاد صلب غير مناسب لتنفيذ البرنامج:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1377_87" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;limits.h&gt;</span><span class="pln">
</span><span class="com">#if CHAR_MIN &gt; -128</span><span class="pln">
</span><span class="com">#error</span><span class="pln"> character range smaller than required
</span><span class="com">#endif</span></pre>

<p>
	سيتسبب ذلك ببعض الأخطاء المتوقعة عند مرحلة التصريف إذا نُفّذ الموجه وعُرضت الرسالة.
</p>

<h2>
	الخاتمة
</h2>

<p>
	على الرغم من الخصائص القوية والمرنة التي يوفرها المعالج المسبق، إلا أنها شديدة التعقيد، وهناك عددٌ قليل من الجوانب المتعلقة به ضرورية الفهم، مثل القدرة على تعريف الماكرو ودوال الماكرو، والمستخدمة بكثرة في أي برنامج سي تقريبًا عدا البرامج البسيطة، كما أن تضمين ملفات الترويسة مهمّ أيضًا طبعًا.
</p>

<p>
	للتصريف الشرطي استخدامان مهمان، أولهما هو القدرة على تصريف البرنامج بوجود أو بعدم وجود تعليمات الاكتشاف عن الأخطاء Debugging ضمن البرنامج، وثانيهما هو القدرة على تحديد تعليمات معينة يعتمد تنفيذها على طبيعة التطبيق أو الآلة التي يعمل عليها البرنامج.
</p>

<p>
	باستثناء ما ذكرنا سابقًا، من الممكن نسيان المزايا الأخرى التي ذُكرت في المقال، إذ لن يتسبب التخلي عنها بفقدان كثيرٍ من الخصائص، ولعلّ الحل الأمثل هو تواجد فرد واحد متخصّص في المعالج المسبق ضمن الفريق البرمجي لتطوير الماكرو التي تفيد مشروعًا ما بعينه باستخدام بعض المزايا الغريبة، مثل التنصيص ولصق المفاتيح. سيستفيد معظم مستخدمي لغة سي C أكثر إذا وجّهوا جهدهم بدلًا من ذلك نحو تعلم بعض الأجزاء الأخرى من لغة سي، أو تقنيات التحكم بجودة البرمجيات إذا أتقنوا اللغة.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter7/" rel="external nofollow">Structured Data Types</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B5%D8%A7%D8%B1%D9%8A%D8%AD-declarations-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%B1%D9%8A%D9%81-definitions-%D9%88%D8%A5%D9%85%D9%83%D8%A7%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-accessibility-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1762/" rel="">التصاريح declarations والتعاريف definitions وإمكانية الوصول accessibility في لغة سي C</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/c/%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1749/" rel="">تهيئة المتغيرات وأنواع البيانات في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A8%D9%86%D9%8A%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1607/" rel="">بنية برنامج لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%B9%D9%88%D8%A7%D9%85%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1613/" rel="">العوامل في لغة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1750</guid><pubDate>Sat, 29 Oct 2022 16:07:00 +0000</pubDate></item><item><title>&#x62A;&#x647;&#x64A;&#x626;&#x629; &#x627;&#x644;&#x645;&#x62A;&#x63A;&#x64A;&#x631;&#x627;&#x62A; &#x648;&#x623;&#x646;&#x648;&#x627;&#x639; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1749/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_10/6343147d56f16_--Initialization---C----------.png.c2867c7a86cbb3e9930380c949fad38c.png" /></p>
<p>
	حان الوقت للتكلم عن التهيئة Initialization في لغة سي بعد أن تكلمنا عن جميع أنواع البيانات المدعومة في اللغة، إذ تسمح لغة سي للمتغيرات الاعتيادية والهياكل و<a href="https://academy.hsoub.com/programming/c/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%AA%D8%AD%D8%A7%D8%AF%D8%A7%D8%AA-unions-%D9%88%D8%AD%D9%82%D9%88%D9%84-%D8%A7%D9%84%D8%A8%D8%AA%D8%A7%D8%AA-bitfields-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%AF%D8%AF%D8%A7%D8%AA-eums-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1748/" rel="">الاتحادات</a> و<a href="https://academy.hsoub.com/programming/c/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1675/" rel="">المصفوفات</a> بأن تحمل قيمةً أوليةً عند التعريف عنها، وكان للغة سي القديمة بعض القوانين الغريبة بهذا الخصوص التي تعكس تقاعس مبرمجي مصرّفات سي عن إنجاز بعض العمل الإضافي، وأتت لغة C المعيارية لحل هذه المشكلات وأصبح من الممكن الآن تهيئة الأشياء عندما تريد وكيفما تريد.
</p>

<h2>
	أنواع التهيئة في لغة سي
</h2>

<p>
	هناك نوعان من التهيئة؛ تهيئة عند وقت التصريف compile time وتهيئة عند وقت التشغيل run time، ويعتمد النوع الذي ستحصل عليه على <strong>مدة التخزين storage duration</strong> للشيء الذي يُهيّأ.
</p>

<p>
	يُصرح عن الكائنات ذات <strong>المدة الساكنة static duration</strong> إما خارج الدوال، أو داخلها باستخدام الكلمة المفتاحية <code>extern</code> أو <code>static</code> على أنها جزءٌ من التصريح، ويُهيّأ هذا النوع عند وقت التصريف <strong>فقط</strong>.
</p>

<p>
	لجميع الكائنات الأخرى <strong>مدةٌ تلقائية automatic duration</strong>، يمكن تهيئتها <strong>فقط</strong> عند وقت التشغيل، إذ أن التصنيفين حصريان فيما بينهما.
</p>

<p>
	على الرغم من ارتباط مدة التخزين بالربط (انظر مقال <a href="https://academy.hsoub.com/programming/c/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D9%86%D8%B7%D8%A7%D9%82-scope-%D9%88%D8%A7%D9%84%D8%B1%D8%A8%D8%B7-linkage-%D8%B9%D9%84%D9%89-%D9%85%D8%B3%D8%AA%D9%88%D9%89-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1674/" rel="">الربط</a>) إلا أنهما مختلفان ويجب عدم الخلط فيما بينهما.
</p>

<p>
	يمكن استخدام التهيئة عند وقت التصريف فقط في حال استخدام التعابير الثابتة constant expressions، بينما يمكن استخدام التهيئة عند وقت التشغيل لأي تعبير كان، وقد أُلغي قيد لغة سي القديمة الذي ينص على إمكانية تهيئة المتغيرات البسيطة وحدها عند وقت التشغيل، وليس المصفوفات، أو الهياكل، أو الاتحادات.
</p>

<h2>
	التعابير الثابتة
</h2>

<p>
	هناك بعض الاستخدامات الضرورية للتعابير الثابتة، ويُعد تعريف التعبير الثابت بسيط الفهم، إذ يُقيّم <strong>التعبير الثابت constant expression</strong> عند وقت التصريف وليس عند وقت التشغيل، ويمكن استخدام هذا النوع من التعابير في أي مكان يسمح باستخدام قيمة ثابتة. يُشترط على التعبير الثابت ألا يحتوي على أي عامل إسناد أو زيادة أو نقصان أو استدعاء لدالة ما أو عامل الفاصلة، إلا إذا كان التعبير جزءًا من معامل <code>sizeof</code>، وقد يبدو هذا الشرط غريبًا بعض الشيء، إلا أنه بسبب أن العامل <code>sizeof</code> يُقيّم نوع التعبير وليس قيمته.
</p>

<p>
	يؤكد المعيار على وجوب تقييم الأعداد الحقيقية عند وقت التصريف بدقة ونطاق مماثلين لحالة تقييمهم في وقت التشغيل. يوجد هناك طريقةٌ محدودة أكثر تدعى <strong>تعابير الأعداد الصحيحة الثابتة integral constant expressions</strong>، ولهذه التعابير نوع عدد صحيح وتحتوي على معاملات operands من نوع عدد صحيح ثابت أو معدّدات ثابتة enumeration constants، أو محارف ثابتة، بالإضافة إلى تعابير <code>sizeof</code> والأعداد الحقيقية الثابتة التي تكون معاملات لتحويل الأنواع casts، ويسمح لعوامل تحويل الأنواع بتحويل الأنواع الحسابية إلى أنواع صحيحة فقط. لا تُطبّق أي قيود على محتويات تعابير <code>sizeof</code> طبقًا لما سبق قوله (يُقيّم نوع التعبير وليس قيمته).
</p>

<p>
	يُشابه التعبير الحسابي الثابت arithmetic constant expression التعبير الصحيح الثابت، ولكنه يسمح باستخدام الأعداد الصحيحة الثابتة، ويحدّ من استخدام تحويل الأنواع بالتحويل من نوع حسابي إلى آخر.
</p>

<p>
	<strong>العنوان الثابت address constant</strong> هو مؤشر يشير إلى كائن ذي مدة تخزين ساكنة أو إلى مؤشر يشير إلى دالةٍ ما، ويمكنك الحصول على هذه العناوين باستخدام العامل "&amp;" أو باستخدام التحويلات الاعتيادية للمصفوفات وأسماء الدوال إلى مؤشرات عندما تُستخدم ضمن تعبير، ويمكن استخدام كلٍ من العوامل "[]" و "." و "&lt;-" و "&amp;" و "*" ضمن التعبير طالما لا يتضمن ذلك الاستخدام محاولة للوصول لقيمة أي كائن.
</p>

<h2>
	استكمال عن التهيئة
</h2>

<p>
	يُسمح لأنواع الثوابت المختلفة بالتواجد في العديد من الأماكن، إلا أن تعابير الأعداد الصحيحة الثابتة شديدة الأهمية على وجه الخصوص لأنها من النوع الوحيد الذي قد يُستخدم لتحديد حجم المصفوفة وقيم ما يسبق تعليمة <code>case</code>. تتميز أنواع الثوابت المسموح باستخدامها مثل قيمة أولية لتهيئة التعابير بأنها أقل محدودية، إذ من المسموح لك باستخدام تعابير حسابية ثابتة، أو مؤشر فراغ، أو عنوان ثابت، أو عنوان ثابت لكائن زائد أو ناقص تعبير صحيح ثابت، ويعتمد الأمر طبعًا على نوع الشيء الذي يجري تهيئته سواءٌ كان نوع تعبير ثابت محدد مناسبًا أم لا.
</p>

<p>
	إليك مثالًا يحتوي على تهيئة لعدة متغيرات:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5753_9" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="com">#define</span><span class="pln"> NMONTHS </span><span class="lit">12</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> month </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">short</span><span class="pln"> month_days</span><span class="pun">[]</span><span class="pln"> </span><span class="pun">=</span><span class="pln">
      </span><span class="pun">{</span><span class="lit">31</span><span class="pun">,</span><span class="lit">28</span><span class="pun">,</span><span class="lit">31</span><span class="pun">,</span><span class="lit">30</span><span class="pun">,</span><span class="lit">31</span><span class="pun">,</span><span class="lit">30</span><span class="pun">,</span><span class="lit">31</span><span class="pun">,</span><span class="lit">31</span><span class="pun">,</span><span class="lit">30</span><span class="pun">,</span><span class="lit">31</span><span class="pun">,</span><span class="lit">30</span><span class="pun">,</span><span class="lit">31</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">mnames</span><span class="pun">[]</span><span class="pln"> </span><span class="pun">={</span><span class="pln">
      </span><span class="str">"January"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"February"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"March"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"April"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"May"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"June"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"July"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"August"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"September"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"October"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"November"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"December"</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">

      </span><span class="typ">int</span><span class="pln"> day_count </span><span class="pun">=</span><span class="pln"> month</span><span class="pun">;</span><span class="pln">

      </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">day_count </span><span class="pun">=</span><span class="pln"> month</span><span class="pun">;</span><span class="pln"> day_count </span><span class="pun">&lt;</span><span class="pln"> NMONTHS</span><span class="pun">;</span><span class="pln">
              day_count</span><span class="pun">++){</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"%d days in %s\n"</span><span class="pun">,</span><span class="pln">
                      month_days</span><span class="pun">[</span><span class="pln">day_count</span><span class="pun">],</span><span class="pln">
                      mnames</span><span class="pun">[</span><span class="pln">day_count</span><span class="pun">]);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	مثال 1
</p>

<p>
	تهيئة المتغيرات الاعتيادية بسيطة، فعليك فقط إضافة <code>‎= expression</code> بعد اسم المتغير في التصريح، والتي تدل <code>expression</code> على التعبير الذي ستُسند قيمته إلى المتغير، وسيُهيّأ المتغير بهذه القيمة، ويعتمد استخدام مجمل التعابير أو استخدام التعابير الثابتة حصرًا على مدة التخزين، وهذا ينطبق على جميع الكائنات.
</p>

<p>
	تهيئة المصفوفات أحادية البعد بسيطةٌ أيضًا، إذ عليك فقط كتابة قائمة بالقيم التي تريدها وتفصل كل قيمة بفاصلة داخل أقواس معقوصة؛ ويوضّح المثال التالي طريقة تهيئة المصفوفة، ويُحدّد حجم المصفوفة طبقًا للقيم الأولية الموجودة إن لم تحدد حجم المصفوفة على نحوٍ صريح، أما إذا حددت الحجم صراحةً، فيجب أن يكون عدد القيم الأولية التي تزودها يساوي أو أصغر من الحجم، إذ يسبب إضافة قيم أكبر من حجم المصفوفة خطئًا، بينما ستهيّئ القيم الموجودة -وإن كانت قليلة- العناصر الأولى من المصفوفة.
</p>

<p>
	يمكنك بناء سلسلة نصية يدويًا بالطريقة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5753_11" style=""><span class="kwd">char</span><span class="pln"> str</span><span class="pun">[]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="str">'h'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'e'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'l'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'l'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'o'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">};</span></pre>

<p>
	يمكن أيضًا استخدام سلسلة نصية ضمن علامتي تنصيص لتهيئة مصفوفة من المحارف:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5753_13" style=""><span class="kwd">char</span><span class="pln"> str</span><span class="pun">[]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"hello"</span><span class="pun">;</span></pre>

<p>
	سيُضمّن المحرف الفارغ في نهاية المصفوفة في حالتنا السابقة تلقائيًا إذا كانت هناك مساحةٌ كافية، أو إذا لم يُحدد حجم المصفوفة، إليك المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5753_15" style=""><span class="com">/* لا يوجد مكان للمحرف الفارغ */</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> str</span><span class="pun">[</span><span class="lit">5</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"hello"</span><span class="pun">;</span><span class="pln">

</span><span class="com">/* يوجد مكان للمحرف الفارغ */</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> str</span><span class="pun">[</span><span class="lit">6</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"hello"</span><span class="pun">;</span></pre>

<p>
	استخدم البرنامج في مثال 1 السلاسل النصية لأهداف مختلفة، إذ كان استخدامهم بهدف تهيئة مصفوفة من مؤشرات تشير إلى محارف، وهذا استخدام مختلفٌ تمامًا.
</p>

<p>
	يمكن استخدام تعبير من نوع مناسب لتهيئة هياكل من نوع مدة تلقائية، وإلا فيجب استخدام قائمة تحتوي على تعابير ثابتة بين قوسين على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5753_17" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> s</span><span class="pun">{</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> a</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">char</span><span class="pln"> b</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">cp</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">ex_s </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">,</span><span class="pln"> </span><span class="str">"hello"</span><span class="pln">
      </span><span class="pun">};</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> s first </span><span class="pun">=</span><span class="pln"> ex_s</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> s second </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
              </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'b'</span><span class="pun">,</span><span class="pln"> </span><span class="str">"byebye"</span><span class="pln">
              </span><span class="pun">};</span><span class="pln">

      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	مثال 2
</p>

<p>
	يمكن تهيئة العنصر الأول فقط من الاتحاد.
</p>

<p>
	يحدث تجاهلٌ للأعضاء عديمة الاسم ضمن الهيكل أو الاتحاد خلال عملية التهيئة، سواءٌ كانت حقول بتات bitfields، أو مسافات فارغة لمحاذاة عنوان التخزين، فمن غير المطلوب أخذهم بالحسبان عندما تختار القيم الأولية لأعضاء الهيكل الحقيقية (ذات الاسم).
</p>

<p>
	هناك طريقتان لكتابة القيم الأولية لكائنات تحتوي على كائنات فرعية بداخلها، فيمكن كتابة قيمة أولية لكل عضو بالطريقة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5753_19" style=""><span class="kwd">struct</span><span class="pln"> s</span><span class="pun">{</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> a</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> ss</span><span class="pun">{</span><span class="pln">
              </span><span class="typ">int</span><span class="pln"> c</span><span class="pun">;</span><span class="pln">
              </span><span class="kwd">char</span><span class="pln"> d</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">e</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">x</span><span class="pun">[]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">,</span><span class="pln">
      </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'b'</span><span class="pln">
      </span><span class="pun">};</span></pre>

<p style="text-align: center;">
	مثال 3
</p>

<p>
	سيُسنِد ما سبق القيمة <code>1</code> إلى <code>x[0].a</code> و <code>2</code> إلى <code>x[0].e.c</code> و <code>a</code> إلى <code>x[0].e.d</code> و <code>3</code> إلى <code>x[1].a</code> وهكذا.
</p>

<p>
	استخدام الأقواس الداخلية <strong>أكثر أمانًا</strong> للتعبير عن القيم التي تقصدها، إذ تسبّب قيمةٌ خاطئةٌ واحدة فوضى عارمة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5753_21" style=""><span class="kwd">struct</span><span class="pln"> s</span><span class="pun">{</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> a</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> ss</span><span class="pun">{</span><span class="pln">
              </span><span class="typ">int</span><span class="pln"> c</span><span class="pun">;</span><span class="pln">
              </span><span class="kwd">char</span><span class="pln"> d</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">e</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">x</span><span class="pun">[]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="pun">{</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">}},</span><span class="pln">
      </span><span class="pun">{</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'b'</span><span class="pun">}}</span><span class="pln">
      </span><span class="pun">};</span></pre>

<p style="text-align: center;">
	مثال 4
</p>

<p>
	استخدم الأقواس دائمًا، لأن هذه الطريقة آمنة، والأمر مماثل بالنسبة للمصفوفات بكونها هياكل:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5753_23" style=""><span class="typ">float</span><span class="pln"> y</span><span class="pun">[</span><span class="lit">4</span><span class="pun">][</span><span class="lit">3</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="pun">{</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">},</span><span class="pln">      </span><span class="com">/* y[0][0], y[0][1], y[0][2] */</span><span class="pln">
      </span><span class="pun">{</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">},</span><span class="pln">      </span><span class="com">/* y[1][0], y[1][1], y[1][2] */</span><span class="pln">
      </span><span class="pun">{</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">}</span><span class="pln">       </span><span class="com">/* y[2][0], y[2][1], y[2][2] */</span><span class="pln">
</span><span class="pun">};</span></pre>

<p style="text-align: center;">
	مثال 5
</p>

<p>
	تُهيّأ قيم الأسطر الثلاث الأولى كاملةً من <code>y</code>، ويبقى السطر الرابع <code>y[3]‎</code> غير مُهيّأ.
</p>

<p>
	تُسند لجميع الكائنات ذات المدة الساكنة قيمٌ أولية ضمنية تلقائية إذا لم يوجد أي قيمة أولية لها، ويكون أثر القيمة الأولية التلقائية الضمنية مماثلًا لأثر إسناد القيمة <code>0</code> الثابتة، وهذا الأمر شائع الاستخدام، وتفترض معظم برامج لغة سي نتيجةً لذلك أن الكائنات الخارجية والكائنات الساكنة الداخلية تبدأ بالقيمة صفر.
</p>

<p>
	تُضمن عملية التهيئة للكائنات ذات المدة التلقائية فقط في حالة وجود تعليمتها المركبة "في الأعلى"، إذ قد يتسبب الانتقال إلى أحد الكائنات فورًا بعدم حصول عملية التهيئة وهذا الأمر غير مستحب ويجب تجنبه. يشير المعيار بصراحة إلى أن وجود القيم الأولية في التصريح ضمن تعليمات <code>switch</code> لن يكون مفيدًا، لأن التصريح لا يُصنف بكونه تعليمة، ويمكن عنونة label التعليمة فقط، ونتيجةً لذلك فلا يمكن للقيم الأولية في تعليمات <code>switch</code> أن تُنفّذ لأن كتلة الشيفرة البرمجية التي تحتويها يجب أن تلي ذكر التصاريح.
</p>

<p>
	يمكن أن يُستخدم التصريح داخل دالة ما (نطاق مرتبط بكتلة الدالة) للإشارة إلى كائن ذي ربطٍ خارجي External linkage أو ربط داخلي Internal linkage باستخدام عدّة طرق تطرقنا إليها في مقال <a href="https://academy.hsoub.com/programming/c/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D9%86%D8%B7%D8%A7%D9%82-scope-%D9%88%D8%A7%D9%84%D8%B1%D8%A8%D8%B7-linkage-%D8%B9%D9%84%D9%89-%D9%85%D8%B3%D8%AA%D9%88%D9%89-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1674/" rel="">الربط والنطاق</a> وهناك مزيدٌ من الطرق التي سنتطرق إليها لاحقًا. إذا اتبعت الطرق السابقة (التي من المستبعد أن تتحقق من قبيل الصدفة)، فلا يمكنك تهيئة الكائن على أنه جزء من التصريح، وإليك الطريقة الوحيدة التي تستطيع تحقيق ذلك بها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5753_25" style=""><span class="typ">int</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">                        </span><span class="com">/* ربط خارجي */</span><span class="pln">
main</span><span class="pun">(){</span><span class="pln">
      </span><span class="kwd">extern</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln">       </span><span class="com">/* استخدام ممنوع */</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لم يكشف مصرّفنا التهيئة الممنوعة في هذا المثال أيضًا.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter6/" rel="external nofollow">Structured Data Types</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-macro-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC-%D8%A7%D9%84%D9%85%D8%B3%D8%A8%D9%82-preprocessor-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1750/" rel="">الماكرو Macro والمعالج المسبق Preprocessor في لغة سي C</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/c/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%AA%D8%AD%D8%A7%D8%AF%D8%A7%D8%AA-unions-%D9%88%D8%AD%D9%82%D9%88%D9%84-%D8%A7%D9%84%D8%A8%D8%AA%D8%A7%D8%AA-bitfields-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%AF%D8%AF%D8%A7%D8%AA-eums-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1748/" rel="">هياكل البيانات: الاتحادات Unions وحقول البتات Bitfields والمعددات Eums في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%82%D9%88%D8%A7%D8%A6%D9%85-%D8%A7%D9%84%D9%85%D8%AA%D8%B1%D8%A7%D8%A8%D8%B7%D8%A9-linked-lists-%D9%88%D8%A7%D9%84%D8%A3%D8%B4%D8%AC%D8%A7%D8%B1-trees-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1747/" rel="">هياكل البيانات: القوائم المترابطة Linked lists والأشجار Trees في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A8%D9%86%D9%8A%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1607/" rel="">بنية برنامج لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%B9%D9%88%D8%A7%D9%85%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1613/" rel="">العوامل في لغة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1749</guid><pubDate>Mon, 24 Oct 2022 16:09:00 +0000</pubDate></item><item><title>&#x647;&#x64A;&#x627;&#x643;&#x644; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A;: &#x627;&#x644;&#x627;&#x62A;&#x62D;&#x627;&#x62F;&#x627;&#x62A; Unions &#x648;&#x62D;&#x642;&#x648;&#x644; &#x627;&#x644;&#x628;&#x62A;&#x627;&#x62A; Bitfields &#x648;&#x627;&#x644;&#x645;&#x639;&#x62F;&#x62F;&#x627;&#x62A; Eums &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%AA%D8%AD%D8%A7%D8%AF%D8%A7%D8%AA-unions-%D9%88%D8%AD%D9%82%D9%88%D9%84-%D8%A7%D9%84%D8%A8%D8%AA%D8%A7%D8%AA-bitfields-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%AF%D8%AF%D8%A7%D8%AA-eums-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1748/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_10/6343119d383a3_----Unions---Bitfields--Eums---C-.png.9f8c40453ced5303be5cdf4847ec0b28.png" /></p>
<p>
	تطرقنا في <a href="https://academy.hsoub.com/programming/c/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%82%D9%88%D8%A7%D8%A6%D9%85-%D8%A7%D9%84%D9%85%D8%AA%D8%B1%D8%A7%D8%A8%D8%B7%D8%A9-linked-lists-%D9%88%D8%A7%D9%84%D8%A3%D8%B4%D8%AC%D8%A7%D8%B1-trees-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1747/" rel="">المقال السابق</a> إلى الهياكل وبعض الهياكل الشائعة، مثل الأشجار والقوائم المرتبطة، وننتقل الآن إلى الاتحادات وحقول البتات والمعددات ونتكلم عن استعمال وخصائص كل منها.
</p>

<h2>
	الاتحادات Unions
</h2>

<p>
	لن تستغرق الاتحادات Unions وقتًا طويلًا لشرحها، فهي تشابه الهياكل بفرق أنك لا تستخدم الكلمة المفتاحية <code>struct</code> بل تستخدم <code>union</code>، وتعمل الاتحادات بالطريقة ذاتها التي تعمل بها الهياكل structures بفرق أن أعضائها مُخزنون على كتلة تخزينية واحدة بعكس أعضاء الهياكل التي تُخزن على كتل تخزينية متفرقة متعاقبة، ولكن ما الذي يفيدنا هذا الأمر؟ تدفعنا الحاجة في بعض الأحيان إلى استخدام الهياكل بهدف تخزين قيم مختلفة بأنواع مختلفة وبأوقاتٍ مختلفة مع المحافظة قدر الإمكان على مساحة التخزين وعدم هدر الموارد؛ في حين يمكننا باستخدام الاتحادات تحديد النوع الذي ندخله إليها والتأكد من استرجاع القيمة بنوعها المناسب فيما بعد. إليك مثالًا عن ذلك:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4060_8" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="kwd">union</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
              </span><span class="typ">float</span><span class="pln"> u_f</span><span class="pun">;</span><span class="pln">
              </span><span class="typ">int</span><span class="pln"> u_i</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">var</span><span class="pun">;</span><span class="pln">

      var</span><span class="pun">.</span><span class="pln">u_f </span><span class="pun">=</span><span class="pln"> </span><span class="lit">23.5</span><span class="pun">;</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"value is %f\n"</span><span class="pun">,</span><span class="pln"> var</span><span class="pun">.</span><span class="pln">u_f</span><span class="pun">);</span><span class="pln">
      var</span><span class="pun">.</span><span class="pln">u_i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"value is %d\n"</span><span class="pun">,</span><span class="pln"> var</span><span class="pun">.</span><span class="pln">u_i</span><span class="pun">);</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	مثال 1
</p>

<p>
	إذا أضفنا قيمةً من نوع <code>float</code> إلى الاتحاد في مثالنا السابق، ثم استعدناه على أنه قيمةٌ من نوع <code>int</code>، فسنحصل على قيمة غير معروفة، لأن النوعان يُخزنان على نحوٍ مختلف وأضف على ذلك أنهما من أطوالٍ مختلفة؛ فالقيمة من نوع <code>int</code> ستكون غالبًا تمثيل الآلة (الحاسوب) لبتات <code>float</code> منخفضة الترتيب، ولربما ستشكل جزءًا من قيمة <code>float</code> العشرية (ما بعد الفاصلة). ينص المعيار على اعتماد النتيجة في هذه الحالة على تعريف التطبيق (وليست سلوكًا غير معرفًا)، والنتيجة معرفةٌ من المعيار في حالة واحدة، ألا وهي أن يكون لبعض أعضاء الاتحاد هياكل ذات "سلسلة مبدئية مشتركة common initial sequence"، أي أن لأول عضو من كل هيكل نوع متوافق compatible type، أو من الطول ذاته في حالة حقول البتات bitfields، ويوافق اتحادنا الشروط التي ذكرناها، وبالتالي يمكننا استخدام السلسلة المبدئية المشتركة على نحوٍ تبادلي، يا لحظنا الرائع.
</p>

<p>
	يعمل مصرّف لغة سي على حجز المساحة اللازمة لأكبر عضو ضمن الاتحاد لا أكثر (بعنوان مناسب إن أمكن)، أي لا يوجد هناك أي تفقد للتأكد من أن استخدام الأعضاء صائب فهذه مهمتك، وستكتشف عاجلًا أم آجلًا إذا فشلت في تحقيق هذه المهمة. تبدأ أعضاء الاتحاد من عنوان التخزين ذاته (من المضمون أنه لا يوجد هناك أي فراغات بين أيٍ من الأعضاء).
</p>

<p>
	يُعد تضمين الاتحاد في هيكل من أكثر الطرق شيوعًا لتذكر طريقة عمل الاتحاد، وذلك باستخدام عضو آخر من الهيكل ذاته ليدل على نوع الشيء الموجود في الاتحاد. إليك مثالًا عمّا سيبدو ذلك:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4060_10" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="com">/* شيفرة للأنواع في الاتحاد */</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> FLOAT_TYPE      </span><span class="lit">1</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> CHAR_TYPE       </span><span class="lit">2</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> INT_TYPE        </span><span class="lit">3</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> var_type</span><span class="pun">{</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> type_in_union</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">union</span><span class="pun">{</span><span class="pln">
              </span><span class="typ">float</span><span class="pln">   un_float</span><span class="pun">;</span><span class="pln">
              </span><span class="kwd">char</span><span class="pln">    un_char</span><span class="pun">;</span><span class="pln">
              </span><span class="typ">int</span><span class="pln">     un_int</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">vt_un</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">var_type</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">void</span><span class="pln">
print_vt</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">){</span><span class="pln">

      </span><span class="kwd">switch</span><span class="pun">(</span><span class="pln">var_type</span><span class="pun">.</span><span class="pln">type_in_union</span><span class="pun">){</span><span class="pln">
              </span><span class="kwd">default</span><span class="pun">:</span><span class="pln">
                      printf</span><span class="pun">(</span><span class="str">"Unknown type in union\n"</span><span class="pun">);</span><span class="pln">
                      </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
              </span><span class="kwd">case</span><span class="pln"> FLOAT_TYPE</span><span class="pun">:</span><span class="pln">
                      printf</span><span class="pun">(</span><span class="str">"%f\n"</span><span class="pun">,</span><span class="pln"> var_type</span><span class="pun">.</span><span class="pln">vt_un</span><span class="pun">.</span><span class="pln">un_float</span><span class="pun">);</span><span class="pln">
                      </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
              </span><span class="kwd">case</span><span class="pln"> CHAR_TYPE</span><span class="pun">:</span><span class="pln">
                      printf</span><span class="pun">(</span><span class="str">"%c\n"</span><span class="pun">,</span><span class="pln"> var_type</span><span class="pun">.</span><span class="pln">vt_un</span><span class="pun">.</span><span class="pln">un_char</span><span class="pun">);</span><span class="pln">
                      </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
              </span><span class="kwd">case</span><span class="pln"> INT_TYPE</span><span class="pun">:</span><span class="pln">
                      printf</span><span class="pun">(</span><span class="str">"%d\n"</span><span class="pun">,</span><span class="pln"> var_type</span><span class="pun">.</span><span class="pln">vt_un</span><span class="pun">.</span><span class="pln">un_int</span><span class="pun">);</span><span class="pln">
                      </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">

      var_type</span><span class="pun">.</span><span class="pln">type_in_union </span><span class="pun">=</span><span class="pln"> FLOAT_TYPE</span><span class="pun">;</span><span class="pln">
      var_type</span><span class="pun">.</span><span class="pln">vt_un</span><span class="pun">.</span><span class="pln">un_float </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3.5</span><span class="pun">;</span><span class="pln">

      print_vt</span><span class="pun">();</span><span class="pln">

      var_type</span><span class="pun">.</span><span class="pln">type_in_union </span><span class="pun">=</span><span class="pln"> CHAR_TYPE</span><span class="pun">;</span><span class="pln">
      var_type</span><span class="pun">.</span><span class="pln">vt_un</span><span class="pun">.</span><span class="pln">un_char </span><span class="pun">=</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">;</span><span class="pln">

      print_vt</span><span class="pun">();</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	مثال 2
</p>

<p>
	يوضح المثال السابق أيضًا استخدام عامل النقطة للوصول إلى ما داخل الهياكل أو الاتحادات التي تحتوي على هياكل أو اتحادات أخرى بدورها، تسمح لك بعض مصرفات لغة سي الحالية بإهمال بعض الأجزاء من أسماء الكائنات المُدمجة شرط ألا يتسبب ذلك بجعل الاسم غامض، فعلى سبيل المثال يسمح استخدام الاسم الواضح <code>var_type.un_int</code> للمصرف بمعرفة ما تقصده، إلا أن هذا غير مسموح في المعيار.
</p>

<p>
	لا يمكن مقارنة الهياكل بحثًا عن المساواة فيما بينها ويقع اللوم على الاتحادات، إذ أن احتمالية احتواء هيكل ما على اتحاد يجعل من مهمة المقارنة مهمةً صعبة، إذ لا يمكن للمصرّف أن يعرف ما الذي يحويه الاتحاد في الوقت الحالي مما لا يسمح له بإجراء عملية المقارنة. قد يبدو الكلام السابق صعب الفهم وغير دقيق بنسبة 100%، إذ أن معظم الهياكل لا تحتوي على اتحادات، ولكن هناك مشكلة فلسفية بخصوص القصد من كلمة "مساواة" عندما نُسقطها على الهياكل. بغض النظر، تمنح الاتحادات عذرًا مناسبًا للمعيار بتجنبه لأي مشاكل بواسطة عدم دعمه لمقارنة الهياكل.
</p>

<h2>
	حقول البتات Bitfields
</h2>

<p>
	دعنا نلقي نظرةً على حقول البتات بما أننا نتكلم عن موضوع هياكل البيانات، إذ يمكن تعريفها فقط بداخل هيكل أو اتحاد، وتسمح لك حقول البتات بتحديد بعض الكائنات الصغيرة بحسب طول بتات محدد، إلا أن فائدتها محدودةٌ ولا تُستخدم إلا في حالات نادرة، ولكننا سنتطرق إلى الموضوع بغض النظر عن ذلك. يوضح لك المثال استخدام حقول البتات:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4060_12" style=""><span class="kwd">struct</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">/* كل حقل بسعة 4 بتات */</span><span class="pln">
      </span><span class="kwd">unsigned</span><span class="pln"> field1 </span><span class="pun">:</span><span class="lit">4</span><span class="pun">;</span><span class="pln">
      </span><span class="com">/*
       * حقل بسعة 3 بتات دون اسم
       * تسمح الحقول عديمة الاسم بالفراغات بين عناوين الذاكرة
       */</span><span class="pln">
      </span><span class="kwd">unsigned</span><span class="pln">        </span><span class="pun">:</span><span class="lit">3</span><span class="pun">;</span><span class="pln">
      </span><span class="com">/*
       * حقل بسعة بت واحد
       * تكون قيمته 0 أو 1- في نظام المتمم الثنائي
       */</span><span class="pln">
      </span><span class="kwd">signed</span><span class="pln"> field2   </span><span class="pun">:</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
      </span><span class="com">/* محاذاة الحقل التالي مع وحدة التخزين */</span><span class="pln">
      </span><span class="kwd">unsigned</span><span class="pln">        </span><span class="pun">:</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">unsigned</span><span class="pln"> field3 </span><span class="pun">:</span><span class="lit">6</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">full_of_fields</span><span class="pun">;</span></pre>

<p style="text-align: center;">
	مثال 3
</p>

<p>
	يمكن التلاعب والوصول إلى كل حقل بصورةٍ منفردة وكأنه عضو اعتيادي من هيكل ما، وتعني الكلمتان المفتاحيتان <code>signed</code> و<code>unsigned</code> ما هو متوقع، إلا أنه يجدر بالذكر أن حقلًا بحجم 1 بت ذا إشارة سيأخذ واحدةً من القيمتين <code>0</code> أو <code>‎-1</code> وذلك في آلة تعمل بنظام المتمم الثنائي، ويُسمح للتصريحات بأن تحتوي المؤهلين <code>const</code> أو <code>volatile</code>.
</p>

<p>
	تُستخدم حقول البتات بشكل رئيس إما للسماح بتحزيم مجموعة من البيانات بأقل مساحة، أو لتحديد الحقول ضمن ملفات بيانات خارجية. لا تقدم لغة سي أي ضمانات بخصوص ترتيب الحقول بكلمات الآلة التي تعمل عليها، لذا إذا كنت تريد استخدام حقول البتات للهدف الثاني، فسيصبح برنامجك غير قابل للتنقل ومعتمدًا على المصرّف الذي يصرف البرنامج أيضًا. ينص المعيار على أن الحقول مُحزّمة بما يدعى "وحدات تخزين"، التي تكون عادةً كلمات آلة. يُحدّد ترتيب التحزيم وفيما إذا كان سيتجاوز حقل البتات حاجز التخزين أم لا بحسب تعريف التطبيق، ونستخدم حقلًا بعرض صفر قبل الحقل الذي تريد تطبيق الحد عنده لإجبار الحقل على البقاء ضمن حدود وحدة التخزين.
</p>

<p>
	كن حذرًا عند استخدام حقول البتات، إذ يتطلب الأمر شيفرة وقت تشغيل run-time طويلة للتلاعب بهذه الأشياء، وقد ينتج ذلك بتوفير الكثير من المساحة (أكثر من حاجتك).
</p>

<p>
	ليس لحقول البتات أي عناوين، وبالتالي لا يمكنك استخدام المؤشرات أو المصفوفات معها.
</p>

<h2>
	المعددات enums
</h2>

<p>
	تقع المُعدّدات enums تحت تصنيف "منجزة جزئيًا"، إذ ليست بأنواع مُعددة بصورٍ كاملة مثل لغة باسكال، ومهمتها الوحيدة هي مساعدتك في التخفيف من عدد تعليمات <code>‎#define</code> في برنامجك، إليك ما تبدو عليه:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4060_14" style=""><span class="kwd">enum</span><span class="pln"> e_tag</span><span class="pun">{</span><span class="pln">
      a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">,</span><span class="pln"> c</span><span class="pun">,</span><span class="pln"> d</span><span class="pun">=</span><span class="lit">20</span><span class="pun">,</span><span class="pln"> e</span><span class="pun">,</span><span class="pln"> f</span><span class="pun">,</span><span class="pln"> g</span><span class="pun">=</span><span class="lit">20</span><span class="pun">,</span><span class="pln"> h
</span><span class="pun">}</span><span class="pln">var</span><span class="pun">;</span></pre>

<p>
	يمثل <code>e_tag</code> الوسم بصورةٍ مشابهة لما تكلمنا عنه في الهياكل والاتحادات، ويمثل <code>var</code> تعريفًا للمتغير.
</p>

<p>
	الأسماء المُعلنة بداخل المُعدد ثوابت من نوع <code>int</code>، إليك قيمها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4060_16" style=""><span class="pln">a </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
b </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
c </span><span class="pun">==</span><span class="pln"> </span><span class="lit">2</span><span class="pln">
d </span><span class="pun">==</span><span class="pln"> </span><span class="lit">20</span><span class="pln">
e </span><span class="pun">==</span><span class="pln"> </span><span class="lit">21</span><span class="pln">
f </span><span class="pun">==</span><span class="pln"> </span><span class="lit">22</span><span class="pln">
g </span><span class="pun">==</span><span class="pln"> </span><span class="lit">20</span><span class="pln">
h </span><span class="pun">==</span><span class="pln"> </span><span class="lit">21</span></pre>

<p>
	تلاحظ أنه بغياب أي قيمة مُسندة للمتغيرات، تبدأ القيم من الصفر تصاعديًا، ويمكنك إسناد قيمة مخصصة إنذا أردت في البداية، إلا أن القيم التي ستتزايد بعدها ستكون من نوع عدد صحيح ثابت integral constant (كما سنرى لاحقًا)، وتُمثّل هذه القيمة بنوع <code>int</code> ومن الممكن أن تحمل عدة أسماء القيمة ذاتها.
</p>

<p>
	تُستخدم المُعدّدات للحصول على إصدار ملائم للنطاق Scope بدلًا من استخدام <code>‎#define</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4060_18" style=""><span class="com">#define</span><span class="pln"> a </span><span class="lit">0</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> b </span><span class="lit">1</span><span class="pln">
</span><span class="com">/* وهكذا دواليك */</span></pre>

<p>
	إذ يتبع استخدام المعددات لقوانين نطاق لغة سي C، بينما تشمل تعليمات <code>‎#define</code> كامل نطاق الملف.
</p>

<p>
	قد لا تهمك هذه المعلومة، ولكن المعيار ينص على أن أنواع المعددات من نوع متوافق مع أنواع الأعداد الصحيحة بحسب تعريف التطبيق، لكن ما الذي يعنيه ذلك؟ لاحظ المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4060_20" style=""><span class="kwd">enum</span><span class="pln"> ee</span><span class="pun">{</span><span class="pln">a</span><span class="pun">,</span><span class="pln">b</span><span class="pun">,</span><span class="pln">c</span><span class="pun">}</span><span class="pln">e_var</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ep</span><span class="pun">;</span></pre>

<p>
	تسلك الأسماء <code>a</code> و <code>b</code> و <code>c</code> سلوك الأعداد الصحيحة الثابتة <code>int</code> عندما تستخدمها، و <code>e_var</code> من نوع <code>enum ee</code> و <code>ep</code> مؤشر يشير إلى المعدد <code>ee</code>. تعني متطلبات التوافقية بين الأنواع (بالإضافة لمشكلات أخرى) أن هناك نوع عدد صحيح ذو عنوان يمكن إسناده إلى <code>ep</code> من غير خرق أي من متطلبات التوافقية بين الأنواع للمؤشرات.
</p>

<h2>
	المؤهلات والأنواع المشتقة
</h2>

<p>
	تعد المصفوفات والهياكل والاتحادات "مشتقةٌ من derived from" (أي تحتوي) أنواعٍ أخرى، ولا يمكن لأي ممّا سبق أن تُشتق من أنواع غير مكتملة incomplete types، وهذا يعني أنه من غير الممكن للهيكل أو الاتحاد أن يحتوي مثالًا من نفسه، لأن نوعه غير مكتمل حتى ينتهي التصريح عنه، وبما أن المؤشر الذي يشير إلى نوع غير مكتمل ليس بنوع غير مكتمل بذات نفسه فمن <strong>الممكن</strong> استخدامه باشتقاق المصفوفات والهياكل والاتحادات.
</p>

<p>
	لا ينتقل التأهيل إلى النوع المُشتق إن كان أي من الأنواع التي اشتُق منها تحتوي على مؤهلات مثل <code>const</code> أو <code>volatile</code>، وهذا يعني أن الهيكل الذي يحتوي على كائن ذو مؤهل ثابت <code>const</code> لا يجعل من الهيكل بنفسه مؤهلًا بهذا المؤهل، ويمكن لأي عضو غير ثابت أن يُعدّل عليه بداخل الهيكل، وهذا ما هو متوقع، إلا أن المعيار ينص على أن أي نوع مشتق يحتوي على نوع مؤهل باستخدام <code>const</code> (أو أي نوع داخلي تعاودي) لا يمكن التعديل عليه، فالهيكل الذي يحتوي الثابت لا يمكن وضعه على الطرف الأيسر من عامل الإسناد.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter6/" rel="external nofollow">Structured Data Types</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/c/%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1749/" rel="">تهيئة المتغيرات وأنواع البيانات في لغة سي C</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/c/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%82%D9%88%D8%A7%D8%A6%D9%85-%D8%A7%D9%84%D9%85%D8%AA%D8%B1%D8%A7%D8%A8%D8%B7%D8%A9-linked-lists-%D9%88%D8%A7%D9%84%D8%A3%D8%B4%D8%AC%D8%A7%D8%B1-trees-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1747/" rel="">هياكل البيانات: القوائم المترابطة Linked lists والأشجار Trees في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A8%D9%86%D9%8A%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1607/" rel="">بنية برنامج لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%B9%D9%88%D8%A7%D9%85%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1613/" rel="">العوامل في لغة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1748</guid><pubDate>Thu, 20 Oct 2022 16:05:00 +0000</pubDate></item><item><title>&#x647;&#x64A;&#x627;&#x643;&#x644; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A;: &#x627;&#x644;&#x642;&#x648;&#x627;&#x626;&#x645; &#x627;&#x644;&#x645;&#x62A;&#x631;&#x627;&#x628;&#x637;&#x629; Linked lists &#x648;&#x627;&#x644;&#x623;&#x634;&#x62C;&#x627;&#x631; Trees &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%82%D9%88%D8%A7%D8%A6%D9%85-%D8%A7%D9%84%D9%85%D8%AA%D8%B1%D8%A7%D8%A8%D8%B7%D8%A9-linked-lists-%D9%88%D8%A7%D9%84%D8%A3%D8%B4%D8%AC%D8%A7%D8%B1-trees-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1747/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_10/63430deeb095e_-----Linked-lists--Trees---C-.png.f89b8f551f3f265a4b2a08d9adc3ed4d.png" /></p>
<p>
	اتجه تطوير لغات الحاسوب سابقًا في اتجاهٍ من اتجاهين، إذ سلكت كوبول COBOL سلوكًا ركز على استخدام هياكل البيانات بعيدًا عن العمليات الحسابية و<a href="https://academy.hsoub.com/programming/advanced/%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA/" rel="">الخوارزميات</a>، بينما سلكت لغات مثل فورتران FORTRAN وألغول Algol سلوكًا معاكسًا. أراد العلماء وقتها إجراء العمليات الحسابية باستخدام بيانات غير مُهيكلة نسبيًا، إلا أنه سرعان ما لاحظ الجميع أن استخدام المصفوفات لا غنى عنه؛ بينما أراد المستخدمون الاعتياديون طريقةً لإجراء العمليات الحسابية البسيطة فقط، إلا أن طريقة هيكلة البيانات كانت عائقًا أمام تحقيق ذلك.
</p>

<p>
	أثر كلا السلوكين في تصميم لغة سي، إذ أنها تحتوي تحكمًا هيكليًا لتدفق البرنامج مناسب للغة من هذا العمر، كما أنها جعلت من مفهوم هياكل البيانات شائعًا. ركزنا على جانب الخوارزميات من اللغة حتى اللحظة، ولم نولي الكثير من الانتباه بخصوص تخزين البيانات، ومع أننا تطرقنا إلى المصفوفات التي تُعدّ هيكل بيانات إلا أنها شائعة الاستخدام وبسيطة ولا تستحق فصلًا مخصصًا لها، واكتفينا إلى الآن بالنظر إلى اللغة انطلاقًا من بينة هيكلية شبيهة بلغة فورتران.
</p>

<p>
	كان استخدام كلٍ من البيانات والخوارزميات هو التوجه الأكثر رواجًا في أواخر ثمانينيات وبداية تسعينيات القرن الماضي، وفق ما يُدعى <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-r1375/" rel="">بالبرمجة كائنية التوجه Object-Oriented Programming</a>. لا يوجد أي دعم لهذه الطريقة في لغة سي، إلا أن <a href="https://academy.hsoub.com/programming/cpp/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-c-r802/" rel="">لغة C++‎</a> قدمت دعمًا لها (وهي لغةٌ مبنيةٌ على لغة سي)، ولكن هذا النقاش خارج موضوعنا حاليًا.
</p>

<p>
	تأخذ البيانات الانتباه الأكبر لمعظم مشاكل الحوسبة المتقدمة وليس الخوارزميات، فستكون مهمتك بسيطةً ببرمجة البرنامج إن استطعت تصميم هياكل بيانات صحيحة ومناسبة، إلا أنك تحتاج إلى دعمٍ من اللغة في هذه الحالة، فمهمتك ستصبح أقل سهولة ومعرضةً أكثر للأخطاء إن لم يكن هناك أي دعم لأنواع هياكل البيانات المختلفة عن المصفوفات. تقع هذه المهمة على كاهل <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغة البرمجة</a>، فليس كافيًا أن تسمح لك اللغة بفعل ما تريد، بل يجب أن <strong>تساعدك</strong> في فعل ما تريد.
</p>

<p>
	تقدم لك لغة سي سعيًا منها بتقديم هياكل بيانات مناسبة كلًا من <a href="https://academy.hsoub.com/programming/c/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1675/" rel="">المصفوفات Arrays</a> والهياكل Structures والاتحادات Unions، وقد برهنت على أنها كافيةٌ لمعظم المستخدمين الاعتياديين وبالتالي لم يُضِف المعيار أي جديد بشأنها.
</p>

<h2>
	الهياكل Structures
</h2>

<p>
	تسمح لك المصفوفات بتخزين مجموعة من الكائنات المتماثلة تحت اسم معين، وهذا مفيدٌ لعدد من المهام، ولكنه ليس مرن التعامل، إذ تحتوي معظم كائنات البيانات ذات التطبيقات الواقعية على هيكل معيّن معقد لا يمكن استخدامه مع طريقة تخزين المصفوفة للبيانات.
</p>

<p>
	لنوضح ما سبق بالمثال التالي: لنفرض أننا نريد تمثيل سلسلةٍ نصية ذات خصائص معينة، بجانب محتواها. هناك نوع الخط وحجمه، وهما سمتان لا تؤثران في محتوى السلسلة، لكنهما تحددان الطريقة التي تُعرض فيها السلسلة على الشاشة سواءٌ كان النص <strong>مكتوبًا بخط غامق</strong> أو <em>مائل</em>، والأمر ذاته ينطبق على حجم الخط. كيف نستطيع تمثيل <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">السلسلة النصية</a> بكائن واحد ضمن مصفوفة إذا كان يحتوي على ثلاث سمات مختلفة؟
</p>

<p>
	يمكننا تحقيق ذلك في لغة سي C بسهولة، حاول أولًا تمثيل السمات الثلاث باستخدام الأنواع الأساسية، فعلى فرض أنه يمكننا تخزين كل محرف باستخدام النوع <code>char</code>، يمكننا الإشارة إلى نوع الخط المستخدم باستخدام النوع <code>short</code> (نستخدم "1" للإشارة إلى الخط الاعتيادي و"2" للخط المائل و"3" للخط الغامق، وهكذا)، كما يمكننا تخزين حجم الخط باستخدام النوع <code>short</code>، وتُعد جميع الفرضيات السابقة معقولةً عمليًا، إذ تدعم معظم الأنظمة عددًا قليلًا من الخطوط مهما كانت هذه الأنظمة معقدة، ويتراوح حجم الخط بين 6 ومرتبة المئات القليلة، فأي خط أصغر من 6 هو صعب القراءة، والخط الأكبر من 50 هو خط أكبر من خطوط عناوين الجرائد. إذًا، لدينا الآن محرف وعددين صغيرين وتُعامل هذه البيانات معاملة كائن واحد، إليك كيف نصرّح عن ذلك في لغة سي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_17" style=""><span class="kwd">struct</span><span class="pln"> wp_char</span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">char</span><span class="pln"> wp_cval</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">short</span><span class="pln"> wp_font</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">short</span><span class="pln"> wp_psize</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	يصرح ما سبق عن نوع جديد من الكائنات يمكنك استخدامه ضمن البرنامج، ويعتمد الأمر بصورةٍ رئيسية على ذكر الكلمة المفتاحية <code>struct</code>، المتبوعة بمعرّف identifier اختياري هو الوسم <code>wp_char</code> في هذه الحالة، ويسمح لنا هذا الوسم بتسمية النوع للإشارة إليه فيما بعد. يمكننا أيضًا استخدام الوسم بالطريقة التالية بعد التصريح عنه:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_19" style=""><span class="kwd">struct</span><span class="pln"> wp_char x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">;</span></pre>

<p>
	يُعرّف ما سبق متغيرين باسم <code>x</code> و <code>y</code>، بالطريقة ذاتها للتعريف التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_21" style=""><span class="typ">int</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">;</span></pre>

<p>
	لكن المتغيرات في المثال الأول من نوع <code>struct wp_char</code> عوضًا عن <code>int</code> في المثال الثاني، ويمثّل الوسم اسمًا للنوع الذي صرحنا عنه سابقًا.
</p>

<p>
	نذّكر هنا أنه من الممكن استخدام اسم وسم الهيكل مثل أي معرّف اعتيادي بصورةٍ آمنة، ويدل الاسم على معنًى مختلف عندما يُسبق بالكلمة المفتاحية <code>struct</code> فقط، ومن الشائع أن يُعرّف كائن مُهيكل باسم مماثل لوسم الهيكل الخاص به.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_24" style=""><span class="kwd">struct</span><span class="pln"> wp_char wp_char</span><span class="pun">;</span></pre>

<p>
	يُعرّف السطر السابق متغيرًا باسم <code>wp_char</code> من النوع <code>struct wp_char</code>، ويمكننا فعل ذلك لأن لوسوم الهياكل "فضاء اسماء name space" خاصة بها ولا تتعارض مع الأسماء الأخرى، وسنتكلم أكثر عن الوسوم عندما نناقش "الأنواع غير المكتملة incomplete types".
</p>

<p>
	يمكن التعريف عن المتغيرات على الفور عقب التصريح عن هيكل ما:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_26" style=""><span class="kwd">struct</span><span class="pln"> wp_char</span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">char</span><span class="pln"> wp_cval</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">short</span><span class="pln"> wp_font</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">short</span><span class="pln"> wp_psize</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">v1</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> wp_char v2</span><span class="pun">;</span></pre>

<p>
	لدينا في هذه الحالة متغيرين، أحدهما باسم <code>v1</code> والآخر باسم <code>v2</code>، وإذا استخدمنا الطريقة السابقة في التعريف عن <code>v1</code>، يصبح الوسم غير ضروري ويُتخلّى عنه غالبًا إلا في حال احتجنا إلى الوسم لاستخدامه مع عامل <code>sizeof</code> والتحويل بين الأنواع casts.
</p>

<p>
	يُعد المتغيران السابقان كائنات مُهيكلة، ويحتوي كل منهما على ثلاثة <strong>أعضاء members</strong> مستقلين عن بعضهم باسم <code>wp_cval</code> و <code>wp_font</code> و <code>wp_psize</code>، ونستخدم عامل النقطة <code>.</code> للوصول إلى كلّ من الأعضاء السابقة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_28" style=""><span class="pln">v1</span><span class="pun">.</span><span class="pln">wp_cval </span><span class="pun">=</span><span class="pln"> </span><span class="str">'x'</span><span class="pun">;</span><span class="pln">
v1</span><span class="pun">.</span><span class="pln">wp_font </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
v1</span><span class="pun">.</span><span class="pln">wp_psize </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">

v2 </span><span class="pun">=</span><span class="pln"> v1</span><span class="pun">;</span></pre>

<p>
	تُضبط أعضاء المتغير <code>v1</code> في المثال السابق إلى قيمها المناسبة، ومن ثم تُنسخ قيم <code>v1</code> إلى <code>v2</code> باستخدام عملية الإسناد.
</p>

<p>
	في الحقيقة، العملية الوحيدة المسموحة في الهياكل بكاملها هي الإسناد؛ إذ يمكن إسناد الهياكل إلى بعضها بعضًا أو تمريرها مثل وسطاء للدوال أو قيمةٍ تُعيدها دالةٌ ما، إلا أن نسخ الهياكل عمليةٌ غير فعالة وتتفاداها معظم البرامج عن طريق التلاعب بالمؤشرات التي تشير إلى الهياكل عوضًا عن ذلك، إذ من الأسرع عمومًا نسخ المؤشرات عوضًا عن الهياكل. تسمح اللغة بمقارنة الهياكل بحثًا عن المساواة فيما بينها، وهي سهوة مفاجئة ولا يوجد سبب مقنع لسماحها بذلك كما سنذكر قريبًا.
</p>

<p>
	إليك مثالًا يستخدم مصفوفةً من الهياكل، وهو الهيكل ذاته الذي تكلمنا عنه سابقًا، إذ تُستخدم دالةٌ لقراءة المحارف من دخل البرنامج القياسي وتُعيد هيكلًا مهيّأً بقيمٍ مناسبة مقابله، ومن ثم تُرتّب الهياكل بحسب قيمة كل محرف وتُطبع وذلك عندما يُقرأ محرف سطر جديد، أو عندما تمتلئ المصفوفة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_30" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="com">#define</span><span class="pln"> ARSIZE </span><span class="lit">10</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> wp_char</span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">char</span><span class="pln"> wp_cval</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">short</span><span class="pln"> wp_font</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">short</span><span class="pln"> wp_psize</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">ar</span><span class="pun">[</span><span class="pln">ARSIZE</span><span class="pun">];</span><span class="pln">

</span><span class="com">/* نوع دخل الدالة الذي كان من الممكن التصريح عنه سابقًا، وتعيد الدالة هيكلًا ولا تأخذ أي وسطاء */</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> wp_char infun</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> icount</span><span class="pun">,</span><span class="pln"> lo_indx</span><span class="pun">,</span><span class="pln"> hi_indx</span><span class="pun">;</span><span class="pln">

      </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">icount </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> icount </span><span class="pun">&lt;</span><span class="pln"> ARSIZE</span><span class="pun">;</span><span class="pln"> icount</span><span class="pun">++){</span><span class="pln">
              ar</span><span class="pun">[</span><span class="pln">icount</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> infun</span><span class="pun">();</span><span class="pln">
              </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">ar</span><span class="pun">[</span><span class="pln">icount</span><span class="pun">].</span><span class="pln">wp_cval </span><span class="pun">==</span><span class="pln"> </span><span class="str">'\n'</span><span class="pun">){</span><span class="pln">
                      </span><span class="com">/*
                       * غادر الحلقة التكرارية
                       * ‫دون زيادة قيمة ‪ icount
                       * ‫مع تجاهل ‪‪\n 
                       */</span><span class="pln">
                      </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
              </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">

      </span><span class="com">/* نجري الآن عملية الترتيب */</span><span class="pln">

      </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">lo_indx </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> lo_indx </span><span class="pun">&lt;=</span><span class="pln"> icount</span><span class="pun">-</span><span class="lit">2</span><span class="pun">;</span><span class="pln"> lo_indx</span><span class="pun">++)</span><span class="pln">
              </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">hi_indx </span><span class="pun">=</span><span class="pln"> lo_indx</span><span class="pun">+</span><span class="lit">1</span><span class="pun">;</span><span class="pln"> hi_indx </span><span class="pun">&lt;=</span><span class="pln"> icount</span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln"> hi_indx</span><span class="pun">++){</span><span class="pln">
                      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">ar</span><span class="pun">[</span><span class="pln">lo_indx</span><span class="pun">].</span><span class="pln">wp_cval </span><span class="pun">&gt;</span><span class="pln"> ar</span><span class="pun">[</span><span class="pln">hi_indx</span><span class="pun">].</span><span class="pln">wp_cval</span><span class="pun">){</span><span class="pln">
                              </span><span class="com">/*
                               * التبديل بين الهيكلين
                               */</span><span class="pln">
                              </span><span class="kwd">struct</span><span class="pln"> wp_char wp_tmp </span><span class="pun">=</span><span class="pln"> ar</span><span class="pun">[</span><span class="pln">lo_indx</span><span class="pun">];</span><span class="pln">
                              ar</span><span class="pun">[</span><span class="pln">lo_indx</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> ar</span><span class="pun">[</span><span class="pln">hi_indx</span><span class="pun">];</span><span class="pln">
                              ar</span><span class="pun">[</span><span class="pln">hi_indx</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> wp_tmp</span><span class="pun">;</span><span class="pln">
                      </span><span class="pun">}</span><span class="pln">
              </span><span class="pun">}</span><span class="pln">

      </span><span class="com">/* طباعة القيم */</span><span class="pln">
      </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">lo_indx </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> lo_indx </span><span class="pun">&lt;</span><span class="pln"> icount</span><span class="pun">;</span><span class="pln"> lo_indx</span><span class="pun">++){</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"%c %d %d\n"</span><span class="pun">,</span><span class="pln"> ar</span><span class="pun">[</span><span class="pln">lo_indx</span><span class="pun">].</span><span class="pln">wp_cval</span><span class="pun">,</span><span class="pln">
                              ar</span><span class="pun">[</span><span class="pln">lo_indx</span><span class="pun">].</span><span class="pln">wp_font</span><span class="pun">,</span><span class="pln">
                              ar</span><span class="pun">[</span><span class="pln">lo_indx</span><span class="pun">].</span><span class="pln">wp_psize</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> wp_char
infun</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">){</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> wp_char wp_char</span><span class="pun">;</span><span class="pln">

      wp_char</span><span class="pun">.</span><span class="pln">wp_cval </span><span class="pun">=</span><span class="pln"> getchar</span><span class="pun">();</span><span class="pln">
      wp_char</span><span class="pun">.</span><span class="pln">wp_font </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
      wp_char</span><span class="pun">.</span><span class="pln">wp_psize </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">

      </span><span class="kwd">return</span><span class="pun">(</span><span class="pln">wp_char</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	مثال 1
</p>

<p>
	من الطبيعي أن نلجأ إلى التصريح عن مصفوفات من الهياكل حالما نستطيع ونتعلم كيفية التصريح عنها، وأن نستخدم هذه الهياكل عناصرًا لهياكل أخرى وما إلى ذلك، والقيد الوحيد هنا هو أنه لا يمكن للهيكل أن يحتوي مثالًا لنفسه على أنه عضو داخله (يصبح حينها حجمها موضع جدل مثير للفلاسفة، ولكنه غير مفيد لأي مبرمج سي C).
</p>

<h3>
	المؤشرات والهياكل
</h3>

<p>
	ذكرنا سابقًا أنه من الشائع استخدام المؤشرات في الهياكل بدلًا من استخدام الهياكل مباشرةً، لنتعلم كيفية تحقيق ذلك إذَا. يُعد التصريح عن المؤشرات سهلًا، ونعتقد أنك أتقنته:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_32" style=""><span class="kwd">struct</span><span class="pln"> wp_char </span><span class="pun">*</span><span class="pln">wp_p</span><span class="pun">;</span></pre>

<p>
	يمنحنا التصريح السابق مؤشرًا مباشرةً، ولكن كيف يمكننا الوصول إلى أعضاء الهيكل؟ تتمثل إحدى الطرق باستخدام المؤشر الذي يشير إلى الهيكل، ثم اختيار العضو على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_34" style=""><span class="com">/* نحصل على الهيكل ومن ثم نحدد العضو*/</span><span class="pln">
</span><span class="pun">(*</span><span class="pln">wp_p</span><span class="pun">).</span><span class="pln">wp_cval</span></pre>

<p>
	نستخدم الأقواس لأن أسبقية عامل النقطة <code>.</code> أعلى من <code>*</code>، إلا أن الطريقة السابقة غير سهلة التعامل وقدمت لغة سي نتيجة لذلك عاملًا جديدًا لجعل التعليمة أنيقة ويُعرف باسم العامل "المُشير إلى pointing-to"، إليك مثالًا عن استخدامه:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_36" style=""><span class="com">// ‫العضو الذي يشير إليه المؤشر ‪‎‫wp_p في الهيكل ‎‫wp_cval‫</span><span class="pln">
wp_p</span><span class="pun">-&gt;</span><span class="pln">wp_cval </span><span class="pun">=</span><span class="pln"> </span><span class="str">'x'</span><span class="pun">;</span></pre>

<p>
	ومع أن مظهره غير مثالي، إلا أنه مفيد جدًا في حال احتواء هيكل ما على المؤشرات، مثل القوائم المترابطة Linked list، إذ أن استخدام الطريقة السابقة أسهل بكثير إن أردت تتبع مرحلة أو مرحلتين من الروابط ضمن قائمة مترابطة. إذا لم تصادف القوائم المترابطة بعد، فلا تقلق، إذ سنتطرق إليها لاحقًا.
</p>

<p>
	إذا كان الكائن على يسار العامل <code>.</code> أو <code>&lt;-</code> نوعًا مؤهّلًا qualified type (باستخدام الكلمة المفتاحية <code>const</code> أو <code>volatile</code>)، فستكون النتيجة أيضًا معرفةً حسب هذه المؤهلات qualifiers. إليك مثالًا عن ذلك باستخدام المؤشرات؛ فعندما يشير المؤشر إلى نوع مؤهل، تكون النتيجة من نوع مؤهل أيضًا.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_38" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> somestruct</span><span class="pun">{</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> somestruct </span><span class="pun">*</span><span class="pln">ssp</span><span class="pun">,</span><span class="pln"> s_item</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> somestruct </span><span class="pun">*</span><span class="pln">cssp</span><span class="pun">;</span><span class="pln">

      s_item</span><span class="pun">.</span><span class="pln">i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">   </span><span class="com">/* مسموح */</span><span class="pln">
      ssp </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">s_item</span><span class="pun">;</span><span class="pln">
      ssp</span><span class="pun">-&gt;</span><span class="pln">i </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">    </span><span class="com">/* مسموح */</span><span class="pln">
      cssp </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">s_item</span><span class="pun">;</span><span class="pln">
      cssp</span><span class="pun">-&gt;</span><span class="pln">i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">    </span><span class="com">/*يشير إلى كائن ثابت cssp  غير مسموح لأن المؤشر  */</span><span class="pln">

      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يبدو أن بعض مبرمجي المصرّفات نسوا هذا المتطلب، إذ استخدمنا مصرفًا لتجربة المثال السابق ولم يحذرنا بخصوص الإسناد الأخير الذي خرق القيد.
</p>

<p>
	إليك المثال 1 مكتوبًا باستخدام المؤشرات، وبتغيير دالة الدخل <code>infun</code> بحيث تقبل مؤشرًا يشير إلى هيكل بدلًا من إعادة مؤشر، وهذا ما ستراه على الأغلب عندما تنظر إلى بعض البرامج العملية.
</p>

<p>
	نتخلى عن نسخ الهياكل في البرامج إن أردنا زيادة فاعلية تنفيذها ونستخدم بدلًا من ذلك مصفوفات تحتوي على مؤشرات؛ إذ تُستخدم هذه المؤشرات للحصول على البيانات المخزنة، إلا أن هذه الطريقة ستزيد من تعقيد الأمور، ولا تستحق العناء في التطبيقات البسيطة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_40" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="com">#define</span><span class="pln"> ARSIZE </span><span class="lit">10</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> wp_char</span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">char</span><span class="pln"> wp_cval</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">short</span><span class="pln"> wp_font</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">short</span><span class="pln"> wp_psize</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">ar</span><span class="pun">[</span><span class="pln">ARSIZE</span><span class="pun">];</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> infun</span><span class="pun">(</span><span class="kwd">struct</span><span class="pln"> wp_char </span><span class="pun">*);</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> wp_char wp_tmp</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">lo_indx</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">hi_indx</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">in_p</span><span class="pun">;</span><span class="pln">

      </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">in_p </span><span class="pun">=</span><span class="pln"> ar</span><span class="pun">;</span><span class="pln"> in_p </span><span class="pun">&lt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ar</span><span class="pun">[</span><span class="pln">ARSIZE</span><span class="pun">];</span><span class="pln"> in_p</span><span class="pun">++){</span><span class="pln">
              infun</span><span class="pun">(</span><span class="pln">in_p</span><span class="pun">);</span><span class="pln">
              </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">in_p</span><span class="pun">-&gt;</span><span class="pln">wp_cval </span><span class="pun">==</span><span class="pln"> </span><span class="str">'\n'</span><span class="pun">){</span><span class="pln">
                      </span><span class="com">/*
                       * غادر الحلقة التكرارية
                       * دون زيادة قيمة‫ ‪ in_p 
                       * ‫مع تجاهل ‪\n 
                       */</span><span class="pln">
                      </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
              </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">

      </span><span class="com">/*
       * نبدأ بترتيب القيم
       * علينا الحرص هنا وتجنب حالة طفحان
       * لذا نتفقد دائمًا وجود قيمتين لترتيبهما
       */</span><span class="pln">

      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">in_p</span><span class="pun">-</span><span class="pln">ar </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">lo_indx </span><span class="pun">=</span><span class="pln"> ar</span><span class="pun">;</span><span class="pln"> lo_indx </span><span class="pun">&lt;=</span><span class="pln"> in_p</span><span class="pun">-</span><span class="lit">2</span><span class="pun">;</span><span class="pln"> lo_indx</span><span class="pun">++){</span><span class="pln">
              </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">hi_indx </span><span class="pun">=</span><span class="pln"> lo_indx</span><span class="pun">+</span><span class="lit">1</span><span class="pun">;</span><span class="pln"> hi_indx </span><span class="pun">&lt;=</span><span class="pln"> in_p</span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln"> hi_indx</span><span class="pun">++){</span><span class="pln">
                      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">lo_indx</span><span class="pun">-&gt;</span><span class="pln">wp_cval </span><span class="pun">&gt;</span><span class="pln"> hi_indx</span><span class="pun">-&gt;</span><span class="pln">wp_cval</span><span class="pun">){</span><span class="pln">
                              </span><span class="com">/*
                               * التبديل بين الهيكلين
                               */</span><span class="pln">
                              </span><span class="kwd">struct</span><span class="pln"> wp_char wp_tmp </span><span class="pun">=</span><span class="pln"> </span><span class="pun">*</span><span class="pln">lo_indx</span><span class="pun">;</span><span class="pln">
                              </span><span class="pun">*</span><span class="pln">lo_indx </span><span class="pun">=</span><span class="pln"> </span><span class="pun">*</span><span class="pln">hi_indx</span><span class="pun">;</span><span class="pln">
                              </span><span class="pun">*</span><span class="pln">hi_indx </span><span class="pun">=</span><span class="pln"> wp_tmp</span><span class="pun">;</span><span class="pln">
                      </span><span class="pun">}</span><span class="pln">
              </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">

      </span><span class="com">/* طباعة القيم */</span><span class="pln">
      </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">lo_indx </span><span class="pun">=</span><span class="pln"> ar</span><span class="pun">;</span><span class="pln"> lo_indx </span><span class="pun">&lt;</span><span class="pln"> in_p</span><span class="pun">;</span><span class="pln"> lo_indx</span><span class="pun">++){</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"%c %d %d\n"</span><span class="pun">,</span><span class="pln"> lo_indx</span><span class="pun">-&gt;</span><span class="pln">wp_cval</span><span class="pun">,</span><span class="pln">
                              lo_indx</span><span class="pun">-&gt;</span><span class="pln">wp_font</span><span class="pun">,</span><span class="pln">
                              lo_indx</span><span class="pun">-&gt;</span><span class="pln">wp_psize</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">void</span><span class="pln">
infun</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> wp_char </span><span class="pun">*</span><span class="pln">inp</span><span class="pun">){</span><span class="pln">

      inp</span><span class="pun">-&gt;</span><span class="pln">wp_cval </span><span class="pun">=</span><span class="pln"> getchar</span><span class="pun">();</span><span class="pln">
      inp</span><span class="pun">-&gt;</span><span class="pln">wp_font </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
      inp</span><span class="pun">-&gt;</span><span class="pln">wp_psize </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">

      </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	مثال 2
</p>

<p>
	هناك مشكلةٌ أخرى يجب النظر إليها، ألا وهي كيف سيبدو الهيكل عند تخزينه في الذاكرة؟ إلا أننا لن نقلق بهذا الخصوص كثيرًا في الوقت الحالي، ولكن من المفيد أن تستخدم في بعض الأحيان هياكل لغة سي C مكتوبة بواسطة برامج أخرى. تُحجز المساحة للهيكل <code>wp_char</code> كما هو موضح على النحو التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="109380" href="https://academy.hsoub.com/uploads/monthly_2022_10/009Storage_Layout_of_a_Structure.png.c6a1d8f9ee91b0860f4582eec9ddb169.png" rel=""><img alt="009Storage_Layout_of_a_Structure.png" class="ipsImage ipsImage_thumbnailed" data-fileid="109380" data-unique="1vm9cdaxu" src="https://academy.hsoub.com/uploads/monthly_2022_10/009Storage_Layout_of_a_Structure.png.c6a1d8f9ee91b0860f4582eec9ddb169.png"></a>
</p>

<p style="text-align: center;">
	شكل 1 مخطط تخزين الهيكل
</p>

<p>
	يفترض الشكل بعض الأشياء مسبقًا: يأخذ المتغير من نوع <code>char</code> بايتًا واحدًا من الذاكرة، بينما يأخذ <code>short‏</code> 2 بايت من الذاكرة، وأن لجميع المتغيرات من نوع <code>short</code> عنوانًا زوجيًا على هذه المعمارية، ونتيجةً لما سبق يبقى عضو واحد بحجم 1 بايت ضمن الهيكل دون تسمية مُدخل من المصرف وذلك لأغراض تتعلق بمعمارية الذاكرة. القيود السابقة شائعة الوجود وتتسبب غالبًا بما يسمى هياكل ذات "ثقوب holes".
</p>

<p>
	تضمن لغة سي المعيارية بعض الأمور بخصوص تنسيق الهياكل والاتحادات:
</p>

<ul>
	<li>
		تُحجز الذاكرة لكلٍ من أعضاء الهياكل بحسب الترتيب التي ظهرت بها هذه الأعضاء ضمن التصريح عن الهيكل وبترتيبٍ تصاعدي للعناوين.
	</li>
	<li>
		لا يجب أن يكون هناك أي حشو padding في الذاكرة أمام العضو الأول.
	</li>
	<li>
		يماثل عنوان الهيكل عنوان العضو الأول له، وذلك بفرض استخدام تحويل الأنواع casting المناسب، وبالنظر إلى التصريح السابق للهيكل <code>wp_char</code> فإن التالي محقق: <code>‎(char *)item == &amp;item.wp_cval</code>.
	</li>
	<li>
		ليس لحقول البتات bit fields (سنذكرها لاحقًا) أي عناوين، فهي محزّمةٌ تقنيًا إلى وحدات units وتنطبق عليها القوانين السابقة.
	</li>
</ul>

<h3>
	القوائم المترابطة وهياكل أخرى
</h3>

<p>
	يفتح استخدام الهياكل مع المؤشرات الباب لكثيرٍ من الإمكانات. لسنا بصدد تقديم شرح مفصل ومعقد عن هياكل البيانات المترابطة هنا، ولكننا سنشرح مثالين شائعين جدًا من هذه الطبيعة، ألا وهما القوائم المترابطة Linked lists والأشجار Trees، ويجمع بين الهيكلين السابقين استخدام المؤشرات بداخلهما تشير إلى هياكل أخرى، وتكون الهياكل الأخرى عادةً من النوع ذاته. يوضح الشكل 2 طبيعة القائمة المترابطة.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="109381" href="https://academy.hsoub.com/uploads/monthly_2022_10/010List_linked_by_pointers.png.8552543771657d9e8152a436a4d872ac.png" rel=""><img alt="010List_linked_by_pointers.png" class="ipsImage ipsImage_thumbnailed" data-fileid="109381" data-unique="w228tvlhv" src="https://academy.hsoub.com/uploads/monthly_2022_10/010List_linked_by_pointers.png.8552543771657d9e8152a436a4d872ac.png"></a>
</p>

<p style="text-align: center;">
	شكل 2 قائمة مترابطة باستخدام المؤشرات
</p>

<p>
	نحتاج للحصول على ما سبق إلى لتصريح عنه بما يوافق التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_42" style=""><span class="kwd">struct</span><span class="pln"> list_ele</span><span class="pun">{</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> data</span><span class="pun">;</span><span class="pln">       </span><span class="com">/* تستطيع تسمية العضو بأي اسم*/</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> list_ele </span><span class="pun">*</span><span class="pln">ele_p</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	يبدو للوهلة الأولى أن الهيكل يحتوي نفسه (وهو ممنوع) ولكن يحتوي الهيكل في حقيقة الأمر مؤشرًا يشير إلى نفسه فقط، لكن لمَ يُعد التصريح عن المؤشر بالشكل السابق مسموحًا؟ يعلم المصرف بحلول وصوله إلى تلك النقطة بوجود <code>struct list_ele</code>، ولهذا السبب يكون التصريح مسموح، ومن الممكن أيضًا كتابة تصريح غير مكتمل للهيكل على النحو التالي قبل التصريح الكامل:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_44" style=""><span class="kwd">struct</span><span class="pln"> list_ele</span><span class="pun">;</span></pre>

<p>
	يصرح التصريح السابق عن <strong>نوع غير مكتمل incomplete type</strong>، سيسمح بالتصريح عن المؤشرات قبل التصريح الكامل، يفيد ذلك أيضًا في حال وجود حالة للإشارة إلى هياكل فيما بينها التي يجب أن تحتوي مؤشرًا لكل منها كما هو موضح في المثال.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_46" style=""><span class="kwd">struct</span><span class="pln"> s_1</span><span class="pun">;</span><span class="pln">     </span><span class="com">/* نوع غير مكتمل */</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> s_2</span><span class="pun">{</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> something</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> s_1 </span><span class="pun">*</span><span class="pln">sp</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> s_1</span><span class="pun">{</span><span class="pln">     </span><span class="com">/* التصريح الكامل */</span><span class="pln">
      </span><span class="typ">float</span><span class="pln"> something</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> s_2 </span><span class="pun">*</span><span class="pln">sp</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p style="text-align: center;">
	مثال 3
</p>

<p>
	يوضح المثال السابق حاجتنا للأنواع غير المكتملة، كما يوضح خاصيةً مهمةً لأسماء أعضاء الهيكل إذ يشكّل كل هيكل فضاء أسماء name space خاصٍ به، ويمكن بذلك أن تتماثل أسماء عناصر من هياكل مختلفة دون أي مشاكل.
</p>

<p>
	تُستخدم الأنواع غير المكتملة فقط في حال لم نكن بحاجة استخدام حجم الهيكل، وإلا فيجب التصريح كاملًا عن الهيكل قبل استخدام حجمه، ولا يجب أن يكون هذا التصريح بداخل كتلة برمجية داخلية وإلا سيصبح تصريحًا جديدًا لهيكل مختلف.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_48" style=""><span class="kwd">struct</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">       </span><span class="com">/* نوع غير مكتمل */</span><span class="pln">

</span><span class="com">/* استخدام مسموح للوسوم */</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> x </span><span class="pun">*</span><span class="pln">p</span><span class="pun">,</span><span class="pln"> func</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> f1</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">){</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> x</span><span class="pun">{</span><span class="typ">int</span><span class="pln"> i</span><span class="pun">;};</span><span class="pln">       </span><span class="com">/* إعادة تصريح */</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/* التصريح الكامل */</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> x</span><span class="pun">{</span><span class="pln">
      </span><span class="typ">float</span><span class="pln"> f</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">s_x</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> f2</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">){</span><span class="pln">
      </span><span class="com">/* تعليمات صالحة */</span><span class="pln">
      p </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">s_x</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">*</span><span class="pln">p </span><span class="pun">=</span><span class="pln"> func</span><span class="pun">();</span><span class="pln">
      s_x </span><span class="pun">=</span><span class="pln"> func</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> x
func</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">){</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> x tmp</span><span class="pun">;</span><span class="pln">
      tmp</span><span class="pun">.</span><span class="pln">f </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">tmp</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	مثال 4
</p>

<p>
	يجدر الانتباه إلى أنك تحصل على هيكل من نوع غير مكتمل فقط عن طريق <strong>ذكر اسمه</strong>، وبناءً على ما سبق، تعمل الشيفرة التالية دون مشاكل:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_50" style=""><span class="kwd">struct</span><span class="pln"> abc</span><span class="pun">{</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> xyz </span><span class="pun">*</span><span class="pln">p</span><span class="pun">;};</span><span class="pln">
      </span><span class="com">/* struct xyz تصريح النوع غير المكتمل */</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> xyz</span><span class="pun">{</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> abc </span><span class="pun">*</span><span class="pln">p</span><span class="pun">;};</span><span class="pln">
      </span><span class="com">/* أصبح النوع غير المكتمل مكتملًا */</span></pre>

<p>
	هناك خطرٌ كبير في المثال السابق، كما هو موضح هنا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_52" style=""><span class="kwd">struct</span><span class="pln"> xyz</span><span class="pun">{</span><span class="typ">float</span><span class="pln"> x</span><span class="pun">;}</span><span class="pln"> var1</span><span class="pun">;</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> abc</span><span class="pun">{</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> xyz </span><span class="pun">*</span><span class="pln">p</span><span class="pun">;}</span><span class="pln"> var2</span><span class="pun">;</span><span class="pln">

      </span><span class="com">/*  struct xyz إعادة تصريح للهيكل  */</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> xyz</span><span class="pun">{</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> abc </span><span class="pun">*</span><span class="pln">p</span><span class="pun">;}</span><span class="pln"> var3</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نتيجةً لما سبق، يمكن للمتغير <code>var2.p</code> أن يخزن عنوان <code>var1</code>، وليس عنوان <code>var3</code> قطعًا الذي هو من نوع مختلف. يمكن تصحيح ما سبق (بفرض أنك لم تتعمد فعله) على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_54" style=""><span class="kwd">struct</span><span class="pln"> xyz</span><span class="pun">{</span><span class="typ">float</span><span class="pln"> x</span><span class="pun">;}</span><span class="pln"> var1</span><span class="pun">;</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> xyz</span><span class="pun">;</span><span class="pln">     </span><span class="com">/* نوع جديد غير مكتمل */</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> abc</span><span class="pun">{</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> xyz </span><span class="pun">*</span><span class="pln">p</span><span class="pun">;}</span><span class="pln"> var2</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> xyz</span><span class="pun">{</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> abc </span><span class="pun">*</span><span class="pln">p</span><span class="pun">;}</span><span class="pln"> var3</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُستكمل نوع الهيكل أو الاتحاد عند الوصول إلى قوس الإغلاق <code>{</code>، ويجب أن يحتوي عضوًا واحدًا على الأقل أو نحصل على سلوك غير محدد.
</p>

<p>
	نستطيع الحصول على أنواع غير مكتملة عن طريق تصريح مصفوفة دون تحديد حجمها، ويصنف النوع على أنه غير مكتمل حتى يقدم تصريحًا آخرًا حجمها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_56" style=""><span class="typ">int</span><span class="pln"> ar</span><span class="pun">[];</span><span class="pln">       </span><span class="com">/* نوع غير مكتمل */</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> ar</span><span class="pun">[</span><span class="lit">5</span><span class="pun">];</span><span class="pln">      </span><span class="com">/* نكمل النوع هنا */</span></pre>

<p>
	سيعمل المثال السابق إن جربته فقط في حال كانت التصريحات خارج أي كتلة برمجية (تصريحات خارجية)، إلا أن السبب في ذلك ليس متعلقًا بموضوعنا.
</p>

<p>
	بالعودة إلى مثال القوائم المترابطة، كان لدينا ثلاث عناصر مترابطة في القائمة، التي يمكن بناؤها على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_58" style=""><span class="kwd">struct</span><span class="pln"> list_ele</span><span class="pun">{</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> data</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> list_ele </span><span class="pun">*</span><span class="pln">pointer</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">ar</span><span class="pun">[</span><span class="lit">3</span><span class="pun">];</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">

      ar</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">data </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln">
      ar</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">pointer </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ar</span><span class="pun">[</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
      ar</span><span class="pun">[</span><span class="lit">1</span><span class="pun">].</span><span class="pln">data </span><span class="pun">=</span><span class="pln"> </span><span class="lit">99</span><span class="pun">;</span><span class="pln">
      ar</span><span class="pun">[</span><span class="lit">1</span><span class="pun">].</span><span class="pln">pointer </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ar</span><span class="pun">[</span><span class="lit">2</span><span class="pun">];</span><span class="pln">
      ar</span><span class="pun">[</span><span class="lit">2</span><span class="pun">].</span><span class="pln">data </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">7</span><span class="pun">;</span><span class="pln">
      ar</span><span class="pun">[</span><span class="lit">2</span><span class="pun">].</span><span class="pln">pointer </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">      </span><span class="com">/* ترميز نهاية المصفوفة */</span><span class="pln">
      </span><span class="kwd">return</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	مثال 5
</p>

<p>
	يمكننا طباعة محتويات القائمة بطريقتين، إما بالمرور على المصفوفة بحسب دليلها index، أو باستخدام المؤشرات كما سنوضح في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_60" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> list_ele</span><span class="pun">{</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> data</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> list_ele </span><span class="pun">*</span><span class="pln">pointer</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">ar</span><span class="pun">[</span><span class="lit">3</span><span class="pun">];</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">

      </span><span class="kwd">struct</span><span class="pln"> list_ele </span><span class="pun">*</span><span class="pln">lp</span><span class="pun">;</span><span class="pln">

      ar</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">data </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln">
      ar</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">pointer </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ar</span><span class="pun">[</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
      ar</span><span class="pun">[</span><span class="lit">1</span><span class="pun">].</span><span class="pln">data </span><span class="pun">=</span><span class="pln"> </span><span class="lit">99</span><span class="pun">;</span><span class="pln">
      ar</span><span class="pun">[</span><span class="lit">1</span><span class="pun">].</span><span class="pln">pointer </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ar</span><span class="pun">[</span><span class="lit">2</span><span class="pun">];</span><span class="pln">
      ar</span><span class="pun">[</span><span class="lit">2</span><span class="pun">].</span><span class="pln">data </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">7</span><span class="pun">;</span><span class="pln">
      ar</span><span class="pun">[</span><span class="lit">2</span><span class="pun">].</span><span class="pln">pointer </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">      </span><span class="com">/* ترميز نهاية المصفوفة */</span><span class="pln">

      </span><span class="com">/* اتباع المؤشرات */</span><span class="pln">
      lp </span><span class="pun">=</span><span class="pln"> ar</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">lp</span><span class="pun">){</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"contents %d\n"</span><span class="pun">,</span><span class="pln"> lp</span><span class="pun">-&gt;</span><span class="pln">data</span><span class="pun">);</span><span class="pln">
              lp </span><span class="pun">=</span><span class="pln"> lp</span><span class="pun">-&gt;</span><span class="pln">pointer</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	مثال 6
</p>

<p>
	الطريقة التي تُستخدم فيها المؤشرات في المثال السابق مثيرةٌ للاهتمام، لاحظ كيف أن المؤشر الذي يشير إلى عنصر ما يُستخدم للإشارة إلى العنصر الذي يليه حتى إيجاد المؤشر ذو القيمة <code>0</code>، مما يتسبب بتوقف حلقة <code>while</code> التكرارية. يمكن ترتيب المؤشرات بأي طريقة وهذا ما يجعل القائمة هيكلًا مرن التعامل. إليك دالةً يمكن تضمينها مثل جزء من برنامجنا السابق بهدف ترتيب القائمة المترابطة بحسب قيمة بياناتها العددية، وذلك عن طريق إعادة ترتيب المؤشرات حتى الوصول إلى عناصر القائمة عند المرور عليها بالترتيب. من المهم أن نشير هنا إلى أن البيانات لا تُنسخ، إذ تعيد الدالة مؤشرًا إلى بداية القائمة لأن بدايتها لا تساوي إلى التعبير <code>ar[0]‎</code> بالضرورة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_62" style=""><span class="kwd">struct</span><span class="pln"> list_ele </span><span class="pun">*</span><span class="pln">
sortfun</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> list_ele </span><span class="pun">*</span><span class="typ">list</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">

      </span><span class="typ">int</span><span class="pln"> exchange</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> list_ele </span><span class="pun">*</span><span class="pln">nextp</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">thisp</span><span class="pun">,</span><span class="pln"> dummy</span><span class="pun">;</span><span class="pln">

      </span><span class="com">/*
       * ‏الخوارزمية على النحو التالي‏‏:‏
       * البحث عبر القائمة بصورةٍ متكررة
       * ‏إذا وجد عنصرين خارج الترتيب‏‎‏
       * اِربطهما بصورةٍ معاكسة
       * توقف عند المرور بجميع عناصر القائمة
       * دون أي تبديل مطلوب
       * يحدث الخلط عند العمل على العنصر خلف العنصر الأول المثير للاهتمام
       * وهذا بسبب الآليات البسيطة المتعلقة بربط العناصر وإلغاء ربطها 
       */</span><span class="pln">

      dummy</span><span class="pun">.</span><span class="pln">pointer </span><span class="pun">=</span><span class="pln"> </span><span class="typ">list</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">do</span><span class="pun">{</span><span class="pln">
              exchange </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
              thisp </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">dummy</span><span class="pun">;</span><span class="pln">
              </span><span class="kwd">while</span><span class="pun">(</span><span class="pln"> </span><span class="pun">(</span><span class="pln">nextp </span><span class="pun">=</span><span class="pln"> thisp</span><span class="pun">-&gt;</span><span class="pln">pointer</span><span class="pun">)</span><span class="pln">
                      </span><span class="pun">&amp;&amp;</span><span class="pln"> nextp</span><span class="pun">-&gt;</span><span class="pln">pointer</span><span class="pun">){</span><span class="pln">
                      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">nextp</span><span class="pun">-&gt;</span><span class="pln">data </span><span class="pun">&lt;</span><span class="pln"> nextp</span><span class="pun">-&gt;</span><span class="pln">pointer</span><span class="pun">-&gt;</span><span class="pln">data</span><span class="pun">){</span><span class="pln">
                              </span><span class="com">/* exchange */</span><span class="pln">
                              exchange </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
                              thisp</span><span class="pun">-&gt;</span><span class="pln">pointer </span><span class="pun">=</span><span class="pln"> nextp</span><span class="pun">-&gt;</span><span class="pln">pointer</span><span class="pun">;</span><span class="pln">
                              nextp</span><span class="pun">-&gt;</span><span class="pln">pointer </span><span class="pun">=</span><span class="pln">
                                      thisp</span><span class="pun">-&gt;</span><span class="pln">pointer</span><span class="pun">-&gt;</span><span class="pln">pointer</span><span class="pun">;</span><span class="pln">
                              thisp</span><span class="pun">-&gt;</span><span class="pln">pointer</span><span class="pun">-&gt;</span><span class="pln">pointer </span><span class="pun">=</span><span class="pln"> nextp</span><span class="pun">;</span><span class="pln">
                      </span><span class="pun">}</span><span class="pln">
                      thisp </span><span class="pun">=</span><span class="pln"> thisp</span><span class="pun">-&gt;</span><span class="pln">pointer</span><span class="pun">;</span><span class="pln">
              </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="kwd">while</span><span class="pun">(</span><span class="pln">exchange</span><span class="pun">);</span><span class="pln">

      </span><span class="kwd">return</span><span class="pun">(</span><span class="pln">dummy</span><span class="pun">.</span><span class="pln">pointer</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	مثال 7
</p>

<p>
	ستلاحظ استخدام تعابير مشابهة للتعبير <code>thisp-&gt;pointer-&gt;pointer</code> عند التعامل مع القوائم، وبالتالي يجب أن تفهم هذه التعابير، وهي بسيطة إذ يدل شكلها على الروابط المتبعة.
</p>

<h3>
	الأشجار
</h3>

<p>
	تُعد الأشجار هيكل بيانات شائع أيضًا، وهي في حقيقة الأمر قائمةٌ مترابطةٌ ذات فروع، والنوع الأكثر شيوعًا هو <strong>الشجرة الثنائية binary tree</strong>، التي تحتوي على عناصر تُدعى العقد "nodes" كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_64" style=""><span class="kwd">struct</span><span class="pln"> tree_node</span><span class="pun">{</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> data</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> tree_node </span><span class="pun">*</span><span class="pln">left_p</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">right_p</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	تعمل الأشجار في علوم الحاسوب من الأعلى إلى الأسفل (لأسباب تاريخية لن نناقشها)، إذ توجد عقدة <strong>الجذر root</strong> أعلى الشجرة وتتفرع فروع هذه الشجرة في الأسفل. تُستبدل بيانات أعضاء الهيكل الخاصة بالعقد بقيمها في الشكل التالي والتي سنستخدمها لاحقًا.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="109382" href="https://academy.hsoub.com/uploads/monthly_2022_10/011Tree.png.618c1bddf1823acf054b0b69b4f7f508.png" rel=""><img alt="011Tree.png" class="ipsImage ipsImage_thumbnailed" data-fileid="109382" data-unique="mu2yir2ow" src="https://academy.hsoub.com/uploads/monthly_2022_10/011Tree.png.618c1bddf1823acf054b0b69b4f7f508.png"></a>
</p>

<p style="text-align: center;">
	شكل 3 شجرة
</p>

<p>
	لن تجذب الأشجار انتباهك إذا كان اهتمامك الرئيس هو التعامل مع المحارف والتلاعب بها، ولكنها مهمة جدًا بالنسبة لمصمّمي كل من قواعد البيانات والمصرّفات والأدوات المعقدة الأخرى.
</p>

<p>
	تتميز الأشجار بميزة خاصة جدًا ألا وهي أنها مرتبة، فيمكن أن تدعم بكل سهولة خوارزميات البحث الثنائي، ومن الممكن دائمًا إضافة مزيدٍ من العقد الجديدة إلى الشجرة في الأماكن المناسبة، فالشجرة هيكل بيانات مفيدٌ ومرن.
</p>

<p>
	بالنظر إلى الشكل السابق، نلاحظ أن الشجرة مبنيّةٌ بحرص حتى تكون مهمة البحث عن قيمة ما في حقول البيانات من العقد مهمةً سهلةً، وإن فرضنا أننا نريد أن نعرف فيما إذا كانت القيمة <code>x</code> موجودةً في الشجرة عبر البحث عنها، نتبع الخوارزمية التالية:
</p>

<ul>
	<li>
		نبدأ بعقدة جذر الشجرة:
	</li>
	<li>
		إذا كانت الشجرة فارغة (لا تحتوي على عقد)
		<ul>
			<li>
				إعادة القيمة "فشل البحث"
			</li>
		</ul>
	</li>
	<li>
		إذا كانت القيمة التي نبحث عنها مساويةً إلى قيمة العقدة الحالية
		<ul>
			<li>
				إعادة القيمة "نجاح البحث"
			</li>
		</ul>
	</li>
	<li>
		إذا كانت القيمة في العقدة الحالية أكبر من القيمة التي نبحث عنها
		<ul>
			<li>
				ابحث عن القيمة في الشجرة المُشار إليها بواسطة المؤشر الأيسر
			</li>
		</ul>
	</li>
	<li>
		فيما عدا ذلك، ابحث عن القيمة في الشجرة المُشار إليها بواسطة المؤشر الأيمن
	</li>
</ul>

<p>
	إليك الخوارزمية بلغة سي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_66" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> tree_node</span><span class="pun">{</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> data</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> tree_node </span><span class="pun">*</span><span class="pln">left_p</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">right_p</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">tree</span><span class="pun">[</span><span class="lit">7</span><span class="pun">];</span><span class="pln">
</span><span class="com">/*
* خوارزمية البحث ضمن الشجرة
* تبحث عن القيمة‪في الشجرة ‎v  
* تُعيد مؤشر يشير إلى أول عقدة تحوي النتيجة 
* أو تُعيد القيمة 0
*/</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> tree_node </span><span class="pun">*</span><span class="pln">
t_search</span><span class="pun">(</span><span class="kwd">struct</span><span class="pln"> tree_node </span><span class="pun">*</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> v</span><span class="pun">){</span><span class="pln">

      </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">root</span><span class="pun">){</span><span class="pln">
              </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">data </span><span class="pun">==</span><span class="pln"> v</span><span class="pun">)</span><span class="pln">
                      </span><span class="kwd">return</span><span class="pun">(</span><span class="pln">root</span><span class="pun">);</span><span class="pln">
              </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">data </span><span class="pun">&gt;</span><span class="pln"> v</span><span class="pun">)</span><span class="pln">
                      root </span><span class="pun">=</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">left_p</span><span class="pun">;</span><span class="pln">
              </span><span class="kwd">else</span><span class="pln">
                      root </span><span class="pun">=</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">right_p</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="com">/* لا يوجد أي شجرة متبقية ولم يُعثر على القيمة */</span><span class="pln">
      </span><span class="kwd">return</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="com">/* بناء الشجرة يدويًا */</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> tree_node </span><span class="pun">*</span><span class="pln">tp</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">root_p</span><span class="pun">;</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">7</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">
              </span><span class="typ">int</span><span class="pln"> j</span><span class="pun">;</span><span class="pln">
              j </span><span class="pun">=</span><span class="pln"> i</span><span class="pun">+</span><span class="lit">1</span><span class="pun">;</span><span class="pln">

              tree</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">data </span><span class="pun">=</span><span class="pln"> j</span><span class="pun">;</span><span class="pln">
              </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">j </span><span class="pun">==</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> j </span><span class="pun">==</span><span class="pln"> </span><span class="lit">6</span><span class="pun">){</span><span class="pln">
                      tree</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">left_p </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">tree</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
                      tree</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">right_p </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">tree</span><span class="pun">[</span><span class="pln">i</span><span class="pun">+</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
              </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="com">/* الجذر */</span><span class="pln">
      root_p </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">tree</span><span class="pun">[</span><span class="lit">3</span><span class="pun">];</span><span class="pln">
      root_p</span><span class="pun">-&gt;</span><span class="pln">left_p </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">tree</span><span class="pun">[</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
      root_p</span><span class="pun">-&gt;</span><span class="pln">right_p </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">tree</span><span class="pun">[</span><span class="lit">5</span><span class="pun">];</span><span class="pln">

      </span><span class="com">/* حاول أن تبحث */</span><span class="pln">
      tp </span><span class="pun">=</span><span class="pln"> t_search</span><span class="pun">(</span><span class="pln">root_p</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">tp</span><span class="pun">)</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"found at position %d\n"</span><span class="pun">,</span><span class="pln"> tp</span><span class="pun">-</span><span class="pln">tree</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">else</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"value not found\n"</span><span class="pun">);</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	مثال 8
</p>

<p>
	يعمل المثال السابق بنجاح دون أخطاء، ومن الجدير بالذكر أنه يمكننا استخدام ذلك في جعل أي قيمة مدخلة إلى الشجرة تُخزن في مكانها الصحيح باستخدام خوارزمية البحث ذاتها، أي بإضافة شيفرة برمجية إضافية تحجز مساحة لقيمة جديدة باستخدام دالة <code>malloc</code> عندما لا تجد الخوارزمية القيمة، وتُضاف العقدة الجديدة في مكان مؤشر الفراغ null pointer الأول. من المعقد تحقيق ما سبق، وذلك بسبب مشكلة التعامل مع مؤشر العقدة الجذر، ونلجأ في هذه الحالة إلى مؤشر يشير إلى مؤشر آخر. اقرأ المثال التالي بانتباه، إذ أنه أحد أكثر الأمثلة تعقيدًا حتى اللحظة، وإذا استطعت فهمه فهذا يعني أنك تستطيع فهم الأغلبية الساحقة من برامج سي.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_68" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> tree_node</span><span class="pun">{</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> data</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> tree_node </span><span class="pun">*</span><span class="pln">left_p</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">right_p</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="com">/*
* خوارزمية البحث ضمن شجرة
* ابحث عن القيمة ‫v ضمن الشجرة
* أعد مؤشرًا إلى أول عقدة تحتوي على القيمة هذه
* أعد القيمة 0 إن لم تجد نتيجة
*/</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> tree_node </span><span class="pun">*</span><span class="pln">
t_search</span><span class="pun">(</span><span class="kwd">struct</span><span class="pln"> tree_node </span><span class="pun">*</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> v</span><span class="pun">){</span><span class="pln">

      </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">root</span><span class="pun">){</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"looking for %d, looking at %d\n"</span><span class="pun">,</span><span class="pln">
                      v</span><span class="pun">,</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">data</span><span class="pun">);</span><span class="pln">
              </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">data </span><span class="pun">==</span><span class="pln"> v</span><span class="pun">)</span><span class="pln">
                      </span><span class="kwd">return</span><span class="pun">(</span><span class="pln">root</span><span class="pun">);</span><span class="pln">
              </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">data </span><span class="pun">&gt;</span><span class="pln"> v</span><span class="pun">)</span><span class="pln">
                      root </span><span class="pun">=</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">left_p</span><span class="pun">;</span><span class="pln">
              </span><span class="kwd">else</span><span class="pln">
                      root </span><span class="pun">=</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">right_p</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="com">/* value not found, no tree left */</span><span class="pln">
      </span><span class="kwd">return</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">/*
* أدخل عقدة ضمن شجرة
* أعد 0 عند نجاح العملية أو
* أعد 1 إن كانت القيمة موجودة في الشجرة
* ‫أعد 2 إن حصل خطأ في عملية حجز الذاكرة malloc error
*/</span><span class="pln">
</span><span class="typ">int</span><span class="pln">
t_insert</span><span class="pun">(</span><span class="kwd">struct</span><span class="pln"> tree_node </span><span class="pun">**</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> v</span><span class="pun">){</span><span class="pln">

      </span><span class="kwd">while</span><span class="pun">(*</span><span class="pln">root</span><span class="pun">){</span><span class="pln">
              </span><span class="kwd">if</span><span class="pun">((*</span><span class="pln">root</span><span class="pun">)-&gt;</span><span class="pln">data </span><span class="pun">==</span><span class="pln"> v</span><span class="pun">)</span><span class="pln">
                      </span><span class="kwd">return</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
              </span><span class="kwd">if</span><span class="pun">((*</span><span class="pln">root</span><span class="pun">)-&gt;</span><span class="pln">data </span><span class="pun">&gt;</span><span class="pln"> v</span><span class="pun">)</span><span class="pln">
                      root </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;((*</span><span class="pln">root</span><span class="pun">)-&gt;</span><span class="pln">left_p</span><span class="pun">);</span><span class="pln">
              </span><span class="kwd">else</span><span class="pln">
                      root </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;((*</span><span class="pln">root</span><span class="pun">)-&gt;</span><span class="pln">right_p</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="com">/* value not found, no tree left */</span><span class="pln">
      </span><span class="kwd">if</span><span class="pun">((*</span><span class="pln">root </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">struct</span><span class="pln"> tree_node </span><span class="pun">*)</span><span class="pln">
              malloc</span><span class="pun">(</span><span class="kwd">sizeof</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">struct</span><span class="pln"> tree_node</span><span class="pun">)))</span><span class="pln">
                      </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
              </span><span class="kwd">return</span><span class="pun">(</span><span class="lit">2</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">(*</span><span class="pln">root</span><span class="pun">)-&gt;</span><span class="pln">data </span><span class="pun">=</span><span class="pln"> v</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">(*</span><span class="pln">root</span><span class="pun">)-&gt;</span><span class="pln">left_p </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">(*</span><span class="pln">root</span><span class="pun">)-&gt;</span><span class="pln">right_p </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">return</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="com">/* construct tree by hand */</span><span class="pln">
      </span><span class="kwd">struct</span><span class="pln"> tree_node </span><span class="pun">*</span><span class="pln">tp</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">root_p </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">

      </span><span class="com">/* we ingore the return value of t_insert */</span><span class="pln">
      t_insert</span><span class="pun">(&amp;</span><span class="pln">root_p</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">);</span><span class="pln">
      t_insert</span><span class="pun">(&amp;</span><span class="pln">root_p</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span><span class="pln">
      t_insert</span><span class="pun">(&amp;</span><span class="pln">root_p</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">);</span><span class="pln">
      t_insert</span><span class="pun">(&amp;</span><span class="pln">root_p</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln">
      t_insert</span><span class="pun">(&amp;</span><span class="pln">root_p</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">);</span><span class="pln">
      t_insert</span><span class="pun">(&amp;</span><span class="pln">root_p</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">);</span><span class="pln">
      t_insert</span><span class="pun">(&amp;</span><span class="pln">root_p</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">);</span><span class="pln">

      </span><span class="com">/* try the search */</span><span class="pln">
      </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">9</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">
              tp </span><span class="pun">=</span><span class="pln"> t_search</span><span class="pun">(</span><span class="pln">root_p</span><span class="pun">,</span><span class="pln"> i</span><span class="pun">);</span><span class="pln">
              </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">tp</span><span class="pun">)</span><span class="pln">
                      printf</span><span class="pun">(</span><span class="str">"%d found\n"</span><span class="pun">,</span><span class="pln"> i</span><span class="pun">);</span><span class="pln">
              </span><span class="kwd">else</span><span class="pln">
                      printf</span><span class="pun">(</span><span class="str">"%d not found\n"</span><span class="pun">,</span><span class="pln"> i</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	مثال 9
</p>

<p>
	تسمح لك الخوارزمية التالية بالمرور على الشجرة وزيارة جميع العُقد بالترتيب باستخدام التعاودية recursion، وهي أحد أكثر الأمثلة أناقةً، انظر إليها وحاول فهمها.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2705_70" style=""><span class="kwd">void</span><span class="pln">
t_walk</span><span class="pun">(</span><span class="kwd">struct</span><span class="pln"> tree_node </span><span class="pun">*</span><span class="pln">root_p</span><span class="pun">){</span><span class="pln">

      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">root_p </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
              </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
      t_walk</span><span class="pun">(</span><span class="pln">root_p</span><span class="pun">-&gt;</span><span class="pln">left_p</span><span class="pun">);</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"%d\n"</span><span class="pun">,</span><span class="pln"> root_p</span><span class="pun">-&gt;</span><span class="pln">data</span><span class="pun">);</span><span class="pln">
      t_walk</span><span class="pun">(</span><span class="pln">root_p</span><span class="pun">-&gt;</span><span class="pln">right_p</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	مثال 10
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter6/" rel="external nofollow">Structured Data Types</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/c/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%AA%D8%AD%D8%A7%D8%AF%D8%A7%D8%AA-unions-%D9%88%D8%AD%D9%82%D9%88%D9%84-%D8%A7%D9%84%D8%A8%D8%AA%D8%A7%D8%AA-bitfields-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%AF%D8%AF%D8%A7%D8%AA-eums-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1748/" rel="">هياكل البيانات: الاتحادات Unions وحقول البتات Bitfields والمعددات Eums في لغة سي C</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-pointers-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1682/" rel="">التعامل مع المؤشرات Pointers في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A8%D9%86%D9%8A%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1607/" rel="">بنية برنامج لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%B9%D9%88%D8%A7%D9%85%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1613/" rel="">العوامل في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%B9%D9%88%D8%A7%D9%85%D9%84-%D8%A7%D9%84%D9%85%D9%86%D8%B7%D9%82%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-%D9%88%D8%B9%D9%88%D8%A7%D9%85%D9%84-%D8%A3%D8%AE%D8%B1%D9%89-r1616/" rel="">العوامل المنطقية في لغة سي C وعوامل أخرى</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1747</guid><pubDate>Fri, 14 Oct 2022 16:07:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x645;&#x624;&#x634;&#x631;&#x627;&#x62A; Pointers &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-pointers-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1682/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_08/63060f53d92ff_----Pointers---C-.png.c5b6c0446ca89a3b47ecfcd305ba15a4.png" /></p>
<p>
	تحدثنا في مقال سابق عن <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-pointers-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1679/" rel="">المؤشرات Pointers في لغة سي C</a> وتعرفنا عليها بوصفها موضوعًا مهمًا للغاية في لغة سي، وسنكمل في هذا المقال الحديث عنها وكيفية التعامل معها مثل استخدامها ضمن التعابير التي تحوي عوامل الإسناد والزيادة والنقصان بالإضافة لأمثلة عملية، فإن لم تطلع على الفصل المذكور، فارجع له أولًا لارتباط المقالين مع بعضهما.
</p>

<h2>
	مؤشرات الدوال
</h2>

<p>
	من المفيد أن يكون لدينا إمكانية استخدام المؤشرات على الدوال، كما أن التصريح عن هذا النوع من المؤشرات سهلٌ عن طريق كتابته وكأنك تصرّح عن دالة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6975_10" style=""><span class="typ">int</span><span class="pln"> func</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> a</span><span class="pun">,</span><span class="pln"> </span><span class="typ">float</span><span class="pln"> b</span><span class="pun">);</span></pre>

<p>
	ومن ثم إضافة قوسين حول اسم الدالة والرمز <code>*</code> أمامه، مما يدل على أن هذا التصريح يعود لمؤشر. لاحظ أن التخلي عن القوسين يتسبب بالتصريح عن دالة تُعيد مؤشرًا حسب قوانين الأسبقية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6975_12" style=""><span class="com">/* int دالةٌ تعيد مؤشرًا إلى قيمة صحيحة */</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">func</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> a</span><span class="pun">,</span><span class="pln"> </span><span class="typ">float</span><span class="pln"> b</span><span class="pun">);</span><span class="pln">

</span><span class="com">/* مؤشر إلى دالة تعيد قيمة صحيحة int*/</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> </span><span class="pun">(*</span><span class="pln">func</span><span class="pun">)(</span><span class="typ">int</span><span class="pln"> a</span><span class="pun">,</span><span class="pln"> </span><span class="typ">float</span><span class="pln"> b</span><span class="pun">);</span></pre>

<p>
	حالما تحصل على المؤشر تستطيع إسناد العنوان إلى النوع المحدد للدالة باستخدام اسمها، إذ يُحوَّل اسم الدالة إلى عنوان في أي تعبير تحتويه بصورةٍ مشابهة لاسم <a href="https://academy.hsoub.com/programming/c/%D8%A8%D8%B9%D8%B6-%D8%A7%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A7%D9%84%D8%A8%D8%B3%D9%8A%D8%B7%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D8%B3%D8%A7%D8%A8%D9%8A%D8%A9-r1608/" rel="">المصفوفة</a>، ويمكنك استدعاء الدالة في هذه الحالة باستخدام إحدى الطريقتين:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6975_15" style=""><span class="pun">(*</span><span class="pln">func</span><span class="pun">)(</span><span class="lit">1</span><span class="pun">,</span><span class="lit">2</span><span class="pun">);</span><span class="pln">
</span><span class="com">/* or */</span><span class="pln">
func</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="lit">2</span><span class="pun">);</span></pre>

<p>
	تفضّل لغة سي المعيارية الطريقة الثانية، إليك مثالًا بسيطًا عنها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6975_19" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> func</span><span class="pun">(</span><span class="typ">int</span><span class="pun">);</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">(*</span><span class="pln">fp</span><span class="pun">)(</span><span class="typ">int</span><span class="pun">);</span><span class="pln">

      fp </span><span class="pun">=</span><span class="pln"> func</span><span class="pun">;</span><span class="pln">

      </span><span class="pun">(*</span><span class="pln">fp</span><span class="pun">)(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
      fp</span><span class="pun">(</span><span class="lit">2</span><span class="pun">);</span><span class="pln">

      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">void</span><span class="pln">
func</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> arg</span><span class="pun">){</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"%d\n"</span><span class="pun">,</span><span class="pln"> arg</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 1]
</p>

<p>
	يمكنك توظيف مصفوفة من المؤشرات التي تشير إلى مصفوفات مختلفة إذا أردت كتابة آلةً محدودة الحالات finite state machine، وسيبدو التصريح عنها مماثلًا لما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6975_21" style=""><span class="kwd">void</span><span class="pln"> </span><span class="pun">(*</span><span class="pln">fparr</span><span class="pun">[])(</span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="typ">float</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                              </span><span class="com">/* المهيئات initializers*/</span><span class="pln">
                      </span><span class="pun">};</span><span class="pln">
</span><span class="com">/* استدعاء أحد القيم */</span><span class="pln">

fparr</span><span class="pun">[</span><span class="lit">5</span><span class="pun">](</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3.4</span><span class="pun">);</span></pre>

<p style="text-align: center;">
	[مثال 17.5]
</p>

<p>
	ولكننا لن نتكلم عن هذه الطريقة.
</p>

<h2>
	المؤشرات في التعابير
</h2>

<p>
	بعد إدخال الأنواع المؤهّلة qualified types ومفهوم الأنواع غير المُكتملة incomplete types مع استخدام مؤشر الفراغ <code>* void</code>، أصبح هناك بعض القواعد المعقدة عن مزج المؤشرات وما هو مسموحٌ لك فعليًا في العمليات الحسابية معها. قد تستطيع تجاوز هذه القواعد دون أي مشكلات، لأن معظمها "بديهي" ولكننا سنتكلم عنها بغض النظر عن ذلك، ولا شكّ أنك سترغب بقراءة معيار لغة سي لتحرّي الدقة، لأن ما سيأتي هو تفسير بلغة بسيطة لما ورد في المعيار.
</p>

<p>
	لعلك لا تعلم بدقة ما الذي يقصده المعيار عندما يذكر مصطلحي <strong>الكائنات objects</strong> و<strong>الأنواع غير المُكتملة incomplete types</strong>، فقد استخدمنا هذه المصطلحات حتى الآن بتهاون. يُعد الكائن جزءًا من البيانات المخزنة التي يمكن تفسير محتوياتها إلى قيمة، وبناءً على ذلك فالدالة ليست كائنًا؛ بينما يُعرف النوع غير المكتمل بكونه نوعًا معروفًا ذا اسم معروف لكن دون حجم محدّد بعد، ويمكنك الحصول على هذا النوع عن طريق وسيلتين، هما:
</p>

<ol>
	<li>
		التصريح عن مصفوفة دون تحديد حجمها: <code>int x[];‎</code> ويجب توفير المزيد من المعلومات بخصوص هذه المصفوفة في التعريف لاحقًا، ويبقى النوع غير مُكتملًا حتى الوصول لنقطة التعريف.
	</li>
	<li>
		التصريح عن <strong>هيكل Structure</strong> أو <strong>اتحاد Union</strong> دون التعريف عن محتوياته، ويجب التعريف عن محتوياته لاحقًا في هذه الحالة، ويبقى النوع غير مُكتملًا حتى الوصول لنقطة التعريف.
	</li>
</ol>

<p>
	سنناقش المزيد عن الأنواع غير المكتملة لاحقًا.
</p>

<h3>
	التحويلات
</h3>

<p>
	يمكن تحويل المؤشرات التي تشير إلى <code>void</code> إلى مؤشرات تشير إلى أي كائن أو نوع غير مكتمل، وتحصل على قيمةٍ مساوية لقيمة المؤشر الأصل بعد تحويل مؤشر يشير إلى كائن أو نوع غير مكتمل إلى مؤشر من نوع <code>* void</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6975_23" style=""><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ip</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">vp</span><span class="pun">;</span><span class="pln">

ip </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">i</span><span class="pun">;</span><span class="pln">
vp </span><span class="pun">=</span><span class="pln"> ip</span><span class="pun">;</span><span class="pln">
ip </span><span class="pun">=</span><span class="pln"> vp</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">if</span><span class="pun">(</span><span class="pln">ip </span><span class="pun">!=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">i</span><span class="pun">)</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"Compiler error\n"</span><span class="pun">);</span></pre>

<p>
	يمكن تحويل مؤشر من نوع غير مؤهّل unqualified إلى مؤشر من نوع مؤهل، ولكن العكس غير ممكن، وستكون قيمة المؤشرين متكافئتين:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6975_25" style=""><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ip</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">cpi</span><span class="pun">;</span><span class="pln">

ip </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">i</span><span class="pun">;</span><span class="pln">
cpi </span><span class="pun">=</span><span class="pln"> ip</span><span class="pun">;</span><span class="pln">       </span><span class="com">/* مسموح*/</span><span class="pln">
</span><span class="kwd">if</span><span class="pun">(</span><span class="pln">cpi </span><span class="pun">!=</span><span class="pln"> ip</span><span class="pun">)</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"Compiler error\n"</span><span class="pun">);</span><span class="pln">
ip </span><span class="pun">=</span><span class="pln"> cpi</span><span class="pun">;</span><span class="pln">       </span><span class="com">/* ممنوع */</span></pre>

<p>
	لا يساوي مؤشر ثابت فارغ null pointer constant (سنتكلم عن هذا النوع لاحقًا) أيّ مؤشر يشير لأي كائن أو دالة.
</p>

<h3>
	العمليات الحسابية
</h3>

<p>
	يمكن للتعابير Expressions أن تجمع (أو تطرح، وهو ما يكافئ جمع قيم سالبة) أعدادًا صحيحةً إلى قيمة المؤشرات بغض النظر عن نوع الكائن الذي تشير إليه، وتكون النتيجة مماثلةً لنوع المؤشر؛ وفي حالة إضافة القيمة <code>n</code>، فسيشير المؤشر إلى العنصر الذي يلي العنصر السابق ضمن المصفوفة بمقدار <code>n</code>، والاستخدام الأكثر شيوعًا لهذه الميزة هي بإضافة <code>1</code> إلى المؤشر لتمريره على المصفوفة من بدايتها إلى نهايتها، إلا أن استخدام قيم مغايرة للقيمة <code>1</code> والطرح بدلًا من الجمع ممكن.
</p>

<p>
	نحصل على حالة طفحان overflow أو طفحان تجاوز الحد الأدنى underflow إذا كان المؤشر الناتج عن عملية الجمع يشير إلى ما يسبق المصفوفة أو ما يلي العنصر المعدوم الأخير للمصفوفة، وهذا يعني أن النتيجة غير مُعرّفة.
</p>

<p>
	يملك العنصر الأخير الزائد في المصفوفة عنوانًا صالحًا، ويؤكد المعيار لنا ذلك، إلا أنه ليس من المفترض أن تحاول الوصول إلى ذلك العنصر، وعنوانه موجودٌ للتأكد من وجوده لتجنُّب الوقوع في حالة طفحان.
</p>

<p>
	تعمّدنا استخدام الكلمة "تعبير" عوضًا عن قولنا "إضافة قيمة إلى المؤشر بنفسه"، إلا أنه يمكنك فعل ذلك شرط ألا يكون المؤشر مؤهلًا بالكلمة المفتاحية "const"، ويكافئ طبعًا استخدام عامل الزيادة "++" وعامل النقصان "--" جمع أو طرح واحد.
</p>

<p>
	يمكن طرح مؤشرين من <strong>أنواع متوافقة compatible types</strong> أو غير مؤهلة من بعضهما بعضًا، وتكون النتيجة من النوع "ptrdiff_t"، المعرّف في ملف الترويسة <code>stddef.h</code>، إلا أنه يجب أن يشير كلا المؤشرين إلى المصفوفة ذاتها، أو على الأقل أن يشير واحدًا منها إلى ما بعد أو قبل المصفوفة، وإلا سنحصل على سلوك غير محدد، وتكون نتيجة عملية الطرح هي عدد العناصر التي تفصل المؤشرين ضمن المصفوفة. إليك المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6975_27" style=""><span class="typ">int</span><span class="pln"> x</span><span class="pun">[</span><span class="lit">100</span><span class="pun">];</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">pi</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">cpi </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">x</span><span class="pun">[</span><span class="lit">99</span><span class="pun">];</span><span class="pln"> </span><span class="com">/* x إلى العنصر الأخير من ال cpi يشير*/</span><span class="pln">

pi </span><span class="pun">=</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">if</span><span class="pun">((</span><span class="pln">cpi </span><span class="pun">-</span><span class="pln"> pi</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">99</span><span class="pun">)</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"Error\n"</span><span class="pun">);</span><span class="pln">

pi </span><span class="pun">=</span><span class="pln"> cpi</span><span class="pun">;</span><span class="pln">
pi</span><span class="pun">++;</span><span class="pln">                   </span><span class="com">/* increment past end of x */</span><span class="pln">
</span><span class="kwd">if</span><span class="pun">((</span><span class="pln">pi </span><span class="pun">-</span><span class="pln"> cpi</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"Error\n"</span><span class="pun">);</span></pre>

<h3>
	التعابير العلاقية
</h3>

<p>
	تسمح لنا التعابير العلاقية بالمقارنة بين المؤشرات، لكن يمكنك فقط مقارنة:
</p>

<ul>
	<li>
		المؤشرات التي تشير لكائنات ذات أنواع متوافقة مع بعضها الآخر.
	</li>
	<li>
		المؤشرات التي تشير لأنواع غير مكتملة متوافقة مع بعضها الآخر.
	</li>
</ul>

<p>
	ولا يهم إذا كانت الأنواع المُشارة إليها مؤهلة أو غير مؤهلة.
</p>

<p>
	إذا تساوت قيم مؤشرين، فهذا يعني أن المؤشرين يشيران إلى الشيء ذاته، سواءٌ كان هذا الشيء كائنًا أو عنصرًا غير موجودًا خارج مصفوفة ما (راجع فقرة العمليات الحسابية أعلاه). تقدِّم العوامل العلاقية "&gt;" و "=&gt;" وغيرها النتيجة التي تتوقعها عند استخدامها مع المؤشرات ضمن نفس المصفوفة، وإذا كانت قيمة أحد المؤشرات أقل مقارنةً مع الآخر، فهذا يعني أنه يشير لقيمةٍ أقرب لمقدمة المصفوفة (العنصر ذو الدليل index الأقل).
</p>

<p>
	يمكن إسناد مؤشر فارغ ثابت إلى مؤشر آخر، وسيكون متساوي مع مؤشر فارغ ثابت آخر إذا فحصناهما باستخدام عامل المقارنة، بينما لن يتساوى مؤشر فارغ ثابت أو غير ثابت عند مقارنتهما مع أي مؤشر آخر يشير لشيءٍ ما.
</p>

<h3>
	الإسناد
</h3>

<p>
	يمكنك <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-pointers-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1679/" rel="">استخدام المؤشرات</a> مع عوامل الإسناد، شرط أن يستوفي الاستخدام الشروط التالية:
</p>

<ul>
	<li>
		يجب أن يكون الجانب الأيسر من عامل الإسناد مؤشرًا، وأن يكون الجانب الأيمن منه مؤشرًا فارغًا ثابتًا.
	</li>
	<li>
		يجب أن يكون مُعاملٌ من المعاملات مؤشرًا يشير إلى كائن أو نوع غير مُكتمل، والمعامل الآخر مؤشرًا إلى الفراغ "void"، سواءٌ كان مؤهلًا أو لا.
	</li>
	<li>
		يُعدّ المُعاملان مؤشرين لأنواع متوافقة سواءٌ كانت مؤهلةً أم لا.
	</li>
</ul>

<p>
	يجب أن يكون للنوع المُشار إليه في الحالتين الأخيرتين على الجانب الأيسر من عامل الإسناد النوع ذاته من المؤهلات على الأقل، والموافق لمؤهل النوع الواقع على الجانب الأيمن من عامل الإسناد، أو أكثر من مؤهل مماثل.
</p>

<p>
	يمكنك إذًا إسناد مؤشر يشير إلى قيمة صحيحة "int" إلى مؤشر يشير إلى قيمة من نوع عدد صحيح ثابت "const int" (مؤهلات النوع الأيسر تزيد عن مؤهلات النوع الأيمن) ولكن لا يمكنك إسناد مؤشر يشير إلى "const int" إلى مؤشر يشير إلى "int"، والأمر منطقي جدًا إذا أخذت لحظةً للتفكير به.
</p>

<p>
	يمكن استخدام العاملين "=+" و "=-" مع المؤشرات طالما أن الجانب الأيسر من العامل مؤشر يشير إلى كائن، والجانب الأيمن من العامل تعبير ينتج قيمةً صحيحة integral، وتوضح قوانين العمليات الحسابية في الفقرات السابقة ما سيحصل في هذه الحالة.
</p>

<h3>
	العامل الشرطي
</h3>

<p>
	وضّحنا سابقًا سلوك العامل الشرطي conditional operator عند استخدامه مع المؤشرات.
</p>

<h2>
	المصفوفات وعامل &amp; والدوال
</h2>

<p>
	ذكرنا عدّة مرات أنه يجري تحويل اسم المصفوفة إلى عنوانها وعنصرها الأول، وقلنا أن الاستثناء الوحيد هو عند استخدام اسم المصفوفة مع عامل "sizeof"، وهو عاملٌ مهمٌ إذا أردت استخدام الدالة "malloc"، إلا أن هناك استثناءً آخر، ألا وهو عندما يكون اسم المصفوفة مُعاملًا لعامل "&amp;" (عنوان العامل)، إذ يُحوّل اسم المصفوفة هنا إلى عنوان <strong>كامل</strong> المصفوفة بدلًا من عنوان عنصرها الأول عادةً، لكن ما الفرق؟ لعلك تعتقد أن العنوانين متماثلان، إلا أن الفرق هو نوعهما، فبالنسبة لمصفوفةٍ تحتوي "n" عنصر بنوع "T"، يكون عنوان عنصرها الأول من نوع "مؤشر إلى T"، بينما يكون عنوان كامل المصفوفة من نوع " مؤشر إلى مصفوفة من n عنصر من نوع T"، وهو مختلف جدًا. إليك مثالًا عن ذلك:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6975_30" style=""><span class="typ">int</span><span class="pln"> ar</span><span class="pun">[</span><span class="lit">10</span><span class="pun">];</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ip</span><span class="pun">;</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> </span><span class="pun">(*</span><span class="pln">ar10i</span><span class="pun">)[</span><span class="lit">10</span><span class="pun">];</span><span class="pln">       </span><span class="com">/* مؤشر لمصفوفة من 10 عناصر صحيحة */</span><span class="pln">

ip </span><span class="pun">=</span><span class="pln"> ar</span><span class="pun">;</span><span class="pln">                </span><span class="com">/* عنوان العنصر الأول */</span><span class="pln">
ip </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ar</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">            </span><span class="com">/* عنوان العنصر الأول */</span><span class="pln">
ar10i </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ar</span><span class="pun">;</span><span class="pln">            </span><span class="com">/* عنوان كامل المصفوفة */</span></pre>

<p>
	أين تُستخدم المؤشرات إلى المصفوفات؟ في الحقيقة ليس غالبًا، إلا أننا نعلم أن التصريح عن مصفوفة متعددة الأبعاد هو في الحقيقة تصريح عن مصفوفة مصفوفات. إليك مثالًا يستخدم هذا المفهوم (إلا أن فهم ما يفعل يقع على عاتقك)، وليس من الشائع استخدام هذه الطريقة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6975_32" style=""><span class="typ">int</span><span class="pln"> ar2d</span><span class="pun">[</span><span class="lit">5</span><span class="pun">][</span><span class="lit">4</span><span class="pun">];</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> </span><span class="pun">(*</span><span class="pln">ar4i</span><span class="pun">)[</span><span class="lit">4</span><span class="pun">];</span><span class="pln"> </span><span class="com">/* مؤشر إلى مصفوفة من 4 أعداد صحيحة */</span><span class="pln">

</span><span class="kwd">for</span><span class="pun">(</span><span class="pln">ar4i</span><span class="pun">=</span><span class="pln"> ar2d</span><span class="pun">;</span><span class="pln"> ar4i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="pun">&amp;(</span><span class="pln">ar2d</span><span class="pun">[</span><span class="lit">5</span><span class="pun">]);</span><span class="pln"> ar4i</span><span class="pun">++)</span><span class="pln">
      </span><span class="pun">(*</span><span class="pln">ar4i</span><span class="pun">)[</span><span class="lit">2</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> </span><span class="com">/* ar2d[n][2] = 0 */</span></pre>

<p>
	ما قد يثير اهتمامك أكثر من عناوين المصفوفات هو ما الذي قد يحدث عندما نصرِّح عن دالة تأخذ مصفوفةً في أحد وسطائها. بالنظر إلى أن المصفوفة تحوّل إلى عنوان عنصرها الأول فحتى لو حاولت تمرير مصفوفة إلى دالة باستخدام اسم المصفوفة وسيطًا، فسينتهي بك الأمر بتمرير مؤشر إلى عنصر المصفوفة الأول. لكن ماذا لو صرّحت عن الدالة بكونها تأخذ وسيطًا من نوع "مصفوفة من نوع ما" على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6975_34" style=""><span class="kwd">void</span><span class="pln"> f</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> ar</span><span class="pun">[</span><span class="lit">10</span><span class="pun">]);</span></pre>

<p>
	ما الذي يحدث في هذه الحالة؟ قد تفاجئك الإجابة هنا، إذ أن المصرّف ينظر إلى السطر السابق ويقول لنفسه "سيكون هذا مؤشرًا لهذه المصفوفة" ويعيد كتابة الوسيط على أنه من نوع مؤشر، ووفقًا لذلك نجد أن التصريحات الثلاثة التالية متكافئة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6975_36" style=""><span class="kwd">void</span><span class="pln"> f</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> ar</span><span class="pun">[</span><span class="lit">10</span><span class="pun">]);</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> f</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ar</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> f</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> ar</span><span class="pun">[]);</span><span class="pln">       </span><span class="com">/* !حجم المصفوفة هنا لا علاقة له */</span></pre>

<p>
	قد تضع يدك على رأسك بعد هذه المعلومة، لكن تمهّل! إليك بعض الأسئلة للتهدئة من غضبك وإحباطك:
</p>

<ul>
	<li>
		لم كانت المعلومة السابقة منطقية؟
	</li>
	<li>
		لماذا تعمل التعابير بالصياغة <code>[ar[5</code> أو أي صياغةٍ أخرى ضمن التصريح عن دالة، ثم داخل الدالة كما هو متوقعٌ منها؟
	</li>
</ul>

<p>
	فكّر في الأسئلة السابقة، وستفهم استخدام المؤشرات مع المصفوفات بصورةٍ ممتازة عندما تتوصل لإجابة ترضيك.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter5/" rel="external nofollow">Arrays and Pointers</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/c/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%82%D9%88%D8%A7%D8%A6%D9%85-%D8%A7%D9%84%D9%85%D8%AA%D8%B1%D8%A7%D8%A8%D8%B7%D8%A9-linked-lists-%D9%88%D8%A7%D9%84%D8%A3%D8%B4%D8%AC%D8%A7%D8%B1-trees-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1747/" rel="">هياكل البيانات: القوائم المترابطة Linked lists والأشجار Trees في لغة سي C</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/c/%D8%B9%D8%A7%D9%85%D9%84-sizeof-%D9%88%D8%AD%D8%AC%D8%B2-%D9%85%D8%B3%D8%A7%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1681/" rel="">عامل sizeof وحجز مساحات التخزين في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-pointers-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1679/" rel="">المؤشرات Pointers في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">التعامل مع المحارف والسلاسل النصية في لغة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1682</guid><pubDate>Tue, 20 Sep 2022 16:08:00 +0000</pubDate></item><item><title>&#x639;&#x627;&#x645;&#x644; sizeof &#x648;&#x62D;&#x62C;&#x632; &#x645;&#x633;&#x627;&#x62D;&#x627;&#x62A; &#x627;&#x644;&#x62A;&#x62E;&#x632;&#x64A;&#x646; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D8%B9%D8%A7%D9%85%D9%84-sizeof-%D9%88%D8%AD%D8%AC%D8%B2-%D9%85%D8%B3%D8%A7%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1681/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_08/63060af9016db_--sizeof------C-.png.712162ea8dc481c656d02325de1678a0.png" /></p>
<p>
	يُعيد العامل "sizeof" حجم المُعامل operator بالبايتات، وتعتمد نتيجة العامل "sizeof" بكونها عددًا صحيحًا عديم الإشارة "unsigned int" أو عددًا كبيرًا عديم الإشارة "unsigned long" على التطبيق implementation، وهذا هو السبب في تفادينا لأي مشكلات في المثال السابق (<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">المقال السابق</a>) عند التصريح عن دالة <code>malloc</code> على الرغم من عدم تزويد التصريح بأي تفاصيل عن معاملاتها؛ إذ يجب استخدام ملف الترويسة <code>stdlib.h</code> عوضًا عن ذلك عادةً للتصريح عن <code>malloc</code> على النحو الصحيح. إليك المثال ذاته ولكن بتركيز على جعله قابلًا للتنقل portable عبر مختلف الأجهزة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8996_8" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">     </span><span class="com">/* malloc() يتضمن ملف الترويسة تصريحًا عن */</span><span class="pln">
</span><span class="typ">float</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fp</span><span class="pun">;</span><span class="pln">

fp </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">float</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">malloc</span><span class="pun">(</span><span class="kwd">sizeof</span><span class="pun">(</span><span class="typ">float</span><span class="pun">));</span></pre>

<p>
	يجب أن يُكتب معامل <code>sizeof</code> داخل قوسين إذا كان فقط اسمًا لنوع بيانات (وهي الحالة في مثالنا السابق)، بينما يمكنك التخلي عن القوسين إذا كنت تستخدم اسم كائن بيانات عوضًا عن ذلك، ولكن هذه الحالة نادرة الحدوث.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8996_10" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ip</span><span class="pun">,</span><span class="pln"> ar</span><span class="pun">[</span><span class="lit">100</span><span class="pun">];</span><span class="pln">
ip </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">malloc</span><span class="pun">(</span><span class="kwd">sizeof</span><span class="pln"> ar</span><span class="pun">);</span></pre>

<p>
	لدينا في المثال السابق مصفوفة باسم <code>ar</code> مكونةٌ من 100 عنصر من نوع عدد صحيح <code>int</code>، ويشير <code>ip</code> إلى مساحة التخزين الخاصة بهذه المصفوفة (مساحةٌ لمئة قيمة من نوع <code>int</code>) بعد استدعاء <code>malloc</code> (بفرض أن الاستدعاء كان ناجحًا).
</p>

<p>
	تعدّ <code>char</code> (محرف وهي اختصارٌ إلى character) وحدة القياس الأساسية للتخزين في لغة سي، وتساوي بايتًا واحدًا، جرّب نتيجة التعليمة الآتية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8996_12" style=""><span class="kwd">sizeof</span><span class="pun">(</span><span class="kwd">char</span><span class="pun">)</span></pre>

<p>
	وبناءً على ذلك، يمكنك حجز مساحة لعشرة قيم من نوع <code>char</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8996_14" style=""><span class="pln">malloc</span><span class="pun">(</span><span class="lit">10</span><span class="pun">)</span></pre>

<p>
	ولحجز مساحة لمصفوفة بحجم عشرة قيم من نوع <code>int</code>، نكتب:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8996_16" style=""><span class="pln">malloc</span><span class="pun">(</span><span class="kwd">sizeof</span><span class="pun">(</span><span class="typ">int</span><span class="pun">[</span><span class="lit">10</span><span class="pun">]))</span></pre>

<p>
	تُعيد الدالة <code>malloc</code> مؤشرًا إلى الفراغ null pointer في حال لم تتوفر المساحة الكافية للإشارة إلى خطأ ما. يحتوي ملف الترويسة <code>stdio.h</code> ثابتًا معرّفًا باسم <code>NULL</code>، والذي يُستخدم عادةً للتحقق من القيمة المُعادة من الدالة <code>malloc</code> ودوال أخرى من المكتبة القياسية، وتُعد القيمة <code>0</code> أو <code>‎(void *)0</code> مساويةً لهذا الثابت ويمكن استخدامها.
</p>

<p>
	إليك المثال التالي لتوضيح استخدام الدالة <code>malloc</code>، إذ يقرأ البرنامج في المثال سلاسلًا نصيةً بعدد <code>MAXSTRING</code> من الدخل، ثمّ <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">يرتب السلاسل النصية</a> أبجديًّا باستخدام الدالة <code>strcmp</code>. يُشار إلى نهاية السلسلة النصية بمحرف الهروب escape character التالي <code>n\</code>، وتُرتَّب السلاسل <a href="https://academy.hsoub.com/programming/c/%D8%A8%D8%B9%D8%B6-%D8%A7%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A7%D9%84%D8%A8%D8%B3%D9%8A%D8%B7%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D8%B3%D8%A7%D8%A8%D9%8A%D8%A9-r1608/" rel="">باستخدام مصفوفة</a> من المؤشرات تُشير إلى السلسلة النصية وتبديل مواضع المؤشرات حتى الوصول إلى الترتيب الصحيح، مما يجنُّبنا عناء نسخ السلاسل النصية ويحسّن من سرعة تنفيذ البرنامج ويحد من هدر الموارد إلى حدٍّ ما.
</p>

<p>
	استخدمنا في الإصدار الأول من المثال مصفوفةً ثابتة الحجم، ثم استخدمنا في الإصدار الثاني حجز المساحة باستخدام <code>malloc</code> لكل سلسلة نصية عند وقت التشغيل run-time، بينما بقيت مصفوفة المؤشرات -لسوء الحظ- ثابتة الحجم، إلا أنه يمكننا تطبيق حلّ أفضل باستخدام قائمة مترابطة Linked list، أو أي هيكل بيانات مشابه لتخزين المؤشرات دون الحاجة لاستخدام المصفوفات ثابتة الحجم إطلاقًا، ولكننا لم نتكلم عن هياكل البيانات بعد.
</p>

<p>
	إليك ما يبدو عليه هيكل برنامجنا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8996_18" style=""><span class="kwd">while</span><span class="pun">(</span><span class="pln">number of strings read </span><span class="pun">&lt;</span><span class="pln"> MAXSTRING
      </span><span class="pun">&amp;&amp;</span><span class="pln"> input still remains</span><span class="pun">){</span><span class="pln">

              read next string</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
sort array of pointers</span><span class="pun">;</span><span class="pln">
print array of pointers</span><span class="pun">;</span><span class="pln">
exit</span><span class="pun">;</span></pre>

<p>
	سنستخدم بعض الدوال في برنامجنا أيضًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8996_20" style=""><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">next_string</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">destination</span><span class="pun">)</span></pre>

<p>
	تقرأ الدالة السابقة سطرًا من المحارف بحيث ينتهي السطر بالمحرف <code>n\</code> من دخل البرنامج، وتُسند المحارف البالغ عددها <code>MAXLEN-1</code> إلى المصفوفة المُشار إليها بالمصفوفة الهدف <code>destination</code>، إذ يمثّل <code>MAXLEN</code> قيمةً ثابتةً لطول السلسلة النصية العظمى.
</p>

<p>
	إذا كان المحرف الأول المقروء هو <code>EOF</code> (أي نهاية الملف)، أعِد مؤشرًا إلى الفراغ، وفيما عدا ذلك أعِد عنوان بداية السلسلة النصية (الهدف destination)، بحيث تحتوي السلسلة النصية الهدف دائمًا على المحرف <code>n\</code>، الذي يشير إلى نهاية السلسلة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8996_23" style=""><span class="kwd">void</span><span class="pln"> sort_arr</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p_array</span><span class="pun">[])</span></pre>

<p>
	تمثل المصفوفة <code>p_array[]‎</code> مصفوفة المؤشرات التي تشير <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1609/" rel="">للمحارف</a>، ويمكن أن تكون المصفوفة كبيرة الحجم ولكن يُشار إلى نهايتها بأول عنصر يحتوي على مؤشر فراغ null pointer.
</p>

<p>
	ترتّب الدالة <code>sort_arr</code> المؤشرات بحيث تُشير إلى السلاسل النصية المرتبة أبجديًا عند اجتياز مصفوفة المؤشرات بناءً على دليل index المؤشر.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8996_25" style=""><span class="kwd">void</span><span class="pln"> print_arr</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p_array</span><span class="pun">[])</span></pre>

<p>
	تُشابه دالة <code>print_arr</code> الدالة <code>sort_arr</code> ولكنها تطبع السلاسل النصية حسب ترتيبها الأبجدي.
</p>

<p>
	تذكّر أنه يجري تحويل اسم المصفوفة إلى عنوانها وعنصرها الأول في أي تعبير يحتوي على اسمها، ومن شأن ذلك أن يساعدك في فهم الأمثلة على نحوٍ أفضل؛ والأمر مماثلٌ بالنسبة لمصفوفة ثنائية البعد، مثل مصفوفة <code>strings</code> في المثال التالي، فنوع التعبير <code>strings[1][2]‎</code> هو <code>char</code>، ولكن للعنصر <code>strings[1]‎</code> نوع "مصفوفة من <code>char</code>"، ولذلك يُحوَّل اسم المصفوفة إلى عنوان العنصر الأول ونحصل على <code>‎&amp;strings[1][0]‎</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8996_27" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;string.h&gt;</span><span class="pln">

</span><span class="com">#define</span><span class="pln"> MAXSTRING       </span><span class="lit">50</span><span class="pln">      </span><span class="com">/* العدد الأعظمي للسلاسل النصية */</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> MAXLEN          </span><span class="lit">80</span><span class="pln">      </span><span class="com">/* الطول الأعظمي لكل سلسلة نصية */</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> print_arr</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p_array</span><span class="pun">[]);</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> sort_arr</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p_array</span><span class="pun">[]);</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">next_string</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">destination</span><span class="pun">);</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="com">/* نصرح عن المصفوفة مع إضافة عنصر فارغ في نهايتها */</span><span class="pln">
      </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p_array</span><span class="pun">[</span><span class="pln">MAXSTRING</span><span class="pun">+</span><span class="lit">1</span><span class="pun">];</span><span class="pln">

      </span><span class="com">/* مصفوفة تخزين السلاسل النصية */</span><span class="pln">
      </span><span class="kwd">char</span><span class="pln"> strings</span><span class="pun">[</span><span class="pln">MAXSTRING</span><span class="pun">][</span><span class="pln">MAXLEN</span><span class="pun">];</span><span class="pln">

      </span><span class="com">/* عدد السلاسل النصية المقروءة */</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> nstrings</span><span class="pun">;</span><span class="pln">

      nstrings </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">nstrings </span><span class="pun">&lt;</span><span class="pln"> MAXSTRING </span><span class="pun">&amp;&amp;</span><span class="pln">
              next_string</span><span class="pun">(</span><span class="pln">strings</span><span class="pun">[</span><span class="pln">nstrings</span><span class="pun">])</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">){</span><span class="pln">

              p_array</span><span class="pun">[</span><span class="pln">nstrings</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> strings</span><span class="pun">[</span><span class="pln">nstrings</span><span class="pun">];</span><span class="pln">
              nstrings</span><span class="pun">++;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="com">/* إعدام قيمة المصفوفة */</span><span class="pln">
      p_array</span><span class="pun">[</span><span class="pln">nstrings</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

      sort_arr</span><span class="pun">(</span><span class="pln">p_array</span><span class="pun">);</span><span class="pln">
      print_arr</span><span class="pun">(</span><span class="pln">p_array</span><span class="pun">);</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> print_arr</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p_array</span><span class="pun">[]){</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> index</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">index </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> p_array</span><span class="pun">[</span><span class="pln">index</span><span class="pun">]</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> index</span><span class="pun">++)</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"%s\n"</span><span class="pun">,</span><span class="pln"> p_array</span><span class="pun">[</span><span class="pln">index</span><span class="pun">]);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


</span><span class="kwd">void</span><span class="pln"> sort_arr</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p_array</span><span class="pun">[]){</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> comp_val</span><span class="pun">,</span><span class="pln"> low_index</span><span class="pun">,</span><span class="pln"> hi_index</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">tmp</span><span class="pun">;</span><span class="pln">

      </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">low_index </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
              p_array</span><span class="pun">[</span><span class="pln">low_index</span><span class="pun">]</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln">
                              p_array</span><span class="pun">[</span><span class="pln">low_index</span><span class="pun">+</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
                      low_index</span><span class="pun">++){</span><span class="pln">

              </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">hi_index </span><span class="pun">=</span><span class="pln"> low_index</span><span class="pun">+</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
                      p_array</span><span class="pun">[</span><span class="pln">hi_index</span><span class="pun">]</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
                              hi_index</span><span class="pun">++){</span><span class="pln">

                      comp_val</span><span class="pun">=</span><span class="pln">strcmp</span><span class="pun">(</span><span class="pln">p_array</span><span class="pun">[</span><span class="pln">hi_index</span><span class="pun">],</span><span class="pln">
                              p_array</span><span class="pun">[</span><span class="pln">low_index</span><span class="pun">]);</span><span class="pln">
                      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">comp_val </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
                              </span><span class="kwd">continue</span><span class="pun">;</span><span class="pln">
                      </span><span class="com">/* التبديل بين السلسلتين النصيتين */</span><span class="pln">
                      tmp </span><span class="pun">=</span><span class="pln"> p_array</span><span class="pun">[</span><span class="pln">hi_index</span><span class="pun">];</span><span class="pln">
                      p_array</span><span class="pun">[</span><span class="pln">hi_index</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> p_array</span><span class="pun">[</span><span class="pln">low_index</span><span class="pun">];</span><span class="pln">
                      p_array</span><span class="pun">[</span><span class="pln">low_index</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> tmp</span><span class="pun">;</span><span class="pln">
              </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">



</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">next_string</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">destination</span><span class="pun">){</span><span class="pln">
      </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">cp</span><span class="pun">;</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> c</span><span class="pun">;</span><span class="pln">

      cp </span><span class="pun">=</span><span class="pln"> destination</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">while</span><span class="pun">((</span><span class="pln">c </span><span class="pun">=</span><span class="pln"> getchar</span><span class="pun">())</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="str">'\n'</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> c </span><span class="pun">!=</span><span class="pln"> EOF</span><span class="pun">){</span><span class="pln">
              </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">cp</span><span class="pun">-</span><span class="pln">destination </span><span class="pun">&lt;</span><span class="pln"> MAXLEN</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">
                      </span><span class="pun">*</span><span class="pln">cp</span><span class="pun">++</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> c</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">*</span><span class="pln">cp </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">c </span><span class="pun">==</span><span class="pln"> EOF </span><span class="pun">&amp;&amp;</span><span class="pln"> cp </span><span class="pun">==</span><span class="pln"> destination</span><span class="pun">)</span><span class="pln">
              </span><span class="kwd">return</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">return</span><span class="pun">(</span><span class="pln">destination</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 1]
</p>

<p>
	إعادة الدالة <code>next_string</code> لمؤشر ليس من قبيل المصادفة، إذ أصبح بإمكاننا الآن الاستغناء عن استخدام مصفوفة السلاسل النصية واستخدام <code>next_string</code> لحجز مساحة التخزين الموافقة لها.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8996_29" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;string.h&gt;</span><span class="pln">

</span><span class="com">#define</span><span class="pln"> MAXSTRING       </span><span class="lit">50</span><span class="pln">      </span><span class="com">/* العدد الأعظمي للسلاسل النصية */</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> MAXLEN          </span><span class="lit">80</span><span class="pln">      </span><span class="com">/* الطول الأعظمي لكل سلسلة نصية  */</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> print_arr</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p_array</span><span class="pun">[]);</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> sort_arr</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p_array</span><span class="pun">[]);</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">next_string</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p_array</span><span class="pun">[</span><span class="pln">MAXSTRING</span><span class="pun">+</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> nstrings</span><span class="pun">;</span><span class="pln">

      nstrings </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">nstrings </span><span class="pun">&lt;</span><span class="pln"> MAXSTRING </span><span class="pun">&amp;&amp;</span><span class="pln">
              </span><span class="pun">(</span><span class="pln">p_array</span><span class="pun">[</span><span class="pln">nstrings</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> next_string</span><span class="pun">())</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">){</span><span class="pln">

              nstrings</span><span class="pun">++;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="com">/* إعدام قيمة المصفوفة */</span><span class="pln">
      p_array</span><span class="pun">[</span><span class="pln">nstrings</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

      sort_arr</span><span class="pun">(</span><span class="pln">p_array</span><span class="pun">);</span><span class="pln">
      print_arr</span><span class="pun">(</span><span class="pln">p_array</span><span class="pun">);</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> print_arr</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p_array</span><span class="pun">[]){</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> index</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">index </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> p_array</span><span class="pun">[</span><span class="pln">index</span><span class="pun">]</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> index</span><span class="pun">++)</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"%s\n"</span><span class="pun">,</span><span class="pln"> p_array</span><span class="pun">[</span><span class="pln">index</span><span class="pun">]);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


</span><span class="kwd">void</span><span class="pln"> sort_arr</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p_array</span><span class="pun">[]){</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> comp_val</span><span class="pun">,</span><span class="pln"> low_index</span><span class="pun">,</span><span class="pln"> hi_index</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">tmp</span><span class="pun">;</span><span class="pln">

      </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">low_index </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
              p_array</span><span class="pun">[</span><span class="pln">low_index</span><span class="pun">]</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln">
                      p_array</span><span class="pun">[</span><span class="pln">low_index</span><span class="pun">+</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
                      low_index</span><span class="pun">++){</span><span class="pln">

              </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">hi_index </span><span class="pun">=</span><span class="pln"> low_index</span><span class="pun">+</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
                      p_array</span><span class="pun">[</span><span class="pln">hi_index</span><span class="pun">]</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
                              hi_index</span><span class="pun">++){</span><span class="pln">

                      comp_val</span><span class="pun">=</span><span class="pln">strcmp</span><span class="pun">(</span><span class="pln">p_array</span><span class="pun">[</span><span class="pln">hi_index</span><span class="pun">],</span><span class="pln">
                              p_array</span><span class="pun">[</span><span class="pln">low_index</span><span class="pun">]);</span><span class="pln">
                      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">comp_val </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
                              </span><span class="kwd">continue</span><span class="pun">;</span><span class="pln">
                      </span><span class="com">/* التبديل بين السلسلتين النصيتين */</span><span class="pln">
                      tmp </span><span class="pun">=</span><span class="pln"> p_array</span><span class="pun">[</span><span class="pln">hi_index</span><span class="pun">];</span><span class="pln">
                      p_array</span><span class="pun">[</span><span class="pln">hi_index</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> p_array</span><span class="pun">[</span><span class="pln">low_index</span><span class="pun">];</span><span class="pln">
                      p_array</span><span class="pun">[</span><span class="pln">low_index</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> tmp</span><span class="pun">;</span><span class="pln">
              </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">next_string</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">){</span><span class="pln">
      </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">cp</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">destination</span><span class="pun">;</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> c</span><span class="pun">;</span><span class="pln">

      destination </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">malloc</span><span class="pun">(</span><span class="pln">MAXLEN</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">destination </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">){</span><span class="pln">
              cp </span><span class="pun">=</span><span class="pln"> destination</span><span class="pun">;</span><span class="pln">
              </span><span class="kwd">while</span><span class="pun">((</span><span class="pln">c </span><span class="pun">=</span><span class="pln"> getchar</span><span class="pun">())</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="str">'\n'</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> c </span><span class="pun">!=</span><span class="pln"> EOF</span><span class="pun">){</span><span class="pln">
                      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">cp</span><span class="pun">-</span><span class="pln">destination </span><span class="pun">&lt;</span><span class="pln"> MAXLEN</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">
                              </span><span class="pun">*</span><span class="pln">cp</span><span class="pun">++</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> c</span><span class="pun">;</span><span class="pln">
              </span><span class="pun">}</span><span class="pln">
              </span><span class="pun">*</span><span class="pln">cp </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
              </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">c </span><span class="pun">==</span><span class="pln"> EOF </span><span class="pun">&amp;&amp;</span><span class="pln"> cp </span><span class="pun">==</span><span class="pln"> destination</span><span class="pun">)</span><span class="pln">
                      </span><span class="kwd">return</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="kwd">return</span><span class="pun">(</span><span class="pln">destination</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 2]
</p>

<p>
	وأخيرًا إليك المثال كاملًا مع استخدام مصفوفة <code>p_array</code> للدالة <code>malloc</code>، ولاحظ إعادة كتابة معظم أدلة المصفوفة لتستخدم ترميز المؤشرات. إذا كنت تشعر بالإرهاق من جميع المعلومات التي قرأتها فتجاوز المثال التالي، فهو صعبٌ بعض الشيء.
</p>

<p>
	شرح المثال: تعني <code>char **p</code> مؤشرًا يشير إلى المؤشر الذي يشير إلى محرف، ويجد معظم مبرمجو لغة سي هذه الطريقة في استخدام المؤشرات صعبة الفهم.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8996_31" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">stdlib</span><span class="pun">.</span><span class="pln">hi</span><span class="pun">&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;string.h&gt;</span><span class="pln">

</span><span class="com">#define</span><span class="pln"> MAXSTRING       </span><span class="lit">50</span><span class="pln">      </span><span class="com">/* العدد الأعظمي للسلاسل النصية */</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> MAXLEN          </span><span class="lit">80</span><span class="pln">      </span><span class="com">/*الطول الأعظمي لكل سلسلة نصية  */</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> print_arr</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">**</span><span class="pln">p_array</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> sort_arr</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">**</span><span class="pln">p_array</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">next_string</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">**</span><span class="pln">p_array</span><span class="pun">;</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> nstrings</span><span class="pun">;</span><span class="pln">   </span><span class="com">/* عدد السلاسل النصية المقروءة */</span><span class="pln">

      p_array </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">**)</span><span class="pln">malloc</span><span class="pun">(</span><span class="pln">
                      </span><span class="kwd">sizeof</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*[</span><span class="pln">MAXSTRING</span><span class="pun">+</span><span class="lit">1</span><span class="pun">]));</span><span class="pln">
      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">p_array </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">){</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"No memory\n"</span><span class="pun">);</span><span class="pln">
              exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">

      nstrings </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">nstrings </span><span class="pun">&lt;</span><span class="pln"> MAXSTRING </span><span class="pun">&amp;&amp;</span><span class="pln">
              </span><span class="pun">(</span><span class="pln">p_array</span><span class="pun">[</span><span class="pln">nstrings</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> next_string</span><span class="pun">())</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">){</span><span class="pln">

              nstrings</span><span class="pun">++;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="com">/* إعدام قيمة المصفوفة */</span><span class="pln">
      p_array</span><span class="pun">[</span><span class="pln">nstrings</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

      sort_arr</span><span class="pun">(</span><span class="pln">p_array</span><span class="pun">);</span><span class="pln">
      print_arr</span><span class="pun">(</span><span class="pln">p_array</span><span class="pun">);</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> print_arr</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">**</span><span class="pln">p_array</span><span class="pun">){</span><span class="pln">
      </span><span class="kwd">while</span><span class="pun">(*</span><span class="pln">p_array</span><span class="pun">)</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"%s\n"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p_array</span><span class="pun">++);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


</span><span class="kwd">void</span><span class="pln"> sort_arr</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">**</span><span class="pln">p_array</span><span class="pun">){</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">**</span><span class="pln">lo_p</span><span class="pun">,</span><span class="pln"> </span><span class="pun">**</span><span class="pln">hi_p</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">tmp</span><span class="pun">;</span><span class="pln">

      </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">lo_p </span><span class="pun">=</span><span class="pln"> p_array</span><span class="pun">;</span><span class="pln">
              </span><span class="pun">*</span><span class="pln">lo_p </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="pun">*(</span><span class="pln">lo_p</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
                                      lo_p</span><span class="pun">++){</span><span class="pln">
              </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">hi_p </span><span class="pun">=</span><span class="pln"> lo_p</span><span class="pun">+</span><span class="lit">1</span><span class="pun">;</span><span class="pln"> </span><span class="pun">*</span><span class="pln">hi_p </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> hi_p</span><span class="pun">++){</span><span class="pln">

                      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">strcmp</span><span class="pun">(*</span><span class="pln">hi_p</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">lo_p</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
                              </span><span class="kwd">continue</span><span class="pun">;</span><span class="pln">
                      </span><span class="com">/* التبديل بين السلسلتين النصيتين */</span><span class="pln">
                      tmp </span><span class="pun">=</span><span class="pln"> </span><span class="pun">*</span><span class="pln">hi_p</span><span class="pun">;</span><span class="pln">
                      </span><span class="pun">*</span><span class="pln">hi_p </span><span class="pun">=</span><span class="pln"> </span><span class="pun">*</span><span class="pln">lo_p</span><span class="pun">;</span><span class="pln">
                      </span><span class="pun">*</span><span class="pln">lo_p </span><span class="pun">=</span><span class="pln"> tmp</span><span class="pun">;</span><span class="pln">
              </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">



</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">next_string</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">){</span><span class="pln">
      </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">cp</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">destination</span><span class="pun">;</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> c</span><span class="pun">;</span><span class="pln">

      destination </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">malloc</span><span class="pun">(</span><span class="pln">MAXLEN</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">destination </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">){</span><span class="pln">
              cp </span><span class="pun">=</span><span class="pln"> destination</span><span class="pun">;</span><span class="pln">
              </span><span class="kwd">while</span><span class="pun">((</span><span class="pln">c </span><span class="pun">=</span><span class="pln"> getchar</span><span class="pun">())</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="str">'\n'</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> c </span><span class="pun">!=</span><span class="pln"> EOF</span><span class="pun">){</span><span class="pln">
                      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">cp</span><span class="pun">-</span><span class="pln">destination </span><span class="pun">&lt;</span><span class="pln"> MAXLEN</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">
                              </span><span class="pun">*</span><span class="pln">cp</span><span class="pun">++</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> c</span><span class="pun">;</span><span class="pln">
              </span><span class="pun">}</span><span class="pln">
              </span><span class="pun">*</span><span class="pln">cp </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
              </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">c </span><span class="pun">==</span><span class="pln"> EOF </span><span class="pun">&amp;&amp;</span><span class="pln"> cp </span><span class="pun">==</span><span class="pln"> destination</span><span class="pun">)</span><span class="pln">
                      </span><span class="kwd">return</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="kwd">return</span><span class="pun">(</span><span class="pln">destination</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 3]
</p>

<p>
	سنستعرض مثالًا آخر لتوضيح استخدام دالة <code>malloc</code> وإمكاناتها في التعامل مع السلاسل النصية الطويلة؛ إذ يقرأ المثال السلاسل النصية من الدخل ويبحث عن محرف سطر جديد لتحديد نهاية السلسلة النصية (أي <code>n\</code>)، ثم يطبع السلسلة النصية إلى الخرج، ويتوقف البرنامج عن العمل عندما يصادف محرف نهاية الملف <code>EOF</code>. تُسنَد المحارف إلى مصفوفة، ويُدلّ على نهاية المصفوفة -كما هو معتاد- بالقيمة صفر، مع ملاحظة أن محرف السطر الجديد لا يُخزَّن بالمصفوفة بل يُستخدم فقط لتحديد سطر الدخل الواجب طباعته للخرج. لا يعلم البرنامج طول السلسلة النصية تحديدًا، ولذلك يبدأ بفحص كل عشرة محارف وحجز المساحة الخاصة بهم (الثابت <code>GROW_BY</code>).
</p>

<p>
	تُستدعى الدالة <code>malloc</code> في حال كانت السلسلة النصية أطول من عشرة محارف لحجز المساحة للسلسلة النصية وإضافة عشرة محارف أخرى، ثم تُنسخ المحارف الحالية للمساحة الجديدة وتُستخدم من البرنامج وتُحرّر المساحة القديمة.
</p>

<p>
	تُستخدم الدالة <code>free</code> لتحرير المساحة القديمة المحجوزة من <code>malloc</code> مسبقًا، إذ يجب عليك تحرير المساحة غير المُستخدمة بعد الآن دوريًا قبل أن تتراكم، واستخدام <code>free</code> يحرّر المساحة ويسمح بإعادة استخدامها لاحقًا.
</p>

<p>
	يستخدم البرنامج الدالة <code>fprintf</code> لعرض أي أخطاء، وهي دالةٌ مشابهة للدالة <code>printf</code> التي اعتدنا على رؤيتها، والفرق الوحيد بينهما هو أن الدالة <code>fprintf</code> تأخذ وسيطًا إضافيًّا يدل على وسيط الخرج الذي سيُطبع إليه، وهناك ثابتان لهذا الغرض معرّفان في ملف الترويسة <code>stdio.h</code>؛ إذ أن استخدام الثابت الأول <code>stdout</code> يعني استخدام خرج البرنامج القياسي، بينما يشير استخدام الثابت الثاني <code>stderr</code> إلى مجرى أخطاء البرنامج القياسي standard error stream، وقد يكون وسيطا الخرج متماثلين في بعض الأنظمة إلا أن بعض الأنظمة الأخرى تفصل بين الاثنين.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8996_33" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;string.h&gt;</span><span class="pln">

</span><span class="com">#define</span><span class="pln"> GROW_BY </span><span class="lit">10</span><span class="pln">      </span><span class="com">/* يزداد حجم السلسلة النصية كل مرة بمقدار 10 */</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">str_p</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">next_p</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">tmp_p</span><span class="pun">;</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> ch</span><span class="pun">,</span><span class="pln"> need</span><span class="pun">,</span><span class="pln"> chars_read</span><span class="pun">;</span><span class="pln">

      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">GROW_BY </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">2</span><span class="pun">){</span><span class="pln">
              fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="pln">
                      </span><span class="str">"Growth constant too small\n"</span><span class="pun">);</span><span class="pln">
              exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">

      str_p </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">malloc</span><span class="pun">(</span><span class="pln">GROW_BY</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">str_p </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">){</span><span class="pln">
              fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="str">"No initial store\n"</span><span class="pun">);</span><span class="pln">
              exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">

      next_p </span><span class="pun">=</span><span class="pln"> str_p</span><span class="pun">;</span><span class="pln">
      chars_read </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">while</span><span class="pun">((</span><span class="pln">ch </span><span class="pun">=</span><span class="pln"> getchar</span><span class="pun">())</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> EOF</span><span class="pun">){</span><span class="pln">
</span><span class="com">/* (*) */</span><span class="pln">
              </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">ch </span><span class="pun">==</span><span class="pln"> </span><span class="str">'\n'</span><span class="pun">){</span><span class="pln">
                      </span><span class="com">/* الإشارة إلى نهاية السطر */</span><span class="pln">
                      </span><span class="pun">*</span><span class="pln">next_p </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
                      printf</span><span class="pun">(</span><span class="str">"%s\n"</span><span class="pun">,</span><span class="pln"> str_p</span><span class="pun">);</span><span class="pln">
                      free</span><span class="pun">(</span><span class="pln">str_p</span><span class="pun">);</span><span class="pln">
                      chars_read </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
                      str_p </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">malloc</span><span class="pun">(</span><span class="pln">GROW_BY</span><span class="pun">);</span><span class="pln">
                      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">str_p </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">){</span><span class="pln">
                              fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="str">"No initial store\n"</span><span class="pun">);</span><span class="pln">
                              exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
                      </span><span class="pun">}</span><span class="pln">
                      next_p </span><span class="pun">=</span><span class="pln"> str_p</span><span class="pun">;</span><span class="pln">
                      </span><span class="kwd">continue</span><span class="pun">;</span><span class="pln">
              </span><span class="pun">}</span><span class="pln">
              </span><span class="com">/*
               * التحقق من وصولنا إلى نهاية المساحة المحجوزة
               */</span><span class="pln">
              </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">chars_read </span><span class="pun">==</span><span class="pln"> GROW_BY</span><span class="pun">-</span><span class="lit">1</span><span class="pun">){</span><span class="pln">
                      </span><span class="pun">*</span><span class="pln">next_p </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">    </span><span class="com">/* للدلالة على نهاية السلسلة النصية */</span><span class="pln">
                      </span><span class="com">/* نستخدم الطرح بين المؤشرات لإيجاد طول السلسلة النصية الحالية*/</span><span class="pln">
                      need </span><span class="pun">=</span><span class="pln"> next_p </span><span class="pun">-</span><span class="pln"> str_p </span><span class="pun">+</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
                      tmp_p </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">malloc</span><span class="pun">(</span><span class="pln">need</span><span class="pun">+</span><span class="pln">GROW_BY</span><span class="pun">);</span><span class="pln">
                      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">tmp_p </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">){</span><span class="pln">
                              fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="str">"No more store\n"</span><span class="pun">);</span><span class="pln">
                              exit</span><span class="pun">(</span><span class="pln">EXIT_FAILURE</span><span class="pun">);</span><span class="pln">
                      </span><span class="pun">}</span><span class="pln">
                      </span><span class="com">/*
                      ننسخ السلسلة النصية باستخدام دالة المكتبة
                       */</span><span class="pln">
                      strcpy</span><span class="pun">(</span><span class="pln">tmp_p</span><span class="pun">,</span><span class="pln"> str_p</span><span class="pun">);</span><span class="pln">
                      free</span><span class="pun">(</span><span class="pln">str_p</span><span class="pun">);</span><span class="pln">
                      str_p </span><span class="pun">=</span><span class="pln"> tmp_p</span><span class="pun">;</span><span class="pln">
                      </span><span class="com">/*
                       * next_p إعادة ضبط
                       */</span><span class="pln">
                      next_p </span><span class="pun">=</span><span class="pln"> str_p </span><span class="pun">+</span><span class="pln"> need</span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
                      chars_read </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
              </span><span class="pun">}</span><span class="pln">
              </span><span class="com">/*
               * إسناد المحرف إلى نهاية السلسلة النصية
               */</span><span class="pln">
              </span><span class="pun">*</span><span class="pln">next_p</span><span class="pun">++</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> ch</span><span class="pun">;</span><span class="pln">
              chars_read</span><span class="pun">++;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="com">/*
       * عند وصولنا إلى نهاية الملف
       * هل توجد محارف غير مطبوعة؟
       */</span><span class="pln">
      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">str_p </span><span class="pun">-</span><span class="pln"> next_p</span><span class="pun">){</span><span class="pln">
              </span><span class="pun">*</span><span class="pln">next_p </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
              fprintf</span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">,</span><span class="str">"Incomplete last line\n"</span><span class="pun">);</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"%s\n"</span><span class="pun">,</span><span class="pln"> str_p</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 4]
</p>

<p>
	(*) تُعاد الحلقة في الموضع المذكور عند كل سطر، وهناك مساحةٌ للعنصر صفر في نهاية السلسلة النصية دائمًا، لأننا نتحقق من أصغر من 2 وهو ما تحققنا منه سابقًا GROW_BY ذلك في الشرط التالي إلا في حال كان.
</p>

<p>
	قد لا يكون برنامجنا السابق مثالًا واقعيًا عن التعامل مع السلاسل النصية الطويلة، إذ يتطلب حجم التخزين الأعظمي <strong>ضعف</strong> الحجم المطلوب لأطول سلسلة نصية، ولكنه برنامج يعمل صحيحًا بغض النظر، إلا أنه يكلفنا الكثير بخصوص الموارد بنسخ السلاسل النصية ويمكن حل المشكلتين عن طريق استخدام دالة <code>realloc</code>.
</p>

<p>
	نستطيع استخدام القوائم المترابطة لطريقة أكثر تعقيدًا، مع استخدام <strong>الهياكل Structures</strong> التي سنتكلم عنها لاحقًا، إلا أن هذه الطريقة تأتي أيضًا ببعض المشكلات لأن دوال المكتبة القياسية لن تعمل عند استخدام طريقة مغايرة لتخزين السلاسل النصية.
</p>

<h2>
	ما الأشياء التي لا يستطيع العامل sizeof فعلها؟
</h2>

<p>
	يرتكب المبتدئون غالبًا الخطأ التالي عند استخدام العامل sizeof:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8996_35" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> arr</span><span class="pun">[]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"hello"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">cp </span><span class="pun">=</span><span class="pln"> arr</span><span class="pun">;</span><span class="pln">
main</span><span class="pun">(){</span><span class="pln">

      printf</span><span class="pun">(</span><span class="str">"Size of arr %lu\n"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">long</span><span class="pun">)</span><span class="pln">
                      </span><span class="kwd">sizeof</span><span class="pun">(</span><span class="pln">arr</span><span class="pun">));</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"Size of *cp %lu\n"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">long</span><span class="pun">)</span><span class="pln">
                      </span><span class="kwd">sizeof</span><span class="pun">(*</span><span class="pln">cp</span><span class="pun">));</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 5]
</p>

<p>
	<strong>لن</strong> تكون الأرقام ذاتها عند الطباعة، إذ سيعرف أولًا حجم <code>arr</code> بكونها <code>6</code> بصورةٍ صحيحة (خمسة محارف متبوعةٍ بمحرف الفراغ null)، بينما ستطبع التعليمة الثانية -على جميع الأنظمة- القيمة <code>1</code>، لأن المؤشر <code>cp*</code> من نوع <code>const char</code> ذو الحجم 1 بايت، بينما <code>arr</code> مختلفةٌ فهي مصفوفةٌ من نوع <code>const char</code>. تسبب هذه المشكلة مصدرًا للحيرة، إذ أن هذه الحالة الوحيدة التي لا يجري فيها تحويل المصفوفة إلى مؤشر أولًا، فمن المستحيل استخدام <code>sizeof</code> لإيجاد طول مصفوفة باستخدام مؤشر يشير إليها، ويجب عليك استخدام اسم المصفوفة <strong>حصرًا</strong>.
</p>

<h2>
	نوع قيمة sizeof
</h2>

<p>
	لعلك تتساءل الآن عن نتيجة التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8996_37" style=""><span class="kwd">sizeof</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="kwd">sizeof</span><span class="pln"> </span><span class="pun">(</span><span class="pln">anything legal</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span></pre>

<p>
	فما هو نوع نتيجة عامل <code>sizeof</code>؟ الإجابة على هذا السؤال معرّفة بحسب التطبيق، وقد تكون <code>unsigned long</code> أو <code>unsigned int</code> بحسب تطبيقك، إلا أن هناك شيئان يمكن فعلهما للتأكد من أنك تستخدم القيمة بصورة صحيحة، وهما:
</p>

<ul>
	<li>
		يمكنك استخدام تحويل الأنواع cast وتحويل القيمة إلى <code>unsigned long</code> قسريًا (كما فعلنا في المثال السابق).
	</li>
	<li>
		يمكنك استخدام النوع المُعرّف <code>size_t</code> الموجود في ملف الترويسة <code>stddef.h</code> كما يوضح المثال التالي:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8996_39" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stddef.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="typ">size_t</span><span class="pln"> sz</span><span class="pun">;</span><span class="pln">
      sz </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">sizeof</span><span class="pun">(</span><span class="pln">sz</span><span class="pun">);</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"size of sizeof is %lu\n"</span><span class="pun">,</span><span class="pln">
              </span><span class="pun">(</span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">long</span><span class="pun">)</span><span class="pln">sz</span><span class="pun">);</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 6]
</p>

<p>
	ترجمة -وبتصرف- لقسم Sizeof and storage allocation من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter5/" rel="external nofollow">Arrays and Pointers</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-pointers-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1682/" rel="">التعامل مع المؤشرات Pointers في لغة سي C</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">التعامل مع المحارف والسلاسل النصية في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%AB%D8%A7%D9%84%D8%AB-%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D8%A7%D9%84%D9%88%D9%87%D9%85%D9%8A%D8%A9-virtual-memory-%D9%81%D9%8A-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-r978/" rel="">الذاكرة الوهمية (Virtual memory) في نظام التشغيل</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%B1%D8%A7%D8%A8%D8%B9-%D9%81%D9%87%D9%85-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-files-%D9%88%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-file-systems-r979/" rel="">فهم الملفات Files وأنظمة الملفات file systems</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1681</guid><pubDate>Wed, 14 Sep 2022 16:08:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x645;&#x62D;&#x627;&#x631;&#x641; &#x648;&#x627;&#x644;&#x633;&#x644;&#x627;&#x633;&#x644; &#x627;&#x644;&#x646;&#x635;&#x64A;&#x629; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_08/6306034be6a55_----Characters---Strings---C.png.59e9b50b397134822541fb1993c77142.png" /></p>
<p>
	تُستخدم لغة سي على نطاق واسع في تطبيقات المعالجة والتعامل <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1609/" rel="">بالمحارف characters</a> والسلاسل النصية strings، وهذا الأمر غريب بعض الشيء لأن اللغة لا تحتوي على مزايا موجهة لهذا الغرض بالتحديد،؛ وإذا كنت معتادًا على <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة</a> التي تحتوي على مزايا موجهة للتعامل مع المحارف والسلاسل النصية، فستجد التعامل مع لغة سي بهذا الخصوص مضجرًا على أقل تقدير.
</p>

<p>
	تحتوي المكتبة القياسية على العديد من الدوال التي تساعدك في التعامل مع السلاسل النصية، إلا أن الأمر يبقى صعبًا بعض الشيء مقارنةً بلغاتٍ أخرى. على سبيل المثال، عليك استدعاء دالة مخصصة للمقارنة ما بين سلسلتين نصيتين بدلًا من استخدام عامل المساواة "="، ولكن هناك جانبٌ مشرقٌ لهذا الأمر، إذ يعني ذلك أن اللغة غير مُثقلة بطرق دعم معالجة السلاسل النصية مما يساعد بالمحافظة على برامج أصغر وأقل تشعّبًا، وحالما تكتب برنامجًا لمعالجة السلاسل النصية بلغة سي بنجاح أخيرًا، ستكون قادرًا على تشغيله بسرعة أكبر مقارنةً باللغات الأخرى.
</p>

<h2>
	التعامل مع المحارف
</h2>

<p>
	يجري التعامل مع محارف السلسلة النصية في لغة سي عن طريق التصريح عن مصفوفات، أو حجزهم ديناميكيًا، والتعامل مع المحارف وتحريكها "يدويًّا". إليك مثالًا عن برنامج يقرأ نصًّا سطرًا بسطر من دخل البرنامج القياسي، ويتوقف البرنامج عن القراءة إذا كان السطر مكوّنًا من السلسلة النصية "stop"، ويُطبع طول السطر فيما عدا ذلك. يعتمد البرنامج على تقنية تُستخدم في معظم برامج سي ألا وهي: يقرأ البرنامج المحارف ويحوّلها إلى مصفوفة ويحدّد نهاية المصفوفة بمحرفٍ إضافي له القيمة صفر؛ كما يستخدم هذا المثال دالة <code>strcmp</code> للمقارنة بين سلسلتين نصيتين من خلال تضمين المكتبة <code>string.h</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1460_7" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;string.h&gt;</span><span class="pln">

</span><span class="com">#define</span><span class="pln"> LINELNG </span><span class="lit">100</span><span class="pln">     </span><span class="com">/* الطول الأعظمي لسطر الدخل الواحد */</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="kwd">char</span><span class="pln"> in_line</span><span class="pun">[</span><span class="pln">LINELNG</span><span class="pun">];</span><span class="pln">
      </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">cp</span><span class="pun">;</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> c</span><span class="pun">;</span><span class="pln">

      cp </span><span class="pun">=</span><span class="pln"> in_line</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">while</span><span class="pun">((</span><span class="pln">c </span><span class="pun">=</span><span class="pln"> getc</span><span class="pun">(</span><span class="pln">stdin</span><span class="pun">))</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> EOF</span><span class="pun">){</span><span class="pln">
              </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">cp </span><span class="pun">==</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">in_line</span><span class="pun">[</span><span class="pln">LINELNG</span><span class="pun">-</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> c </span><span class="pun">==</span><span class="pln"> </span><span class="str">'\n'</span><span class="pun">){</span><span class="pln">
                      </span><span class="com">/*إدخال ما يدل على نهاية السطر*/</span><span class="pln">
                      </span><span class="pun">*</span><span class="pln">cp </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
                      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">strcmp</span><span class="pun">(</span><span class="pln">in_line</span><span class="pun">,</span><span class="pln"> </span><span class="str">"stop"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
                              exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
                      </span><span class="kwd">else</span><span class="pln">
                              printf</span><span class="pun">(</span><span class="str">"line was %d characters long\n"</span><span class="pun">,</span><span class="pln">
                                      </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="pln">cp</span><span class="pun">-</span><span class="pln">in_line</span><span class="pun">));</span><span class="pln">
                      cp </span><span class="pun">=</span><span class="pln"> in_line</span><span class="pun">;</span><span class="pln">
              </span><span class="pun">}</span><span class="pln">
              </span><span class="kwd">else</span><span class="pln">
                      </span><span class="pun">*</span><span class="pln">cp</span><span class="pun">++</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> c</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 1]
</p>

<p>
	يوضح لنا هذا المثال مزيدًا من مزايا وطرق لغة سي المُستخدمة في برامجها، وأهمها هو طريقة تمثيل السلاسل النصية ومعالجتها.
</p>

<p>
	إليك تطبيقًا عمليًّا عن الدالة <code>strcmp</code> التي تقارن بين سلسلتين نصيتين وتعيد القيمة صفر إن تساوت قيمتهما، وللدالة هذه في الحقيقة تطبيقات أكثر ولكننا سنتجاهل المعلومات التي قد تزيد من تعقيد شرحنا. لاحظ استخدام الكلمة المفتاحية <code>const</code> في التصريح عن الوسطاء، والذي يوضِّح أن الدالة لن تعدل على محتويات السلسلة النصية بل ستفحص قيم محتوياتها فقط، ونلاحظ استخدام هذه الطريقة في التعريف عن العديد من دوال المكتبة القياسية.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1460_9" style=""><span class="com">/*
* برنامج يختبر مساواة سلسلتين نصيتين
* يُعيد القيمة "خطأ" إذا تساوت السلسلتين
*/</span><span class="pln">
</span><span class="typ">int</span><span class="pln">
str_eq</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s2</span><span class="pun">){</span><span class="pln">
      </span><span class="kwd">while</span><span class="pun">(*</span><span class="pln">s1 </span><span class="pun">==</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s2</span><span class="pun">){</span><span class="pln">
              </span><span class="com">/*
               * إعادة 0 عند نهاية السلسلة النصية
               */</span><span class="pln">
              </span><span class="kwd">if</span><span class="pun">(*</span><span class="pln">s1 </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
                      </span><span class="kwd">return</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
              s1</span><span class="pun">++;</span><span class="pln"> s2</span><span class="pun">++;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="com">/* عُثر على فرق بين السلسلتين */</span><span class="pln">
      </span><span class="kwd">return</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 2]
</p>

<h2>
	السلاسل النصية
</h2>

<p>
	يعرف كل مبرمجٍ للغة سي معنى السلسلة النصية، فهي مصفوفةٌ من متغيرات من نوع "char"، ويكون المحرف الأخير لهذه السلسلة النصية متبوعًا بمحرف فراغ null. ربما تصرخ الآن وتقول "ولكنني اعتقدت أن السلسلة النصية هي نصٌ محتوًى داخل إشارتي تنصيص!" أنت محقّ، إذ تُعد السلسلة التالية في لغة سي مصفوفةً من المحارف:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1460_20" style=""><span class="str">"a string"</span></pre>

<p>
	وهذا الشيء الوحيد الذي يمكنك التصريح عنه لحظة استخدامه، أي دون التصريح عن مصفوفة المحارف وتحديد حجمها.
</p>

<p>
	<strong>احذر</strong>: كانت السلاسل النصية في لغة سي القديمة تُخزّن مثل أي سلسلة محارف اعتيادية، وكانت قابلةً للتعديل. إلا أن المعيار ينص على أن محاولة التعديل على سلسلة نصية سيتسبب بسلوك غير محدد على الرغم من كون السلاسل النصية مصفوفةً من نوع "char" وليس "const char".
</p>

<p>
	لاستخدام السلسلة النصية داخل علامتي تنصيص أثران: أولهما أن السلسلة النصية تحلّ محل تصريح وبديل عن الاسم، كما أنها تمثِّل تصريحًا خفيًّا لمصفوفة من المحارف، وتُهيّأ قيمة هذه المصفوفة إلى قيم المحارف الموجودة في السلسلة النصية متبوعةً بمحرف قيمته صفر، ولا يوجد لهذه المصفوفة أي اسم، لذا باستثناء الاسم، يكون الوضع مشابهًا للتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1460_22" style=""><span class="kwd">char</span><span class="pln"> secret</span><span class="pun">[</span><span class="lit">9</span><span class="pun">];</span><span class="pln">
secret</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">;</span><span class="pln">
secret</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">' '</span><span class="pun">;</span><span class="pln">
secret</span><span class="pun">[</span><span class="lit">2</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'s'</span><span class="pun">;</span><span class="pln">
secret</span><span class="pun">[</span><span class="lit">3</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'t'</span><span class="pun">;</span><span class="pln">
secret</span><span class="pun">[</span><span class="lit">4</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'r'</span><span class="pun">;</span><span class="pln">
secret</span><span class="pun">[</span><span class="lit">5</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'i'</span><span class="pun">;</span><span class="pln">
secret</span><span class="pun">[</span><span class="lit">6</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'n'</span><span class="pun">;</span><span class="pln">
secret</span><span class="pun">[</span><span class="lit">7</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'g'</span><span class="pun">;</span><span class="pln">
secret</span><span class="pun">[</span><span class="lit">8</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span></pre>

<p>
	وهي مصفوفةٌ من المحارف متبوعةٌ بقيمة صفر، وتحتوي جميع قيم المحارف بداخلها، لكنها عديمة الاسم إذا صُرِّح عنها باستخدام طريقة السلسلة النصية المحاطة بعلامتي تنصيص، فكيف نستطيع استخدامها؟
</p>

<p>
	يكون وجود السلسلة النصية بمثابة اسمٍ مخفي لها حالما تراها لغة سي محاطةً بعلامتي تنصيص، إذ أن وجود السلسلة النصية بهذا الشكل لا يؤدي إلى تصريح ضمني فقط، بل يؤدي إلى إعطاء اسمٍ لمصفوفة موافقة. نتذكر جميعنا أن اسم المصفوفة مساوٍ لعنوان عنصرها الأول (تفقد مقالة المؤشرات) فما نوع السلسلة التالية؟
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1460_24" style=""><span class="str">"a string"</span></pre>

<p>
	النوع مؤشر طبعًا، إذ أن السلسلة النصية المكتوبة بالشكل السابق تكافئ مؤشرًا إلى أول عناصر المصفوفة الخفية عديمة الاسم، وهي مصفوفةٌ من نوع <code>char</code>، فالمؤشر من نوع "مؤشر إلى char"، ويوضح الشكل 1 هذه الحالة.
</p>

<p style="text-align: center;">
	<img alt="EffectString.png" class="ipsImage ipsImage_thumbnailed" data-fileid="106273" data-unique="7gnu25ffo" style="" src="https://academy.hsoub.com/uploads/monthly_2022_08/EffectString.png.1b380954fddd02b144d5aefb9ccc7d0b.png">
</p>

<p style="text-align: center;">
	شكل 1 أثر استخدام السلسلة النصية
</p>

<p>
	للبرهان على السابق، ألقِ نظرةً على البرنامج التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1460_11" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">
main</span><span class="pun">(){</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">cp</span><span class="pun">;</span><span class="pln">

      cp </span><span class="pun">=</span><span class="pln"> </span><span class="str">"a string"</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">while</span><span class="pun">(*</span><span class="pln">cp </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">){</span><span class="pln">
              putchar</span><span class="pun">(*</span><span class="pln">cp</span><span class="pun">);</span><span class="pln">
              cp</span><span class="pun">++;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      putchar</span><span class="pun">(</span><span class="str">'\n'</span><span class="pun">);</span><span class="pln">

      </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">8</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln">
              putchar</span><span class="pun">(</span><span class="str">"a string"</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]);</span><span class="pln">
      putchar</span><span class="pun">(</span><span class="str">'\n'</span><span class="pun">);</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 3]
</p>

<p>
	تضبط الحلقة الأولى مؤشرًا يشير إلى بداية المصفوفة، ويُمرّر المؤشر على المصفوفة إلى أن يصل المؤشر إلى القيمة صفر في النهاية، بينما "تَعرف" الحلقة الثانية طول السلسلة النصية وهي أقل فائدة مقارنةً بسابقتها. لاحظ أن الحلقة الأولى غير معتمدة على طول السلسلة النصية بل على وصولها لنهايتها وهو الشيء الأهم الذي ينبغي عليك تذكره من هذا المثال.
</p>

<p>
	يجري التعامل مع السلاسل النصية في لغة سي بطريقة ثابتة دون أي استثناءات وهي الطريقة التي تتوقعها جميع دوال التعامل مع السلاسل النصية، فيسمح الصفر في نهاية السلسلة النصية للدوال بمعرفة نهاية السلسلة النصية. عُد للمثال السابق الخاص بدالة <code>str_eq</code>، إذ تأخذ الدالة مؤشرين يشيران لمحرفين مثل وسطاء (تُقبل السلسلة النصية ذاتها بكونها واحدًا من الوسيطين أو كلاهما) وتُقارن السلسلة النصية بالتحقق من كل محرف واحدًا تلو الآخر، وإذا تساوى المحرفين فتتحقّق من أن المؤشر لم يصل إلى نهاية السلسلة عن طريق الجملة الشرطية التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1460_13" style=""><span class="kwd">if</span><span class="pun">(*</span><span class="pln">s1 </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">):</span></pre>

<p>
	وإذا وصل المؤشر للنهاية فإنها تُعيد <code>0</code> للدلالة على أن السلسلتين متساويتين، يمكن إجراء الاختبار ذاته باستخدام المؤشر <code>s2*</code> دون أي فارق؛ أما في حالة الفرق بين المحرفين تُعاد القيمة <code>1</code> للدلالة على فشل المساواة.
</p>

<p>
	تُستدعى الدالة <code>strcmp</code> في المثال باستخدام وسيطين مختلفين عن بعضهما، إذ أن الوسيط الأول هو مصفوفة محارف والثاني سلسلة نصية، لكنهما في الحقيقة يمثلان الشيء ذاته؛ فمصفوفة المحارف التي تنتهي بالعنصر صفر (يسند برنامجنا القيمة صفر إلى أول عنصر فارغ في مصفوفة <code>in_line</code>) والسلسلة النصية بين قوسين (التي تُمثّل بدورها أيضًا مصفوفة محارف تنتهي بصفر) من نفس الطبيعة، واستخدامهما مثل وسيطين للدالة <code>strcmp</code> ينتج بتمرير مؤشرَي محارف (وقد شرحنا السبب بذلك بإفاضة سابقًا).
</p>

<h2>
	المؤشرات وعامل الزيادة
</h2>

<p>
	ذكرنا سابقًا التعبير التالي، وقلنا أنّنا ستعيد النظر فيه لاحقًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1460_15" style=""><span class="pun">(*</span><span class="pln">p</span><span class="pun">)++;</span></pre>

<p>
	حان الوقت الآن للكلام عن ذلك؛ إذ تُستخدم المؤشرات بكثرة مع المصفوفات والتمرير بينها، لذلك من الطبيعي استخدام العاملين <code>--</code> و <code>++</code> معها. يوضح المثال التالي إسناد القيمة صفر إلى مصفوفة باستخدام المؤشر وعامل الزيادة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1460_17" style=""><span class="com">#define</span><span class="pln"> ARLEN </span><span class="lit">10</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> ar</span><span class="pun">[</span><span class="pln">ARLEN</span><span class="pun">],</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ip</span><span class="pun">;</span><span class="pln">

ip </span><span class="pun">=</span><span class="pln"> ar</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">while</span><span class="pun">(</span><span class="pln">ip </span><span class="pun">&lt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ar</span><span class="pun">[</span><span class="pln">ARLEN</span><span class="pun">])</span><span class="pln">
      </span><span class="pun">*(</span><span class="pln">ip</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span></pre>

<p style="text-align: center;">
	[مثال 4]
</p>

<p>
	يُضبط المؤشر <code>ip</code> ليبدأ من بداية المصفوفة، وعلى الرغم من إشارة المؤشر إلى داخل المصفوفة إلا أن القيمة التي يشير إليها تساوي الصفر، وعند وصولنا للحركة التكرارية ينجز عامل الزيادة ما هو متوقع ويُنقل المؤشر للعنصر الذي يليه داخل المصفوفة، وبالتالي استخدام العامل <code>++</code> عامل إلحاق postfix مفيدٌ في هذه الحالة.
</p>

<p>
	معظم الأشياء التي ناقشناها شائعة الوجود، وستجدها في معظم البرامج (استخدام عامل الزيادة والمؤشرات بالشكل الموضح في المثال السابق) ليس مرةً واحدةً أو مرتين بل تقريبًا كل عدّة أسطر ضمن الشيفرة البرمجية، وستعتقد أنك تراها بصورةٍ أكثر إذا كنت تجد استخدامها صعب الفهم. لكن ما هي التشكيلات التي يمكننا الحصول عليها؟ بالنظر إلى أن <code>*</code> تعني التأشير و <code>++</code> تعني الزيادة و <code>--</code> تعني النقصان، كما أنه لدينا خياري وضع العاملين السابقين مثل عامل إلحاق postfix أو عامل إسباق prefix، نحصل على الاحتمالات التالية (بغض النظر عن عامل الزيادة أو النقصان) مع التركيز على موضع الأقواس:
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
	<tbody>
		<tr>
			<td>
				<code>(p*)++</code>
			</td>
			<td>
				زيادة سابقة للشيء الذي يشير إليه المؤشر
			</td>
		</tr>
		<tr>
			<td>
				<code>++(p*)</code>
			</td>
			<td>
				زيادة لاحقة للشيء الذي يشير إليه المؤشر
			</td>
		</tr>
		<tr>
			<td>
				<code>(++p)*</code>
			</td>
			<td>
				زيادة لاحقة على المؤشر
			</td>
		</tr>
		<tr>
			<td>
				<code>*(p++)*</code>
			</td>
			<td>
				زيادة سابقة على المؤشر
			</td>
		</tr>
	</tbody>
</table>

<p style="text-align: center;">
	[جدول 1 معاني المؤشرات]
</p>

<p>
	اقرأ الجدول السابق بحرص وتأكد أنك تفهم جميع التركيبات التي ذُكرت.
</p>

<p>
	يمكن فهم محتوى الجدول السابق بعد تفكير بسيط، ولكن هل يمكنك توقع ما الذي سيحدث عند إزالة الأقواس بالنظر إلى أن الأسبقية للعوامل الثلاث <code>*</code> و <code>--</code> و <code>++</code> متساوية؟ تتوقع حدوث أخطاء كارثية، أليس كذلك؟ يوضّح الجدول 1 أن هناك حالةٌ واحدةٌ يجب أن تحافظ فيها على الأقواس.
</p>

<table>
	<thead>
		<tr>
			<th>
				<strong>مع أقواس</strong>
			</th>
			<th>
				<strong>دون أقواس إن أمكن</strong>
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<code>(p*)++</code>
			</td>
			<td>
				<code>p*++</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>++(p*)</code>
			</td>
			<td>
				<code>++(p*)</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>(++p)*</code>
			</td>
			<td>
				<code>++p*</code>
			</td>
		</tr>
		<tr>
			<td>
				<code>(p++)*</code>
			</td>
			<td>
				<code>p++*</code>
			</td>
		</tr>
	</tbody>
</table>

<p style="text-align: center;">
	[جدول 2 المزيد من معاني المؤشرات]
</p>

<p>
	لعلّ الأشكال المثيرة للتشويش في الجدول السابق ستدفعك لاستخدام الأقواس بغض النظر عن أهمية استعمالها في أي حالة سعيًا منك لتحسين قابلية قراءة الشيفرة البرمجية وفهمها، لكن يتخلى معظم مبرمجي لغة سي عن استخدام الأقواس بعد تعلُّم قوانين الأسبقية ونادرًا ما يستخدمون الأقواس في تعابيرهم، لذا عليك الاعتياد على قراءة الأمثلة التالية سواءٌ كانت مع أقواس أو بدونها، فالاعتياد على هذا الأمر سيساعدك بحقّ في تعلمك.
</p>

<h2>
	المؤشرات عديمة النوع
</h2>

<p>
	من المهم في بعض الأحيان تحويل نوعٍ من المؤشرات إلى نوع آخر بمساعدة التحويل بين الأنواع مثل التعبير التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1460_30" style=""><span class="pun">(</span><span class="pln">type </span><span class="pun">*)</span><span class="pln"> expression</span></pre>

<p>
	يُحوَّل التعبير <code>expression</code> في المثال السابق إلى مؤشر من نوع "مؤشر إلى نوع <code>type</code>" بغض النظر عن نوع التعبير السابق، إلا أنه يجب تفادي هذه الطريقة إلا في حال كنت مدركًا تمامًا لما تفعله، فمن غير المحبّذ استخدامها إلا إذا كنت مبرمجًا خبيرًا. <strong>لا</strong> تفترض أن التحويل بين الأنواع يلغي أي حسابات أخرى بخصوص "الأنواع غير المتوافقة مع بعضها" التي تقع على عاتق المصرّف، إذ من المهم إعادة حساب القيم الجديدة للمؤشر بعد تغيير نوعه على العديد من معماريات الحاسوب.
</p>

<p>
	هناك بعض الحالات التي ستحتاج فيها لاستخدام مؤشر "معمّم generic"، وأبرز مثال على ذلك هو تطبيق لدالة المكتبة القياسية <code>malloc</code> التي تُستخدم لحجز المساحة على الذاكرة للكائن الذي لم يصرّح عنه بعد، ويجري تزويد الحجم المراد حجزه عن طريق تزويد مثل وسيطٍ سواءٌ كان الكائن متغيرًا من نوع <code>float</code> أو مصفوفة من نوع <code>int</code> أو أي شيء آخر. تعيد الدالة مؤشرًا إلى عنوان التخزين المحجوز التي تختاره بطريقتها الخاصة (والتي لن نتطرق إليها) من مجموعةٍ من عناوين الذاكرة الفارغة، ومن ثم يُحوَّل المؤشر إلى النوع المناسب. على سبيل المثال، تحتاج القيمة من نوع <code>float</code> إلى 4 بايتات من الذاكرة، وبالتالي نكتب ما يلي لحجز مساحة للقيمة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1460_32" style=""><span class="typ">float</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fp</span><span class="pun">;</span><span class="pln">

fp </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">float</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">malloc</span><span class="pun">(</span><span class="lit">4</span><span class="pun">);</span></pre>

<p>
	تعثر الدالة <code>malloc</code> على 4 بايتات من الذاكرة الفارغة، ويُحوَّل عنوان الذاكرة إلى مؤشر من نوع "مؤشر إلى <code>float</code>"، ثم تُسند القيمة إلى المؤشر (<code>fp</code> في حالة مثالنا السابق).
</p>

<p>
	لكن ما هو نوع المؤشر الذي ستُسند قيمة <code>malloc</code> إليه؟ نحن بحاجة نوع يمكن أن يحتوي جميع أنواع المؤشرات فنحن لا نعلم نوع المؤشر الذي ستعيده الدالة <code>malloc</code>.
</p>

<p>
	الحل هو باستخدام نوع المؤشر <code>* void</code> الذي تكلمنا عنه سابقًا، إليك المثال السابق مع إضافة تصريح للدالة <code>malloc</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1460_28" style=""><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">malloc</span><span class="pun">();</span><span class="pln">
</span><span class="typ">float</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fp</span><span class="pun">;</span><span class="pln">

fp </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">float</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">malloc</span><span class="pun">(</span><span class="lit">4</span><span class="pun">);</span></pre>

<p>
	لا حاجة لاستخدام تحويل الأنواع على القيمة المُعادة من الدالة <code>malloc</code> حسب قوانين الإسناد للمؤشرات، ولكن استُخدم تحويل الأنواع لممارسة الأمر لا أكثر.
</p>

<p>
	لا بد من طريقة لمعرفة قيمة وسيط <code>malloc</code> الدقيقة في نهاية المطاف، ولكن القيمة ستكون مختلفةً على أجهزة بمعماريات مختلفة، لذا لا يمكنك الاكتفاء باستخدام القيمة الثابتة <code>4</code> فقط، بل يجب علينا استخدام عامل <code>sizeof</code>.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter5/" rel="external nofollow">Arrays and Pointers</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/c/%D8%B9%D8%A7%D9%85%D9%84-sizeof-%D9%88%D8%AD%D8%AC%D8%B2-%D9%85%D8%B3%D8%A7%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1681/" rel="">عامل sizeof وحجز مساحات التخزين في لغة سي C</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-pointers-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1679/" rel="">المؤشرات Pointers في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1609/" rel="">المحارف المستخدمة في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%B9%D9%88%D8%A7%D9%85%D9%84-%D8%A7%D9%84%D9%85%D9%86%D8%B7%D9%82%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-%D9%88%D8%B9%D9%88%D8%A7%D9%85%D9%84-%D8%A3%D8%AE%D8%B1%D9%89-r1616/" rel="">العوامل المنطقية في لغة سي C وعوامل أخرى</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%B3%D9%8A-c-r1610/" rel="">البنية النصية لبرامج سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1680</guid><pubDate>Fri, 09 Sep 2022 16:08:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x645;&#x624;&#x634;&#x631;&#x627;&#x62A; Pointers &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-pointers-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1679/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_08/6304da57cdfee_--Pointers---C.png.b748ede7845a4a24fb1bbfb09d91e70c.png" /></p>
<p>
	يشابه استخدام المؤشرات Pointers في لغة سي عملية تعلُّم قيادة الدراجة الهوائية، فعندما تصل إلى النقطة التي تعتقد أنك لن تتعلمها أبدًا، تبدأ بإتقانها، وبعد أن تتعلمها سيكون من الصعب نسيانها. لا يوجد هناك أي شيء مميّز بخصوص المؤشرات، ونعتقد أن معظم القرّاء يعرفون عنها مسبقًا، وفي الحقيقة، واحدةٌ من ميزات لغة سي هي اعتمادها الكبير على استخدام المؤشرات مقارنةً باللغات الأخرى، إضافةً إلى الأشياء الأخرى الممكن إنجازها بواسطة المؤشرات بسهولة ودون قيود إلى حدٍّ ما.
</p>

<h2>
	التصريح عن المؤشرات
</h2>

<p>
	ينبغي التصريح عن المؤشرات قبل استخدامها بصورةٍ مماثلة لأي متغير آخر تعاملنا معه مسبقًا. يتشابه التصريح عن المؤشر مع أي تصريح آخر، ولكن هناك نقطة مهمّة، إذ تدلّ الكلمة المفتاحية عند التصريح عن المؤشر في البداية، مثل <code>int</code> أو<code>char</code> وغيرها عن نوع المتغير الذي سيشير المؤشر إليه، وليس نوع المؤشر بذات نفسه، ويشير المؤشّر على قيمة واحدة كل مرة من ذلك النوع وليس جميع القيم من النوع ذاته. إليك مثالًا يوضح التصريح عن مصفوفة ومؤشر:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9936_47" style=""><span class="typ">int</span><span class="pln"> ar</span><span class="pun">[</span><span class="lit">5</span><span class="pun">],</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ip</span><span class="pun">;</span></pre>

<p>
	يصبح لدينا بعد التصريح مصفوفة ومؤشر، كما يوضح الشكل 1:
</p>

<p style="text-align: center;">
	<img alt="004ArrayPointer.png" class="ipsImage ipsImage_thumbnailed" data-fileid="106208" data-unique="s5hahi81o" style="width: 340px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_08/004ArrayPointer.png.ec36420fae35abddd59541f7858a92bd.png">
</p>

<p style="text-align: center;">
	شكل 1 مصفوفة ومؤشر
</p>

<p>
	يوضح الرمز <code>*</code> الموجود أمام <code>ip</code> أن هذا مؤشر، وليس متغيرًا اعتياديًا، وهو مؤشرٌ من النوع <code>pointer to int</code>، أي يشير إلى قيمة من نوع <code>int</code> فقط، لكن لم تُسند له قيمة أوليّة بعد، ولا يمكننا استخدامه في هذه الحالة قبل أن نجعله يؤشّر على قيمةٍ ما. لاحظ أنه لا يمكنك تعيين قيمةٍ من نوع <code>int</code> فورًا، لأن القيم الصحيحة لها النوع <code>int</code> ونحن نريد هنا قيمةً من نوع "مؤشر إلى نوع صحيح pointer to int". لكن، ما الذي سيشير إليه <code>ip</code> في هذه الحالة إذا كانت التعليمة التالية صحيحة ؟
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9936_11" style=""><span class="pln">ip </span><span class="pun">=</span><span class="pln"> </span><span class="lit">6</span><span class="pun">;</span></pre>

<p>
	قد تختلف الإجابة هنا، ولا يوجد إجابةً واحدةً عمّا قد يشير إليه <code>ip</code>، ولكن خلاصة الأمر أن هذا النوع من الإسناد غير صحيح في لغة سي.
</p>

<p>
	إليك الطريقة الصحيحة لإسناد قيمة أولية لمؤشر ما:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9936_13" style=""><span class="typ">int</span><span class="pln"> ar</span><span class="pun">[</span><span class="lit">5</span><span class="pun">],</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ip</span><span class="pun">;</span><span class="pln">
ip </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ar</span><span class="pun">[</span><span class="lit">3</span><span class="pun">];</span></pre>

<p>
	يؤشّر المؤشر في هذا المثال إلى عنصرٍ من المصفوفة <code>ar</code> بدليل <code>3</code>، أي العنصر الرابع من المصفوفة.
</p>

<p>
	<strong>تحذير هام:</strong> يمكنك إسناد القيم إلى المؤشرات كما في أي متغير اعتدت عليه، ولكن تكمن الأهمية في نوع هذه القيمة وما الذي تعنيه. يدل الشكل 2 على قيم المتغيرات الموجودة بعد التصريح، ويدل <code>??</code> على كون المتغير غير مُسند لقيمة أولية أي غير مهيَّأ.
</p>

<p style="text-align: center;">
	<img alt="005ArrayInitializedPointer.png" class="ipsImage ipsImage_thumbnailed" data-fileid="106209" data-unique="s73e9jugb" style="" src="https://academy.hsoub.com/uploads/monthly_2022_08/005ArrayInitializedPointer.png.c2c664372c76fb0c6a58616182f854e9.png">
</p>

<p style="text-align: center;">
	شكل 2 مصفوفة ومؤشر مهيّأ
</p>

<p>
	نلاحظ أن قيمة المتغير <code>ip</code> مساوية لقيمة التعبير <code>‎&amp;ar[3]‎</code>، ويشير السهم إلى أن المؤشر <code>ip</code> يشير إلى المتغير <code>[ar[3‏</code>.
</p>

<p>
	لكن ما الذي يعنيه العامل الأحادي <code>&amp;</code>؟ يُشار إلى هذا العامل بكونه عامل "عنوان المتغير"، إذ أن المؤشرات تخزّن عنوان المتغير الذي تؤشّر عليه في معظم الأنظمة. ربّما ستواجه صعوبةً بخصوص هذا الأمر إذا كنت تفهم ما نعني هنا بالعنوان مقارنةً بالأشخاص الذين لا يفهمون هذا الأمر، إذ أن التفكير بالمؤشرات كونها عناوينًا يؤدي إلى كثيرٍ من المشاكل في الفهم.
</p>

<p>
	قد تكون عملية التلاعب بعناوين معالج "أ" مستحيلةً على متحكّم آلة غسيل من نوع "ب" يستخدم عناوينًا بسعة 17-بِت عندما تكون في طور الغسيل، ويقلب ترتيب البِتات الزوجية والفردية عندما ينفد من مسحوق الغسيل. من المستبعد <strong>لأي أحد</strong> أن يستخدم لغة سي بمعمارية مشابهة لمثالنا السابق، ولكن هناك بعض الحالات الأخرى والأقل شدّة التي قد <strong>يمكن</strong> تشغيل لغة سي على معماريتها.
</p>

<p>
	لكننا سنتابع استخدام الكلمة "عنوان المتغير"، لأن استخدام مصطلح أو كلمة مغايرة لذلك سيتسبب بمزيدٍ من المشكلات.
</p>

<p>
	يعيد تطبيق العامل <code>&amp;</code> لمعامل ما مؤشّرًا لهذا المعامل:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9936_15" style=""><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
</span><span class="typ">float</span><span class="pln"> f</span><span class="pun">;</span><span class="pln">
      </span><span class="com">/* مؤشر إلى عدد صحيح '&amp;i' */</span><span class="pln">
      </span><span class="com">/* مؤشر إلى عدد حقيقي '&amp;f'*/</span></pre>

<p>
	وسيشير المؤشر في كل حالة إلى الكائن الذي يوافق اسمه اسم المستخدم في التعبير.
</p>

<p>
	المؤشر مفيدٌ فقط في حال وجود طريقة للوصول إلى الشيء الذي يشير إليه، وتستخدم لغة سي العامل الأحادي <code>*</code> لتحقيق ذلك؛ فإذا كان <code>p</code> من نوع "مؤشر إلى نوعٍ ما pointer to something"، فيشير التعبير <code>‎*p‎</code> إلى الشيء الذي يشير إليه ذلك المؤشر. على سبيل المثال، نتبع مايلي للوصول إلى المتغير <code>x</code> باستخدام المؤشر <code>p</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9936_17" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">
main</span><span class="pun">(){</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p</span><span class="pun">;</span><span class="pln">

      p </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">x</span><span class="pun">;</span><span class="pln">        </span><span class="com">//تهيئة المؤشر </span><span class="pln">
     </span><span class="pun">*</span><span class="pln">p </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">         </span><span class="com">// ‪ x إسناد القيمة 0 إلى المتغير</span><span class="pln">
     printf</span><span class="pun">(</span><span class="str">"x is %d\n"</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">);</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"*p is %d\n"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p</span><span class="pun">);</span><span class="pln">

      </span><span class="pun">*</span><span class="pln">p </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">        </span><span class="com">/* زيادة القيمة التي يشير إليها المؤشر */</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"x is %d\n"</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">);</span><span class="pln">

      </span><span class="pun">(*</span><span class="pln">p</span><span class="pun">)++;</span><span class="pln">         </span><span class="com">/* زيادة القيمة التي يشير إليها المؤشر */</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"x is %d\n"</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">);</span><span class="pln">

      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 1]
</p>

<p>
	من الجدير بالذكر معرفة أن استخدام التركيب المؤلف من <code>&amp;</code> و <code>*</code> بالشكل <code>&amp;*</code> يلغي تأثير كلٍّ منهما، لأن <code>&amp;</code> تعيد عنوان الكائن أي قيمة مؤشّره، و<code>*</code> تعني "القيمة التي يشير إليها المؤشر". لكن انتبه، فليس لبعض الأشياء مثل الثوابت أي عنوان، وبذلك لا يمكن تطبيق العامل <code>&amp;</code> عليها، والتعبير <code>1.5&amp;</code> ليسَ مؤشّرًا بل تعبيرًا خاطئًا. من المثير للانتباه أيضًا إلى أن لغة سي من اللغات القليلة التي تسمح بوجود تعبير على الجانب الأيسر من عامل الإسناد. انظر مجدّدًا إلى المثال؛ إذ نصادف التعبير <code>p*</code> مرتين، ومن ثم التعليمة <code>‎(‎*p)++‎;‎</code> المثيرة للاهتمام، وستثير هذه التساؤلات من معظم المبتدئين حتى لو استطعت فهم أن التعليمة <code>p = 0*</code> تسند القيمة <code>0</code> إلى المتغير المُشار إليه بواسطة المؤشر <code>p</code>، وأن التعليمة <code>p += 1*</code> تضيف واحدًا إلى المتغير المُشار إليه بالمؤشر <code>p</code>، فاستخدام العامل <code>++</code> مع <code>p*</code> يبدو صعب الفهم قليلًا.
</p>

<p>
	تستحق أسبقية التعبير <code>++(p*)</code> النظر إليها بدقة، وسنناقش مزيدًا من التفاصيل بهذا الخصوص، ولكن دعونا نركّز عمّا يحدث في هذا المثال تحديدًا. تُستخدم الأقواس للتأكد بأن <code>*</code> تُطبَّق على <code>p</code> فقط، ومن ثم تحدث زيادةً بمقدار واحد على الشيء المُشار إليه بالمؤشر <code>p</code>، وبالنظر إلى جدول الأسبقية في مقال <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%B9%D9%88%D8%A7%D9%85%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1613/" rel="">العوامل في لغة سي C</a> نلاحظ أن للعاملين <code>++</code> و<code>*</code> الأسبقية ذاتها، ولكن العاملين يرتبطان من اليمين إلى اليسار، وبمعنى آخر تصبح العملية بالتخلص من الأقواس مكافئةً للعملية <code>(++p)*</code>، وبغض النظر عن معنى هذه العملية (التي سنتكلم عن معناها لاحقًا)، لا بُدّ من الحفاظ على الأقواس في هذه الحالة والانتباه إلى مواضعها الصحيحة.
</p>

<p>
	لذا، وبما أن المؤشر يعطي عنوان الشيء الذي يشير إليه، فاستخدام <code>pointer*</code> (إذ أن <code>pointer</code> هو أيضًا مؤشر) يعيد الشيء بذاته مباشرةً، ولكن ما الذي نستفيد من ذلك؟ أول الأمور التي نستفيد منها هي تجاوز قيد الاستدعاء بالقيمة call-by-value عند استخدام الدوال؛ فعلى سبيل المثال تخيل دالةً تعيد قيمتين ممثلتين بأعداد صحيحة تمثّل الشهر واليوم لهذا الشهر، وأن لهذه الدالة طريقةً (غير محددة) لتحديد هذه القيم، والتحدي هنا هو إعادة قيمتين منفصلتين بنفس الوقت. إليك طريقةً لتجاوز هذه العقبة بالمثال:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9936_19" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln">
date</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> </span><span class="pun">*,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="pun">*);</span><span class="pln">     </span><span class="com">/* التصريح عن الدالة */</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> month</span><span class="pun">,</span><span class="pln"> day</span><span class="pun">;</span><span class="pln">
      date </span><span class="pun">(&amp;</span><span class="pln">day</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">month</span><span class="pun">);</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"day is %d, month is %d\n"</span><span class="pun">,</span><span class="pln"> day</span><span class="pun">,</span><span class="pln"> month</span><span class="pun">);</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">void</span><span class="pln">
date</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">day_p</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">month_p</span><span class="pun">){</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> day_ret</span><span class="pun">,</span><span class="pln"> month_ret</span><span class="pun">;</span><span class="pln">
      </span><span class="com">/*حساب قيمة day و month في day_ret و month_ret*/</span><span class="pln">
      </span><span class="pun">*</span><span class="pln">day_p </span><span class="pun">=</span><span class="pln"> day_ret</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">*</span><span class="pln">month_p </span><span class="pun">=</span><span class="pln"> month_ret</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 2]
</p>

<p>
	لاحظ طريقة التصريح عن <code>date</code> المتقدمة، التي توضح أنها دالةٌ تأخذ وسيطين من نوع "مؤشر إلى قيمة من نوع <code>int</code>"، وتعيد <code>void</code> لأن القيم التي تُمرّر بواسطة المؤشرات ليست من نوع قيمة اعتيادية. تمرِّر الدالة <code>main</code> المؤشرات إلى الدالة <code>date</code> على أنها وسطاء باستخدام المتغيرات الداخلية <code>day_ret</code> و <code>month_ret</code>، ومن ثم تأخذ قيمتهما وتسندها إلى الأماكن التي تشير إليها وسطاء الدالة (المؤشرات).
</p>

<p>
	يوضح الشكل 3 ما الذي يحدث عند استدعاء الدالة <code>date</code>:
</p>

<p style="text-align: center;">
	<img alt="006dateCall.png" class="ipsImage ipsImage_thumbnailed" data-fileid="106210" data-unique="hx20678yt" style="width: 250px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_08/006dateCall.png.9386b42ba2f2d41ada911b05cdc90091.png">
</p>

<p style="text-align: center;">
	شكل 3 عند استدعاء الدالة <code>date</code>
</p>

<p>
	تُمرَّر الوسطاء إلى <code>date</code>، ولكن المتغيرين <code>day</code> و <code>month</code> غير مهيّأين بقيمةٍ أولية ضمن الدالة <code>main</code>. يوضح الشكل 4 ما الذي يحدث عندما تصل الدالة <code>date</code> إلى تعليمة <code>return</code>، بفرض أن قيمة <code>day</code> هي "12" وقيمة <code>month</code> هي "5".
</p>

<p style="text-align: center;">
	<img alt="007dateReturn.png" class="ipsImage ipsImage_thumbnailed" data-fileid="106211" data-unique="rlmaqlyxa" style="width: 250px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_08/007dateReturn.png.62da8abe5ec22f9cd5dd34e31397a309.png">
</p>

<p style="text-align: center;">
	شكل 4 عندما تصل الدالة <code>date</code> إلى تعليمة <code>return</code>
</p>

<p>
	واحدةٌ من المزايا الرائعة التي قدّمتها لغة سي المعيارية هي السماح بالتصريح عن أنواع وسطاء دالة <code>date</code> مسبقًا، إذ كان نسيان أن الدالة تقبل مؤشرات وسطاءً لها وتمرير نوع آخر أمرًا شائع الحدوث. تخيل استدعاء الدالة <code>date</code> دون أي تصريح واضح مُسبقٍ لها على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9936_23" style=""><span class="pln">date</span><span class="pun">(</span><span class="pln">day</span><span class="pun">,</span><span class="pln"> month</span><span class="pun">);</span></pre>

<p>
	لن يعرف المصرّف هنا أن الدالة تقبل المؤشرات وسطاءً لها وستُمرر قيمًا من نوع <code>int</code> افتراضيًا لكل من <code>day</code> و <code>month</code>، وبما أن تمرير المؤشرات والأعداد الصحيحة يجري على معظم الحواسيب بالطريقة نفسها، لذا ستُنفَّذ الدالة في هذه الحالة، ثم تعيد القيم في النهاية وتسندها إلى المكان الذي يشير إليه كلًا من <code>day</code> و <code>month</code>، إذا كانا مؤشرين. لن يعطي ذلك أي نتيجة بل وربما يتسبب بضرر مفاجئ للبيانات في مكان آخر بذاكرة الحاسوب، وتعقُّب هذا النوع من الأخطاء صعبٌ جدًا.
</p>

<p>
	لحسن الحظ، يمكن أن يعرف المصرّف المعلومات الكافية عن <code>date</code> بالتصريح عن الدالة مسبقًا، وذلك من شأنه أن يحذره بخصوص أي أخطاء مرتكبة.
</p>

<p>
	قد يفاجئك سماع أن المؤشرات لا تستخدم كثيرًا لتمكين طريقة الاستدعاء بالإشارة call-by-reference، إذ يُعدّ استخدام الاستدعاء بالقيمة call-by-value وإعادة قيمة واحدة باستخدام <code>return</code> كافٍ في معظم الحالات، والاستخدام الأكثر شيوعًا للمؤشرات هو الانتقال ما بين المصفوفات.
</p>

<h2>
	المصفوفات والمؤشرات
</h2>

<p>
	تملك عناصر المصفوفة عناوينًا مثل أي متغير اعتيادي آخر.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9936_25" style=""><span class="typ">int</span><span class="pln"> ar</span><span class="pun">[</span><span class="lit">20</span><span class="pun">],</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ip</span><span class="pun">;</span><span class="pln">

ip </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ar</span><span class="pun">[</span><span class="lit">5</span><span class="pun">];</span><span class="pln">
</span><span class="pun">*</span><span class="pln">ip </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">        </span><span class="com">// ‫يكافئ التعبير ar[5] = 0;‎ </span></pre>

<p>
	في المثال السابق، يُخزَّن عنوان العنصر <code>ar[5]‎</code> في المؤشر <code>ip</code>، ثم تُسند القيمة صفر إلى موضع المؤشر في السطر الذي يليه. هذا الشيء غير مثير للإعجاب بحد ذاته، بل إن طريقة عمل العمليات الحسابية والمؤشر سويًّا هي التي تستدعي الاهتمام، فعلى الرغم من بساطة هذا الأمر، إلا أنه يُعد واحدًا من أساسات لغة سي المميزة.
</p>

<p>
	نحصل على مؤشر إذا أضفنا قيمة عدد صحيح إلى مؤشر ما، ويكون للمؤشر الذي حصلنا عليه نفس نوع المؤشر الأصلي؛ فبإضافة العدد "n" إلى مؤشر ما يشير إلى عنصر في مصفوفة، سنحصل على عنصر يشير إلى العنصر الذي يلي العنصر السابق بمقدار "n" داخل المصفوفة ذاتها، ويمكن تطبيق حالة الطرح بما أن العدد "n" يمكن أن يكون سالبًا. بالرجوع إلى المثال السابق، ينتج عن التعبير التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9936_27" style=""><span class="pun">*(</span><span class="pln">ip</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span></pre>

<p>
	تغيير قيمة <code>ar[6]‎</code> إلى صفر، وهكذا. لا تعدّ هذه الطريقة تحسينًا على طرق الوصول إلى عناصر المصفوفة الاعتيادية، إليك مثالًا عن ذلك بدلًا من السابق:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9936_51" style=""><span class="typ">int</span><span class="pln"> ar</span><span class="pun">[</span><span class="lit">20</span><span class="pun">],</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ip</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">for</span><span class="pun">(</span><span class="pln">ip </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ar</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln"> ip </span><span class="pun">&lt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ar</span><span class="pun">[</span><span class="lit">20</span><span class="pun">];</span><span class="pln"> ip</span><span class="pun">++)</span><span class="pln">
      </span><span class="pun">*</span><span class="pln">ip </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span></pre>

<p>
	يوضّح المثال السابق ميزةً كلاسيكيةً للغة سي، إذ يشير المؤشر إلى بداية المصفوفة، وبينما يشير المؤشر إلى المصفوفة يمكن الوصول إلى عناصر المصفوفة واحدًا تلو الآخر بزيادة المؤشر بمقدار واحد كلّ مرة. يدعم معيار سي بعض الممارسات الموجودة وذلك بالسماح لاستخدام <strong>عنوان</strong> العنصر <code>ar[20]‎</code> على الرغم من عدم وجود هذا العنصر، ويسمح لك هذا باستخدام المؤشرات لاختبار حدود المصفوفة ضمن الحلقات التكرارية كما هو الحال في المثال السابق، إذ أن ضمان عمله يمتد لعنصر واحد فقط خارج نهاية المصفوفة وليس أكثر من ذلك.
</p>

<p>
	لكن ما المميز في هذه الطريقة مقارنةً باستخدام دليل المصفوفة للوصول إلى العنصر بالطريقة الاعتيادية؟ يمكن الوصول إلى عناصر المصفوفات في معظم الحالات بالتعاقب، واستعرضت القليل من الأمثلة البرمجية السابقة خيار الوصول إلى العناصر "عشوائيًا". إذا أردت الوصول إلى عناصر المصفوفة بالتعاقب فسيقدّم استخدام المؤشرات تنفيذًا أسرع، إذ يتطلب الأمر على معظم معماريات الحاسوب عملية ضرب وجمع واحدةً للوصول إلى عنصر ضمن مصفوفة أحادية البعد باستخدام دليله، بينما لا يتطلب الأمر باستخدام المؤشرات إجراء أي عمليات حسابية إطلاقًا، إذ يخزن المؤشر العنوان الدقيق للكائن (عنصر المصفوفة في هذه الحالة). العملية الحسابية الوحيدة المُجراة في المثال السابق هي ضمن حلقة <code>for</code> التكرارية، إذ تحدث عملية إضافة ومقارنة كل دورة داخل الحلقة. إذا أردنا استخدام طريقة الوصول لعناصر المصفوفة باستخدام الأدلة، نكتب:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9936_31" style=""><span class="typ">int</span><span class="pln"> ar</span><span class="pun">[</span><span class="lit">20</span><span class="pun">],</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">20</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln">
      ar</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span></pre>

<p>
	تحدث العمليات الحسابية ذاتها في <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D9%83%D8%B1%D8%A7%D8%B1%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1306/" rel="">الحلقة التكرارية</a> كما في المثال السابق، لكن بإضافة حسابات العنوان المُجراة كل دورة في الحلقة.
</p>

<p>
	لا تعدّ نقطة توفير الوقت والفاعلية مشكلةً كبيرةً في معظم الأحيان، إلا أن الأمر مهمٌ هنا في حالة الحلقات التكرارية، إذ تتكرر الحلقات عددًا كبيرًا من المرات وكل جزء من الثانية يمكن توفيره في كل دورة له تأثير، ولا يستطيع المصرّف مهما كان ذكيًّا التعرُّف على جميع الحالات التي يمكن له استخدام المؤشرات بدلًا من طريقة دليل المصفوفة ضمنيًا.
</p>

<p>
	إذا استوعبت جميع ما قرأته حتى الآن فتابع معنا، وإلّا فتجاوز هذا القسم واذهب للمقال التالي، فعلى الرغم من المعلومات المثيرة للاهتمام في الأقسام التالية إلا أنها غير ضرورية وتُعرَف برهبتها لمبرمجي لغة سي المتمرسين حتى.
</p>

<p>
	في حقيقة الأمر، لا "تفهم" لغة سي مبدأ الوصول لعناصر المصفوفة باستخدام أدلتها (باستثناء نقطة التصريح عن المصفوفة)، فالتعبير <code>x[n]‎</code> يُترجم بالنسبة للمصرّف على النحو التالي: <code>(x+n)*</code>، إذ يُحوَّل اسم المصفوفة إلى مؤشر يشير إلى العنصر الأول للمصفوفة وذلك أينما وجد اسم المصفوفة ضمن تعبير ما. يُعد هذا سببٌ من ضمن أسباب أخرى لبدء عناصر المصفوفة بالرقم صفر؛ فإذا كان <code>x</code> اسم المصفوفة، سيكون التعبير <code>‎&amp;x[0]‎‎</code> مساوٍ للمصفوفة <code>x</code>، أي مؤشر إلى العنصر الأول من المصفوفة.
</p>

<p>
	نستطيع الوصول إلى <code>x[0]‎</code> باستخدام المؤشرات باستخدام التعبير <code>‎*(‎&amp;x[0]‎)‎</code>، مما يعني أن <code>‎*‎(&amp;x[0] + 5‎‎)‎</code> مساوٍ للتعبير <code>‎*(x + 5)‎</code> وهو ذات التعبير <code>[x[5</code>. يفتح ذلك الأمر سيلًا من التساؤلات عن الإمكانيات الناتجة، فإذا كان التعبير <code>x[5]‎</code> يُترجم إلى <code>‎*(x + 5)‎</code> والتعبير <code>x + 5</code> يعطي النتيجة ذاتها للتعبير:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9936_33" style=""><span class="lit">5</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> x </span></pre>

<p>
	فالتعبير <code>5[x]</code> مساوٍ للتعبير <code>x[5]‎</code>. إليك برنامجًا يُصرّف وينفذ دون أي أخطاء إن لم تصدق هذا الأمر:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9936_35" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> ARSZ </span><span class="lit">20</span><span class="pln">
main</span><span class="pun">(){</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> ar</span><span class="pun">[</span><span class="pln">ARSZ</span><span class="pun">],</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> ARSZ</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">
              ar</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
              i</span><span class="pun">[</span><span class="pln">ar</span><span class="pun">]++;</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"ar[%d] now = %d\n"</span><span class="pun">,</span><span class="pln"> i</span><span class="pun">,</span><span class="pln"> ar</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">

      printf</span><span class="pun">(</span><span class="str">"15[ar] = %d\n"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">15</span><span class="pun">[</span><span class="pln">ar</span><span class="pun">]);</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 3]
</p>

<p>
	لتلخيص ما سبق:
</p>

<ul>
	<li>
		تبدأ العناصر في أي مصفوفة من الدليل ذي الرقم صفر.
	</li>
	<li>
		ليس هناك أي وجود للمصفوفات متعددة الأبعاد، بل هي في حقيقة الأمر مصفوفاتٌ تحتوي على مصفوفات.
	</li>
	<li>
		تُشير المؤشرات إلى أشياء، والمؤشرات التي تشير إلى أشياء من أنواع مختلفة هي بدورها من أنواع مختلفة أيضًا ولا يوجد أي تشابه بين الأنواع المختلفة في لغة سي وأي تحويل ضمني تلقائي بينهما.
	</li>
	<li>
		يمكن استخدام المؤشرات لمحاكاة استخدام الاستدعاء بالإشارة ضمن الدوال، ولكن الأمر يستغرق بعضًا من الجهد لتحقيقه.
	</li>
	<li>
		تُستخدم زيادة أو نقصان قيمة مؤشر ما للتنقل بين عناصر المصفوفة.
	</li>
	<li>
		يضمن المعيار أن محاولة الوصول إلى العنصر ذي الدليل "n" في مصفوفة ذات حجم "n" محاولةٌ صالحة على الرغم من عدم وجود هذا العنصر وذلك لتسهيل أمر التنقل داخل المصفوفة بزيادة قيمة المؤشر، ويكون مجال قيم مصفوفة مصرّح عنها على النحو <code>int ar[N]‎</code> هو <code>‎&amp;ar[0]‎</code> وصولًا إلى <code>‎&amp;ar[N]‎</code> ولكن يجب عليك تفادي الوصول إلى قيمة العنصر الأخير الزائف.
	</li>
</ul>

<h2>
	الأنواع المؤهلة Qualified
</h2>

<p>
	تابع قراءة الفقرات التالية إن كنت واثقًا من فهمك لأساسيات عملية التصريح عن المؤشرات واستخدامها، وإلا فمن المهم العودة إلى الفقرات السابقة ومحاولة فهمها جيّدًا قبل قراءة الفقرات التالية، إذ أن المعلومات المذكورة في الفقرات التالية أكثر تعقيدًا، ولا داعِ لجعل الأمر أسوأ بعدم تحضيرك وفهمك لما سبق.
</p>

<p>
	قدم المعيار شيئان باسم <strong>مؤهلات النوع type qualifiers</strong>، إذ لم يكونا في لغة سي القديمة مسبقًا، ويمكن تطبيقهما لأي نوع مصرّحٌ عنه للتعديل من تصرفه، ومن هنا أتى اسمهما. سنتجاهل إحداهما (المدعو باسم "volatile") إلا أنه لا يمكننا تجاهل الآخر "const".
</p>

<p>
	إذا سبقت الكلمة المفتاحية "const" أي تصريح، فهذا يعني أن الشيء الذي صُرّح عنه هو ثابت، ويجب عليك تجنب محاولة التغيير على قيمة الكائنات الثابتة "const" وإلا فستحصل على سلوك غير محدّد undefined behaviour، وسيحذّرك المصرّف عادةً بمنعك من هذه المحاولة إلا إذا تجاوزت هذا القيد بحيلةٍ ما.
</p>

<p>
	هناك فائدتان مرجوتان من تصريح كائن ما بكونه ثابتًا "const":
</p>

<ol>
	<li>
		يشير استخدام الكلمة المفتاحية "const" إلى أن القيمة غير قابلة للتغيير، مما يجبر المصرّف على التحقق من أي تغييرات طرأت على هذه القيمة ضمن الشيفرة البرمجية، وهذا الأمر باعث للطمأنينة في حال أردت استخدام قيمٍ ثابتة، مثل المؤشرات التي تُستخدم وسطاءً في بعض الدوال. إذا احتوى التصريح عن الدالة مؤشرات تشير إلى كائنات ثابتة على أنها وسطاء، فهذا يعني أن الدالة لن تشير إلى أي كائن آخر.
	</li>
	<li>
		عندما يعلم المصرّف بأن الأشياء هي كائنات ثابتة، ينعكس ذلك إيجابيًا على قدرته برفع فاعلية الشيفرة البرمجية وسرعتها.
	</li>
</ol>

<p>
	الثوابت عديمة الفائدة في حال لم تسند إليها أي قيمة، لن نتطرق بالتفصيل بخصوص تهيئة الثوابت (سنناقش الأمر لاحقًا)، كل ما عليك تذكره الآن هو أنه من الممكن لأي تصريح إسناد القيمة لتعبيرٍ ثابت. إليك بعض الأمثلة عن التصريح عن ثوابت:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9936_37" style=""><span class="kwd">const</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">        </span><span class="com">/* ثابت x */</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">float</span><span class="pln"> f </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3.5</span><span class="pun">;</span><span class="pln">    </span><span class="com">/* ثابت f*/</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> y</span><span class="pun">[</span><span class="lit">10</span><span class="pun">];</span><span class="pln">       </span><span class="com">/* y مصفوفة من 10 عناصر ذات قيم صحيحة ثابتة */</span><span class="pln">
                        </span><span class="com">/* لا تفكر بخصوص تهيئة قيمها بعد */</span></pre>

<p>
	ما يثير الاهتمام هو كون هذا المؤهل ممكن التطبيق على المؤشرات بطريقتين: إما بجعل الشيء الذي يشير إليه المؤشر ثابتًا، بحيث يصبح نوع المؤشر "مؤشر إلى ثابت"، أو بجعل المؤشر بذات نفسه ثابتًا (مؤشرًا ثابتًا)، إليك مثالًا عن ذلك:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9936_39" style=""><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">                  </span><span class="com">/* عدد صحيح اعتيادي i */</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> ci </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">       </span><span class="com">/* عدد صحيح ثابت ci */</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">pi</span><span class="pun">;</span><span class="pln">                </span><span class="com">/* مؤشر إلى عدد صحيح pi */</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">pci</span><span class="pun">;</span><span class="pln">         </span><span class="com">/* مؤشر إلى عدد صحيح ثابت pci */</span><span class="pln">
      </span><span class="com">/* بالانتقال إلى الأمثلة الأكثر تعقيدًا */</span><span class="pln">

</span><span class="com">// مؤشر ثابت يشير إلى قيمة عدد صحيح ‪‪cpi </span><span class="pln">
</span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="kwd">const</span><span class="pln"> cpi </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">i</span><span class="pun">;</span><span class="pln">

</span><span class="com">// مؤشر ثابت يشير إلى قيمة عدد صحيح ثابتة cpci</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="kwd">const</span><span class="pln"> cpci </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ci</span><span class="pun">;</span></pre>

<p>
	التصريح الأول (للمتغير <code>i</code>) اعتيادي، ولكن تصريح <code>ci</code> يوضح أنه عدد صحيح ثابت وبذلك لا يمكن التعديل على قيمته، وسيكون بلا فائدة إن لم يُهيّأ (إسناد قيمة أولية له).
</p>

<p>
	ليس من الصعب فهم الغرض من مؤشر لعدد صحيح، ومؤشر لعدد صحيح ثابت، ولكن يجب الانتباه إلى أنواع المؤشرات المختلفة، إذ لا يجوز الخلط بينهم. يمكنك تغيير قيمة كل من <code>pi</code> و <code>pci</code> بجعلهما يشيران إلى أشياء أخرى، كما يمكنك تغيير قيمة الشيء الذي يشير إليه المؤشر <code>pi</code> بالنظر إلى كونه عدد صحيح غير ثابت، ولكن لا يمكنك إلّا الوصول إلى قيمة الشيء الذي يشير إليه المؤشر <code>pci</code> دون التعديل عليه نظرًا لكونه ثابتًا.
</p>

<p>
	يُعد التصريحان الأخيران أكثر التصريحات تعقيدًا ضمن المثال؛ فإذا كانت المؤشرات بذات نفسها ثابتةً، فمن غير المسموح تغيير المكان الذي تشير إليه، وبالتالي يجب تهيأتهما بقيمة أولية مثل <code>ci</code>. يمكن للشيء المُشار إليه بالمؤشر أن يكون ثابتًا أو غير ثابت بغض النظر عن كون المؤشر ثابتًا بدوره أم لا، وهذا يملي بعض القيود على استخدام الكائن المُشار إليه.
</p>

<p>
	أخيرًا للتوضيح: ما الذي يملي وجود نوع مؤهّل؟ كان <code>ci</code> في المثال السابق نوعًا مؤهلًا بكل وضوح، ولكن <code>pci</code> لم تنطبق عليه هذه الحالة بما أن المؤشر ليس من نوع مؤهّل بل الشيء الذي يشير إليه. الأشياء الوحيدة الذي كانت ذات أنواع مؤهلة في المثال هي: <code>ci</code> و <code>cpi</code> و <code>cpci</code>.
</p>

<p>
	سيتطلب منك الأمر بعض الوقت للاعتياد على هذا النوع من التصريحات، ولكن استخدامها سيكون تلقائيًّا وطبيعيًا بعد فترة فلا تقلق، إلا أن التعقيدات تبدأ بالظهور لاحقًا عندما يتوجب عليك الإجابة على السؤال: "هل من المسموح لي -على سبيل المثال- مقارنة مؤشر اعتيادي مع مؤشر ثابت؟ وإذا كان الجواب نعم، ما الذي تعنيه المقارنة؟". معظم القوانين واضحة بهذا الخصوص، ولكن يجب ذكرها وتوضيحها بغض النظر عن سهولتها.
</p>

<p>
	سنتكلم عن أنواع البيانات المؤهلة بتوسعٍ أكبر لاحقًا.
</p>

<h2>
	عمليات المؤشرات الحسابية
</h2>

<p>
	سنتكلم بالتفصيل لاحقًا عن عمليات المؤشرات الحسابية، ولكننا سنكتفي الآن بإصدار مختصرٍ يفي بالغرض.
</p>

<p>
	يمكنك الطرح أو المقارنة بين مؤشرين من النوع نفسه بالإضافة إلى إضافة قيمة عددية صحيحة إلى المؤشر، ويجب أن يشير كلا المؤشرين إلى المصفوفة ذاتها وإلا حصلنا على سلوك غير محدد. نتيجة طرح مؤشرين هي قيمة العناصر التي تفصل بينهما في المصفوفة، ونوع النتيجة معرفٌ بحسب التطبيق وسيكون إما "short" أو "int" أو "long"، ويوضح المثال القادم مثالًا على حساب الفرق بين مؤشرين واستخدام النتيجة، ولكن قبل أن ننتقل إلى المثال يجب أن تعرف معلومةً مهمة.
</p>

<p>
	<strong>يُحوَّل اسم المصفوفة في أي تعبير إلى مؤشر يشير إلى العنصر الأول ضمن هذه المصفوفة</strong>، والحالة الوحيدة الاستثنائية هي عند استخدام اسم المصفوفة مع الكلمة المفتاحية "sizeof"، أو عند استخدام سلسلة نصية لتهيئة قيمة مصفوفة ما، أو عندما يكون اسم المصفوفة مرتبطًا بعامل "عنوان الكائن" (العامل الأحادي "&amp;")، لكننا لم نتطرق إلى أيّ من الحالات السابقة بعد، وسنناقشها لاحقًا. إليك المثال:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9936_41" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> ARSZ </span><span class="lit">10</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="typ">float</span><span class="pln"> fa</span><span class="pun">[</span><span class="pln">ARSZ</span><span class="pun">],</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fp1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fp2</span><span class="pun">;</span><span class="pln">

      fp1 </span><span class="pun">=</span><span class="pln"> fp2 </span><span class="pun">=</span><span class="pln"> fa</span><span class="pun">;</span><span class="pln"> </span><span class="com">/* عنوان العنصر الأول */</span><span class="pln">
      </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">fp2 </span><span class="pun">!=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">fa</span><span class="pun">[</span><span class="pln">ARSZ</span><span class="pun">]){</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"Difference: %d\n"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="pln">fp2</span><span class="pun">-</span><span class="pln">fp1</span><span class="pun">));</span><span class="pln">
              fp2</span><span class="pun">++;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      exit</span><span class="pun">(</span><span class="pln">EXIT_SUCCESS</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 4]
</p>

<p>
	يشير المؤشر <code>fp2</code> إلى عناصر المصفوفة، ويُطبع الفرق بين قيمته الحالية وقيمته الأصلية، وللتأكد من عدم تمرير النوع الخاطئ للوسيط للدالة <code>printf</code> تُحوَّل القيمة الناتجة عن فرق المؤشرين قسريًّا إلى <code>int</code> باستخدام تحويل الأنواع <code>(int)</code>، وهذا يجنّبنا من الأخطاء على الحواسيب التي تعيد قيمة <code>long</code> لهذا النوع من العمليات.
</p>

<p>
	قد يعود إلينا المثال السابق بإجابات خاطئة إذا كان الفرق بين القيمتين من نوع <code>long</code> وكانت المصفوفة كبيرة الحجم، ونلاحظ في المثال التالي إصدارًا آمنًا من المثال السابق، إذ يُستخدم تحويل الأنواع قسريًا للسماح بوجود قيم <code>long</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9936_43" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> ARSZ </span><span class="lit">10</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="typ">float</span><span class="pln"> fa</span><span class="pun">[</span><span class="pln">ARSZ</span><span class="pun">],</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fp1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">fp2</span><span class="pun">;</span><span class="pln">

      fp1 </span><span class="pun">=</span><span class="pln"> fp2 </span><span class="pun">=</span><span class="pln"> fa</span><span class="pun">;</span><span class="pln"> </span><span class="com">/* عنوان العنصر الأول */</span><span class="pln">
      </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">fp2 </span><span class="pun">!=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">fa</span><span class="pun">[</span><span class="pln">ARSZ</span><span class="pun">]){</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"Difference: %ld\n"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">long</span><span class="pun">)(</span><span class="pln">fp2</span><span class="pun">-</span><span class="pln">fp1</span><span class="pun">));</span><span class="pln">
              fp2</span><span class="pun">++;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="kwd">return</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[مثال 5]
</p>

<h2>
	مؤشرات void و null والمؤشرات الإشكالية
</h2>

<p>
	لغة سي حريصة بشأن أنواع المؤشرات، ولن تسمح لك عمومًا باستخدام مؤشرات ذات أنوع مختلفة ضمن التعبير ذاته. يختلف مؤشر إلى نوع "char" عن مؤشر إلى نوع "int" ولا يمكنك -على سبيل المثال- إسناد قيمة أحدهما إلى الآخر أو المقارنة فيما بينهما أو طرحهما من بعضهما واستخدام النتيجة مثل وسيط في دالة ما، كما يمكن أيضًا تخزين النوعين في الذاكرة بصورةٍ مختلفة وأن يكونا بأطوال مختلفة.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix" data-gramm="false">
		<p>
			لا تتماثل المؤشرات من أنواع مختلفة فيما بينها، وليس هناك أي تحويلات ضمنية بين الأنواع كما شاهدنا في الأنواع الحسابية سابقًا
		</p>
	</div>
</blockquote>

<p>
	لكن نريد في بعض الحالات تجاوز هذه القيود، فكيف نفعل ذلك؟
</p>

<p>
	يكمن الحل هنا باستخدام أنواع خاصة، وقد قدمنا واحدًا من هذه الأنواع سابقًا ألا وهو "مؤشر إلى void"، وقُدّمت هذه الميزة مع سي المعيارية إذ افتُرض سابقًا أن المؤشر من نوع "مؤشر إلى char" كافٍ لهذه المهمة، وكان الافتراض صحيحًا إلى حدٍّ ما إلا أنه كان حلًّا غير منظّمًا، بينما قدّم الحل الجديد طريقةً أكثر أمانًا وأقل تشويشًا. لا يوجد أي استخدام للمؤشر هذا، لأن "* void" لا يشير إلى أي قيمة، لذا يحسّن هذا الأمر من سهولة قراءة الشيفرة البرمجية. يمكن أن يخزن المؤشر من النوع "* void" أي قيمة من أي مؤشر آخر، ويمكن إسناده إلى مؤشر آخر من أي نوع أيضًا، إلا أنه يجب استخدام هذا النوع من المؤشرات بحذر لأنه قد ينتهي الأمر بك ببعض الأخطاء الوخيمة، وسنناقش استخدامه الآمن مع دالة "malloc" لاحقًا.
</p>

<p>
	قد تحتاج أيضًا في بعض الحالات إلى مؤشر مضمون أنه لا يشير إلى أي كائن والذي يُدعى مؤشر من نوع "null" أو <strong>مؤشر الفراغ null pointer</strong>. من الشائع في لغة سي كتابة بعض الإجراءات routines التي تعيد مؤشرات، بحيث يمكن الدلالة على فشل ذلك الإجراء بعدم قدرته على إعادة مؤشر صالح عن طريق إعادة مؤشر الفراغ. ومن الأمثلة على ذلك إجراء فحصٍ لقيم موجودة في جدول ما بحثًا عن قيمة معينة ويعيد مؤشرًا على الكائن المُطابق إن وُجدت النتيجة أو مؤشر الفراغ إن لم تُوجد أي نتيجةٍ مطابقة.
</p>

<p>
	لكن كيف يمكن كتابة مؤشر فراغ؟ هناك طريقتان لفعل ذلك وكلا الطريقتين متماثلتين في النتيجة، إما باستخدام رقم صحيح ثابت بقيمة "0" أو تحويل القيمة إلى نوع "* void" باستخدام التحويل بين الأنواع، وتدعى نتيجة الطريقتين <strong>بمؤشر الفراغ الثابت null pointer constant</strong>. إذا أسندت مؤشر فراغ إلى أي مؤشر آخر أو قارنت بين مؤشر الفراغ ومؤشر آخر فسيُحوَّل مؤشر الفراغ إلى نوع المؤشر الآخر تلقائيًّا، مما سيحلّ أي مشكلة بخصوص توافقية الأنواع، ولن تُساوي القيمة التي يشير إليها ذلك المؤشر -مؤشر الفراغ- أي قيمة كائن آخر يشير إليها أي مؤشر داخل البرنامج (أي سيشير إلى قيمة فريدة).
</p>

<p>
	القيم الوحيدة الممكن إسنادها للمؤشرات باستثناء القيمة "0" هي قيم المؤشرات الأخرى من نفس النوع، إلا أن الأمر الذي يجعل من لغة سي مميزة وبديلًا مفيدًا للغة التجميعية هو سماحها لك بفعل بعض الأشياء التي لا تسمح لك بها معظم لغات البرمجة الأخرى، جرّب التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9936_45" style=""><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ip</span><span class="pun">;</span><span class="pln">
ip </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> </span><span class="pun">*)</span><span class="lit">6</span><span class="pun">;</span><span class="pln">
</span><span class="pun">*</span><span class="pln">ip </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0xFF</span><span class="pun">;</span></pre>

<p>
	ما نتيجة السابق؟ تُهيأ قيمة المؤشر إلى <code>6</code> (لاحظ تحويل نوع <code>6</code> من <code>int</code> إلى مؤشر)، وهذه عملية تُجرى على مستوى الآلة غالبًا ويكون تمثيل قيمة المؤشر بالبتات غير مشابه لما قد يكون تمثيل الرقم <code>6</code>، كما تُسند القيمة الست عشرية <code>FF</code> بعد التهيئة إلى الكائن الذي يشير إليه المؤشر. تُكتب القيمة <code>0xFF</code> على الرقم الصحيح ذو الموضع <code>6</code>، إذ يعتمد الموضع <code>6</code> على تفسير الآلة له على الذاكرة.
</p>

<p>
	قد تحتاج لهذا النوع من الأشياء وقد لا تحتاج إليها إطلاقًا، ولكن لغة سي تعطيك الخيار، ومن الواجب معرفتها إذ أنه من الممكن كتابتها على نحوٍ خاطئ غير مقصود مما سيتسبب بنتائج مفاجئة جدًا.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter5/" rel="external nofollow">Arrays and Pointers</a> من كتاب <a href="https://publications.gbdirect.co.uk/c_book/" rel="external nofollow">The C Book</a>‪‏.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D9%88%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1680/" rel="">التعامل مع المحارف والسلاسل النصية في لغة سي C</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/c/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1675/" rel="">مدخل إلى المصفوفات في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D9%86%D8%B7%D8%A7%D9%82-scope-%D9%88%D8%A7%D9%84%D8%B1%D8%A8%D8%B7-linkage-%D8%B9%D9%84%D9%89-%D9%85%D8%B3%D8%AA%D9%88%D9%89-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1674/" rel="">مفهوم النطاق Scope والربط Linkage على مستوى الدوال في لغة C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%88%D8%AF-recursion-%D9%88%D8%AA%D9%85%D8%B1%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D8%B3%D8%B7%D8%A7%D8%A1-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1673/" rel="">مفهوم التعاود Recursion وتمرير الوسطاء إلى الدوال في لغة سي C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%B3%D9%8A-c-r1610/" rel="">البنية النصية لبرامج سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1679</guid><pubDate>Sat, 03 Sep 2022 16:01:00 +0000</pubDate></item></channel></rss>
