<?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/page/2/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x644;&#x63A;&#x629; C</description><language>ar</language><item><title>&#x645;&#x62F;&#x62E;&#x644; &#x625;&#x644;&#x649; &#x627;&#x644;&#x645;&#x635;&#x641;&#x648;&#x641;&#x627;&#x62A; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_08/62fb91dae821b_----Arrays---C-.png.8b955c33ad77df7ab14be27b6fffb8da.png" /></p>
<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="">المصفوفات Arrays</a> مثل سائر اللغات الأخرى لتمثيل مجموعة من متغيرات ذات خصائص متماثلة، إذ يكون لهذه المجموعة اسمًا واحدًا وتُحدّد عناصرها عن طريق <strong>دليل Index</strong>. إليك مثالًا للتصريح عن مصفوفةٍ ما:
</p>

<pre class="ipsCode">double ar[100];
</pre>

<p>
	في هذا المثال اسم المصفوفة هو <code>ar</code> ويمكن الوصول لعناصر المصفوفة عن طريق دليل كلٍّ منها كما يلي: <code>ar[0]‎</code> وصولًا إلى <code>ar[99]‎</code> لا غير، كما يوضح الشكل 1:
</p>

<p style="text-align: center;">
	<img alt="002Array.png" class="ipsImage ipsImage_thumbnailed" data-fileid="105664" data-unique="1757hh2yr" style="" src="https://academy.hsoub.com/uploads/monthly_2022_08/002Array.png.c9e693039993d698e7f26a4efcda4f8c.png">
</p>

<p style="text-align: center;">
	شكل 1 مصفوفة ذات 100 عنصر
</p>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_756_7" style=""><span class="pln">f</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> x</span><span class="pun">){</span><span class="pln">
      </span><span class="kwd">char</span><span class="pln"> var_sized_array</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">
</span><span class="pun">}</span></pre>

<p>
	وهذا ممنوع لأن قيمة <code>x</code> غير معروفة عند تصريف البرنامج، فهي قيمة تُعرف عند تشغيل البرنامج وليس عند تصريفه.
</p>

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

<h2>
	المصفوفات متعددة الأبعاد
</h2>

<p>
	يمكن التصريح عن المصفوفات متعددة الأبعاد Multidimensional arrays على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_756_9" style=""><span class="typ">int</span><span class="pln"> three_dee</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="lit">2</span><span class="pun">];</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> t_d</span><span class="pun">[</span><span class="lit">2</span><span class="pun">][</span><span class="lit">3</span><span class="pun">]</span></pre>

<p>
	تُستخدم الأقواس المعقوفة بعد كلٍّ من المصفوفات السابقة، وإذا نظرت إلى جدول الأسبقية في مقال <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>three_dee</code>، ويحتوي كل عنصر من عناصر هذه المصفوفة بدوره على مصفوفة بحجم أربعة عناصر وكل عنصر من هذه المصفوفة الأخيرة يحتوي على مصفوفة من عنصرين، وجميع العناصر من نوع <code>int</code>، وبهذه الحالة فنحن صرحنا عن مصفوفة مصفوفات، ويوضح الشكل 2 مثالًا على مصفوفة ثنائية البعد باسم <code>t_d</code> في مثال التصريح.
</p>

<p style="text-align: center;">
	<img alt="003MultidimensionalArray.png" class="ipsImage ipsImage_thumbnailed" data-fileid="105663" data-unique="vnlw1x6w5" style="" src="https://academy.hsoub.com/uploads/monthly_2022_08/003MultidimensionalArray.png.8c8f9fb38f0493d165fbf91b7492dfec.png">
</p>

<p style="text-align: center;">
	شكل 2 هيكل مصفوفة ثنائية البعد
</p>

<p>
	ستلاحظ في الشكل السابق أن <code>t_d[0]‎</code> عنصرٌ واحدٌ متبوعٌ بعنصر <code>t_d[1]‎</code> دون أي فواصل، وكلا العنصرين يمثّلان مصفوفةً بحدّ ذاتهما بسعة ثلاث أعداد صحيحة. يأتي العنصر <code>t_d[1][0]‎</code> مباشرةً بعد العنصر <code>t_d[0][2]‎</code>، ومن الممكن الوصول إلى <code>t_d[1][0]‎</code> بالاستفادة من عدم وجود أي طريقة للتحقق من حدود المصفوفة باستخدام التعبير <code>t_d[0][3]‎</code> إلا أن هذا غير محبّذ أبدًا، لأن النتائج ستكون غير متوقعة إذا تغيّرت تفاصيل التصريح عن المصفوفة <code>t_d</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_756_11" style=""><span class="pln">three_dee</span><span class="pun">[</span><span class="lit">1</span><span class="pun">][</span><span class="lit">3</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">
three_dee</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="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></pre>

<p>
	التعبير الأخير مثيرٌ للاهتمام لسببين، أولهما أنه يصل إلى قيمة العنصر الأخير من المصفوفة والمصرّح أنها بحجم <code>[2][4][5]</code>، والدليل الذي نستطيع استخدامه هو أقل بواحد دائمًا من العدد الذي صرّحنا عنه، أما ثانيًا، فنلاحظ أهمية وسهولة استخدام عامل الإسناد المُركّب في هذه الحالة. يفضّل مبرمجو لغة سي المتمرسون هذه الطريقة المختصرة، إليك كيف سيبدو الأمر لو كان التعبير مكتوبًا بلغة أخرى لا تسمح باستخدام هذا العامل:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_756_13" style=""><span class="pln">three_dee</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="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> three_dee</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="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></pre>

<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>، إذ يُحسب دليل العنصر وقيمته مرةً واحدة، مما ينتج شيفرةً برمجيةً أقصر وأسرع. قد ينتبه بعض المصرّفات طبعًا إلى أن العنصرين على طرفَي عامل الإسناد متساويين ولن تلجأ للوصول للقيمة مرتين، ولكن هذه الحالة لا تنطبق على جميع المصرّفات، وهناك العديد من الحالات أيضًا التي لا تستطيع فيها المصرفات الذكية هذه باختصار الخطوات.
</p>

<p>
	على الرغم من تقديم لغة سي دعمًا للمصفوفات متعددة الأبعاد، إلا أنه من النادر أن تجدها مُستخدمةً عمليًّا، إذ تُستخدم المصفوفات أحادية البعد أكثر في معظم البرامج، وأبسط هذه الأسباب هو أن السلسلة النصية String تُمثّل بمصفوفة أحادية البعد، وقد تلاحظ استخدام المصفوفات ثنائية البعد في بعض الحالات، ولكن استخدام المصفوفات ذات أبعاد أكثر من ذلك نادرة الحدوث، وذلك لكون المصفوفة هيكل بيانات غير مرن بالتعامل، كما أن سهولة عملية إنشاء ومعالجة هياكل البيانات وأنواعها في لغة سي تعني إمكانية استبدال المصفوفات في معظم البرامج متقدمة المستوى، وسننظر إلى هذه الطرق عندما ننظر إلى المؤشرات.
</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%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/%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%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%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">1675</guid><pubDate>Tue, 30 Aug 2022 16:04:00 +0000</pubDate></item><item><title>&#x645;&#x641;&#x647;&#x648;&#x645; &#x627;&#x644;&#x646;&#x637;&#x627;&#x642; Scope &#x648;&#x627;&#x644;&#x631;&#x628;&#x637; Linkage &#x639;&#x644;&#x649; &#x645;&#x633;&#x62A;&#x648;&#x649; &#x627;&#x644;&#x62F;&#x648;&#x627;&#x644; &#x641;&#x64A; &#x644;&#x63A;&#x629; C</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_08/62fb4d6e9dfad_--Linkage--Scope---C-.png.b8a6095c4ca54c84ccf823ec3baac6bc.png" /></p>
<p>
	على الرغم من تفادينا لموضوعَي النطاق Scope والربط Linkage في أمثلتنا البسيطة سابقًا، إلا أن الوقت قد حان لشرح هذين المفهومين وأثرهما على قابلية الوصول للكائنات المختلفة في برنامج سي C، ولكن لمَ علينا الاكتراث بذلك على أي حال؟ لأن البرامج العملية التي نستخدمها تُبنى من عدّة ملفات ومكتبات، وبالتالي من المهم للدوال في ملف ما أن تكون قادرةً على الإشارة إلى دوال، أو كائنات في ملفات أو مكتبات أخرى، وهناك عدّة قوانين ومفاهيم تجعل من ذلك ممكنًا.
</p>

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

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

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

<p>
	يمكن للكائنات الخارجية فقط أن تُشارك في هذا الاتصال عبر الملفات والمكتبات، وتُعرف قابلية الوصول للكائنات هذه من ملف إلى آخر أو ضمن الملف نفسه وفقًا للمعيار باسم <strong>الربط Linkage</strong>، وهناك ثلاثة أنواع للربط، هي: <strong>الربط الخارجي External linkage</strong> و<strong>الربط الداخلي Internal Linkage</strong> و<strong>عديم الربط No linkage</strong>. يكون أي شيء داخلي في الدالة سواءٌ كان وسطاء الدالة أو متغيراتها عديم الربط <strong>دائمًا</strong> ويمكن الوصول إليه من داخل الدالة فقط، ويمكنك التصريح عن الشيء الذي تريده داخل الدالة مسبوقًا بالكلمة المفتاحية <code>extern</code> لتجاوز هذا القيد، وهذا سيدل على أن الكائن ليس داخليًّا، وليس عليك القلق بهذا الشأن في الوقت الحالي.
</p>

<p>
	تكون الكائنات ذات الربط الخارجي موجودةً على المستوى الخارجي لبنية البرنامج، وهذا هو نوع الربط الافتراضي للدوال ولأي شيء آخر يُصرَّح عنه خارج الدوال، و<strong>تُشير جميع الأسماء المماثلة لاسم الكائن ذو الربط الخارجي إلى الكائن نفسه</strong>. ستحصل على سلوك غير محدد من البرنامج، إذا صرحت عن كائن بنفس الاسم مرتين أو أكثر بربط خارجي وبأنواع غير متوافقة. المثال الذي يأتي إلى بالنا مباشرةً بخصوص الربط الخارجي هو الدالة <code>printf</code> والمُصرّح عنها في ملف الترويسة <code>&lt;stdio.h&gt;</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4533_7" style=""><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"> </span><span class="pun">...);</span></pre>

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

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

<p>
	تُشير الأسماء ذات الربط الداخلي للكائن ذاته ضمن ملف الشيفرة المصدرية الواحد، ويمكنك التصريح عن كائن ذي ربط داخلي عن طريق بدء التصريح بالكلمة المفتاحية "static" التي ستغيّر ربط الكائن من ربط خارجي (افتراضي) إلى ربط داخلي، كما يمكنك التصريح عن كائنات داخلية باستخدام "static" بهدف استخدامٍ آخر ولكننا لن نتطرق لهذا الاستخدام حاليًا.
</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>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				خارجي
			</td>
			<td>
				خارجي
			</td>
			<td>
				يمكن الوصول إليه من أي مكان ضمن البرنامج
			</td>
		</tr>
		<tr>
			<td>
				داخلي
			</td>
			<td>
				خارجي
			</td>
			<td>
				يمكن الوصول إليه عبر ملف واحد فقط
			</td>
		</tr>
		<tr>
			<td>
				لا يوجد ربط
			</td>
			<td>
				داخلي
			</td>
			<td>
				محلّي لدالة واحدة
			</td>
		</tr>
	</tbody>
</table>

<p style="text-align: center;">
	[جدول 1 الربط وقابلية الوصول]
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4533_11" style=""><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="com">/* تعريف*/</span><span class="pln">
main </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_in_other_place </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">
  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">
</span><span class="com">/* نهاية الملف الأول */</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"> i</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"> f_in_other_place </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">
  i</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;">
	[مثال 1]
</p>

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

<ul>
	<li>
		التصريح عن الدالة دون تضمين متن الدالة هو تصريحٌ لا غير.
	</li>
	<li>
		التصريح عن الدالة مرفقًا بمتن الدالة هو تعريف.
	</li>
	<li>
		عمومًا، التصريح عن كائن على المستوى الخارجي للبرنامج (مثل المتغير <code>i</code> في المثال السابق) هو تعريف، إلا إذا سُبق بالكلمة المفتاحية <code>extern</code> وعندها يصبح تصريحًا فقط.
	</li>
</ul>

<p>
	وسنتكلم لاحقًا عن التعريف والتصريح بصورةٍ أعمق لا تبقي مجالًا للشك.
</p>

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

<p>
	إليك مثالًا آخر يوضح استخدام "static" لتقييد قابلية الوصول إلى الدوال والأشياء الأخرى.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4533_13" style=""><span class="com">/* مثال عن وحدة في مكتبة */</span><span class="pln">
</span><span class="com">/* الدالة callable هي الدالة الوحيدة المرئية على المستوى الخارجي */</span><span class="pln">
</span><span class="kwd">static</span><span class="pln"> buf </span><span class="pun">[</span><span class="lit">100</span><span class="pun">];</span><span class="pln">
</span><span class="kwd">static</span><span class="pln"> length</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"> fillup</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">
callable </span><span class="pun">(){</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">length </span><span class="pun">==</span><span class="lit">0</span><span class="pun">){</span><span class="pln">
              fillup </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">buf </span><span class="pun">[</span><span class="pln">length</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">
fillup </span><span class="pun">(</span><span class="kwd">void</span><span class="pun">){</span><span class="pln">
      </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">length </span><span class="pun">&lt;</span><span class="lit">100</span><span class="pun">){</span><span class="pln">
              buf </span><span class="pun">[</span><span class="pln">length</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="pun">}</span></pre>

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

<p>
	يمكن للمستخدم -بفرض استخدام المثال السابق وحدةً مستقلّة- إعادة استخدام الأسماء "length" و "buf" و "fillup" بأمان دون أي تأثيرات جانبية أو أخطاء غير متوقعة، ونستثني من ذلك الاسم "callable"، إذ إنه قابل الوصول خارج الملف (الوحدة المستقلة).
</p>

<p>
	تكون قيمة الكائن الخارجي الذي يمتلك مُهيّئًا initializer واحدًا مساوية للصفر قبل بدء البرنامج (لم نتكلم عن أي مُهيّئات عدا الدوال حتى الآن)، وتعتمد الكثير من البرامج على ذلك، بما فيها المثال السابق لقيمة <code>length</code> الابتدائية.
</p>

<h2>
	تأثير النطاق
</h2>

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

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

<p>
	<strong>ملاحظة:</strong> يمكنك تجاوز الكلمة المفتاحية "extern" عن طريق <strong>مُهيّئ</strong> للكائن.
</p>

<p>
	التصريح عن أي كائن بيانات (ليس بدالة) ضمن المستوى الخارجي للبرنامج هو تعريفٌ، إلا إذا سبق التصريح الكلمة المفتاحية "extern"، راجع المثال 9.4 من أجل ملاحظة هذه النقطة عمليًّا.
</p>

<p>
	تحتوي تصريحات الدوال الكلمة المفتاحية "extern" ضمنيًّا سواءٌ كانت مكتوبةً أم لا، والطريقتان التاليتان للتصريح عن الدالة <code>some_function</code> متكافئتان، وتُعدان تصريحًا وليس تعريفًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4533_15" style=""><span class="kwd">void</span><span class="pln"> some_function</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">extern</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> some_function</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span></pre>

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

<p>
	لكن ما الذي يحدث في المثال التالي؟
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4533_17" style=""><span class="kwd">void</span><span class="pln"> some_function</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"> i_var</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">extern</span><span class="pln"> </span><span class="typ">float</span><span class="pln"> e_f_var</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"> another_func</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"> i</span><span class="pun">;</span><span class="pln">
      i </span><span class="pun">=</span><span class="pln"> e_f_var</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>
	سنناقش المزيد من القواعد عن الطريقة الأمثل لإنشاء برنامج ذو ملفات متعددة، وما الممكن حدوثه عند المزج بين التصريحات الداخلية والخارجية والكلمات المفتاحية "extern" و "static". لن تكون عملية قراءة هذه القواعد ممتعةً، لكنها ستكون إجابةً لأسئلة من نوع "ماذا لو؟".
</p>

<h2>
	الكائنات الداخلية الساكنة
</h2>

<p>
	يمكنك التصريح عن كائنات داخلية على أنها كائنات داخلية ساكنة باستخدام الكلمة المفتاحية "static"، وتكتسب المتغيرات الداخلية بعضًا من الخصائص باستخدامها هذه الكلمة المفتاحية ألا وهي:
</p>

<ul>
	<li>
		تُهيّأ قيمتها إلى الصفر عند بداية البرنامج.
	</li>
	<li>
		تحافظ على قيمتها من بداية التعليمة التي تضم تصريحها إلى نهايتها.
	</li>
	<li>
		يوجد نسخةٌ واحدةٌ من كل متغيّر داخلي ساكن تتشاركه الاستدعاءات التعاودية للدوال التي تحوي هذه المتغيرات.
	</li>
</ul>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4533_19" style=""><span class="typ">int</span><span class="pln">
small_val </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="kwd">static</span><span class="pln"> </span><span class="kwd">unsigned</span><span class="pln"> count</span><span class="pun">;</span><span class="pln">
      count </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">count </span><span class="pun">%</span><span class="pln"> </span><span class="lit">16</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<p>
	يمكن أن نستخدم هذه الطريقة للكشف عن الاستدعاءات التعاودية مفرطة الحدوث:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4533_21" style=""><span class="kwd">void</span><span class="pln">
r_func </span><span class="pun">(</span><span class="kwd">void</span><span class="pun">){</span><span class="pln">
      </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> depth</span><span class="pun">;</span><span class="pln">
      depth</span><span class="pun">++;</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">depth </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">200</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">"excessive recursion\n"</span><span class="pun">);</span><span class="pln">
              exit </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">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
              </span><span class="com">/* .r_func() نفّذ بعض الأوامر الاعتيادية. قد تتسبب النتائج باستدعاء آخر لدالة التعاود */</span><span class="pln">
              x_func</span><span class="pun">();</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      depth</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/chapter4/" rel="external nofollow">Functions</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%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%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%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%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>
	<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">1674</guid><pubDate>Sun, 28 Aug 2022 16:08:00 +0000</pubDate></item><item><title>&#x645;&#x641;&#x647;&#x648;&#x645; &#x627;&#x644;&#x62A;&#x639;&#x627;&#x648;&#x62F; Recursion &#x648;&#x62A;&#x645;&#x631;&#x64A;&#x631; &#x627;&#x644;&#x648;&#x633;&#x637;&#x627;&#x621; &#x625;&#x644;&#x649; &#x627;&#x644;&#x62F;&#x648;&#x627;&#x644; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_08/62fb487f5177d_---Recursion-------C-.png.be90efa6953844a867881ea405d3a363.png" /></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="">كيفية تعيين نوع للدالة Funtion type</a> (كيفية التصريح عن القيمة المُعادة ونوع أيّ وسيط argument تأخذه الدالة)، وأن تعريف definition الدالة يمثّل متنها أو جسمها body، ولننظر الآن إلى استخدامات الوسطاء.
</p>

<h2>
	استدعاء الوسيط بقيمته call by value
</h2>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9919_8" style=""><span class="kwd">void</span><span class="pln"> called_func</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">

main</span><span class="pun">(){</span><span class="pln">
      called_func</span><span class="pun">(</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="lit">3.5</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">
called_func</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> iarg</span><span class="pun">,</span><span class="pln"> </span><span class="typ">float</span><span class="pln"> farg</span><span class="pun">){</span><span class="pln">
      </span><span class="typ">float</span><span class="pln"> tmp</span><span class="pun">;</span><span class="pln">

      tmp </span><span class="pun">=</span><span class="pln"> iarg </span><span class="pun">*</span><span class="pln"> farg</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<p>
	يوجد للدالة <code>called_func</code> تعبيران يحلّان محل الوسطاء في دالة <code>main</code>، وتُقيّم قيمتهما ويُستخدمان في إسناد قيمة مبدئية للمعاملين <code>irag</code> و <code>frag</code> في الدالة <code>called_func</code>، ويملك المعاملان الخصائص ذاتها التي يملكها أي متغير داخلي مصرّحٌ عنه في الدالة <code>called_func</code> دون أي تفريق، مثل <code>tmp</code>.
</p>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9919_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">
main</span><span class="pun">(){</span><span class="pln">
      </span><span class="kwd">void</span><span class="pln"> changer</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="pln"> i</span><span class="pun">;</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">
      printf</span><span class="pun">(</span><span class="str">"before i=%d\n"</span><span class="pun">,</span><span class="pln"> i</span><span class="pun">);</span><span class="pln">
      changer</span><span class="pun">(</span><span class="pln">i</span><span class="pun">);</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"after i=%d\n"</span><span class="pun">,</span><span class="pln"> 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><span class="pln">

</span><span class="kwd">void</span><span class="pln">
changer</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> x</span><span class="pun">){</span><span class="pln">
      </span><span class="kwd">while</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">"changer: x=%d\n"</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="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<p>
	ستكون نتيجة المثال السابق على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9919_12" style=""><span class="pln">before i</span><span class="pun">=</span><span class="lit">5</span><span class="pln">
changer</span><span class="pun">:</span><span class="pln"> x</span><span class="pun">=</span><span class="lit">5</span><span class="pln">
changer</span><span class="pun">:</span><span class="pln"> x</span><span class="pun">=</span><span class="lit">4</span><span class="pln">
changer</span><span class="pun">:</span><span class="pln"> x</span><span class="pun">=</span><span class="lit">3</span><span class="pln">
changer</span><span class="pun">:</span><span class="pln"> x</span><span class="pun">=</span><span class="lit">2</span><span class="pln">
changer</span><span class="pun">:</span><span class="pln"> x</span><span class="pun">=</span><span class="lit">1</span><span class="pln">
after i</span><span class="pun">=</span><span class="lit">5</span></pre>

<p>
	تَستخدم الدالة <code>changer</code> معاملها الفعلي <code>x</code> بمثابة متغير اعتيادي (وهو فعلًا متغير اعتيادي)، ورغم أن قيمة <code>x</code> تغيرت إلا أن المتغير <code>i</code> في الدالة <code>main</code> لم يتأثر بالتغيير، وهذه هي النقطة التي نريد توضيحها لك بمثالنا، إذ تُمرَّر الوسطاء في سي C إلى الدالة باستخدام قيمها فقط ولا تُمرر أي تغييرات من الدالة بالمقابل.
</p>

<h2>
	استدعاء الوسيط بمرجعه call by reference
</h2>

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

<h2>
	التعاود Recursion
</h2>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9919_14" style=""><span class="com">/* مثال عن الحجز التلقائي */</span><span class="pln">
main</span><span class="pun">(){</span><span class="pln">
      </span><span class="kwd">auto</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> var_name</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></pre>

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

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

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

<p>
	يوضح المثال التالي برنامجًا يحتوي دالةً تعاوديةً تتحقق من التعابير المُدخلة إليها بما فيها الأرقام (0-9) والعوامل "*" و "%" و "/" و "+" و "-"، إضافةً إلى الأقواس، بالطريقة نفسها التي تستخدمها لغة سي، كما استخدم ستروستروب Stroustrup في كتابه عن <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>
	يُقيّم التعبير في المثال التالي، ثُم تُطبع قيمته إن صادف محرفًا غير موجودًا في لغته (المحارف التي ذكرناها سابقًا)، ولغرض البساطة لن يكون في المثال أي طريقة للتحقق من الأخطاء. يعتمد المثال كثيرًا على الدالة <code>ungetc</code> التي تسمح للمحرف الأخير الذي قُرأ بواسطة الدالة <code>getchar</code> أن يُعيّن على أنه "غير مقروء" للسماح بقراءته مرةً أخرى، والمُعامل الثاني المُستخدم في المثال مُصرّحٌ عنه في <code>stdio.h</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9919_17" style=""><span class="str">&lt;primary&gt;</span><span class="pln"> </span><span class="pun">::=</span><span class="pln"> digit </span><span class="pun">|</span><span class="pln"> </span><span class="pun">(&lt;</span><span class="pln">exp</span><span class="pun">&gt;)</span><span class="pln">
</span><span class="str">&lt;unary&gt;</span><span class="pln">   </span><span class="pun">::=</span><span class="pln"> </span><span class="str">&lt;primary&gt;</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="pun">-&lt;</span><span class="pln">unary</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="pun">+&lt;</span><span class="pln">unary</span><span class="pun">&gt;</span><span class="pln">
</span><span class="str">&lt;mult&gt;</span><span class="pln">    </span><span class="pun">::=</span><span class="pln"> </span><span class="str">&lt;unary&gt;</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="str">&lt;mult&gt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="str">&lt;unary&gt;</span><span class="pln"> </span><span class="pun">|</span><span class="pln">
              </span><span class="str">&lt;mult&gt;</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="str">&lt;unary&gt;</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="str">&lt;mult&gt;</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> </span><span class="str">&lt;unary&gt;</span><span class="pln">
</span><span class="str">&lt;exp&gt;</span><span class="pln">     </span><span class="pun">::=</span><span class="pln"> </span><span class="str">&lt;exp&gt;</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">&lt;mult&gt;</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="str">&lt;exp&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="str">&lt;mult&gt;</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="str">&lt;mult&gt;</span></pre>

<p>
	يكمن التعاود في مثالنا ضمن مكانين أساسيين، هما: الدالة <code>unary_exp</code> التي تستدعي نفسها، والدالة <code>primary</code> التي تستدعي الدالة الموجودة على المستوى العلوي للبرنامج (نقصد دالة <code>expr</code>) لتقييم التعابير المكتوبة بين قوسين.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9919_19" style=""><span class="lit">1</span><span class="pln">
</span><span class="lit">1</span><span class="pun">+</span><span class="lit">2</span><span class="pln">
</span><span class="lit">1</span><span class="pun">+</span><span class="lit">2</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="lit">1</span><span class="pun">+--</span><span class="lit">4</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="lit">3</span><span class="pun">)+</span><span class="lit">4</span></pre>

<p>
	سيستغرق هذا بعض الوقت منك.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9919_22" 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="typ">int</span><span class="pln"> expr</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"> mul_exp</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"> unary_exp</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"> primary</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"> val</span><span class="pun">;</span><span class="pln">

      </span><span class="kwd">for</span><span class="pun">(;;){</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"expression: "</span><span class="pun">);</span><span class="pln">
              val </span><span class="pun">=</span><span class="pln"> expr</span><span class="pun">();</span><span class="pln">
              </span><span class="kwd">if</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="pun">){</span><span class="pln">
                      printf</span><span class="pun">(</span><span class="str">"error\n"</span><span class="pun">);</span><span class="pln">
                      </span><span class="kwd">while</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="pun">)</span><span class="pln">
                              </span><span class="com">/* فارغ */</span><span class="pun">;</span><span class="pln">
              </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pun">{</span><span class="pln">
                      printf</span><span class="pun">(</span><span class="str">"result is %d\n"</span><span class="pun">,</span><span class="pln"> val</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><span class="pln">

</span><span class="typ">int</span><span class="pln">
expr</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"> val</span><span class="pun">,</span><span class="pln"> ch_in</span><span class="pun">;</span><span class="pln">

      val </span><span class="pun">=</span><span class="pln"> mul_exp</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">switch</span><span class="pun">(</span><span class="pln">ch_in </span><span class="pun">=</span><span class="pln"> getchar</span><span class="pun">()){</span><span class="pln">
              </span><span class="kwd">default</span><span class="pun">:</span><span class="pln">
                      ungetc</span><span class="pun">(</span><span class="pln">ch_in</span><span class="pun">,</span><span class="pln">stdin</span><span class="pun">);</span><span class="pln">
                      </span><span class="kwd">return</span><span class="pun">(</span><span class="pln">val</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">
                      val </span><span class="pun">=</span><span class="pln"> val </span><span class="pun">+</span><span class="pln"> mul_exp</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">
                      val </span><span class="pun">=</span><span class="pln"> val </span><span class="pun">-</span><span class="pln"> mul_exp</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="typ">int</span><span class="pln">
mul_exp</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"> val</span><span class="pun">,</span><span class="pln"> ch_in</span><span class="pun">;</span><span class="pln">

      val </span><span class="pun">=</span><span class="pln"> unary_exp</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">switch</span><span class="pun">(</span><span class="pln">ch_in </span><span class="pun">=</span><span class="pln"> getchar</span><span class="pun">()){</span><span class="pln">
              </span><span class="kwd">default</span><span class="pun">:</span><span class="pln">
                      ungetc</span><span class="pun">(</span><span class="pln">ch_in</span><span class="pun">,</span><span class="pln"> stdin</span><span class="pun">);</span><span class="pln">
                      </span><span class="kwd">return</span><span class="pun">(</span><span class="pln">val</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">
                      val </span><span class="pun">=</span><span class="pln"> val </span><span class="pun">*</span><span class="pln"> unary_exp</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">
                      val </span><span class="pun">=</span><span class="pln"> val </span><span class="pun">/</span><span class="pln"> unary_exp</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">
                      val </span><span class="pun">=</span><span class="pln"> val </span><span class="pun">%</span><span class="pln"> unary_exp</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="typ">int</span><span class="pln">
unary_exp</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"> val</span><span class="pun">,</span><span class="pln"> ch_in</span><span class="pun">;</span><span class="pln">

      </span><span class="kwd">switch</span><span class="pun">(</span><span class="pln">ch_in </span><span class="pun">=</span><span class="pln"> getchar</span><span class="pun">()){</span><span class="pln">
      </span><span class="kwd">default</span><span class="pun">:</span><span class="pln">
              ungetc</span><span class="pun">(</span><span class="pln">ch_in</span><span class="pun">,</span><span class="pln"> stdin</span><span class="pun">);</span><span class="pln">
              val </span><span class="pun">=</span><span class="pln"> primary</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">
              val </span><span class="pun">=</span><span class="pln"> unary_exp</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">
              val </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="pln">unary_exp</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="kwd">return</span><span class="pun">(</span><span class="pln">val</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">
primary</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"> val</span><span class="pun">,</span><span class="pln"> ch_in</span><span class="pun">;</span><span class="pln">

      ch_in </span><span class="pun">=</span><span class="pln"> getchar</span><span class="pun">();</span><span class="pln">
      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">ch_in </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="str">'0'</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> ch_in </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="str">'9'</span><span class="pun">){</span><span class="pln">
              val </span><span class="pun">=</span><span class="pln"> ch_in </span><span class="pun">-</span><span class="pln"> </span><span class="str">'0'</span><span class="pun">;</span><span class="pln">
              </span><span class="kwd">goto</span><span class="pln"> out</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">ch_in </span><span class="pun">==</span><span class="pln"> </span><span class="str">'('</span><span class="pun">){</span><span class="pln">
              val </span><span class="pun">=</span><span class="pln"> expr</span><span class="pun">();</span><span class="pln">
              getchar</span><span class="pun">();</span><span class="pln">      </span><span class="com">/* “')” تخطي قوس الإغلاق */</span><span class="pln">
              </span><span class="kwd">goto</span><span class="pln"> out</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">"error: primary read %d\n"</span><span class="pun">,</span><span class="pln"> ch_in</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">
out</span><span class="pun">:</span><span class="pln">
      </span><span class="kwd">return</span><span class="pun">(</span><span class="pln">val</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter4/" rel="external nofollow">Functions</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%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/%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%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>
	<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">1673</guid><pubDate>Thu, 25 Aug 2022 16:01:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62F;&#x648;&#x627;&#x644; &#x641;&#x64A; &#x644;&#x63A;&#x629; C</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_08/62ece71454b70_----C-.png.7b2f6e60966261b8a4e4b2284023cacb.png" /></p>
<p>
	تكلّمنا سابقًا عن أهمية الدوال في لغة سي C وكيف أنها تشكّل اللبنة الأساسية لبرامج سي. إذًا، ليس من المُستغرب أن نخصص هذا المقال بالكامل للتعريف عنها وعن استخدامها، إذ سنطرّق إلى كيفية التصريح عنها واستخدامها بالإضافة إلى أنواع وسطائها وبعض الأمثلة العملية.
</p>

<h2>
	ما التغييرات التي طرأت على لغة سي المعيارية بخصوص الدوال؟
</h2>

<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="">للمصرّف compiler</a> حينها أن يتحقق من صحة استخدام الدالة ضمن البرنامج وفق تصريحها، وعلى الرغم من أنها لم تؤثر على نجاح لغة سي، إلا إنها تسببت بمشاكل تخص قابلية نقل البرنامج وصيانته التي كان من الممكن تفاديها جميعًا.
</p>

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

<p>
	أخذت لغة سي المعيارية الحل لهذه المشاكل من <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>، إذ سبق لها وطبّقت هذه الأفكار بنجاح، كما تبنّت العديد من مصرّفات لغة سي القديمة هذه الحلول من C المعيارية نظرًا لنجاحها.
</p>

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

<h2>
	أنواع الدوال
</h2>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_9" style=""><span class="pln">a </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_11" style=""><span class="lit">1</span><span class="pun">;</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_13" style=""><span class="pln">f</span><span class="pun">(</span><span class="pln">argument</span><span class="pun">);</span></pre>

<p>
	التعبير السابق هو تعبير ذو قيمةٍ مُهملة.
</p>

<p>
	من السهل فهم نقطة أن القيمة المُعادة من الدالة يمكن إهمالها، لكن هذا يعني أن عدم استخدام القيمة المُعادة هو <a href="https://academy.hsoub.com/programming/general/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r1342/" rel="">خطأ برمجي</a>، وعلى العكس تمامًا إن لم يكن هناك أي قيمة مفيدة مُعادة من الدالة، إذًا من الأفضل أن يكون لدينا القدرة على مراقبة فيما إذا كانت القيمة مُستخدمةً عن طريق الخطأ، وللسببين السابقين، يجب التصريح عن أي دالة بكونها لا تعيد أي قيمة مفيدة بالنوع <code>void</code>.
</p>

<p>
	يمكن أن تُعيد الدوال أي نوع مدعوم من لغة سي C عدا <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="">المصفوفات arrays</a> والدوال بحد ذاتها، وهذا يتضمن المؤشرات pointers والبُنى structures والاتحادات unions، وسنتكلم عنهم لاحقًا. يمكننا التحايل على الأنواع التي لا يُمكن إعادتها من الدوال باستخدام المؤشرات بدلًا منها، كما يمكن استدعاء جميع الدوال تعاوديًّا recursively.
</p>

<h2>
	التصريح عن الدوال
</h2>

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

<ul>
	<li>
		<strong>التصريح declaration</strong>: نذكر فيه النوع type الذي يرتبط باسم ما.
	</li>
	<li>
		<strong>التعريف definition</strong>: يماثل التصريح، لكنه يحجز أيضًا مساحة تخزينية للكائن المذكور، وقد تكون القواعد التي تفصل بين التصريح والتعريف معقدة، لكن الأمر بسيط بالنسبة للدوال؛ إذ يصبح التصريح تعريفًا عندما يُضاف محتوى الدالة على أنه تعليمةٌ مُركّبة compound statement.
	</li>
	<li>
		<strong>المُعاملات parameters والمعاملات الصوريّة formal parameters</strong>: الأسماء التي تُشير إلى الوسطاء بداخل الدالة.
	</li>
	<li>
		<strong>الوسطاء arguments والوسطاء الفعلية actual arguments</strong>: القيم المُستخدمة مثل وسطاء في دالة ما، أي قيم المُعاملات الصوريّة عند تنفيذ الدالة.
	</li>
</ul>

<p>
	يُستخدم المصطلحان "مُعامل" و"وسيط" على نحوٍ تبادلي، لذا لا تتساءل عن سبب استخدامنا لمصطلح عن الآخر في الفقرات القادمة.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_18" style=""><span class="kwd">double</span><span class="pln"> aax1</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span></pre>

<p>
	وإليك مثالًا عن استخدامها الخاطئ:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_20" style=""><span class="pln">main</span><span class="pun">(){</span><span class="pln">
      </span><span class="kwd">double</span><span class="pln"> return_v</span><span class="pun">,</span><span class="pln"> aax1</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span><span class="pln">
      return_v </span><span class="pun">=</span><span class="pln"> aax1</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>return_v</code> مما تسبب بإنشاء متغيرٍ جديد، كما صرّحنا عن <code>aax1</code> دون تعريفها، إذ أن الدوال تُعرّف فقط في حالة وجود متن الدالة، كما ذكرنا سابقًا، وفي هذه الحالة يُفترض أن تُعيد الدالة <code>aax1</code> النوع <code>int</code> ضمنيًّا مع أنها تعيد النوع <code>double</code>، مما يعني أن ذلك سيتسبب بتصرف غير محدد، وهو أمر كارثي دائمًا.
</p>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_22" style=""><span class="pln"> </span><span class="kwd">double</span><span class="pln">
  aax1</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="com">/*متن الدالة هنا*/</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1.0</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span></pre>

<p>
	من غير الاعتيادي أن تمنعك لغةٌ تعتمد على الهيكلية الكُتلية عن تعريف الدوال داخل دوالٍ أخرى، ولكن هذه سمةٌ من سمات لغة سي C، ويساعد ذلك على تحسين الأداء وقت التنفيذ run-time للغة سي، لأنه يقلل المهام المطلوبة المتعلقة بتنظيم استدعاء الدوال.
</p>

<h2>
	تعليمة الإعادة return
</h2>

<p>
	تُعد تعليمة <code>return</code> مهمةً للغاية، إذ تستخدمها جميع الدوال -عدا التي تُعيد <code>void</code>- مرةً واحدةً على الأقل، وتوضِّح التعليمة <code>return</code> عند ذكرها القيمة التي يجب أن تُعيدها. من الممكن أن نعيد قيمةً من دالةٍ ما عن طريق وضع <code>return</code> في نهاية الدالة قبل القوس المعقوص الأخير "{"؛ إلا أن هذا الأمر سيتسبب بتصرف غير محدد عند استخدامها في دالة تُعيد <code>void</code>، إذ ستُعاد قيمةٌ غير معروفة.
</p>

<p>
	إليك مثالًا عن دالة أخرى تستخدم <code>getchar</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> من دخل البرنامج ومن ثمّ تعيدها باستثناء المسافة space ومسافة الجدولة tab والأسطر الجديدة newline.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_24" 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">
non_space</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"> c</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">while</span><span class="pln"> </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">getchar </span><span class="pun">())==</span><span class="str">'\t'</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="pln"> </span><span class="pun">||</span><span class="pln"> c</span><span class="pun">==</span><span class="str">' '</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="pln"> </span><span class="pun">(</span><span class="pln">c</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_32" style=""><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">something</span><span class="pun">);</span></pre>

<p>
	فمن السهل ألّا تلاحظ الفاصلة المنقوطة في نهاية السطر عند قراءة البرنامج، وتفترض أن التعليمات أسفلها تتبع لتعليمة <code>while</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_34" style=""><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span></pre>

<p>
	سيحوَّل بذلك العدد الصحيح إلى نوع <code>double</code>، ومن الممكن أيضًا كتابة <code>return</code> دون أي تعبير مصاحب لها، لكن ذلك سيتسبب <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%A7%D9%84%D8%B3%D8%A8%D8%B9-%D8%A7%D9%84%D9%82%D8%A7%D8%AA%D9%84%D8%A9-%D9%84%D8%A3%D9%8A%D9%91-%D9%85%D8%B4%D8%B1%D9%88%D8%B9-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA-r742/" rel="">بخطأ برمجي</a> إن استخدمت هذه الطريقة ما لم تُعيد الدالة النوع <code>void</code>. من <strong>غير المسموح</strong> إلحاق تعبير بتعليمة <code>return</code> إذا كانت الدالة تعيد النوع <code>void</code>.
</p>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_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">#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">void</span><span class="pln"> pmax</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">j</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="pun">-</span><span class="lit">10</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">10</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">j </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">10</span><span class="pun">;</span><span class="pln"> j </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln"> j</span><span class="pun">++){</span><span class="pln">
                      pmax</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="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><span class="pln">
</span><span class="com">/*
* Function pmax.
* Returns:      void
* Prints larger of its two arguments.
*/</span><span class="pln">
</span><span class="kwd">void</span><span class="pln">
pmax</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="com">/* التعريف*/</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> biggest</span><span class="pun">;</span><span class="pln">

      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">a1 </span><span class="pun">&gt;</span><span class="pln"> a2</span><span class="pun">){</span><span class="pln">
              biggest </span><span class="pun">=</span><span class="pln"> a1</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="kwd">else</span><span class="pun">{</span><span class="pln">
              biggest </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">

      printf</span><span class="pun">(</span><span class="str">"larger of %d and %d is %d\n"</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"> biggest</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

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

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

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

<p>
	دعنا ننتقل الآن إلى تعريف الدالة حيث يقع متنها، ولاحظ أنه يدل على أن الدالة تأخذ وسيطين باسم <code>a1</code> و<code>a2</code>، كما أن نوع الوسطاء محدد بالنوع <code>int</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_45" style=""><span class="com">/* مثال سيء على التصريح عن الدالة */</span><span class="pln">

</span><span class="kwd">void</span><span class="pln">
pmax</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="com">/* and so on */</span></pre>

<p>
	الطريقة المثالية للتصريح والتعريف عن الدوال هي باستخدام ما يُدعى <strong>النماذج الأولية prototypes</strong>.
</p>

<h2>
	نماذج الدوال الأولية
</h2>

<p>
	كان تقديم <strong>نماذج الدوال الأولية function prototypes</strong> من أكبر التغييرات في لغة سي C المعيارية؛ إذ أن نموذج الدالة الأولية هو تصريح أو تعريف يتضمن معلومات عن عدد وأنواع الوسطاء التي تستخدمها الدالة.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_47" 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">void</span><span class="pln"> pmax</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> first</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> second</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">j</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="pun">-</span><span class="lit">10</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">10</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">j </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">10</span><span class="pun">;</span><span class="pln"> j </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln"> j</span><span class="pun">++){</span><span class="pln">
                      pmax</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="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><span class="pln">

</span><span class="kwd">void</span><span class="pln">
pmax</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="com">/*التعريف*/</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> biggest</span><span class="pun">;</span><span class="pln">

      </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">a1 </span><span class="pun">&gt;</span><span class="pln"> a2</span><span class="pun">){</span><span class="pln">
              biggest </span><span class="pun">=</span><span class="pln"> a1</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="kwd">else</span><span class="pun">{</span><span class="pln">
              biggest </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">

      printf</span><span class="pun">(</span><span class="str">"largest of %d and %d is %d\n"</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"> biggest</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_49" style=""><span class="kwd">void</span><span class="pln"> pmax </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> xx</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> yy </span><span class="pun">);</span></pre>

<p>
	وهنا نستطيع القول أن الدالة <code>pmax</code> تطبع القيمة الأكبر ضمن الوسيطين <code>xx</code> و <code>yy</code> بدلًا عن الإشارة إلى الوسطاء بتموضعها، أي الوسيط الثاني واﻷول وهكذا، لأنها طريقةٌ معرضةٌ لسوء الفهم أو الخطأ في عدّ الترتيب.
</p>

<p>
	عدا عن ذلك، تستطيع التخلص من أسماء الوسطاء في تصريح الدالة ويكون التصريح التالي مساويًا للتصريح السابق:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_51" style=""><span class="kwd">void</span><span class="pln"> pmax </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>
	الفرق بين التصريحين هو فقط أسماء الوسطاء.
</p>

<p>
	يكون التصريح عن دالة لا تأخذ أي وسطاء على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_55" style=""><span class="kwd">void</span><span class="pln"> f_name </span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span></pre>

<p>
	وللتصريح عن دالة تأخذ وسيطًا من نوع <code>int</code> وآخرًا من نوع <code>double</code> وعددًا غير محدّد من الوسطاء الآخرين، نكتب:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_57" style=""><span class="kwd">void</span><span class="pln"> f_name </span><span class="pun">(</span><span class="typ">int</span><span class="pun">,</span><span class="kwd">double</span><span class="pun">,...);</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_59" style=""><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_string</span><span class="pun">,...)</span></pre>

<p>
	الوسيط الأول هو "مؤشر pointer للقيمة من النوع <code>const char</code>"، وسنناقش معنى ذلك لاحقًا.
</p>

<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> حسب النموذج الأولي في حال استُدعيت الدالة باستخدام نوع وسيطٍ مغاير "بصورةٍ مشابهة للتحويل الحاصل عند عملية الإسناد".
</p>

<p>
	إليك مثالًا توضيحيًّا: دالةٌ تحسب الجذر التربيعي لقيمةٍ ما باستخدام طريقة نيوتن للتقريبات المتعاقبة Newton's method of successive approximations.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_61" 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"> DELTA </span><span class="lit">0.0001</span><span class="pln">
main</span><span class="pun">(){</span><span class="pln">
      </span><span class="kwd">double</span><span class="pln"> sq_root</span><span class="pun">(</span><span class="kwd">double</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="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">100</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"root of %d is %f\n"</span><span class="pun">,</span><span class="pln"> i</span><span class="pun">,</span><span class="pln"> sq_root</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><span class="pln">

</span><span class="kwd">double</span><span class="pln">
sq_root</span><span class="pun">(</span><span class="kwd">double</span><span class="pln"> x</span><span class="pun">){</span><span class="pln">      </span><span class="com">/* التعريف*/</span><span class="pln">
      </span><span class="kwd">double</span><span class="pln"> curr_appx</span><span class="pun">,</span><span class="pln"> last_appx</span><span class="pun">,</span><span class="pln"> diff</span><span class="pun">;</span><span class="pln">

      last_appx </span><span class="pun">=</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">
      diff </span><span class="pun">=</span><span class="pln"> DELTA</span><span class="pun">+</span><span class="lit">1</span><span class="pun">;</span><span class="pln">

      </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">diff </span><span class="pun">&gt;</span><span class="pln"> DELTA</span><span class="pun">){</span><span class="pln">
              curr_appx </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0.5</span><span class="pun">*(</span><span class="pln">last_appx
                      </span><span class="pun">+</span><span class="pln"> x</span><span class="pun">/</span><span class="pln">last_appx</span><span class="pun">);</span><span class="pln">
              diff </span><span class="pun">=</span><span class="pln"> curr_appx </span><span class="pun">-</span><span class="pln"> last_appx</span><span class="pun">;</span><span class="pln">
              </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">diff </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
                      diff </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="pln">diff</span><span class="pun">;</span><span class="pln">
              last_appx </span><span class="pun">=</span><span class="pln"> curr_appx</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">curr_appx</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<p>
	يوضّح النموذج الأولي للدالة أن <code>sq_root</code> تأخذ وسيطًا واحدًا من نوع <code>double</code>، وفي حقيقة الأمر، تُمرّر قيمة الوسيط ضمن الدالة <code>main</code> بنوع <code>int</code> لذا يجب تحويلها إلى <code>double</code> أوّلًا؛ لكن يجدر الانتباه هنا إلى أن سي ستفترض أن المبرمج تقصَّد تمرير قيمة <code>int</code>، إذا لم يكُن هناك أي نموذج أولي وفي هذه الحالة ستُعامَل القيمة على أنها <code>int</code> دون تحويل.
</p>

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

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

<h2>
	تحويلات الوسطاء
</h2>

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

<p>
	تتضمن هذه القواعد <strong>ترقيات الوسطاء الافتراضية default argument promotions</strong> و<strong>الأنواع المتوافقة compatible types</strong>، وفيما يخص ترقية الوسطاء الافتراضية، فهي:
</p>

<ul>
	<li>
		تُطبَّق الترقية العددية الصحيحة على كل قيمة وسيط.
	</li>
	<li>
		يُحوَّل الوسيط إلى نوع <code>double</code> إذا كان نوعه <code>float</code>.
	</li>
</ul>

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

<p>
	تُطبّق التحويلات بالاعتماد على القوانين التالية (ليست اقتباسًا مباشرًا من المعيار، بل ستدلك على كيفية تطبيق قوانين سي المعيارية):
</p>

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

<ul>
	<li>
		نحصل على سلوكٍ غير محدد إذا كان عدد الوسطاء المزوَّد لا يكافئ عدد الوسطاء الفعلي للدالة.
	</li>
	<li>
		يجب أن تكون أنواع الوسطاء المزوّدة للدالة <strong>متوافقة</strong> مع أنواع الوسطاء الفعلية وفق تعريف الدالة بعد تطبيق الترقية عليهم وذلك إذا لم يكن لتعريف الدالة نموذجٌ أولي، ونحصل على سلوك غير محدد فيما عدا ذلك.
	</li>
	<li>
		نحصل على سلوك غير محدد إذا لم تحتوي الدالة على نموذج أولي في تعريفها وكانت أنواع الوسطاء المزوّدة غير متوافقة مع أنواع الوسطاء الفعلية، كما يكون السلوك غير محدد أيضًا إذا تضمّن النموذج الأولي للدالة علامة الاختصار "…".
	</li>
</ul>

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

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

<p>
	لا يحدّد المعيار صراحةً ترتيب تقييم وسطاء الدالة عند استدعائها.
</p>

<h2>
	تعريف الدوال
</h2>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_63" style=""><span class="kwd">double</span><span class="pln">
some_func</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">float</span><span class="pln"> a2</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> a3</span><span class="pun">);</span></pre>

<p>
	نضيف متنًا إلى الدالة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_65" style=""><span class="kwd">double</span><span class="pln">
some_func</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">float</span><span class="pln"> a2</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> a3</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.0</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وذلك باستبدال الفاصلة المنقوطة في نهاية التصريح بتعليمة مركّبة.
</p>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_67" style=""><span class="kwd">double</span><span class="pln">
some_func</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"> a3</span><span class="pun">)</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> a1</span><span class="pun">;</span><span class="pln">
      </span><span class="typ">float</span><span class="pln"> a2</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> a3</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.0</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

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

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

<p>
	دعنا نلخّص ما تكلمنا عنه سابقًا بإيجاز:
</p>

<ul>
	<li>
		يمكن استدعاء الدوال على نحوٍ تعاودي.
	</li>
	<li>
		يمكن للدوال إعادة أي قيمة تصرح عنها عدا المصفوفات والدوال (إلا أنه يمكنك التحايل على هذا القيد باستخدام المؤشرات)، ويجب أن تكون الدوال من النوع <code>void</code> إذا لم تُعد أي قيمة.
	</li>
	<li>
		استخدم نماذج الدوال الأولية دائمًا.
	</li>
	<li>
		نحصل على سلوك غير محدد، إذا استُدعيت دالة أو تعريفها إلا إذا:
	</li>
	<li>
		كان النموذج الأولي <strong>دائمًا</strong> ضمن النطاق في <strong>كل مرة</strong> تُستدعى فيها الدالة أو تُعرَّف.
	</li>
	<li>
		كنت حريصًا جداً بهذا الخصوص.
	</li>
	<li>
		تُحوّل قيم الوسطاء عند استدعاء الدالة إلى أنواع المُعاملات الفعلية للدالة (المعرّفة وفقها)، بصورةٍ مشابهة للتحويل عند عملية الإسناد باستخدام العامل operator "="، وذلك <strong>بفرض</strong> أنك تستخدم نموذجًا أوّليًّا.
	</li>
	<li>
		يجب أن يشير النموذج الأولي إلى النوع <code>void</code>، إذا لم تأخذ الدالة أي وسطاء.
	</li>
	<li>
		يجب تحديد اسم وسيط واحد على الأقل في دالة تقبل عددًا متغيرًا من الوسطاء، ومن ثم الإشارة إلى العدد المتغير من الوسطاء بالعلامة "…"، كما هو موضح:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_70" style=""><span class="typ">int</span><span class="pln">
vfunc</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> </span><span class="typ">float</span><span class="pln"> y</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...);</span></pre>

<p>
	سنناقش لاحقًا استخدام هذا النوع من الدوال.
</p>

<h2>
	التعليمات المركبة والتصريحات
</h2>

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

<p>
	كيف يمكن التغطية على أسماء المتغيرات؟ يوضح المثال التالي ما نقصد بذلك:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_72" style=""><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">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="typ">float</span><span class="pln"> a</span><span class="pun">;</span><span class="pln">        </span><span class="com">/* مختلف a يمثّل متغير*/</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">
              </span><span class="kwd">char</span><span class="pln"> a</span><span class="pun">;</span><span class="pln"> </span><span class="com">/* مختلف أيضًا a متغير */</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">
                      </span><span class="com">/* يمكن رؤية المتغير ذو النوع قيمة صحيحة */</span></pre>

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

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

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

<p>
	يمكنك تنفيذ حيل طريفة مثل المثال التالي باستخدام قوانين النطاق:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_74" style=""><span class="pln">main </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">
f </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
f2 </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{}</span></pre>

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

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

<p>
	كان خطأ إعادة التعريف غير المقصود في لغة سي C القديمة خطأً صعب التتبع والحل، إليك ما قد يبدو عليه الخطأ:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_997_76" style=""><span class="com">/* إعادة تصريح خاطئة للوسطاء */</span><span class="pln">

func</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">
      </span><span class="typ">int</span><span class="pln"> a</span><span class="pun">;</span><span class="pln">  </span><span class="com">/* AAAAgh! */</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الجزء المسبب للمتاعب هنا هو التصريح الجديد للمتغير <code>a</code> في متن الدالة، الذي سيغطّي على المعامل <code>a</code>، ولن نتكلم بالمزيد عن هذه المشكلة بما أنها غير ممكنة الحدوث بعد الآن.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter4/" rel="external nofollow">Functions</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%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%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>
	<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">1646</guid><pubDate>Sun, 21 Aug 2022 16:09:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x639;&#x648;&#x627;&#x645;&#x644; &#x627;&#x644;&#x645;&#x646;&#x637;&#x642;&#x64A;&#x629; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C &#x648;&#x639;&#x648;&#x627;&#x645;&#x644; &#x623;&#x62E;&#x631;&#x649;</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_06/62b065075682e_-----C-(-)-.png.50973e214c9bce05757437de9eb1bf6f.png" /></p>
<p>
	تحدثنا في المقال السابق عن <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D9%86%D8%B7%D9%82%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%A7%D9%84%D8%AA%D8%AF%D9%81%D9%82-%D9%81%D9%8A-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-c-r1615/" rel="">حكم بالتدفق في برنامج C والتعبيرات المنطقية</a> وسنكمل الحديث حول العوامل المنطقية في لغة سي C والعوامل الأخرى فيها.
</p>

<h2>
	العوامل المنطقية
</h2>

<p>
	وضحنا سابقًا كيف أن لغة سي C لا تميّز بين القيم "المنطقية" والقيم الأخرى، إذ تعطي المُعاملات العلاقيّة relational operators نتيجة متمثلةً بالقيمة 0 للخطأ، أو 1 للصواب. وتُقيّم قيمة تعبير منطقي لتحديد ما إذا كانت تعليمة تحكم بتدفق البرنامج ستنفّذ أم لا، إذ تعني القيمة 0 "لا تنفِّذ" وأي قيمة أخرى تعني "نفِّذ". تُعد جميع الأمثلة التالية تعابير منطقية صالحة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1111_7" style=""><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">a</span><span class="pun">&lt;</span><span class="pln">b</span><span class="pun">)...</span><span class="pln">
</span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">a</span><span class="pun">)....</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> </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">getchar</span><span class="pun">())</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> EOF </span><span class="pun">)...</span></pre>

<p>
	لن يتفاجأ أي مبرمج لغة سي متمرس بأي تعبير من التعابير السابقة، إذ يمثّل التعبير الثاني <code>(while (a</code> اختصارًا شائعًا للتعبير <code>(while (a != 0</code> (على الأغلب استنتجت معناه ضمنيًّا).
</p>

<p>
	نحتاج الآن إلى طريقةٍ تمكِّننا من كتابة تعابير أكثر تعقيدًا باستخدام هذه القيم المنطقية "خطأ وصواب"، إذ استخدمنا حتى الآن الطريقة التالية لكتابة التعبير الذي يعطينا <code>(if(a&lt;b AND c&lt;d</code>، إلا أن هناك طريقةٌ أخرى لكتابة التعليمة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1111_11" style=""><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">a </span><span class="pun">&lt;</span><span class="pln"> b</span><span class="pun">){</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">c </span><span class="pun">&lt;</span><span class="pln"> d</span><span class="pun">)...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هناك ثلاثة <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="">عوامل</a> مشاركة في هذا النوع من العمليات، هي: العامل المنطقي AND أو "&amp;&amp;" والعامل المنطقي OR أو "||" والعامل NOT أو "!"، إذ يُعد العامل الأخير عاملًا أحاديًا، أما العاملان الآخران فهما ثنائيان. تستخدِم هذه العوامل تعابيرًا مثل مُعاملات وتعطي نتيجةً مساويةً إلى "1" أو "0"؛ إذ يعطي العامل "&amp;&amp;" القيمة "1" فقط في حال كانت قيمة المُعاملان غير صفرية؛ ويعطي "||" القيمة "0" فقط في حال كانت قيمة المُعاملان صفر؛ بينما يعطي "!" القيمة صفر إذا كانت قيمة المُعامل غير صفرية وبالعكس، فالأمر بسيطٌ للغاية، وتكون نتيجة العوامل الثلاث من النوع "int".
</p>

<p>
	لا تخلط عوامل العمليات الثنائية bitwise operators "|" و"&amp;" مع عواملها المنطقية المشابهة، فهي مختلفةٌ عن بعضها البعض؛ إذ أن للعوامل المنطقية ميزةٌ لا نجدها في العوامل الأخرى، ألا وهي تأثيرها على عملية تقييم التعبير، إذ تُقيّم من اليسار إلى اليمين بعد أخذ الأسبقية في الحسبان، ويتوقف أي تعبير منطقي عن عملية التقييم عند التوصُّل إلى قيمة التعبير النهائية. على سبيل المثال، تتوقف سلسلة من عوامل "||" عن التقييم حالما تجد مُعاملًا ذا قيمةٍ غير صفرية. يوضِّح المثالين التاليين تعبيرين منطقيين يمنعان القسمة على صفر.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1111_15" style=""><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">a</span><span class="pun">!=</span><span class="lit">0</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> b</span><span class="pun">/</span><span class="pln">a </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">5</span><span class="pun">)...</span><span class="pln">
</span><span class="com">/* alternative */</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">a </span><span class="pun">&amp;&amp;</span><span class="pln"> b</span><span class="pun">/</span><span class="pln">a </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">5</span><span class="pun">)</span></pre>

<p>
	ستُقيَّم قيمة <code>b/a</code> في كلا الحالتين فقط في حالة كون قيمة <code>a</code> غير صفرية؛ وفي حال كانت <code>a</code> مساويةً الصفر، ستُقيّم قيمة التعبير فورًا، وبذلك ستتوقف عملية تقييم التعبير وفق قوانين لغة سي C للعوامل المنطقية.
</p>

<p>
	عامل النفي الأحادي NOT بسيط، لكن استخدامه غير شائع جدًا نظرًا لإمكانية كتابة معظم التعبيرات دون استخدامه، كما توضح الأمثلة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1111_17" style=""><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">a</span><span class="pun">)...</span><span class="pln">
</span><span class="com">/* alternative */</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> </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="kwd">if</span><span class="pun">(!(</span><span class="pln">a</span><span class="pun">&gt;</span><span class="pln">b</span><span class="pun">))</span><span class="pln">
</span><span class="com">/* alternative */</span><span class="pln">
</span><span class="kwd">if</span><span class="pun">(</span><span class="pln">a </span><span class="pun">&lt;=</span><span class="pln"> b</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!(</span><span class="pln">a</span><span class="pun">&gt;</span><span class="pln">b </span><span class="pun">&amp;&amp;</span><span class="pln"> c</span><span class="pun">&lt;</span><span class="pln">d</span><span class="pun">))...</span><span class="pln">
</span><span class="com">/* alternative */</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">a</span><span class="pun">&lt;=</span><span class="pln">b </span><span class="pun">||</span><span class="pln"> c</span><span class="pun">&gt;=</span><span class="pln">d</span><span class="pun">)...</span></pre>

<p>
	توضح الأمثلة السابقة مع بدائلها طريقةً لتجنُّب أو على الأقل الاستغناء عن استخدام العامل <code>!</code>. في الحقيقة، يُستخدم هذا العامل بهدف تسهيل قراءة التعبير، فإذا كانت المشكلة التي تحلّها تستخدم علاقة منطقية مثل العلاقة "b*b-4*a*c) &gt; 0)" الموجودة في حل المعادلات التربيعية، فمن الأفضل كتابتها بالطريقة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1111_19" style=""><span class="pun">(</span><span class="pln"> </span><span class="pun">!((</span><span class="pln">b</span><span class="pun">*</span><span class="pln">b</span><span class="pun">-</span><span class="lit">4</span><span class="pun">*</span><span class="pln">a</span><span class="pun">*</span><span class="pln">c</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></pre>

<p>
	بدلًا من كتابتها بالطريقة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1111_22" style=""><span class="kwd">if</span><span class="pun">(</span><span class="pln"> </span><span class="pun">(</span><span class="pln">b</span><span class="pun">*</span><span class="pln">b</span><span class="pun">-</span><span class="lit">4</span><span class="pun">*</span><span class="pln">a</span><span class="pun">*</span><span class="pln">c</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></pre>

<p>
	لكن التعبيران يؤديان الغرض ذاته، فاختر الطريقة التي تناسبك.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1111_25" style=""><span class="kwd">if</span><span class="pun">(</span><span class="pln">a</span><span class="pun">&amp;</span><span class="pln">b </span><span class="pun">==</span><span class="pln"> c</span><span class="pun">){...</span></pre>

<p>
	إذ تُوازَن <code>b</code> بالمساواة مع <code>c</code> أولًا في هذه الحالة، ومن ثم تُضاف القيمة إلى <code>a</code> سواءٌ كانت <code>0</code> أو <code>1</code>، مما يتسبب بسلوك غير متوقع للبرنامج بسبب هذا الخطأ.
</p>

<h2>
	عوامل غريبة
</h2>

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

<h3>
	عامل الشرط :?
</h3>

<p>
	كما هو الحال في عزف آلة الأكورديون، سيكون من الأسهل النظر إلى كيفية عمل هذا العامل بدلًا من وصفه.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1111_27" style=""><span class="pln">expression1</span><span class="pun">?</span><span class="pln">expression2</span><span class="pun">:</span><span class="pln">expression3</span></pre>

<p>
	إذا كان التعبير <code>expression1</code> صحيحًا، فهذا يعني أن قيمة التعبير بالكامل هي من قيمة التعبير <code>expression2</code>، وإلا فهي قيمة التعبير <code>expression3</code>، إذ ستُقيَّم قيمة واحدٍ من التعبيرين حسب قيمة التعبير <code>expression1</code>.
</p>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1111_29" style=""><span class="pln">a</span><span class="pun">&gt;</span><span class="pln">b</span><span class="pun">?</span><span class="lit">1</span><span class="pun">:</span><span class="lit">3.5</span></pre>

<p>
	يحتوي المثال الثابت <code>1</code> من النوع <code>int</code> والثابت <code>3.5</code> من النوع <code>double</code>، وبتطبيق قوانين التحويل الحسابية نحصل على نتيجة النوع <code>double</code>.
</p>

<p>
	هناك بعض الحالات المسموحة أيضًا، هي:
</p>

<ul>
	<li>
		إذا كان المُعاملان من نوع ذا هيكلية structure أو اتحاد union متوافق، فالنتيجة هي من هذا النوع.
	</li>
	<li>
		إذا كان المُعاملان من نوع "void"، فالنتيجة هي من هذا النوع.
	</li>
</ul>

<p>
	ويمكن مزج عدة أنواع مؤشرات على النحو التالي:
</p>

<ul>
	<li>
		يمكن للمُعاملين أن يكونا من أنواع مؤشرات متوافقة (من المحتمل أن تكون <strong>مُؤهلة qualified</strong>).
	</li>
	<li>
		يمكن لمُعامل أن يكون مؤشرًا والمُعامل الآخر <strong>مؤشر ثابت فارغ Null pointer constant</strong>.
	</li>
	<li>
		يمكن لمُعامل أن يكون <strong>مؤشرًا لكائن pointer to an object</strong> أو <strong>نوع غير مكتمل incomplete type</strong> والمُعامل الآخر مؤشر إلى نوع "void" (من المحتمل أن يكون <strong>مُؤهل</strong>).
	</li>
</ul>

<p>
	لمعرفة النوع الناتج عن التعبير عند استخدام المؤشرات نتبع الخطوتين:
</p>

<ol>
	<li>
		إذا كان أيٌ من المُعاملَين مؤشرًا إلى نوع قيمة مؤهلة، فستكون النتيجة مؤشرًا إلى نوع مؤهل من جميع المؤهِّلات في كلا المُعاملين.
	</li>
	<li>
		إذا كان أحد المُعاملات مؤشر ثابت فارغ، فستكون النتيجة هي نوع المُعامل الآخر؛ فإذا كان أحد المُعاملات يؤشر إلى الفراغ، سيُحوَّل المُعامل الآخر إلى مؤشر إلى "void" وسيكون هذا نوع النتيجة؛ أما إذا كان كلا المُعاملات مؤشرات من أنواع متوافقة (بتجاهل أي مؤهلات) فالنوع الناتج هو من <strong>نوع مركب composite type</strong>.
	</li>
</ol>

<p>
	سنناقش كلًا من المؤهّلات والأنواع المركبة والمتوافقة في المقالات القادمة.
</p>

<p>
	نلاحظ استخدام هذا العامل في المثال البسيط أدناه، الذي يختار السلسلة النصية لطباعتها باستخدام الدالة <code>printf</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1111_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;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">for</span><span class="pun">(</span><span class="pln">i</span><span class="pun">=</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">10</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">
              printf</span><span class="pun">((</span><span class="pln">i</span><span class="pun">&amp;</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">"odd\n"</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">"even\n"</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;">
	[مثال 10.3]
</p>

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

<p>
	بعد الانتهاء من تقييم قيمة المُعامل الأول، هناك مرحلة <strong>نقاط التسلسل sequence points</strong> التي سنشرحها لاحقًا.
</p>

<h3>
	عامل الفاصلة
</h3>

<p>
	يربح هذا العامل جائزة "أكثر العوامل غرابةً"، إذ يسمح لك بإنشاء قائمة طويلة من التعابير المفصول بينها بالفاصلة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1111_34" style=""><span class="pln">expression</span><span class="pun">-</span><span class="lit">1</span><span class="pun">,</span><span class="pln">expression</span><span class="pun">-</span><span class="lit">2</span><span class="pun">,</span><span class="pln">expression</span><span class="pun">-</span><span class="lit">3</span><span class="pun">,...,</span><span class="pln">expression</span><span class="pun">-</span><span class="pln">n</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1111_36" 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"> j</span><span class="pun">;</span><span class="pln">

      </span><span class="com">/* comma used - this loop has two counters */</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="lit">0</span><span class="pun">,</span><span class="pln"> j</span><span class="pun">=</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">10</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="pln"> i</span><span class="pun">*</span><span class="pln">i</span><span class="pun">){</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"i %d j %d\n"</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="pln">
      </span><span class="pun">}</span><span class="pln">

      </span><span class="com">/*
       * In this futile example, all but the last
       * constant value is discarded.
       * Note use of parentheses to force a comma
       * expression in a function call.
       */</span><span class="pln">
      printf</span><span class="pun">(</span><span class="str">"Overall: %d\n"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="str">"abc"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1.2e6</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">*</span><span class="lit">3</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></pre>

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

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

<p>
	بعد تقييم كل مُعامل، تأتي مرحلة <strong>نقاط التسلسل sequence points</strong> التي سنشرحها لاحقًا.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter3//" rel="external nofollow">Control of Flow and Logical Expressions</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%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%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D9%86%D8%B7%D9%82%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%A7%D9%84%D8%AA%D8%AF%D9%81%D9%82-%D9%81%D9%8A-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-c-r1615/" 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%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="">إدارة الذاكرة (Memory management) في لغة 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%B9%D8%A7%D8%B4%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D8%B1%D8%B7%D9%8A%D8%A9-%D9%88%D8%AD%D9%84%D9%87%D8%A7-%D9%85%D8%B4%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1013/" 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%AD%D8%A7%D8%AF%D9%8A-%D8%B9%D8%B4%D8%B1-%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%AA%D9%82%D9%8A%D9%8A%D8%AF-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-semaphores-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%B3%D9%8A-c-r1014/" rel="">متغيرات تقييد الوصول (Semaphores) في لغة البرمجة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1616</guid><pubDate>Thu, 18 Aug 2022 16:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x628;&#x64A;&#x631; &#x627;&#x644;&#x645;&#x646;&#x637;&#x642;&#x64A;&#x629; &#x648;&#x627;&#x644;&#x62A;&#x62D;&#x643;&#x645; &#x628;&#x627;&#x644;&#x62A;&#x62F;&#x641;&#x642; &#x641;&#x64A; &#x628;&#x631;&#x646;&#x627;&#x645;&#x62C; C</title><link>https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D9%86%D8%B7%D9%82%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%A7%D9%84%D8%AA%D8%AF%D9%81%D9%82-%D9%81%D9%8A-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-c-r1615/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_06/62b05fd64b4ff_----C-.png.a68f2fa07d73940b3b72cb9586861f3d.png" /></p>

<p>
	سننظر في هذا المقال إلى الطرق المختلفة التي تُستخدم بها تعليمات التحكم بتدفق Flow control statements في <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>، بما فيها بعض التعليمات التي لم نتطرق إليها بعد، والتي تُستخدَم في معظم الحالات مع تعبيرات منطقية تحدّد الخطوة القادمة، وتُعد تعابير <code>if</code> و <code>while</code> البسيطة المُستخدمة سابقًا مثالًا على هذه <strong>التعابير المنطقية Logical expressions</strong>، ويمكنك استخدام تعابير أكثر تعقيدًا من الموازنات البسيطة، مثل <code>&gt;</code> و <code>=&gt;</code> و <code>==</code> وغيرها، لكن ما قد يفاجئك هو نوع النتيجة.
</p>

<h2>
	التعابير المنطقية والعوامل العلاقية
</h2>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_8" style="">
<span class="kwd">if</span><span class="pun">(</span><span class="pln">a </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">100</span><span class="pun">){...</span></pre>

<p>
	ومن المفترض الآن أنك تعرف دعم لغة C لمفهوم "صواب True" و"خطأ False" لهذه العلاقات، لكنها تدعمها بطريقةٍ مختلفة عن المتوقّع.
</p>

<p>
	تُستخدم العوامل العلاقية Relational operators المذكورة في الجدول 1 للموازنة بين مُعاملين بالطريقة المذكورة، وعندما تكون المُعاملات من أنواع عددية، تطبّق التحويلات الحسابية الموافقة إليهما.
</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>&gt;</code>
			</td>
			<td>
				أصغر من
			</td>
		</tr>
<tr>
<td>
				<code>=&gt;</code>
			</td>
			<td>
				أصغر من أو يساوي
			</td>
		</tr>
<tr>
<td>
				<code>&lt;</code>
			</td>
			<td>
				أكبر من
			</td>
		</tr>
<tr>
<td>
				<code>=&lt;</code>
			</td>
			<td>
				أكبر من أو يساوي
			</td>
		</tr>
<tr>
<td>
				<code>==</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> وإمكانية خلطه مع عامل الإسناد <code>=</code>، إذ لا تحذرك لغة سي C عند حدوث ذلك كون التعبير صالح، ولكن ستكون نتائج التعبيرين مختلفة، ويستغرق المبتدئون وقتًا طويلًا للاعتياد على استخدام <code>==</code> و <code>=</code>.
</p>

<p>
	لعلّك تطرح على نفسك السؤال "لماذا؟"، أي "لماذا التعبيران صالحان؟" الإجابة بسيطة، إذ تفسر لغة سي مفهوم "صواب True" و"خطأ False" بقيمة "غير صفرية" و" صفريّة"، وعلى الرغم من استخدامنا للعوامل العلاقيّة في التعابير المُستخدمة للتحكم بتعليمات <code>if</code> و <code>do</code>، إلا أننا نستخدم في الحقيقة القيمة العددية الناتجة عن هذا التعبير؛ فإذا كانت قيمة التعبير غير صفرية، فهذا يعني أن النتيجة صحيحة؛ أما إذا تحققت الحالة المعاكسة فالنتيجة خاطئ، وينطبق هذا على جميع التعابير والعوامل العلاقيّة.
</p>

<p>
	توازن العوامل العلاقيّة ما بين المُعاملات وتعطي نتيجة صفر للنتيجة الخاطئة (موازنة غير محقّقة) وواحد للنتيجة الصحيحة، وتكون النتيجة من نوع <code>int</code>، ويوضح المثال التالي كيفية عملها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_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">

    i </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">10</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">i </span><span class="pun">&lt;=</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 of i is %d, "</span><span class="pun">,</span><span class="pln"> i</span><span class="pun">);</span><span class="pln">
            printf</span><span class="pun">(</span><span class="str">"i == 0 = %d, "</span><span class="pun">,</span><span class="pln"> i</span><span class="pun">==</span><span class="lit">0</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
            printf</span><span class="pun">(</span><span class="str">"i &gt; -5 = %d\n"</span><span class="pun">,</span><span class="pln"> i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</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>
	يعطينا المثال السابق الخرج القياسي التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_15" style="">
<span class="pln">value of i is </span><span class="pun">-</span><span class="lit">10</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"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
value of i is </span><span class="pun">-</span><span class="lit">9</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"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
value of i is </span><span class="pun">-</span><span class="lit">8</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"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
value of i is </span><span class="pun">-</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="lit">0</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"> i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
value of i is </span><span class="pun">-</span><span class="lit">6</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"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
value of i is </span><span class="pun">-</span><span class="lit">5</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"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
value of i is </span><span class="pun">-</span><span class="lit">4</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"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
value of i is </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"> </span><span class="lit">0</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"> i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
value of i is </span><span class="pun">-</span><span class="lit">2</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"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
value of i is </span><span class="pun">-</span><span class="lit">1</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"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
value of i is </span><span class="lit">0</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"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
value of i is </span><span class="lit">1</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"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
value of i is </span><span class="lit">2</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"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
value of i is </span><span class="lit">3</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"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
value of i is </span><span class="lit">4</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"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
value of i is </span><span class="lit">5</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"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span></pre>

<p>
	ما الذي تعتقد حدوثه عند تنفيذ هذه التعليمة المحتمل أنها تكون خاطئة؟
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_17" style="">
<span class="kwd">if</span><span class="pun">(</span><span class="pln">a </span><span class="pun">=</span><span class="pln"> b</span><span class="pun">)...</span></pre>

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

<p>
	سيُفحَص التعبير في تعليمات <code>if</code> و <code>while</code> و <code>do</code> فيما إذا كان مساويًا للصفر أم لا، وسننظر إلى كلٍّ من هذه التعليمات عن كثب.
</p>

<h2>
	التحكم بالتدفق
</h2>

<p>
	يتم التحكم بالتدفق في لغة C من خلال التعليمات الآتية:
</p>

<h3>
	تعليمة إذا if الشرطية
</h3>

<p>
	تُكتب تعليمة <code>if</code> بطريقتين:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_21" style="">
<span class="kwd">if</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">)</span><span class="pln"> statement

</span><span class="kwd">if</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">)</span><span class="pln"> statement1
</span><span class="kwd">else</span><span class="pln"> statement2</span></pre>

<p>
	تُنفّذ <strong>تعليمة إذا</strong> الشرطية في الطريقة الأولى، إذا (وفقط إذا) كان <strong>التعبير expression</strong> لا يساوي إلى الصفر؛ أما إذا كان <strong>التعبير</strong> مساويًا للصفر، ستهُمل <strong>التعليمة statement</strong>. تذكر بأن <strong>التعليمة</strong> قد تكون مُركبّة، وذلك بوضع عدة تعليمات تابعة لتعليمة <code>if</code> واحدة.
</p>

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

<p>
	تصنَّف الطريقتان بكونهما تعليمةً واحدة حسب قواعد صياغة لغة سي، وبالتالي يكون المثال التالي صالح تمامًا.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_23" style="">
<span class="kwd">if</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">)</span><span class="pln"> statement</span></pre>

<p>
	إذ تُتبع تعليمة <code>(if (expression</code> بتعليمة <code>if</code> متكاملة أخرى، وبما أنها تعليمةٌ صالحة، يمكننا قراءة التعليمة الشرطية الأولى على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_25" style="">
<span class="kwd">if</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">)</span><span class="pln"> statement</span></pre>

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

<p>
	ينطبق ما سبق على طريقة كتابة تعليمة <code>else</code>، ويمكنك كتابتها على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_28" style="">
<span class="kwd">if</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">)</span><span class="pln">
    statement
  </span><span class="kwd">else</span><span class="pln">
    statement</span></pre>

<p>
	ما تزال طريقة الكتابة هذه باستخدام المسافات فقط غامضة وغير واضحة، فأي تعليمات <code>else</code> تتبع لتعليمات <code>if</code>؟ إذا اتبعنا المسافات في مثالنا فسيوحي هذا لنا بأن التعليمة <code>if</code> الثانية متبوعةٌ بالتعليمة <code>statement</code> مما يجعل كل منها تعليمةً مستقلة، و <code>else</code> إذًا تنتمي للتعليمة الشرطية الأولى.
</p>

<p>
	هذه <strong>ليست</strong> الطريقة التي تنظر بها لغة C إلى المثال، إذ تنص القاعدة على أن <code>else</code> تتبع التعليمة الشرطية <code>if</code> التي تسبقها إن لم تحتوي هذه التعليمة على <code>else</code>؛ وبالتالي تتبع <code>else</code> إلى التعليمة الشرطية الثانية في المثال الذي ناقشناه.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_30" style="">
<span class="kwd">if</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">){</span><span class="pln">
    </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">)</span><span class="pln">
            statement
</span><span class="pun">}</span><span class="kwd">else</span><span class="pln">
    statement</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_32" style="">
<span class="kwd">if</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">){</span><span class="pln">
    </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">){</span><span class="pln">
        statement
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="kwd">else</span><span class="pun">{</span><span class="pln">
    statement
</span><span class="pun">}</span></pre>

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

<h3>
	تعليمة do و while التكرارية
</h3>

<p>
	تعليمة <code>while</code> بسيطة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_34" style="">
<span class="kwd">while</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">)</span><span class="pln">
    statement</span></pre>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_36" style="">
<span class="kwd">do</span><span class="pln">
    statement
</span><span class="kwd">while</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">);</span></pre>

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

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

<h4>
	اختصار عملية الإسناد والتحقق في تعبير واحد
</h4>

<p>
	يُعد استخدام نتيجة عملية الإسناد للتحكم بعمل حلقات <code>while</code> و <code>do</code> التكرارية حيلةً مستخدمةً <strong>كثيرًا</strong> في برامج سي C، وهي شائعة الاستخدام كثيرًا إذ سترغب بتعلُّمها إن صادفتها. تقع هذه الحيلة تحت تصنيف لغة سي "الاصطلاحية" واستخدامها طبيعيٌ وتلقائي لكل من يستخدم اللغة. إليك المثال اﻷكثر شيوعًا لاستخدامها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_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">

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

    </span><span class="com">/* The Classic Bit */</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">input_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">
            printf</span><span class="pun">(</span><span class="str">"%c was read\n"</span><span class="pun">,</span><span class="pln"> input_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;">
	[مثال 2.3]
</p>

<p>
	تكمن الحيلة في تعبير إسناد <code>input_c</code>، إذ يُستخدم هذا التعبير في إسناد القيمة إلى المتغير وموازنته مع <code>EOF</code> (نهاية الملف End of File) والتحكم بعمل الحلقة التكرارية في آنٍ واحد. يُعد تضمين عملية الإسناد على هذا النحو تحسينًا عمليًا سهلًا، وعلى الرغم من كونها تختصر سطرًا واحدًا فقط إلا أن فائدتها في تسهيل عملية القراءة (بعد الاعتياد على استخدامها) كبيرة. لا بُد أيضًا من تعلُّم مكان استخدام الأقواس أيضًا، فهي مهمةٌ في تحديد الأسبقية.
</p>

<p>
	لاحظ أن <code>input_c</code> من النوع <code>int</code>، وذلك لتمكين الدالة <code>getchar</code> من إعادة أي قيمة ممكنة لأي <code>char</code> إضافةً لقيمة <code>EOF</code>، ولهذا احتجنا لنوع أطول من <code>char</code>.
</p>

<p>
	تُعد التعليمتان <code>while</code> و <code>do</code> وفق قواعد صياغة لغة سي تعليمةً واحدةً مثل تعليمة <code>if</code>، ويمكن استخدامهما في أي مكان يُمكن استخدام تعليمةٍ واحدةٍ فيه. ينبغي عليك استخدام تعليمة مركّبة إذا أردت التحكُّم <strong>بعدّة</strong> تعليمات، كما وضّحت أمثلة فقرة تعليمة <code>if</code>.
</p>

<h3>
	تعليمة for التكرارية
</h3>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_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">
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="com">/* initialise التهيئة*/</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">/* check التحقق*/</span><span class="pln">
      </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">i </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">10</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"> i</span><span class="pun">);</span><span class="pln">
              </span><span class="com">/* update التحديث*/</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;">
	<em>مثال 3.3</em>
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_42" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">initialize</span><span class="pun">;</span><span class="pln"> check</span><span class="pun">;</span><span class="pln"> update</span><span class="pun">)</span><span class="pln"> statement</span></pre>

<p>
	جزء <strong>التهيئة</strong> هو تعبير إسناد معظم الحالات، ويُستخدم لتهيئة المتحول الذي يتحكم بالحلقة. يأتي تعبير <strong>التحقق</strong> بعد التهيئة، إذ تُنفَّذ التعليمة داخل الحلقة إذا كان هذا التعبير ذو قيمةٍ غير صفرية، ويُتبع ذلك بتعبير <strong>التحديث</strong> الذي يزيد من قيمة المتغير المُتحكم (في معظم الحالات)، وتُعاد هذه العملية عند كل دورة، وتنتهي الحلقة التكرارية في حال كانت قيمة تعبير التحقق مساويةً الصفر.
</p>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_44" 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">

      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">while</span><span class="pun">(</span><span class="pln">i </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">10</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"> i</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">/* the same done using ``for'' */</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">10</span><span class="pun">;</span><span class="pln"> i</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"> 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;">
	[مثال 4.3]
</p>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_46" style="">
<span class="kwd">for</span><span class="pun">(;;)</span></pre>

<p>
	أو
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_48" style="">
<span class="kwd">while</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)</span></pre>

<p>
	يمكنك ملاحظة هذه التعليمات في بعض البرامج المكتوبة بلغة سي C.
</p>

<h3>
	أهمية تعليمات التحكم بالتدفق
</h3>

<p>
	يمكننا كتابة برامج بدرجات تعقيد متفاوتة باستخدام تعليمات التحكم بالتدفق، إذ تعد هذه التعليمات من صلب لغة C وستوضح قراءتك لبعض البرامج الأساسية في لغة C أهمية هذه التعليمات بما يخص تقديم الأدوات الأساسية وهيكلة البرامج. ستعطي التعليمات المتبقية التي سنذكرها للمبرمج تحكمًا أكبر بهذه الحلقات أو ربما ستساعده في بعض الحالات الاستثنائية. لا تحتاج تعليمة <code>switch</code> إلى أي شرح بخصوص أهمية استخدامها؛ فمن الممكن استبدالها بالعديد من تعليمات <code>if</code>، ولكنها تسهِّل قراءة البرنامج كثيرًا. يمكنك النظر إلى <code>break</code> و <code>continue</code> و <code>goto</code> على أنها بهارات لصلصةٍ حساسة المقادير، إذ يمكن لاستخدامها الحريص أن يجعل من هذه الصلصة أكلةً لذيذة، وبالمقابل سيجعل الاستخدام المفرط لها طعم الصلصة مشتّتًا وضائعًا.
</p>

<h3>
	تعليمة switch
</h3>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_50" style="">
<span class="kwd">switch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">expression</span><span class="pun">){</span><span class="pln">
</span><span class="kwd">case</span><span class="pln"> const1</span><span class="pun">:</span><span class="pln">    statements
</span><span class="kwd">case</span><span class="pln"> const2</span><span class="pun">:</span><span class="pln">    statements
</span><span class="kwd">default</span><span class="pun">:</span><span class="pln">        statements
</span><span class="pun">}</span></pre>

<p>
	تُقدَّر قيمة <strong>التعبير expression</strong> وتُوازن قيمته مع جميع تعابير <strong>const1</strong> و<strong>const2</strong> إلى آخره، والتي تكون قيمها مختلفة (من نوع <strong>تعابير أعداد صحيحة ثابتة</strong> حصرًا)؛ فإذا تساوت القيمة مع قيمة <strong>التعبير</strong>، تُنفّذ التعليمة التي تتبع الكلمة المفتاحية <code>case</code>؛ وتُنفَّذ الحالة الافتراضية التابعة للكلمة المفتاحية <code>default</code> -إن وجدت- في حال عدم وجود أي قيمة مكافئة؛ وإن لم تتواجد هذه الكلمة المفتاحية (ولم تتواجد أي قيمة مكافئة)، فلن تنفِّذ التعليمة <code>switch</code> أي شيء وسيتابع البرنامج تنفيذ التعليمة التالية.
</p>

<p>
	من الميزات المثيرة للاهتمام هي أن الحالات <strong>ليست</strong> استثنائية، كما هو موضح في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_52" 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">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">10</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">
              </span><span class="kwd">switch</span><span class="pun">(</span><span class="pln">i</span><span class="pun">){</span><span class="pln">
                      </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">1</span><span class="pun">:</span><span class="pln">
                      </span><span class="kwd">case</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">"1 or 2\n"</span><span class="pun">);</span><span class="pln">
                      </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">7</span><span class="pun">:</span><span class="pln">
                              printf</span><span class="pun">(</span><span class="str">"7\n"</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">"default\n"</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;">
	[مثال 5.3]
</p>

<p>
	تتكرّر الحلقة بحسب قيمة <code>i</code>، إذ تأخذ قيمًا من <code>0</code> إلى <code>10</code>، تتسبب قيمة <code>1</code> أو <code>2</code> بطباعة الرسالة "1or 2" عن طريق تنفيذ تعليمة <code>printf</code> الأولى. وما قد يفاجئك هنا هو طباعة الرسائل التي تليها أيضًا، ﻷن تعليمة <code>switch</code> تختار نقطة دخول واحدة إلى متن التعليمة، فبعد البدء في نقطة معينة، تُنفّذ جميع التعليمات التي تليها. تُستخدم عناوين <code>case</code> و <code>default</code> ببساطة لتحديد <strong>أي من</strong> التعليمات التي سيقع عليها الاختيار. عندما تكون قيمة <code>i</code> مساويةً للقيمة <code>7</code>، ستُطبع الرسالتان الأخيرتان فقط، وأي قيمة لا تساوي إلى <code>1</code> أو <code>2</code> أو <code>7</code> ستتسبّب بطباعة الرسالة الأخيرة فقط.
</p>

<p>
	يمكن أن تُكتب العناوين labels بأي ترتيب كان، لكن لا يجب أن تتكرر أي قيمة ويمكنك استخدام عنوان <code>default</code> واحد فقط أو الاستغناء عنه، وليس من الضروري أن يكون العنوان الأخير، كما يمكن وضع تعليمةٍ واحدةٍ لعدّة عناوين أو عدّة تعليمات لعنوان واحد.
</p>

<p>
	يمكن أن يكون التعبير الذي يتحكم بتعليمة <code>switch</code> من أي نوع عدد صحيح. كانت لغة سي C القديمة تقبل نوع <code>int</code> <strong>فقط</strong>، واقتطعت المصرفات قسريًّا الأنواع الأطول مما تسبب ببعض الأخطاء الغامضة بعض الأحيان.
</p>

<h4>
	أكبر قيود تعليمة Switch
</h4>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_54" 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">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">10</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">
              </span><span class="kwd">switch</span><span class="pun">(</span><span class="pln">i</span><span class="pun">){</span><span class="pln">
                      </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">1</span><span class="pun">:</span><span class="pln">
                      </span><span class="kwd">case</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">"1 or 2\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"> </span><span class="lit">7</span><span class="pun">:</span><span class="pln">
                              printf</span><span class="pun">(</span><span class="str">"7\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">default</span><span class="pun">:</span><span class="pln">
                              printf</span><span class="pun">(</span><span class="str">"default\n"</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;">
	[مثال 6.3]
</p>

<p>
	يوجد مزيدٌ من الاستخدامات لتعليمة <code>break</code> سنتكلم عنها في فقرتها الخاصة.
</p>

<h4>
	تعبير العدد الصحيح الثابت
</h4>

<p>
	سنتكلم لاحقًا عن التعابير الثابتة، ولكن من المهم التطرق إلى طبيعة تعبير العدد الصحيح الثابت كونه التعبير الذي يجب أن تُلحقه بعنوان <code>case</code> في تعليمة <code>switch</code>. عمومًا، لا يحتوي تعبير العدد الصحيح الثابت أي عوامل تغيِّر من قيمته، مثل عامل الزيادة أو الإسناد، أو استدعاءٍ للدوال أو عوامل الفاصلة، ويجب أن تكون جميع المُعاملات في التعبير ثوابت صحيحة وثوابت محرفية وثوابت تعداد numeration constants وتعابير <code>sizeof</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> الفاصلة العائمة وهي المُعاملات المباشرة لمحوّلات النوع casts، كما يجب أن تكون أنواع العدد الصحيح هي الأنواع الناتجة عن أي مُعامل محوِّلٍ للنوع، وجميع هذه القيم يمكن أن تتوقعها بكونها تحت تصنيف تعبير العدد الصحيح الثابت.
</p>

<h3>
	تعليمة break
</h3>

<p>
	هذه التعليمة بسيطة، ويمكن فهمها في سياق استخدامها ضمن تعليمة <code>switch</code>، أو <code>do</code>، أو <code>while</code>، أو <code>for</code>، إذ يقفز تدفق البرنامج عند استخدامها إلى التعليمة التالية خارج متن التعليمة الحالية التي تتضمن تعليمة <code>break</code>؛ وتُستخدم كثيرًا في تعليمات <code>switch</code>، إذ إنها ضرورية بطريقةٍ أو بأخرى للحصول على التحكم الذي يحتاجه معظم الناس.
</p>

<p>
	استخدام تعليمة <code>break</code> بداخل الحلقات التكرارية استخدامٌ غير محبّذ بحسب الحالة، إذ إنه مبرّر عند حدوث ظروف استثنائية بداخل الحلقة مما يتطلب مغادرتها. من الجيد أن نستطيع مغادرة عدة حلقات دفعةً واحدة باستخدام تعليمة <code>break</code> واحدة فقط، لكن هذا غير محقّق. ألقِ نظرةً على المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_57" 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">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">10000</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">getchar</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">
                      </span><span class="kwd">break</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"> 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;">
	[مثال 7.3]
</p>

<p>
	يقرأ البرنامج السابق محرفًا وحيدًا من الدخل قبل طباعة قيمة <code>i</code> وفق سلسلةٍ من الأعداد، وتتسبب تعليمة <code>break</code> بالخروج من الحلقة في حال إدخال المحرف <code>s</code>.
</p>

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

<h3>
	تعليمة continue
</h3>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_59" 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">for</span><span class="pun">(</span><span class="pln">i </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">10</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">10</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">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">continue</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"> </span><span class="lit">15.0</span><span class="pun">/</span><span class="pln">i</span><span class="pun">);</span><span class="pln">
              </span><span class="com">/*
               * Lots of other statements .....
               */</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;">
	[مثال 8.3]
</p>

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

<p>
	أبقِ في بالك أن التعليمة <code>continue</code> ليس لها أي معنًى داخل تعليمة <code>switch</code> على عكس <code>break</code>، إذ أن استخدام <code>continue</code> بداخل <code>switch</code> ذو قيمةٍ فقط في حالة وجود حلقة تكرارية تحتوي <code>switch</code>، وفي هذه الحالة يبدأ التكرار التالي من الحلقة عند تنفيذ <code>continue</code>.
</p>

<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> المكتوبة باستخدام <code>while</code> و <code>for</code>؛ ففي حلقات <code>while</code> وعند استخدام <code>continue</code> يقفز التنفيذ إلى فحص قيمة التعبير التي تتحكم بالحلقة؛ بينما تُنفذ تعليمة التحديث وتعبير التحكم في حالة <code>for</code>.
</p>

<h3>
	تعليمة goto والعناوين labels
</h3>

<p>
	يعرف الجميع أن استخدام تعليمة <code>goto</code> هو "تصرف سيء"، إذ أنها تجعل برنامجك صعب المتابعة والقراءة، وتشتِّت هيكله وتدفقه إذا استُخدمت من دون عناية. كتبت مجموعة دايجكسترا Dijkstra ورقةً شهيرةً في 1968 باسم "تعليمة Goto مُضرة Goto Statement Considered Harmful" التي يذكرها الجميع ولكن لم يقرأها أي أحد.
</p>

<p>
	الشيء الأكثر إزعاجًا هو عندما يكون استخدامها في بعض الحالات ضروريًّا للغاية، إذ تُستخدم في لغة سي C للخروج من عدة حلقات تكرارية متداخلة أو للانتقال إلى مخرج للتعامل مع الأخطاء في نهاية الدالة، وستحتاج لاستخدام <strong>عنوان label</strong> عندما تقرر استخدام <code>goto</code>، ويوضح المثال التالي استخدامهما:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_62" style="">
<span class="kwd">goto</span><span class="pln"> L1</span><span class="pun">;</span><span class="pln">
</span><span class="com">/* whatever you like here */</span><span class="pln">
L1</span><span class="pun">:</span><span class="pln"> </span><span class="com">/* anything else */</span></pre>

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

<p>
	<strong>يجب</strong> أن تكون العناوين جزءًا من تعليمةٍ كاملة، حتى لو كانت فارغة، وعادة ما يكون هذا مهمًا فقط عندما تحاول وضع عنوان في نهاية التعليمة المركبة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4322_64" style="">
<span class="pln">label_at_end</span><span class="pun">:</span><span class="pln"> </span><span class="pun">;</span><span class="pln"> </span><span class="com">/* empty statement */</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تعمل <code>goto</code> بطريقة واضحة، إذ تنتقل إلى التعليمة المعنونة، ولأن اسم العنوان مرئي فقط من داخل الدالة التي تحتويه فمن غير الممكن الانتقال من دالةٍ إلى دالةٍ أخرى.
</p>

<p>
	من الصعب إعطاء قوانين صارمة حول استخدام تعليمة <code>goto</code> ولكن على نحوٍ مشابه لتعليمات <code>do</code> و <code>continue</code> و <code>break</code> (عدا وجودها في تعليمة <code>switch</code>)، فإن الاستخدام المفرط لها غير محبذ. فكّر طويلًا قبل استخدماها وليكن استخدامها بالنسبة لهيكلية البرنامج مقنعًا، إذ استخدامك لتعليمة <code>goto</code> كل 3 أو 5 دوال يدل على مشكلة ويجب أن تجد طريقةً مختلفة لكتابة برنامجك.
</p>

<h2>
	خلاصة
</h2>

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

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

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter3//" rel="external nofollow">Control of Flow and Logical Expressions</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%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%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="">الثوابت وسلاسل الهروب في لغة سي 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%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="">إدارة الذاكرة (Memory management) في لغة 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%B9%D8%A7%D8%B4%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D8%B1%D8%B7%D9%8A%D8%A9-%D9%88%D8%AD%D9%84%D9%87%D8%A7-%D9%85%D8%B4%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1013/" 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%AD%D8%A7%D8%AF%D9%8A-%D8%B9%D8%B4%D8%B1-%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%AA%D9%82%D9%8A%D9%8A%D8%AF-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-semaphores-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%B3%D9%8A-c-r1014/" rel="">متغيرات تقييد الوصول (Semaphores) في لغة البرمجة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1615</guid><pubDate>Mon, 20 Jun 2022 12:37:04 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62B;&#x648;&#x627;&#x628;&#x62A; &#x648;&#x633;&#x644;&#x627;&#x633;&#x644; &#x627;&#x644;&#x647;&#x631;&#x648;&#x628; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_06/62b05a8166c9a_------C-.png.c7be08c3a5fd5e724672b3a4a30f708f.png" /></p>

<p>
	يشرح المقال الأعداد الحقيقية والصحيحة الثابتة وسلاسل الهروب في لغة C.
</p>

<h2>
	الأعداد الصحيحة الثابتة
</h2>

<p>
	مبدأ الأعداد الصحيحة الثابتة بسيط، فهي تمثل أي عدد صحيح مثل 1 أو 1034 وغيرها، ويمكنك إضافة الحرف l أو L في نهاية عدد صحيح ثابت لتحويله قسريًّا إلى نوع <code>long</code>، وإضافة u أو U لتحويلها إلى <code>unsigned</code>، ويمكن كتابة الأعداد الصحيحة الثابتة بالنظام الست عشري عن طريق استخدام 0x أو 0X قبل كتابة الثابت، والأحرف a و b و c و d و e و f.
</p>

<p>
	وتُكتب الأعداد الصحيحة الثابتة بالنظام الثماني أيضًا باستخدام 0 في بداية الرقم، وتستخدم الأرقام 0، 1، 2، 3، 4، 5، 6، 7 فقط. عليك الحذر هنا بهذا الشأن، إذ من السهل النظر إلى 015 ومعاملته على أنه رقم صحيح بالنظام العشري، يقع المبتدئون في هذا الخطأ أغلب الأحيان، ولكنك ستبدأ بالاعتياد على الأمر بعد اقتراف بعض الأخطاء.
</p>

<p>
	قدّم معيار سي طريقًة جديدة لمعرفة نوع العدد الصحيح الثابت، إذ تحدث ترقية promoted للثابت في لغة سي القديمة إلى النوع <code>long</code> في حال كان كبيرًا ولا يتسع في النوع <code>int</code> دون أي تحذيرات، وتنص القاعدة على أن التحويل يجري بهذا الترتيب إلى أن تتسع قيمة الثابت بالنظام العشري:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_7" style="">
<span class="typ">int</span><span class="pln">   </span><span class="kwd">long</span><span class="pln">   </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">long</span></pre>

<p>
	بينما تستخدم الأعداد الست عشرية والثمانية الصحيحة هذه القائمة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_9" style="">
<span class="typ">int</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">long</span><span class="pln">   </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">long</span></pre>

<p>
	إذا كان الثابت مسبوقًا بالحرف "u" أو "U":
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_11" style="">
<span class="kwd">unsigned</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">long</span></pre>

<p>
	إذا كان مسبوقًا بالحرف "l" أو "L":
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_13" style="">
<span class="kwd">long</span><span class="pln">   </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">long</span></pre>

<p>
	وأحيرًا، إذا كان مسبوقًا بكلٍّ من u أو U و l أو L فالنوع هو <code>unsigned long</code> حصرًا.
</p>

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

<p>
	تحوي هذه التعليمة على خطأ مُخبأ:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_15" style="">
<span class="pln">printf</span><span class="pun">(</span><span class="str">"value of 32768 is %d\n"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">32768</span><span class="pun">);</span></pre>

<p>
	سيكون العدد 32768 طويلًا بالنسبة لآلة تعمل بنظام المتمم الثنائي ذي 16 بتًا وفقًا للقواعد المذكورة أعلاه، ولكن تتوقع الدالة <code>printf</code> عددًا صحيحًا فقط على أنه وسيط، ويشير<code>d%</code> إلى ذلك، إلا أن نوع هذا الوسيط خاطئ وينبغي عليك توخي الحذر وتحويل مثل هذه الحالات إلى النوع الصحيح:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_17" style="">
<span class="pln">printf</span><span class="pun">(</span><span class="str">"value of 32768 is %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="lit">32768</span><span class="pun">);</span></pre>

<p>
	من الجدير بالذكر أنه لا وجود للثوابت السالبة، فكتابة <code>23-</code> هو تعبيرٌ مكون من ثابت موجب وعامل.
</p>

<p>
	تمتلك ثوابت المحارف نوع <code>int</code> لأسباب تاريخية، وتُكتب عن طريق وضعها بين علامتي تنصيص أحادية على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_20" style="">
<span class="str">'a'</span><span class="pln">
</span><span class="str">'b'</span><span class="pln">
</span><span class="str">'like this'</span></pre>

<p>
	تُكتب المحارف الموسعة الثابتة بالطريقة ذاتها ولكن يسبقها وجود <code>L</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_22" style="">
<span class="pln">L</span><span class="str">'a'</span><span class="pln">
L</span><span class="str">'b'</span><span class="pln">
L</span><span class="str">'like this'</span></pre>

<p>
	للأسف، يمكن أن تحتوي المحارف الثابتة على أكثر من محرف واحد، ولكن تنفيذها يعطي نتيجةً مرتبطةً بالجهاز الذي تعمل عليه. تُعد المحارف الوحيدة من أفضل الحلول للبرامج المحمولة Portable، إذ تعطي قيمة عدد صحيح ثابت اعتيادي حسب تمثيل الجهاز لهذا الحرف. صادفت في تعريفنا عن المحارف الموسعة هذا المحرف <code>&lt;a&gt;</code> الذي يمثّل محرفًا متعدّد البايتات (مُرمّزًا لعمليات الإدخال بالإزاحة والإخراج بالإزاحة)، ويُعد <code>&lt;a&gt;</code> هنا محرفًا ثابتًا، مثل المحرف <code>abcde</code>. سيتسبب هذا النوع من المحارف بالعديد من المشاكل في المستقبل، نأمل أن يحذّرك المصرّف بشأنهم.
</p>

<p>
	هناك ما يُدعى باسم <strong>سلسلة الهروب Escape sequence</strong>، والتي تهدف إلى تسهيل عملية تمثيل بعض المحارف الخاصة التي سيكون من الصعب استخدامها ضمن محرف ثابت (هل المحرف ' ' هو محرف مسافة space أم مسافة جدولة tab؟). يوضح الجدول 10.2 <strong>سلاسل الهروب</strong> المُعرفة في المعيار.
</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>a\</code>
			</td>
			<td>
				تحذير صوتي
			</td>
		</tr>
<tr>
<td>
				<code>b\</code>
			</td>
			<td>
				فراغ للخلف Backspace
			</td>
		</tr>
<tr>
<td>
				<code>f\</code>
			</td>
			<td>
				فاصل صفحة
			</td>
		</tr>
<tr>
<td>
				<code>n\</code>
			</td>
			<td>
				سطر جديد
			</td>
		</tr>
<tr>
<td>
				<code>r\</code>
			</td>
			<td>
				إرجاع المؤشر
			</td>
		</tr>
<tr>
<td>
				<code>t\</code>
			</td>
			<td>
				مسافة جدولة
			</td>
		</tr>
<tr>
<td>
				<code>v\</code>
			</td>
			<td>
				مسافة جدولة شاقولية
			</td>
		</tr>
<tr>
<td>
				<code>\\</code>
			</td>
			<td>
				شرطة مائلة للخلف
			</td>
		</tr>
<tr>
<td>
				<code>'\</code>
			</td>
			<td>
				علامة تنصيص فردية
			</td>
		</tr>
<tr>
<td>
				<code>"\</code>
			</td>
			<td>
				علامة تنصيص مزدوجة
			</td>
		</tr>
<tr>
<td>
				<code>?\</code>
			</td>
			<td>
				إشارة استفهام
			</td>
		</tr>
</tbody>
</table>
<p style="text-align: center;">
	[جدول 10.2. سلاسل الهروب في لغة C]
</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="">محرف</a> باستخدام القيمة الداخلية التي تمثّله، مثل السلسلة <code>000\</code> أو <code>xhhh\</code>، إذ أن <code>000</code> هي ثلاث خانات ثمانية و <code>hhh</code> هو أي عدد ممثل بالنظام الست عشري. أكثر السلاسل شيوعًا هي <code>033\</code>، التي تُستخدم لتمثل زرّ ESC (الهروب Escape) على لوحة المفاتيح في الحواسيب التي تعمل بترميز ASCII. انتبه إلى أن المحارف الثابتة الممثلة بالقيمة الست عشرية تشمل جميع المحارف الموجودة ضمنها، فعلى سبيل المثال إذا أردت سلسلةً نصيةً تحتوي على القيمة الست عشرية <code>ff</code> متبوعةً <strong>بالحرف</strong> <code>f</code>، فالطريقة الآمنة لكتابة ذلك هو استخدام خاصية ضمّ السلاسل النصية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_26" style="">
<span class="str">"\xff"</span><span class="pln"> </span><span class="str">"f"</span></pre>

<p>
	إذ تمثّل السلسلة النصية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_28" style="">
<span class="str">"\xfff"</span></pre>

<p>
	محرفًا واحدًا، مكوّنًا من ثلاثة حروف <code>f</code> تمثل قيمة السلسلة الست عشرية.
</p>

<p>
	تتطلب بعض محارف الهروب تفسيرًا إذ أن بعضها غير واضح الوظيفة. للحصول على علامة تنصيص فردية على أنها محرف ثابت نستخدم <code>'\</code>، وللحصول على علامة استفهام نستخدم <code>?\</code>، للحصول على علامتي استفهام لا يمكنك استخدام <code>??</code>، لأن السلسلة <code>??</code> تعد ثلاثية محارف Trigraph، وبالتالي، عليك استخدام <code>?\?\</code>. محرف الهروب <code>"\</code> مهمٌ فقط في حالة السلاسل النصية، وسنتكلم عن ذلك لاحقًا.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_37" style="">
<span class="pln">\a</span></pre>

<p>
	اقرع الجرس في حال وجود شيء ما للطباعة، ولا تتحرك.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_39" style="">
<span class="pln">\b</span></pre>

<p>
	فراغ للخلف.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_41" style="">
<span class="pln">\f</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_43" style="">
<span class="pln">\n</span></pre>

<p>
	اِذهب إلى بداية السطر التالي.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_45" style="">
<span class="pln">\r</span></pre>

<p>
	عُد إلى بداية السطر الحالي.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_47" style="">
<span class="pln">\t</span></pre>

<p>
	اِذهب إلى مسافة الجدولة الأفقية التالية.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_49" style="">
<span class="pln">\v</span></pre>

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

<p>
	بالنسبة للمحارف <code>b\</code> و <code>t\</code> و <code>v\</code> إن لم يكن هناك موضع موافق فسيكون التصرف غير محدد. يتجنب المعيار ذكر الوجهات الفيزيائية للحركة بالنسبة لأجهزة الخرج، لأنها لا تعمل من الأعلى إلى الأسفل ومن اليسار إلى اليمين بالضرورة كما في بيئات العمل الموجودة في الثقافة الغربية في جميع الحالات.
</p>

<p>
	من المضمون أن لكل محرف هروب قيمة عدد صحيح فريدة، وتُخزَّن في النوع <code>char</code>.
</p>

<h2 id="-">
	الأعداد الحقيقية الثابتة
</h2>

<p>
	تتبع هذه الأعداد التنسيق الاعتيادي للأعداد الحقيقية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_51" style="">
<span class="lit">1.0</span><span class="pln">
</span><span class="lit">2.</span><span class="pln">
</span><span class="pun">.</span><span class="lit">1</span><span class="pln">
</span><span class="lit">2.634</span><span class="pln">
</span><span class="pun">.</span><span class="lit">125</span><span class="pln">
</span><span class="lit">2.e5</span><span class="pln">
</span><span class="lit">2.e+5</span><span class="pln">
</span><span class="pun">.</span><span class="lit">125e-3</span><span class="pln">
</span><span class="lit">2.5e5</span><span class="pln">
</span><span class="lit">3.1E-6</span></pre>

<p>
	وإلى آخره. حتى لو كان هناك جزء من الرقم الحقيقي ذو قيمة صفرية يجب إظهاره بهدف تسهيل القراءة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_53" style="">
<span class="lit">1.0</span><span class="pln">
</span><span class="lit">0.1</span></pre>

<p>
	يدل الجزء الأسي exponent على مرتبة قوة العدد، مثلًا:
</p>

<pre>
 </pre>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_55" style="">
<span class="lit">3.0e3</span></pre>

<p>
	يكافئ قيمة العدد الصحيح الثابت:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1850_57" style="">
<span class="lit">3000</span></pre>

<p>
	وكما ترى، يمكن استبدال <code>e</code> بالحرف <code>E</code> أيضًا لنفس الغرض، وهذه الثوابت من نوع <code>double</code> إلا في حال سبق القيمة المحرف <code>f</code> أو <code>F</code> وفي هذه الحالة هي من نوع <code>float</code>؛ وإذا سبقها <code>l</code> أو <code>L</code> فهي في هذه الحالة من نوع <code>long double</code>.
</p>

<p>
	بهدف الوصف الكامل، إليك وصف رسمي يصف طبيعة الأعداد الحقيقية الثابتة:
</p>

<p>
	العدد الحقيقي الثابت يحقق واحدةً من الحالات التالية:
</p>

<ul>
<li>
		عدد <strong>كسري ثابت</strong> متبوعٌ <strong>بأُس</strong> اختياري.
	</li>
	<li>
		<strong>سلسلة أرقام</strong> متبوعة <strong>بأُس</strong>.
	</li>
</ul>
<p>
	في الحالتين السابقتين، يمكن أن يُتبع العدد الحقيقي بالأحرف الاختيارية <code>f</code> و <code>l</code> و <code>F</code> و <code>L</code>، بحيث يتحقق:
</p>

<ul>
<li>
		الثابت الكسري واحدٌ من الحالات التالية:
		<ul>
<li>
				سلسلة اختيارية من <strong>الخانات</strong> متبوعةٌ بفاصلة عشرية متبوعةٌ <strong>بسلسلة من الخانات</strong>.
			</li>
			<li>
				<strong>سلسلةٌ من الخانات</strong> متبوعةٌ بفاصلةٍ عشرية.
			</li>
		</ul>
</li>
	<li>
		الأس واحدٌ من الحالات التالية:
		<ul>
<li>
				الحرف <code>e</code> أو <code>E</code> متبوعٌ برمز <code>+</code> أو <code>-</code> اختياري متبوعٌ <strong>بسلسلة من الخانات</strong>.
			</li>
			<li>
				<strong>سلسلة من الخانات</strong> هي تركيب اعتباطي من خانةٍ واحدة أو أكثر.
			</li>
		</ul>
</li>
</ul>
<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter2//" rel="external nofollow">Variables and Arithmetic</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%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D9%86%D8%B7%D9%82%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%A7%D9%84%D8%AA%D8%AF%D9%81%D9%82-%D9%81%D9%8A-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-c-r1615/" 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%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="">إدارة الذاكرة (Memory management) في لغة 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%B9%D8%A7%D8%B4%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D8%B1%D8%B7%D9%8A%D8%A9-%D9%88%D8%AD%D9%84%D9%87%D8%A7-%D9%85%D8%B4%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1013/" 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%AD%D8%A7%D8%AF%D9%8A-%D8%B9%D8%B4%D8%B1-%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%AA%D9%82%D9%8A%D9%8A%D8%AF-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-semaphores-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%B3%D9%8A-c-r1014/" rel="">متغيرات تقييد الوصول (Semaphores) في لغة البرمجة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1614</guid><pubDate>Sat, 06 Aug 2022 07:29:30 +0000</pubDate></item><item><title>&#x627;&#x644;&#x639;&#x648;&#x627;&#x645;&#x644; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_06/62b053c25d940_----C-.png.ddf3dd000d1eb6a39bcbe2674d79018c.png" /></p>

<p>
	يعرض هذا المقال مفهوم العوامل في لغة C وكيفية استخدامها في العمليات والتعليمات.
</p>

<h2>
	عوامل المضاعفة
</h2>

<p>
	تتضمن عوامل المضاعفة multiplicative operators عامل الضرب "*" والقسمة "/" وباقي القسمة "%"، ويعمل عاملا الضرب والقسمة بالطريقة التي تتوقع أن تعملان بها، لكلٍّ من الأنواع الحقيقية والصحيحة، إذ تنتج قيمة مقتطعة دون فواصل عشرية عند تقسيم الأعداد الصحيحة ويكون الاقتطاع باتجاه الصفر. يعمل عامل باقي القسمة وفق تعريفه فقط مع الأنواع الصحيحة، لأن القسمة الناتجة عن الأعداد الحقيقية لن تعطينا باقيًا.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_7" style="">
<span class="lit">9</span><span class="pun">/</span><span class="lit">2</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">4</span><span class="pln">
</span><span class="lit">9</span><span class="pun">%</span><span class="lit">2</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span></pre>

<p>
	إذا كان أحد المُعاملات سالب، فنتيجة العامل "/" قد تكون أقرب عدد صحيح لنتيجة القسمة على أي من الاتجاهين (باتجاه الأكبر أو الأصغر)، وقد تكون إشارة نتيجة العامل "%" موجبةً أو سالبة، وتعتمد النتيجتين السابقتين حسب تعريف التنفيذ.
</p>

<p>
	التعبير الآتي مساوٍ الصفر في جميع الحالات عدا حالة <code>b</code> مساوية للصفر.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_9" style="">
<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">b </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></pre>

<p>
	تُطبّق التحويلات الحسابية الاعتيادية على كلا المُعاملين.
</p>

<h2>
	عوامل الجمع
</h2>

<p>
	تتضمن عوامل الجمع additive operators عامل الجمع "+" والطرح "-"، وتتبع طريقة عمل هذه الدوال قواعدها المعتادة التي تعرفها. للعامل الثنائي والأحادي نفس الرمز، ولكن لكل واحد منهما معنًى مختلف؛ فعلى سبيل المثال، يَستخدم التعبيران <code>a+b</code> و<code>a-b</code> عاملًا ثنائيًّا (العامل <code>-</code> للجمع و <code>+</code> للطرح).
</p>

<p>
	إذا أردنا استخدام العوامل الأحادية بذات الرموز، فسنكتب <code>b+</code> أو <code>b-</code>، ولعامل الطرح الأحادي وظيفةٌ واضحة ألا وهي أخذ القيمة السالبة لقيمة المُعامل، ولكن ما وظيفة عامل الجمع الأحادي؟ في الحقيقة لا يؤدي أي دور؛ ويُعد عامل الجمع الأحادي إضافةً جديدة إلى اللغة، إذ يعادل وجوده وجود عامل الطرح الأحادي ولكنه لا يؤدي أي دور لتغيير قيمة التعبير. القلة من مستخدمي لغة سي القديمة لاحظ عدم وجوده.
</p>

<p>
	تُطبّق التحويلات الحسابية الاعتيادية على كلٍّ من مُعاملات العوامل الثنائية (للجمع والطرح)، وتُطبّق الترقية العددية الصحيحة على مُعاملات العوامل الأحادية فقط.
</p>

<h2>
	عوامل العمليات الثنائية
</h2>

<p>
	واحدة من مزايا لغة سي هي الطريقة التي تسمح بها لمبرمجي النظم بالتعامل مع الشيفرة البرمجية وكأنها شيفرة تجميعية Assembly code، وهو نوع شيفرة برمجية كان شائعًا قبل مجيء لغة سي، وكان هذا النوع من الشيفرات صعب التشغيل على عدة منصات (غير محمول non-portable). كما وضحت لنا لغة سي أن هذا الأمر لا يتطلب سحرًا لجعل الشيفرة محمولة، لكن ما هو هذا الشيء؟ هو ما يُعرف أحيانًا باسم "العبث بالبِتات bit-twiddling" وهي عملية التلاعب ببتات متغيرات الأعداد الصحيحة على نحوٍ منفرد. لا يمكن استخدام عوامل العمليات الثنائية bitwise operators على مُعاملات من نوع أعداد حقيقية إذ لا تُعد البتات الخاصة بها منفردة individual أو يمكن الوصول إليها.
</p>

<p>
	هناك ستة عوامل للعمليات الثنائية موضحة في الجدول 7.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>
		</tr></thead>
<tbody>
<tr>
<td>
				<code>&amp;</code>
			</td>
			<td>
				العملية الثنائية AND
			</td>
			<td>
				التحويلات الحسابية الاعتيادية
			</td>
		</tr>
<tr>
<td>
				<code>\</code>
			</td>
			<td>
				العملية الثنائية OR
			</td>
			<td>
				التحويلات الحسابية الاعتيادية
			</td>
		</tr>
<tr>
<td>
				<code>^</code>
			</td>
			<td>
				العملية الثنائية XOR
			</td>
			<td>
				التحويلات الحسابية الاعتيادية
			</td>
		</tr>
<tr>
<td>
				<code>&gt;&gt;</code>
			</td>
			<td>
				إزاحة إلى اليسار
			</td>
			<td>
				الترقية العددية الصحيحة
			</td>
		</tr>
<tr>
<td>
				<code>&lt;&lt;</code>
			</td>
			<td>
				إزاحة إلى اليمين
			</td>
			<td>
				الترقية العددية الصحيحة
			</td>
		</tr>
<tr>
<td>
				<code>~</code>
			</td>
			<td>
				المتمم الأحادي
			</td>
			<td>
				الترقية العددية الصحيحة
			</td>
		</tr>
</tbody>
</table>
<p style="text-align: center;">
	[جدول 7.2. عوامل العمليات الثنائية]
</p>

<p>
	العامل الوحيد الأحادي هو الأخير (المتمم الأحادي)، إذ يعكس حالة كل بِت في قيمة المُعامل وله نفس تأثير عامل الطرح الأحادي في حاسوب يعمل بنظام المتمم الأحادي، لكن معظم الحواسيب الآن تعمل بنظام المتمم الثنائي، لذلك وجوده مهم.
</p>

<p>
	سيسهّل استخدام النظام الست عشري عن استخدام النظام العشري فهم طريقة هذه العوامل، لذا حان الوقت الآن لأن نعرفك على الثوابت الست عشرية. أي رقم يُكتب في بدايته "0x" يفسّر على أنه رقم ست عشري، على سبيل المثال القيمة "15" و"0xf"، أو "0XF" متكافئتان، جرّب تشغيل المثال التالي على حاسوبك، أو الأفضل من ذلك تنبّأ بوظيفة البرنامج قبل تشغيله.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_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"> x</span><span class="pun">,</span><span class="pln">y</span><span class="pun">;</span><span class="pln">
      x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> y </span><span class="pun">=</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">while</span><span class="pun">(</span><span class="pln">x </span><span class="pun">!=</span><span class="pln"> y</span><span class="pun">){</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"%x &amp; %x = %x\n"</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0xff</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">&amp;</span><span class="lit">0xff</span><span class="pun">);</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"%x | %x = %x\n"</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0x10f</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">|</span><span class="lit">0x10f</span><span class="pun">);</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"%x ^ %x = %x\n"</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0xf00f</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">^</span><span class="lit">0xf00f</span><span class="pun">);</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"%x &gt;&gt; 2 = %x\n"</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> x </span><span class="pun">&gt;&gt;</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">"%x &lt;&lt; 2 = %x\n"</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> x </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="lit">2</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">&lt;&lt;</span><span class="pln"> </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">1</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;">
	[مثال 2.9]
</p>

<p>
	لنتكلم أوّلًا عن طريقة عمل الحلقة التكرارية في مثالنا، إذ أن المتغير الذي يتحكم بالحلقة هو <code>x</code> ومُهيّأ بالقيمة صفر، وفي كل دورة تُوازن قيمته مع قيمة <code>y</code> الذي ضُبِط بنمطٍ مستقل بنفس طول الكلمة ومكون من الواحدات، وذلك بأخذ المتمم الأحادي للصفر، وفي أسفل الحلقة يُزاح المتغير <code>x</code> إلى اليسار مرةً واحدةً وتُجرى العملية الثنائية OR عليه، مما ينتج سلسلةً تبدأ على النحو التالي: "0، 1، 11، 111، …" بالنظام الثنائي.
</p>

<p>
	تُجرى العمليات الثنائية على <code>x</code> باستخدام كل من عوامل AND وOR وXOR (أي OR الحصرية أو دارة عدم التماثل) إضافةً إلى مُعاملات أخرى جديرة بالاهتمام، ومن ثم تُطبع النتيجة.
</p>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_13" style="">
<span class="typ">int</span><span class="pln"> i</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="pln"> </span><span class="pun">(</span><span class="kwd">unsigned</span><span class="pun">)</span><span class="pln">j </span><span class="pun">&gt;&gt;</span><span class="pln"> </span><span class="lit">4</span><span class="pun">;</span></pre>

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

<p>
	لنتكلم عن شيء مختلف قليلًا، وهي إحدى الحيل المفيدة التي يستخدمها مبرمجو لغة سي لكتابة برامجهم على نحوٍ أفضل. إذا أردت تشكيل قيمةٍ تحتوي على واحدات "1" في جميع خاناتها عدا البِت الأقل أهمية، بهدف تخزين نمطٍ آخر فيها، فمن غير المطلوب معرفة طول الكلمة في النظام الذي تستخدمه. على سبيل المثال، تستطيع استخدام الطريقة التالية لضبط البتات الأقل ترتيبًا لمتغيرٍ من نوع <code>int</code> إلى <code>0x0f0</code> وجميع البتات الأخرى إلى <code>1</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_15" style="">
<span class="typ">int</span><span class="pln"> some_variable</span><span class="pun">;</span><span class="pln">
some_variable </span><span class="pun">=</span><span class="pln"> </span><span class="pun">~</span><span class="lit">0xf0f</span><span class="pun">;</span></pre>

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

<p>
	لا يوجد هناك مزيدٌ من الأشياء لقولها عن عوامل التلاعب بالبتات، وتجربتنا في تعليم لغة سي تدلنا على أنها سهلة الفهم والتعلم من معظم الناس، لذا دعنا ننتقل للموضوع التالي.
</p>

<h2>
	عوامل الإسناد
</h2>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_17" style="">
<span class="pln">x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">4</span><span class="pun">;</span></pre>

<p>
	تُسند القيمة <code>4</code> إلى المتغير <code>x</code>، والنتيجة عن إسناد القيمة إلى المتغير <code>x</code> هو استخدامها على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_19" style="">
<span class="pln">a </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="lit">4</span><span class="pun">);</span></pre>

<p>
	إذ ستخزِّن <code>a</code> القيمة <code>4</code> المُسندة إليها بعد أن تُسنَد إلى <code>x</code>. تجاهلت جميع عمليات الإسناد السابقة التي نظرنا إليها لحد اللحظة (عدا مثال واحد) ببساطة القيمة الناتجة عن عملية الإسناد بالرغم من وجودها.
</p>

<p>
	بفضل هذه القيمة، يمكننا كتابة تعابير مشابهة لهذه:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_21" style="">
<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></pre>

<p>
	إذ تُسند قيمة المتغير <code>d</code> إلى المتغير <code>c</code> وتُسند هذه النتيجة إلى <code>b</code> وهكذا دواليك، ونلاحظ هنا معالجة تعابير الإسناد من الجهة اليمنى إلى الجهة اليسرى، ولكن ماعدا ذلك فهي تعابير اعتيادية (القوانين التي تصف اتجاه المعالجة من اليمين أو من اليسار موجودةٌ في الجدول 9.2.).
</p>

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

<p>
	عوامل الإسناد المتبقية هي عوامل إسناد مركّبة، وتعد اختصاراتٌ مفيدة يمكن اختصارها عندما يكون المُعامل ذاته على يمين ويسار عامل الإسناد، على سبيل المثال، يمكن اختصار التعبير التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_23" style="">
<span class="pln">x </span><span class="pun">=</span><span class="pln"> x </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span></pre>

<p>
	إلى التعبير:
</p>

<pre class="ipsCode">
x += 1;
</pre>

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

<table><tbody>
<tr>
<td>
				<code>=*</code>
			</td>
			<td>
				<code>=/</code>
			</td>
			<td>
				<code>=%</code>
			</td>
		</tr>
<tr>
<td>
				<code>=+</code>
			</td>
			<td>
				<code>=-</code>
			</td>
		</tr>
<tr>
<td>
				<code>=&amp;</code>
			</td>
			<td>
				<code>=\</code>
			</td>
			<td>
				<code>=^</code>
			</td>
		</tr>
<tr>
<td>
				<code>=&lt;&lt;</code>
			</td>
			<td>
				<code>=&gt;&gt;</code>
			</td>
		</tr>
</tbody></table>
<p style="text-align: center;">
	[جدول 8.2. عوامل الإسناد المركبة]
</p>

<p>
	تُطبَّق التحويلات الحسابية في كل حالة وكأنها تُطبق على كامل التعبير، أي كأن التعبير <code>a+=b</code> مثلًا مكتوبٌ على النحو <code>a=a+b</code>.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			للتذكير: تحمل نتيجة عامل الإسناد نوع وقيمة الكائن الذي أُسند إليه.
		</p>
	</div>
</blockquote>

<h2>
	عوامل الزيادة والنقصان
</h2>

<p>
	قدّمت لغة سي عاملين أحاديين مميزين لإضافة واحد أو طرحه من تعبيرٍ ما نظرًا لشيوع هذه العملية البسيطة؛ إذ يضيف عامل الزيادة "++" واحدًا، ويطرح عامل النقصان "--" واحدًا، وتُستخدم هذه العوامل على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_25" style="">
<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="pun">--</span><span class="pln">x</span><span class="pun">;</span></pre>

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

<p>
	إليك الفرق موضحًا في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_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">
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="pln">b</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"> </span><span class="lit">5</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"> </span><span class="pun">++</span><span class="pln">a</span><span class="pun">+</span><span class="lit">5</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"> a</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"> b</span><span class="pun">++</span><span class="pln"> </span><span class="pun">+</span><span class="lit">5</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"> b</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;">
	<em>مثال 10.2</em>
</p>

<p>
	خرج المثال السابق هو:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_29" style="">
<span class="lit">11</span><span class="pln">
</span><span class="lit">6</span><span class="pln">
</span><span class="lit">10</span><span class="pln">
</span><span class="lit">6</span></pre>

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

<p>
	لا يستخدم مبرمجو سي عادةً التعليمة التالية لطرح أو جمع واحد:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_31" style="">
<span class="pln">x </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span></pre>

<p>
	بل يستخدمون التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_33" style="">
<span class="pln">x</span><span class="pun">++;</span><span class="pln"> </span><span class="com">/* or */</span><span class="pln"> </span><span class="pun">++</span><span class="pln">x</span><span class="pun">;</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_37" style="">
<span class="pln">y </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">x</span><span class="pun">;</span></pre>

<p>
	وقد يُقيّم كما لو كان قد جرى توسعته إلى التعبير التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_39" style="">
<span class="pln">y </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="lit">1</span><span class="pun">);</span></pre>

<p>
	لأن المصرّف يلاحظ عدم وجود تأثير أبدًا على قيمة <code>x</code>.
</p>

<p>
	تُجرى العمليات الحسابية في هذه الحالة تمامًا كما في حالة تعبير جمع، مثلًا <code>x=x+1</code>، وتُطبّق التحويلات الحسابية الاعتيادية.
</p>

<h2>
	الأسبقية والتجميع
</h2>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_41" style="">
<span class="pln">a </span><span class="pun">+</span><span class="pln"> b </span><span class="pun">+</span><span class="pln"> c</span></pre>

<p>
	مساويةٌ للتعبير:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_43" style="">
<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"> </span><span class="pun">+</span><span class="pln"> c</span></pre>

<p>
	أو التعبير:
</p>

<pre class="ipsCode">
a + (b + c)
</pre>

<p>
	أليس كذلك؟ في الحقيقة لا، فهناك فرقٌ بين التعابير السابقة؛ فإذا تسبَّب التعبير <code>a+b</code> بحالة طفحان، وكانت قيمة المتغير <code>c</code> قريبة من قيمة <code>b-</code>، فسيعطي التعبير الثاني الإجابة الصحيحة، بينما سيتسبب الأول بسلوك غير محدد. يمكننا ملاحظة هذه المشكلة بوضوح أكبر باستخدام قسمة الأعداد الصحيحة، إذ يعطي التعبير التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_45" style="">
<span class="pln">a</span><span class="pun">/</span><span class="pln">b</span><span class="pun">/</span><span class="pln">c</span></pre>

<p>
	نتائج مختلفةً تمامًا عند تجميعه بالطريقة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_47" style="">
<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></pre>

<p>
	أو بالطريقة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_49" style="">
<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></pre>

<p>
	يمكنك تجربة استخدام القيم <code>a=10</code> و <code>b=2</code> و <code>c=3</code> للتأكُّد، إذ سيكون التعبير الأول: <code>(2/3)/10</code>، ونتيجة <code>2/3</code> في قسمة الأعداد الصحيحة هي <code>0</code>، لذلك سنحصل على <code>10/0</code>، مما سيسبب طفحانًا overflow؛ بينما سيعطينا التعبير الثاني القيمة <code>(10/2)</code>، وهي <code>5</code>، وبتقسيمها على <code>3</code> تعطينا <code>1</code>.
</p>

<p>
	يُعرَف تجميع العوامل على هذا النحو بمصطلح <strong>الارتباط associativity</strong>، والمكون الثاني لتحديد طريقة عمل العوامل هو <strong>الأسبقية precedence</strong>، إذ لبعض العوامل أسبقيةٌ عن عوامل أخرى، وتُحسب قيم العوامل هذه في التعابير الفرعية أولًا قبل الانتقال إلى العوامل الأقل أهمية. تُستخدم قوانين الأسبقية في معظم لغات البرمجة عالية المستوى. "نعلم" أن التعبير:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_51" style="">
<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></pre>

<p>
	يُجمّع على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_53" style="">
<span class="pln">a </span><span class="pun">+</span><span class="pln"> </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"> d</span></pre>

<p>
	إذ أن عملية الضرب ذات أسبقية أعلى موازنةً بعملية الجمع.
</p>

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

<table>
<thead><tr>
<th>
				العامل
			</th>
			<th>
				الاتجاه
			</th>
			<th>
				ملاحظات
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				<code>() [] &lt;- .</code>
			</td>
			<td>
				من اليسار إلى اليمين
			</td>
			<td>
				1
			</td>
		</tr>
<tr>
<td>
				<code>! ~ ++ -- - + (cast) * &amp; sizeof</code>
			</td>
			<td>
				من اليمين إلى اليسار
			</td>
			<td>
				جميع العوامل أحادية
			</td>
		</tr>
<tr>
<td>
				<code>* / %</code>
			</td>
			<td>
				من اليسار إلى اليمين
			</td>
			<td>
				عوامل ثنائية
			</td>
		</tr>
<tr>
<td>
				<code>+ -</code>
			</td>
			<td>
				من اليسار إلى اليمين
			</td>
			<td>
				عوامل ثنائية
			</td>
		</tr>
<tr>
<td>
				<code>&lt;&lt; &gt;&gt;</code>
			</td>
			<td>
				من اليسار إلى اليمين
			</td>
			<td>
				عوامل ثنائية
			</td>
		</tr>
<tr>
<td>
				<code>&lt; =&lt; &gt; =&gt;</code>
			</td>
			<td>
				من اليسار إلى اليمين
			</td>
			<td>
				عوامل ثنائية
			</td>
		</tr>
<tr>
<td>
				<code>== =!</code>
			</td>
			<td>
				من اليسار إلى اليمين
			</td>
			<td>
				عوامل ثنائية
			</td>
		</tr>
<tr>
<td>
				<code>&amp;</code>
			</td>
			<td>
				من اليسار إلى اليمين
			</td>
			<td>
				عامل ثنائي
			</td>
		</tr>
<tr>
<td>
				<code>^</code>
			</td>
			<td>
				من اليسار إلى اليمين
			</td>
			<td>
				عامل ثنائي
			</td>
		</tr>
<tr>
<td>
				<code>\</code>
			</td>
			<td>
				من اليسار إلى اليمين
			</td>
			<td>
				عامل ثنائي
			</td>
		</tr>
<tr>
<td>
				<code>&amp;&amp;</code>
			</td>
			<td>
				من اليسار إلى اليمين
			</td>
			<td>
				عامل ثنائي
			</td>
		</tr>
<tr>
<td>
				<code>\\</code>
			</td>
			<td>
				من اليسار إلى اليمين
			</td>
			<td>
				عامل ثنائي
			</td>
		</tr>
<tr>
<td>
				<code>:?</code>
			</td>
			<td>
				من اليمين إلى اليسار
			</td>
			<td>
				2
			</td>
		</tr>
<tr>
<td>
				<code>= =+</code> وجميع عوامل الإسناد المركّبة
			</td>
			<td>
				من اليمين إلى اليسار
			</td>
			<td>
				عوامل ثنائية
			</td>
		</tr>
<tr>
<td>
				<code>,</code>
			</td>
			<td>
				من اليسار إلى اليمين
			</td>
			<td>
				عامل ثنائي
			</td>
		</tr>
</tbody>
</table>
<p style="text-align: center;">
	[جدول 9.2. أسبقية العوامل وترابطها]
</p>

<p>
	حيث أن:
</p>

<ol>
<li>
		الأقواس بهدف تجميع التعابير، و<strong>ليس</strong> استدعاء الدوال.
	</li>
	<li>
		هذا العامل غير مألوف، راجع القسم 1.4.3.
	</li>
</ol>
<p>
	السؤال هنا هو، كيف أستطيع الاستفادة من هذه المعلومات؟ من المهم طبعًا أن تكون قادرًا على كتابة تعابير تعطي قيمًا صحيحة بمعرفة ترتيب تنفيذ العمليات، إضافةً إلى فهم وقراءة تعابير مكتوبة من مبرمجين آخرين. تبدأ خطوات كتابة تعبير أو قراءة تعبير مكتوب بالعثور على العامل الأحادي والمُعاملات المرتبطة معه، وهذه ليست بالمهمة الصعبة ولكنها تحتاج إلى بعض التمرين، بالأخص عندما تعرف أنه يمكن استخدام العوامل عددًا من المرات الاعتباطية بجانب مُعاملاتها، مثل التعبير التالي باستخدام العامل <code>*</code> الأحادي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_55" style="">
<span class="pln">a</span><span class="pun">*****</span><span class="pln">b</span></pre>

<p>
	يعني التعبير السابق أن المتغير <code>a</code> مضروبٌ <strong>بقيمة ما</strong>، وهذه القيمة هي تعبير يتضمن <code>b</code> وعددًا من عوامل <code>*</code> الأحادية.
</p>

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

<ol>
<li>
		العاملان "++" و "-" أحاديان في جميع الحالات.
	</li>
	<li>
		العامل الذي يقع على يمين المُعامل مباشرةً هو عامل ثنائي (في حالة لم تتحقق القاعدة 1)، إذا كان العامل الذي يقع على يمين المُعامل ذاك ثنائيًّا.
	</li>
	<li>
		جميع العوامل الواقعة على يسار المُعامل أحادية (في حالة لم تتحقق القاعدة 2).
	</li>
</ol>
<p>
	يمكنك دائمًا التفكير بعمل العوامل الأحادية أولًا قبل العوامل الأخرى بسبب أسبقيتها المرتفعة، إذ من الأشياء التي يجب عليك الانتباه عليها هي مواضع العاملين "++" و "--" إذا كانا قبل أو بعد المُعامل، فعلى سبيل المثال يحتوي التعبير التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_57" style="">
<span class="pln">a </span><span class="pun">+</span><span class="pln"> </span><span class="pun">-</span><span class="pln">b</span><span class="pun">++</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> c</span></pre>

<p>
	على عاملين أحاديين مُطبقّان على <code>b</code>. ترتبط العوامل الأحادية من اليمين إلى اليسار، إذًا على الرغم من قدوم <code>-</code> أولًا، يُكتب التعبير على النحو التالي (باستخدام الأقواس للتوضيح):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_59" style="">
<span class="pln">a </span><span class="pun">+</span><span class="pln"> </span><span class="pun">-(</span><span class="pln">b</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> c</span></pre>

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

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

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

<p>
	لكن الأمر <strong>المهم</strong> هنا هو الخطوة التي تلي تجميعك لهذه التعابير، أتذكر "التحويلات الحسابية الاعتيادية"؟ إذ فسَّرت هذه التحويلات كيف يمكنك التنبؤ بنوع التعبير عن طريق النظر إلى المُعاملات الموجودة. والآن إذا مزجت أنواعًا مختلفة في تعبير معقد، ستُحدّد أنواع التعابير الجزئية فقط من خلال أنواع المُعاملات الموجودة في التعبير الجزئي، ألقِ نظرةً على المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_61" 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">j</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">

      i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</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="pun">;</span><span class="pln">
      f </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3.0</span><span class="pun">;</span><span class="pln">

      f </span><span class="pun">=</span><span class="pln"> f </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="pln">
      printf</span><span class="pun">(</span><span class="str">"value of f is %f\n"</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></pre>

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

<p>
	قيمة الخرج هي <code>3.0000</code> وليس <code>5.0000</code> مما يفاجئ البعض الذي اعتقد أن القسمة ستكون قسمة أعداد حقيقية فقط لأن متغير من نوع <code>float</code> كان موجودًا في التعليمة.
</p>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_63" style="">
<span class="pun">(</span><span class="typ">float</span><span class="pun">)(</span><span class="pln">j</span><span class="pun">/</span><span class="pln">i</span><span class="pun">)</span></pre>

<p>
	ثم يحوّل النتيجة إلى <code>float</code>، وللمحافظة على باقي القسمة، يجب عليك كتابة التحويل بالطريقة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_65" style="">
<span class="pun">(</span><span class="typ">float</span><span class="pun">)</span><span class="pln">j</span><span class="pun">/</span><span class="pln">i</span></pre>

<p>
	مما سيجبر استخدام القسمة الحقيقية.
</p>

<h2>
	الأقواس
</h2>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_67" style="">
<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">
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">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">
a</span><span class="pun">+(</span><span class="pln">b</span><span class="pun">+</span><span class="pln">c</span><span class="pun">);</span></pre>

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

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

<p>
	على سبيل المثال، لا يمكن للمصرّف أن يعيد كتابة التعبير التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7932_69" style="">
<span class="pln">a </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</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="lit">5</span><span class="pun">;</span></pre>

<p>
	إلى:
</p>

<pre class="ipsCode">
a = 15+a+b;
</pre>

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

<h2>
	الآثار الجانبية
</h2>

<p>
	نعيد ونكرر التحذير الوارد بشأن عوامل الزيادة: ليس من الآمن استخدام المتغير ذاته أكثر من مرة في نفس التعبير، وذلك في حالة كان تقييم التعبير يغيّر من قيمة المتغير ويؤثر على القيمة النهائية للتعبير، وذلك بسبب التغيير (أو التغييرات) "المحفوظة" والمُطبّقة فقط عند الوصول لنهاية التعليمة. على سبيل المثال التعبير <code>f = f+1</code> آمن على الرغم من ظهور المتغير <code>f</code> مرتين في تعبير يغير من قيمتها، والتعبير <code>++f</code> آمن أيضًا، ولكن <code>;f = f++</code> غير آمن.
</p>

<p>
	تنبع المشكلة من استخدام عامل الإسناد أو عوامل الزيادة والنقصان أو استدعاء دالة تغيّر من قيمة متغير خارجي External مُستخدم في تعبير ما، وتُعرف هذه المشاكل باسم "الآثار الجانبية Side Effects"، ولا تحدد سي ترتيب حدوث هذه الآثار الجانبية ضمن تعبير ما (سنتوسّع لاحقًا بخصوص هذه المشكلة، ونناقش "نقاط التسلسل Sequence points").
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter2//" rel="external nofollow">Variables and Arithmetic</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%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="">الثوابت وسلاسل الهروب في لغة سي C</a>
	</li>
	<li>
		المقال السابق: <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="">التحويلات ما بين الأنواع في تعابير لغة سي 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%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="">إدارة الذاكرة (Memory management) في لغة 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%B9%D8%A7%D8%B4%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D8%B1%D8%B7%D9%8A%D8%A9-%D9%88%D8%AD%D9%84%D9%87%D8%A7-%D9%85%D8%B4%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1013/" 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%AD%D8%A7%D8%AF%D9%8A-%D8%B9%D8%B4%D8%B1-%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%AA%D9%82%D9%8A%D9%8A%D8%AF-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-semaphores-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%B3%D9%8A-c-r1014/" rel="">متغيرات تقييد الوصول (Semaphores) في لغة البرمجة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1613</guid><pubDate>Thu, 04 Aug 2022 16:01:01 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x62D;&#x648;&#x64A;&#x644;&#x627;&#x62A; &#x645;&#x627; &#x628;&#x64A;&#x646; &#x627;&#x644;&#x623;&#x646;&#x648;&#x627;&#x639; &#x641;&#x64A; &#x62A;&#x639;&#x627;&#x628;&#x64A;&#x631; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_06/62b047c92fb75_--------C-.png.c1b628550a439fc889519317065fa7c0.png" /></p>

<p>
	سنتعرف في هذا المقال عن كيفية إجراء التحويلات ما بين الأنواع في تعابير لغة سي C
</p>

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

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

<p>
	دعنا نبدأ أولًا ببعض المصطلحات، إذ تُبنى التعابير في لغة سي من مزيجٍ يتكون من <strong>العوامل</strong> و<strong>المُعاملات operands</strong>. لنأخذ على سبيل المثال التعبير التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3222_7" style="">
<span class="pln">x </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></pre>

<p>
	لدينا العوامل <code>=</code> و <code>+</code> و <code>*</code> و <code>-</code>، والمُعاملات التي هي المتغيرات <code>x</code> و <code>a</code> و <code>b</code> و <code>c</code>، كما يمكنك ملاحظة القوسين أيضًا اللذين يمكن استخدامهما في تجميع التعبيرات الجزئية مثل <code>c-</code>. تنقسم معظم مجموعة عوامل لغة سي الواسعة إلى <strong>عوامل ثنائية binary operators</strong> تأخذ مُعاملين، أو <strong>عوامل أحادية unary operators</strong> تأخذ مُعاملًا واحدًا؛ ففي مثالنا كان <code>-</code> مُستخدمًا مثل عاملٍ أحادي، ويؤدي دورًا مختلفًا عن عامل الطرح الثنائي الذي يُمثّل بالرمز ذاته. قد تنظر إلى الفرق بأنه لا يستحق الذكر وأن وظيفة العامل ذاتها أو متشابهة في الحالتين، ولكنه على النقيض تمامًا فإنه يستحقّ الذكر، لأن لبعض العوامل -كما ستجد لاحقًا- شكل ثنائي وآخر أحادي وكلّ وظيفة مختلفة كاملًا عن الأخرى، ويُعد عامل الضرب الثنائي <code>*</code> الذي يعمل عمل الموجّه باستخدام المؤشرات في حالته الأحادية مثالًا جيدًا على ذلك.
</p>

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

<pre class="ipsCode">
x = a+b*-c;
</pre>

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

<h2>
	التحويلات
</h2>

<p>
	تسمح لغة سي بمزج عدّة أنواع ضمن التعبير الواحد، وتسمح أيضًا بالعوامل التي يؤدي استخدامها إلى تحويلات للأنواع ضمنيًا، يصف هذا القسم الطريقة التي تحدث بها هذه التحويلات. ينبغي على مبرمجي لغة سي القديمة (التي سبقت المعيار) قراءة هذا القسم بانتباه، إذ تغيّرت العديد من القواعد وبالأخص التحويل من <code>float</code> إلى <code>double</code> والتحويل من أنواع الأعداد الصحيحة <code>short</code>، كما أن القواعد الأساسية في <strong>حفظ القيم value preserving</strong> قد تغيرت جدًأ في لغة سي المعيارية.
</p>

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

<p>
	إليك أنواع التحويلات المتنوعة في التعابير الحسابية:
</p>

<ul>
<li>
		الترقيات العددية الصحيحة integral promotions.
	</li>
	<li>
		التحويلات بين الأنواع العددية الصحيحة.
	</li>
	<li>
		التحويلات بين الأنواع العددية العشرية.
	</li>
	<li>
		التحويلات ما بين الأنواع العددية الصحيحة والعشرية.
	</li>
</ul>
<p>
	سبق وأن ناقشنا التحويلات بين الأنواع العددية الصحيحة في مقال الأنواع الحقيقية والصحيحة في لغة سي فقرة الأعداد الصحيحة، وما سنفعله في الوقت الحالي هو تحديد طريقة عمل التحويلات الأخرى، ومن ثم سننظر <strong>متى</strong> يجب استخدامها، عليك أن تحفظ هذه التحويلات عن ظهر قلب إذا أردت أن تصبح مبرمجًا بارعًا بلغة سي.
</p>

<p>
	من الأشياء المختلف عليها التي قدمها المعيار، هي قواعد <strong>الحفاظ على القيمة value preserving</strong>، إذ تتطلب معرفةً معينةً من الحاسوب الهدف الذي ينفذ البرنامج من أجل معرفة نوع القيمة الناتجة من التعبير. عندما كنا نصادف في السابق نوعًا عديم الإشارة ضمن تعبير ما، كان هذا يعني ضمانًا بأن القيمة الناتجة من نوع <code>unsigned</code> أيضًا، ولكن في الوقت الحالي النتيجة ستكون من نوع <code>unsigned</code> فقط إذا كان التحويل يتطلب ذلك، وهذا يعني أنه في معظم الحالات ستكون النتيجة من نوع <code>signed</code>.
</p>

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

<h2>
	الترقية العددية الصحيحة
</h2>

<p>
	أقل العمليات الحسابية دقةً في لغة سي هي باستخدام نوع الأعداد الصحيحة <code>int</code>، لذلك تحصل هذه التحويلات ضمنيًّا في كل مرة تُستخدم الكائنات المذكورة في الأسفل ضمن تعبيرٍ ما. التحويل مُعرّف كما يلي:
</p>

<ul>
<li>
		عند تطبيق الترقية العددية الصحيحة إلى نوع <code>short</code> أو <code>char</code> (أو <strong>حقل البِت bitfield</strong> أو <strong>نوع المعدّد enumeration type</strong> الذَين لم نتطرق إليهما بعد):

		<ul>
<li>
				ستُحّول القيمة إلى <code>int</code>، إذا كان من الممكن للمتغير تخزين جميع قيم النوع الأصل.
			</li>
			<li>
				عدا ذلك، ستُحوَّل إلى <code>unsigned int</code>.
			</li>
		</ul>
</li>
</ul>
<p>
	يحفظ هذا التحويل كلًا من القيمة والإشارة الخاصة بالقيمة الأصلية، تذكر أن موضوع معاملة نوع <code>char</code> بإشارة أو دون إشارة يعود إلى التنفيذ.
</p>

<p>
	تُطبَّق هذه الترقيات على نحوٍ متكرر بمثابة جزءٍ من <strong>التحويلات الحسابية الاعتيادية</strong> ومُعاملات عوامل الإزاحة الأحادية، مثل <code>+</code> و <code>-</code> و <code>~</code>، كما تُطبّق عندما يكون التعبير مُستخدمًا مثل وسيط لدالة ما دون أي معلومات عن النوع ضمن نموذج الدالة الأولي function prototype، كما سنشرح لاحقًا.
</p>

<h3>
	الأعداد الصحيحة ذات الإشارة وعديمة الإشارة
</h3>

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

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

<p>
	لا يوجد هناك أي حالات "طفحان overflow" في جميع حالات تحويل عدد صحيح إلى نوع عديم إشارة قصير، فالنتيجة معرّفةٌ وفق "الباقي غير السالب مقسومًا على القيمة العظمى للرقم عديم الإشارة الذي يمكن تمثيله باستخدام النوع القصير زائد واحد". يعني هذا ببساطة أنه في بيئة تعمل بنظام المتمم الثنائي، تُنسخ البِتات منخفضة الترتيب low-order إلى الهدف ويكون التخلُّص من البتات مرتفعة الترتيب high-order.
</p>

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

<p>
	من الممكن أن يكون البند الأخير مثيرًا للقلق بعض الشيء إذا كنت تتذكر الترقية العددية الصحيحة، لأنك قد تنظر إلى الأمر على النحو التالي: إذا أسندت متغيرًا من نوع <code>char</code> إلى متحولٍ من نوع <code>char</code>، فسيُرقّى المتغير على اليمين إلى نوع من أنوع <code>int</code>. إذًا، هل من الممكن أن يؤدي الإسناد إلى تحويل <code>int</code> إلى <code>char</code> (مثلًا) وتفعيل البند "التعريف حسب التنفيذ"؟ الإجابة هي لا، لأن عملية الإسناد لا تضمّ ترقية الأعداد الصحيحة، لذا لا تقلق.
</p>

<h3>
	الأعداد العشرية والصحيحة
</h3>

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

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

<h3>
	التحويلات الحسابية الاعتيادية
</h3>

<p>
	هناك العديد من التعابير التي تتضمن استخدام تعابير فرعية subexpressions تحتوي على خليطٍ من الأنواع مع عوامل، مثل "+" و"*" وما شابه. إذا كانت للمُعاملات ضمن التعبير عدّة أنواع، فهذا يعني أن هناك بعض التحويلات الواجب إجراؤها حتى تكون النتيجة النهائية من نوع معين، والتحويلات هي:
</p>

<ul>
<li>
		إذا كان أي من المُعاملَين من نوع <code>long double</code> يُحوَّل المُعامل الآخر إلى <code>long double</code> ويكون هذا هو نوع النتيجة.
	</li>
	<li>
		ماعدا ذلك، إذا كان أيٌ من المُعاملَين من نوع <code>double</code>، يُحوَّل المُعامل الآخر إلى <code>double</code> ويكون هذا هو نوع النتيجة.
	</li>
	<li>
		ماعدا ذلك، إن كان أي من المُعاملَين من نوع <code>float</code>، يُحوًل المُعامل الآخر إلى <code>float</code> ويكون هذا هو نوع النتيجة.
	</li>
	<li>
		ماعدا ذلك، تُطبّق الترقية العددية الصحيحة لكلا المُعاملين حسب التحويلات التالية:
		<ul>
<li>
				إذا كان أيٌ من المُعاملَين من نوع <code>unsigned long int</code>، يُحوَّل المُعامل الآخر إلى <code>unsigned long int</code> ويكون هذا نوع النتيجة.
			</li>
			<li>
				ماعدا ذلك، إذا كان أيٌ من المُعاملَين من نوع <code>long int</code>، يُحوَّل المُعامل الآخر إلى <code>long int</code> ويكون هذا هو نوع النتيجة.
			</li>
			<li>
				ماعدا ذلك، إذا كان أيٌ من المُعاملَين من نوع <code>unsigned int</code>، يُحوَّل المُعامل الآخر إلى <code>unsigned int</code> ويكون هذا نوع النتيجة.
			</li>
			<li>
				ماعدا ذلك، يجب أن يكون كلا المُعاملين من نوع <code>int</code> وعلى هذا نوع النتيجة أيضًا.
			</li>
		</ul>
</li>
</ul>
<p>
	يتضمن المعيار جملةً غريبة: "يمكن تمثيل قيم المُعاملات من نوع الأعداد العشرية ونتائج تعابيرها بدقة ومجال أكبر من المطلوبة بالنسبة لنوعها، بالتالي لا يحدث تغيير للأنواع". السبب في هذا هو الحفاظ على معاملة لغة سي القديمة للمتغيرات من أنواع الأعداد العشرية، إذ كانت تُرقّى المتغيرات من نوع <code>float</code> في سي القديمة تلقائيًّا إلى <code>double</code> بالطريقة ذاتها التي تُرقّى متغيرات من نوع <code>char</code> إلى <code>int</code>، لذلك يمكن إنجاز التعبير الذي يحوي متغيرات من نوع <code>float</code> فقط كما لو كانت المتغيرات من نوع <code>double</code>، ولكن نوع النتيجة سيكون دائمًا <code>float</code>.
</p>

<p>
	التأثير الوحيد لهذه العملية هو على حساب الأداء، وهو غير مهم لمعظم المستخدمين؛ ويُحدَّد ما إذا كانت التحويلات ستُطبّق أم لا، وأي نوع منها سيطبّق، عند الوصول إلى العامل operator.
</p>

<p>
	لا تسبّب التحويلات بين الأنواع ومزجها أي مشكلات عمومًا، ولكن هناك بعض النقاط التي يجب الانتباه إليها؛ إذ يُعد المزج بين الأنواع ذات الإشارة وعديمة الإشارة بسيطًا إلى أن يحتوي النوع ذو الإشارة قيمة سالبة، إذ لا يمكن تمثيل قيمته باستخدام متغير عديم الإشارة، وعلينا إيجاد حل لهذه المشكلة. ينص المعيار على أن نتيجة تحويل عدد سالب إلى نوع عديم الإشارة هي أكبر قيمة يمكن تخزينها في النوع عديم الإشارة زائد واحد مضافةً إلى العدد السالب، ولأن الطفحان غير ممكن الحدوث في الأنواع عديمة الإشارة فالنتيجة دائمًا معرّفة على المجال. ولنأخذ <code>int</code> بطول 16 بِت مثالًا، إذ أن مجال النوع عديم الإشارة هو 0 إلى 65535، وبتحويل قيمة سالبة (ولتكن "7-") للنوع هذا يجب إضافة 7- إلى 65536 الذي يعطينا الناتج 65529.
</p>

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

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

<p>
	من السهل القول "لا تفعل هذا"، ولكن الأمر قد يحدث عن طريق الخطأ وفي هذه الحالة ستكون النتائج مفاجئة <strong>جدًا</strong>. ألقِ نظرةً على المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3222_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">
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">unsigned</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> stop_val</span><span class="pun">;</span><span class="pln">

      stop_val </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">=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">10</span><span class="pun">;</span><span class="pln">

      </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">i </span><span class="pun">&lt;=</span><span class="pln"> stop_val</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"> i</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"> </span><span class="lit">1</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;">
	[مثال 7.2]
</p>

<p>
	ربما تتوقع أن يطبع البرنامج لائحة قيم من "10-" إلى "0"، لكن هذا خاطئ، إذ تكمن المشكلة هنا في الموازنة؛ أي يُوازَن المتغير <code>i</code> الذي يخزن القيمة <code>10-</code> مع متغير عديم الإشارة يخزن القيمة <code>0</code>، ووفقًا لقواعد الحساب (استذكرها إن أردت) يجب أن نحوّل كلا النوعين إلى <code>unsigned int</code> أوّلًا ومن ثم نجري الموازنة، وتصبح القيمة <code>10-</code> مساوية <code>65526</code> على الأقل (تفقد ملف الترويسة <code>&lt;limits.h&gt;</code>) بعد تحويلها، وتُوازن فيما بعد مع <code>0</code> وهي أكبر من القيمة كما هو واضح، وبذلك لا تُنفّذ الحلقة التكرارية إطلاقًا. العبرة هنا هو أنه عليك تجنُّب استخدام الأعداد عديمة الإشارة إلا في حالة استخدامك المقصود لها، وعندما تستخدمها انتبه جيّدًا بخصوص مزجها مع الأعداد ذات الإشارة.
</p>

<h3>
	المحارف العريضة
</h3>

<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="">المحارف</a> الموسّعة، إذ يمكنك استخدام ترميز الإدخال بالإزاحة shift-in والإخراج بالإزاحة shift-out، التي تسمح بتخزين المحارف متعددة البايتات في سلاسل نصية اعتيادية في لغة سي، والتي في حقيقة الأمر مصفوفات من نوع <code>char</code> كما سنتعرف لاحقًا؛ أو يمكنك استخدام التمثيل الذي يستخدم أكثر من بايت واحد لتخزين كل محرف من المحارف. يمكننا استخدام سلاسل الإزاحة فقط في حالة معالجة المحارف بترتيب محدد، إذ إن الطريقة عديمة الفائدة في حال أردت إنشاء مصفوفة محارف والوصول إليهم بغض النظر عن ترتيبهم. إليك مثالًا استخدمناه سابقًا مضافًا إليه أدلة indexes مصفوفة منطقية وفعلية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3222_12" style="">
<span class="lit">0</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">2</span><span class="pln">  </span><span class="lit">3</span><span class="pln">   </span><span class="lit">4</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="lit">6</span><span class="pln">  </span><span class="lit">7</span><span class="pln">   </span><span class="lit">8</span><span class="pln"> </span><span class="lit">9</span><span class="pln"> </span><span class="pun">(</span><span class="pln">actual array index</span><span class="pun">)</span><span class="pln">
a b c </span><span class="pun">&lt;</span><span class="pln">SI</span><span class="pun">&gt;</span><span class="pln"> a b g </span><span class="pun">&lt;</span><span class="pln">SO</span><span class="pun">&gt;</span><span class="pln"> x y
</span><span class="lit">0</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">2</span><span class="pln">      </span><span class="lit">3</span><span class="pln"> </span><span class="lit">4</span><span class="pln"> </span><span class="lit">5</span><span class="pln">      </span><span class="lit">6</span><span class="pln"> </span><span class="lit">7</span><span class="pln"> </span><span class="pun">(</span><span class="pln">logical index</span><span class="pun">)</span></pre>

<p>
	حتى لو استطعنا الوصول إلى المُدخلة "الصالحة correct" ذات الدليل "5" في المصفوفة، فلن تنتهِ المشكلة، إذ لا يمكن تمييز النتيجة التي حصلنا عليها إن كانت مرمّزة أو هي "g" حرفيًّا. الحل الواضح لهذه المشكلة هو استخدام قيم مميّزة لجميع المحارف في مجموعة المحارف التي نستخدمها، لكن هذا الأمر يتطلب مزيدًا من البتات الموجودة في <code>char</code> اعتيادي، وأن نكون قادرين على تخزين كل قيمة على نحوٍ منفصل دون استخدام تقنية الإزاحة أو أي تقنية تعتمد على موضع القيم، وهذا هو الغرض من استخدام النوع <code>wchar_t</code>؛ إذ يُعدّ هذا النوع مرادفًا لأنواع الأعداد الصحيحة الأخرى (يمكنك الاطلاع على تعريفه في ملف الترويسة "<stddef.h>")، وهو نوعٌ معرّفٌ حسب التنفيذ ويُستخدم في تخزين المحارف الموسعة عندما تريد إنشاء مصفوفة منها. يضمن المعيار التفاصيل التالية المتعلقة بقيمة المحارف العريضة:</stddef.h></p>

<ul>
<li>
		يستطيع المتغير من نوع <code>wchar_t</code> تخزين قيم فريدة لكل محرف من أكبر مجموعة محارف يدعمها التنفيذ.
	</li>
	<li>
		المحرف الفارغ null قيمته الصفر.
	</li>
	<li>
		تماثل قيمة ترميز كل محرف من مجموعة المحارف الأساسية (ألقِ نظرةً على مقال المحارف المُستخدمة في لغة C فقرة الأبجدية الاعتيادية) في النوع <code>wchar_t</code> القيمة المُخزنة في <code>char</code>.
	</li>
</ul>
<p>
	هناك دعم أكبر لطريقة ترميز المحارف هذه، مثل السلاسل النصية strings التي تكلمنا عنها سابقًا، إذ تُنفَّذ على أنها مصفوفة من المحارف <code>char</code>، مع أن قيمتها تبدو على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3222_14" style="">
<span class="str">"a string"</span></pre>

<p>
	للحصول على سلاسل نصية من نوع <code>wchar_t</code>، اكتب السلسلة النصية كما هي مسبوقة بالحرف <code>L</code>، على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3222_16" style="">
<span class="pln">L</span><span class="str">"a string"</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3222_18" style="">
<span class="str">"a string"</span><span class="pun">[</span><span class="lit">4</span><span class="pun">]</span><span class="pln">
L</span><span class="str">"a string"</span><span class="pun">[</span><span class="lit">4</span><span class="pun">]</span></pre>

<p>
	كلا التعبيرين السابقين صالح، إذ أن التعبير الأول من نوع <code>char</code> وقيمته ممثلةٌ بالحرف <code>r</code> (تذكر أن دليل المصفوفات يبدأ من صفر وليس واحد)، والتعبير الثاني من نوع <code>wchar_t</code> وقيمته ممثلةٌ أيضًا بالحرف <code>r</code>.
</p>

<p>
	يصبح الأمر مثيرًا للاهتمام عند استخدامنا للمحارف الموسعة، إذ تظهر لنا بعض المشكلات إذا استخدمنا الترميز <code>&lt;a&gt;</code> و <code>&lt;b&gt;</code> للدلالة على محارف "إضافية" عن مجموعة المحارف الاعتيادية، أي ترميز هذه المحارف باستخدام تقنية إزاحة ما، لاحظ المثالين:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3222_20" style="">
<span class="str">"abc&lt;a&gt;&lt;b&gt;"</span><span class="pun">[</span><span class="lit">3</span><span class="pun">]</span><span class="pln">
L</span><span class="str">"abc&lt;a&gt;&lt;b&gt;"</span><span class="pun">[</span><span class="lit">3</span><span class="pun">]</span></pre>

<p>
	الحالة الثانية سهلة الفهم، فهي مصفوفةٌ من نوع <code>wchar_t</code> والترميز الموافق لها يبدأ بالمحرف <code>&lt;a&gt;</code> أيًّا كان هذا الترميز (لنفترض أنه ترميز إلى الحرف اليوناني الموافق)؛ أما الحالة الأولى فهي غير ممكنة التنبؤ، إذ أن النوع هو <code>char</code> بلا شك لكن قيمته هي وسم الإدخال بالإزاحة غالبًا.
</p>

<p>
	كما هو الحال مع السلاسل النصية، هناك ثوابت محارف عريضة، مثل 'a' التي لها نوع <code>char</code> وقيمة الترميز متجاوبة مع قيمة المحرف a، أما المحرف التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3222_22" style="">
<span class="pln">L</span><span class="str">'a'</span></pre>

<p>
	فهو ثابت من نوع <code>wchar_t</code>، وعند استخدام المحارف متعددة البايتات في المثال الذي سبقه، فهذا يعني أن قيمته تساوي محارف متعددة في محرف ثابت واحد على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3222_24" style="">
<span class="str">'xy'</span></pre>

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

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

<h3>
	التحويل بين الأنواع
</h3>

<p>
	في بعض الأحيان، ينتج نوع بيانات من تعبير ما ولكنك لا تريد استخدام هذا النوع، وتريد تحويله قسريًّا إلى نوع مختلف، وهذا هو الغرض من <strong>التحويل بين الأنواع casts</strong>. عند وضع اسم النوع بين قوسين على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3222_26" style="">
<span class="pun">(</span><span class="typ">int</span><span class="pun">)</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3222_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">/*
* Illustrates casts.
* For each of the numbers between 2 and 20,
* print the percentage difference between it and the one
* before
*/</span><span class="pln">
main</span><span class="pun">(){</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> curr_val</span><span class="pun">;</span><span class="pln">
      </span><span class="typ">float</span><span class="pln"> temp</span><span class="pun">,</span><span class="pln"> pcnt_diff</span><span class="pun">;</span><span class="pln">

      curr_val </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">curr_val </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">20</span><span class="pun">){</span><span class="pln">
              </span><span class="com">/*
               * % difference is
               * 1/(curr_val)*100
               */</span><span class="pln">
              temp </span><span class="pun">=</span><span class="pln"> curr_val</span><span class="pun">;</span><span class="pln">
              pcnt_diff </span><span class="pun">=</span><span class="pln"> </span><span class="lit">100</span><span class="pun">/</span><span class="pln">temp</span><span class="pun">;</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"Percent difference at %d is %f\n"</span><span class="pun">,</span><span class="pln">
                      curr_val</span><span class="pun">,</span><span class="pln"> pcnt_diff</span><span class="pun">);</span><span class="pln">
              </span><span class="com">/*
               * Or, using a cast:
               */</span><span class="pln">
              pcnt_diff </span><span class="pun">=</span><span class="pln"> </span><span class="lit">100</span><span class="pun">/(</span><span class="typ">float</span><span class="pun">)</span><span class="pln">curr_val</span><span class="pun">;</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"Percent difference at %d is %f\n"</span><span class="pun">,</span><span class="pln">
                      curr_val</span><span class="pun">,</span><span class="pln"> pcnt_diff</span><span class="pun">);</span><span class="pln">
              curr_val </span><span class="pun">=</span><span class="pln"> curr_val </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</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;">
	[مثال 8.2]
</p>

<p>
	الطريقة الأسهل لتتذكر الاستخدام الصحيح للتحويل بين الأنواع هو كتابته وكأنك تُصرّح عن متحول من نوع تريده، ومن ثم ضع الأقواس حول التصريح بالكامل واحذف اسم المتغير، مما سيعطيك التحويل بين الأنواع. يوضح الجدول 6.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>
		</tr></thead>
<tbody>
<tr>
<td>
				<code>;int x</code>
			</td>
			<td>
				<code>(int)</code>
			</td>
			<td>
				<code>int</code>
			</td>
		</tr>
<tr>
<td>
				<code>;float f</code>
			</td>
			<td>
				<code>(float)</code>
			</td>
			<td>
				<code>float</code>
			</td>
		</tr>
<tr>
<td>
				<code>;char x[30]</code>
			</td>
			<td>
				<code>(char [30])</code>
			</td>
			<td>
				مصفوفة من <code>char</code>
			</td>
		</tr>
<tr>
<td>
				<code>;int *ip</code>
			</td>
			<td>
				<code>(* int)</code>
			</td>
			<td>
				مؤشر إلى <code>int</code>
			</td>
		</tr>
<tr>
<td>
				<code>;()int (*f)</code>
			</td>
			<td>
				<code>(() (*) int)</code>
			</td>
			<td>
				مؤشر لدالة تُعيد النوع <code>int</code>
			</td>
		</tr>
</tbody>
</table>
<p style="text-align: center;">
	[جدول 6.2. التحويل بين الأنواع]
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter2//" rel="external nofollow">Variables and Arithmetic</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%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%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="">الأنواع الحقيقية والصحيحة في لغة سي 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%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="">إدارة الذاكرة (Memory management) في لغة 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%B9%D8%A7%D8%B4%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D8%B1%D8%B7%D9%8A%D8%A9-%D9%88%D8%AD%D9%84%D9%87%D8%A7-%D9%85%D8%B4%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1013/" 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%AD%D8%A7%D8%AF%D9%8A-%D8%B9%D8%B4%D8%B1-%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%AA%D9%82%D9%8A%D9%8A%D8%AF-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-semaphores-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%B3%D9%8A-c-r1014/" rel="">متغيرات تقييد الوصول (Semaphores) في لغة البرمجة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1612</guid><pubDate>Sun, 31 Jul 2022 16:03:02 +0000</pubDate></item><item><title>&#x627;&#x644;&#x623;&#x646;&#x648;&#x627;&#x639; &#x627;&#x644;&#x62D;&#x642;&#x64A;&#x642;&#x64A;&#x629; &#x648;&#x627;&#x644;&#x635;&#x62D;&#x64A;&#x62D;&#x629; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_06/62af75e1a5eab_------C-.png.ea27bb91de4d60bcc7f36e2eed06f2e2.png" /></p>

<p>
	سنتطرق في هذا المقال إلى الأنواع الحقيقية والصحيحة في لغة سي C
</p>

<h2>
	الأنواع الحقيقية
</h2>

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

<p>
	أنواع الأعداد الحقيقية هي:
</p>

<ul>
<li>
		<code>float</code>: العدد العشري
	</li>
	<li>
		<code>double</code>: العدد العشري مضاعف الدقة
	</li>
	<li>
		<code>long double</code>: العدد العشري الأدق
	</li>
</ul>
<p>
	يسمح لنا كل واحد من هذه الأنواع بتمثيل الأعداد الحقيقية بطريقة معينة باستخدام الحاسوب؛ فإذا كان هناك نوعٌ واحدٌ لتمثيل الأعداد الحقيقية، فهذا يعني أن <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%AE%D8%A7%D9%85%D8%B3-%D8%AA%D9%85%D8%AB%D9%8A%D9%84-%D8%A7%D9%84%D8%A3%D8%B9%D8%AF%D8%A7%D8%AF-%D9%88%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D8%A8%D8%A7%D9%84%D8%A8%D8%AA%D8%A7%D8%AA-%D9%88%D8%A5%D8%AC%D8%B1%D8%A7%D8%A1-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D8%B9%D9%84%D9%89-%D9%85%D8%B3%D8%AA%D9%88%D9%89-%D8%A7%D9%84%D8%A8%D8%AA-r993/" rel="">تمثيل الأعداد</a> سيكون متماثلًا بغض النظر عن الاستخدام؛ أما إذا كان العدد يتجاوز الثلاثة أنوع، فهذا يعني أن لغة سي لن تستطيع تصنيف أي من الأنواع الإضافية. يُستخدم النوع <code>float</code> للتمثيل السريع والبسيط للأرقام الصغيرة وهو مشابهٌ للنوع <code>REAL</code> في لغة فورتران؛ أما <code>double</code> فيستخدم للدقة الإضافية، و <code>long double</code> لدقة أكبر من سابقتها.
</p>

<p>
	التركيز الأساسي هنا هو أنّ الزيادة في "دقة" كلٍ من <code>float</code> و <code>double</code> و <code>long double</code> تعطي لكل نوعٍ نطاقًا ودقّة مساويةً للنوع الذي يسبقها على الأقل، فأخذ القيمة من متغير نوع <code>double</code> مثلًا، وتخزينها في متغير من نوع <code>long double</code>، يجب أن يمثِّل القيمة ذاتها.
</p>

<p>
	لا توجد هناك أي متطلبات للأنواع الثلاثة من متغيرات الأعداد "الحقيقية" لتختلف في خصائصها، وبالتالي إن لم توفّر الآلة سوى نوع واحدٍ من أنواع متغيرات الأعداد الحقيقية، فيمكن عندئذٍ تمثيل جميع أنواع الأعداد الحقيقية الثلاثة في لغة سي بهذا النوع المتوفِّر. لكن بغض النظر، يجب أن يُنظر إلى هذه الأنواع الثلاثة بأنها مختلفة، وكأنّ هناك فرقٌ بينها حقًأ، وهذا يساعد في نقل البرنامج إلى نظام تختلف فيه هذه الأنواع حقًّا، بحيث لن يظهر لك مجموعةٌ من التحذيرات من المصرّف بخصوص عدم توافق الأنواع التي لم تكُن موجودةً على النظام الأول.
</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>التحويلات الحسابية الاعتيادية usual arithmetic conversions</strong>، ومن المفيد أن تتذكرها، إذ سنشرح كامل هذه القواعد لاحقًا، إلا أننا سننظر في الوقت الحالي إلى الحالات التي تتضمن مزيجًا من <code>float</code> و <code>double</code> و <code>long double</code> ونحاول فهمها.
</p>

<p>
	الحالة الوحيدة التي نحتاج فيها إجراء التحويلات المذكورة هي عندما يُمزج نوعان من البيانات في تعبير، كما في هو موضح في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4768_8" style="">
<span class="typ">int</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="typ">float</span><span class="pln"> f_var</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">double</span><span class="pln"> d_var</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> l_d_var</span><span class="pun">;</span><span class="pln">

        f_var </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> d_var </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> l_d_var </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
        d_var </span><span class="pun">=</span><span class="pln"> d_var </span><span class="pun">+</span><span class="pln"> f_var</span><span class="pun">;</span><span class="pln">
        l_d_var </span><span class="pun">=</span><span class="pln"> d_var </span><span class="pun">+</span><span class="pln"> f_var</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">(</span><span class="pln">l_d_var</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<p>
	نلاحظ في المثال السابق وجود كثيرٍ من التحويلات القسرية، لنبدأ بأسهلها أوّلًا، ولننظر إلى تعيين القيمة الثابتة <code>1</code> لكلٍ من المتغيرات الثلاثة. لا بُد من التنويه (كما سيشير القسم الذي يتكلم عن القيم الثابتة constants لاحقًا) إلى أن القيمة <code>1</code> هي من نوع <code>int</code>، أي تمثّل عددًا صحيحًا وليس قيمةً ثابتةً حقيقية، ويحوّل الإسناد قيمة العدد الصحيح إلى نوع العدد الحقيقي المناسب والأسهل للتعامل معه.
</p>

<p>
	التحويلات المثيرة للاهتمام تأتي بعدها، وأولها ضمن السطر التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4768_10" style="">
<span class="pln">d_var </span><span class="pun">=</span><span class="pln"> d_var </span><span class="pun">+</span><span class="pln"> f_var</span><span class="pun">;</span></pre>

<p>
	ما هو نوع التعبير الذي يتضمن العامل <code>+</code>؟ الإجابة عن هذا السؤال سهلة ما دمت ملمًّا ببعض القواعد؛ إذ يُحوَّل النوع الأقل دقةً ضمنيًا إلى النوع الأكثر دقةً وتُنجز العملية الحسابية باستخدام هذه الدقة، وذلك عندما يجتمع نوعان من الأعداد الحقيقية في التعبير ذاته. يتضمن المثال السابق استخدام كلٍّ من <code>double</code> و <code>float</code>، لذلك تُحوّل قيمة المتغير <code>f_var</code> إلى النوع <code>double</code> وتُضاف فيما بعد إلى قيمة النوع <code>double</code> أي المتغير <code>d_var</code>، وتكون نتيجة هذا التعبير هي من نوع <code>double</code> أيضًا، لذا من الواضح أن عملية الإسناد إلى المتغير <code>d_var</code> صائبة.
</p>

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

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

<p>
	لا ضرر من تكرار فكرتنا السابقة، إذ يقصد المعيار بحالة <strong>السلوك غير المحدد undefined behaviour</strong> معنى اسمه حرفيًا، وحالما يدخل البرنامج منطقة السلوك غير المحدد، يمكن لأي شيء أن يحدث؛ فمن الممكن إيقاف البرنامج من طرف <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>

<ul>
<li>
		تُجرى العمليات الحسابية التي تتضمن نوعين باستخدام النوع الأعلى دقّة منهما.
	</li>
	<li>
		قد يتضمن الإسناد خسارة لدقة القيمة في حال كان نوع المتغير الهدف ذو دقة أقل من دقة القيمة التي تُسنَد لهذا المتغير.
	</li>
	<li>
		هناك مزيدٌ من التحويلات التي تُجرى عند مزج الأنواع ضمن تعبير واحد، إذ لم نصِف جميعها بعد.
	</li>
</ul>
<h2>
	طباعة الأعداد الحقيقية
</h2>

<p>
	يمكن استخدام دالة الخرج التقليدي <code>printf</code> لتنسيق الأعداد الحقيقية وطباعتها، كما يوجد العديد من الطرق لتنسيق هذه الأعداد، ولكننا سنتطرق إلى طريقة واحدة في الوقت الحالي. يوضح الجدول 4.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>
		</tr></thead>
<tbody>
<tr>
<td>
				<code>float</code>
			</td>
			<td>
				<code>f%</code>
			</td>
		</tr>
<tr>
<td>
				<code>double</code>
			</td>
			<td>
				<code>f%</code>
			</td>
		</tr>
<tr>
<td>
				<code>long double</code>
			</td>
			<td>
				<code>Lf%</code>
			</td>
		</tr>
</tbody>
</table>
<p style="text-align: center;">
	[جدول 4.2. رموز التنسيق للأعداد الحقيقية]
</p>

<p>
	ألقِ نظرةً على المثال التالي لتجربة المعلومة السابقة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4768_15" 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"> BOILING </span><span class="lit">212</span><span class="pln">     </span><span class="com">/* degrees Fahrenheit */</span><span class="pln">

main</span><span class="pun">(){</span><span class="pln">
      </span><span class="typ">float</span><span class="pln"> f_var</span><span class="pun">;</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> d_var</span><span class="pun">;</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> l_d_var</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">

      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="str">"Fahrenheit to Centigrade\n"</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">i </span><span class="pun">&lt;=</span><span class="pln"> BOILING</span><span class="pun">){</span><span class="pln">
              l_d_var </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">*(</span><span class="pln">i</span><span class="pun">-</span><span class="lit">32</span><span class="pun">);</span><span class="pln">
              l_d_var </span><span class="pun">=</span><span class="pln"> l_d_var</span><span class="pun">/</span><span class="lit">9</span><span class="pun">;</span><span class="pln">
              d_var </span><span class="pun">=</span><span class="pln"> l_d_var</span><span class="pun">;</span><span class="pln">
              f_var </span><span class="pun">=</span><span class="pln"> l_d_var</span><span class="pun">;</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"%d %f %f %Lf\n"</span><span class="pun">,</span><span class="pln"> i</span><span class="pun">,</span><span class="pln">
                      f_var</span><span class="pun">,</span><span class="pln"> d_var</span><span class="pun">,</span><span class="pln"> l_d_var</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="lit">1</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;">
	[مثال 2.2]
</p>

<p>
	جرّب المثال السابق على حاسوبك الشخصي، ولاحظ النتائج.
</p>

<h2>
	الأنواع الصحيحة
</h2>

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

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

<h3>
	الأعداد الصحيحة البسيطة
</h3>

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

<p>
	عندما تبحث عن نوع بيانات بسيط لتمثيل عدد صحيح، فإن النوع <code>int</code> هو الاختيار البديهي لأي استخدام مُتساهل، مثل عدّاد ضمن حلقة تكرارية قصيرة، إذ لا توجد هناك أي قاعدة تحدّد عدد البتّات التي يخزنها نوع <code>int</code> لقيمةٍ ما، لكنه سيكون <strong>دائمًا</strong> مساويًا إلى 16 بِت أو أكثر، ويفصِّل ملف الترويسة القياسي "<limits.h>" العدد الفعلي للبتات المُتاحة في تنفيذٍ معين.</limits.h></p>

<p>
	لم تحتوِ لغة سي القديمة على أية معلومات بخصوص طول متغيّر من نوع <code>int</code>، ولكن الجميع كان يفترض اصطلاحًا أنها على الأقل 16 بِت. في الحقيقة، لا يحدّد ملف الترويسة "<limits.h>" العدد الدقيق للبتات، ولكنه يقدِّم تقديرًا لأعظم عدد وأقل عدد بتات لقيمةٍ في متغيرٍ من نوع <code>int</code>، والقيم التي يحددها هي ما بين 32767 و32767- أي 16 بت فما فوق، سواءٌ كانت عملية المتمم الأحادي أو الثنائي الحسابية مستخدمةُ أم لا، وبالطبع لا يوجد هناك أي قيود من توفير نطاق أكبر في أي الطرفين في حال توفرت الطريقة المناسبة.</limits.h></p>

<p>
	يتراوح النطاق المحدد وفق المعيار للمتغير من نوع <code>unsigned int</code> من 0 إلى 65535، مما يعني أن القيمة لا يمكن أن تكون سالبة، وسنتكلم بإسهاب عن هذه النقطة لاحقًا.
</p>

<p>
	إذا لم تعتَد التفكير بعدد البتات لمتغيّر ما، وبدأت بالقلق عمّا إذا سيؤثر ذلك على قابلية نقل البرنامج كون هذه المشكلة مرتبطةً بوضوح بالآلة (أي الحاسوب الذي يشغّل البرنامج)، فقلقك في محلّه. تأخذ لغة سي قابلية نقل البرنامج على محمل الجدّ كما تدلّك على القيم والمجالات الآمنة، وتشجِّعك أيضًا عوامل العوامل الثنائية bitwise operators على التفكير بعدد البتّات في متغيرٍ ما، لأنها تمنحك الوصول المباشر إلى بتات المتغيرالتي تعالجها بصورةٍ منفردة (كل بت على حدى) أو في مجموعات. ونتيجة لذلك تكوَّن لدى مبرمجي لغة سي المعرفة الكافية بخصوص مشكلات قابلية نقل البرنامج، ممّا يتسبب ببرمجة برامج قابلة للنقل، لكننا <strong>لا ننفي</strong> هنا إمكانية كتابة برامج غير قابلة للنقل إطلاقًا.
</p>

<h3>
	متغيرات المحارف
</h3>

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

<p>
	يصف ملف ترويسة الحدود "limits" ثلاثة أشياء عن النوع <code>char</code> ألا وهي:
</p>

<ul>
<li>
		عدد البتات 8 على الأقل.
	</li>
	<li>
		يمكنها تخزين قيمة 127+ على الأقل.
	</li>
	<li>
		القيمة الدنيا للنوع <code>char</code> هي صفر أو أقل، مما يعني أن المجال يتراوح ما بين 0 إلى 127.
	</li>
</ul>
<p>
	يحدِّد تنفيذ المتحول <code>char</code> فيما إذا كان سيتصرف تصرُّف المتحولات ذات الإشارة <code>signed</code> أو عديمة الإشارة <code>unsigned</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4768_17" style="">
<span class="pln">include </span><span class="str">&lt;limits.h&gt;</span><span class="pln">
include </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
include </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">char</span><span class="pln"> c</span><span class="pun">;</span><span class="pln">

      c </span><span class="pun">=</span><span class="pln"> CHAR_MIN</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"> CHAR_MAX</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"> c</span><span class="pun">);</span><span class="pln">
              c </span><span class="pun">=</span><span class="pln"> c</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">

      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.2]
</p>

<p>
	تشغيل البرنامج في المثال السابق تمرينٌ لك، وربما ستثير النتائج إعجابك. إذا كنت تتسائل عن قيمة <code>CHAR_MIN</code> و<code>CHAR_MAX</code>، فاطّلع على ملف الترويسة <code>limits.h</code> واقرأه.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4768_19" style="">
<span class="str">'x'</span></pre>

<p>
	لأن القواعد الحسابية تُطبَّق هنا، فسيُحوّل المحرف الثابت السابق ليكون من النوع <code>int</code>، ولكن هذا لا يهم حقًّا لأن قيمة المحرف صغيرة دائمًا ويمكن تخزينها في متغير من نوع <code>char</code> دون فقدان أي دقة (لسوء الحظ هناك بعض الحالات التي لا ينطبق فيها هذا الكلام، تجاهلها في الوقت الحالي). عندما يُطبع محرف باستخدام الرمز <code>c%</code> ضمن دالة <code>printf</code>، يُطبع المحرف كما هو، لكن يمكنك استخدام الرمز <code>d%</code> إذا أردت طباعة قيمة العدد الصحيح الموافقة لهذا المحرف. لماذا استُخدم الرمز <code>d%</code>؟ كما ذكرنا سابقًا، النوع <code>char</code> هو في الحقيقة نوع من أنواع الأعداد الصحيحة.
</p>

<p>
	من المهم أيضًا وجود طريقة لقراءة المحارف إلى البرنامج، وتتكفل الدالة <code>getchar</code> بهذه المهمة، إذ تقرأ المحارف من <strong>الدخل القياسي standard input</strong> للبرنامج وتُعيد قيمةً صحيحةً <code>int</code> موافقة لتخزين هذا المحرف في متغير من نوع <code>char</code>، تخدم هذه القيمة المُمرّرة من نوع <code>int</code> غرضين، هما: تمثيل جميع قيم المحارف الممكنة بواسطتها، إضافةً إلى تمرير قيمة <strong>إضافية</strong> للدلالة على نهاية الدخل. لا يتسع مجال قيم متغير من نوع <code>char</code> في جميع الحالات لهذه القيمة الإضافية، لذلك يُستخدم النوع <code>int</code>.
</p>

<p>
	يقرأ البرنامج التالي الدخل ويعدّ الفواصل والنقاط المُدخلة، وعند وصولة لنهاية الدخل يطبع النتيجة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4768_21" 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"> this_char</span><span class="pun">,</span><span class="pln"> comma_count</span><span class="pun">,</span><span class="pln"> stop_count</span><span class="pun">;</span><span class="pln">

      comma_count </span><span class="pun">=</span><span class="pln"> stop_count </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
      this_char </span><span class="pun">=</span><span class="pln"> getchar</span><span class="pun">();</span><span class="pln">
      </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">this_char </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">this_char </span><span class="pun">==</span><span class="pln"> </span><span class="str">'.'</span><span class="pun">)</span><span class="pln">
                      stop_count </span><span class="pun">=</span><span class="pln"> stop_count</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">this_char </span><span class="pun">==</span><span class="pln"> </span><span class="str">','</span><span class="pun">)</span><span class="pln">
                      comma_count </span><span class="pun">=</span><span class="pln"> comma_count</span><span class="pun">+</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
              this_char </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">
      printf</span><span class="pun">(</span><span class="str">"%d commas, %d stops\n"</span><span class="pun">,</span><span class="pln"> comma_count</span><span class="pun">,</span><span class="pln">
                      stop_count</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.2]
</p>

<p>
	هناك ميزتان نستطيع ملاحظتهما من المثال السابق، الأولى هي الإسناد المتعدّد للعدادين، والثانية هي استخدام الثابت المعرّف <code>EOF</code>؛ وهي قيمة تُمرّر من الدالة <code>getchar</code> في نهاية الدخل وتمثِّل اختصارًا لكلمة نهاية الملف End Of File، وتكون معرفةً ضمن ملف الترويسة "<stdio.h>"؛ أما الإسناد المتعدد فهي ميزةٌ شائعةٌ الاستخدام في <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>.</stdio.h></p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4768_24" 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">char</span><span class="pln"> c</span><span class="pun">;</span><span class="pln">

      c </span><span class="pun">=</span><span class="pln"> </span><span class="str">'a'</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">&lt;=</span><span class="pln"> </span><span class="str">'z'</span><span class="pun">){</span><span class="pln">
              printf</span><span class="pun">(</span><span class="str">"value %d char %c\n"</span><span class="pun">,</span><span class="pln"> c</span><span class="pun">,</span><span class="pln"> c</span><span class="pun">);</span><span class="pln">
              c </span><span class="pun">=</span><span class="pln"> c</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">

      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.2]
</p>

<p>
	يذكرنا هذا المثال مرةً أخرى بأن <code>char</code> شكلٌ مختلفٌ من أشكال متغيرات الأعداد الصحيحة ويمكن استخدامه مثل أي عدد صحيح آخر، فهو <strong>ليس</strong> نوع مميّز بقواعد مختلفة.
</p>

<p>
	تصبح المساحة التي يوفرها <code>char</code> موازنةً مع <code>int</code> ملحوظةً ومهمةً عندما يُستخدام الكثير من المتغيرات. تستخدم معظم عمليات معالجة المحارف مصفوفات كبيرة منها وليس محرفًا واحدًا أو اثنين فقط، وفي هذه الحالة يصبح الفرق واضحًا بين الاثنين. لنتخيل سويًّا مصفوفةً مؤلفةً من 1024 متغيرًا من نوع <code>int</code>، تحجز هذه المصفوفة مساحة 4098 بايت (كل بايت 8-بت) من التخزين على معظم الآلات، على افتراض أن طول كل <code>int</code> هو 4 بايت؛ فإذا كانت معمارية الحاسوب تسمح بتخزين هذه المعلومات بطريقة فعّالة، قد تطبّق لغة سي هذا عن طريق متغيرات من نوع <code>char</code> بحيث يأخذ كل متغير بايتًا واحدًا، وبذلك ستأخذ المصفوفة مساحة 1024 بايت، مما سيوفّر مساحة 3072 بايت.
</p>

<p>
	لا يهمنا في بعض الحالات إن كان سيوفِّر البرنامج مساحةً أم لا، ولكنه يوفِّرها بغض النظر، ومن الجيد أن تعطينا لغة سي فرصة اختيار نوع المتغير المناسب لاستخدامنا.
</p>

<h3>
	المزيد من الأنواع المعقدة
</h3>

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

<p>
	تملك الأنواع عديمة الإشارة خاصيةً مميزة ألا وهي أنها ليست عرضةً للطفحان الحسابي overflowing عند إجراء العمليات الحسابية، إذ سيتسبب إضافة 1 إلى متغيرٍ من نوعٍ ذي إشارة يخزّن أكبر قيمة يمكن تخزينها بحدوث طفحان، ويصبح سلوك البرنامج نتيجةً لذلك غير محدّد، ولا يحصل هذا الأمر مع المتغيرات من نوعٍ عديم الإشارة، لأنّها تعمل وفق "باقي قسمة واحد زائد القيمة العظمى التي يمكن للمتغير تخزينها على هذه القيمة"، أي باقي قسمة "max+1)/max)"، والمثال التالي يوضح ما نقصده:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4768_26" 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">unsigned</span><span class="pln"> </span><span class="typ">int</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="lit">0</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">x </span><span class="pun">&gt;=</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">"%u\n"</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"> x</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">

      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.2]
</p>

<p>
	بفرض أن المتغير <code>x</code> يحتل مساحة 16 بِت، فهذا يعني أن مجال قيمته يترواح بين 0 و 65535، وأن الحلقة التكرارية في المثال ستتكرر لأجل غير مسمّى، إذ أن الشرط التالي محققٌ دائمًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4768_28" style="">
<span class="pln">x </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span></pre>

<p>
	وذلك بالنسبة لأي متغير عديم الإشارة.
</p>

<p>
	يوجد ثلاثة أنواع فرعية لكلٍ من الأعداد الصحيحة ذات الإشارة وعديمة الإشارة، هي: <code>short</code> والنوع الاعتيادي و <code>long</code>، ونستطيع بعد أخذ هذه المعلومة بالحسبان كتابة لائحة بجميع أنواع متغيرات الأعداد الصحيحة في لغة سي باستثناء نوع تخزين المحرف <code>char</code>، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4768_30" style="">
<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">unsigned</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">long</span><span class="pln"> </span><span class="typ">int</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="pln">
</span><span class="kwd">signed</span><span class="pln"> </span><span class="typ">int</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></pre>

<p>
	ليس مهمًّا استخدام الكلمة المفتاحية <code>signed</code> ويمكن الاستغناء عنها في الأنواع الثلاث الأخيرة، إذ أن نوع <code>int</code> ذو إشارة افتراضيًا، ولكن <strong>ينبغي عليك</strong> استخدام الكلمة المفتاحية <code>unsigned</code> إذا أردت الحصول على نتيجة مغايرة لذلك. من الممكن أيضًا التخلِّي عن الكلمة المفتاحية <code>int</code> من أي تعليمة تصريح شرط أن تحتوي على كلمة مفتاحية أخرى، مثل <code>long</code> أو <code>short</code>، وسيُفهم المتغير على أنه <code>int</code> ضمنيًّا ولكنه أمرٌ غير محبّذ، على سبيل المثال الكلمة المفتاحية <code>long </code>مساوية للكلمات <code>signed long int</code>.
</p>

<p>
	يمنحك النوع <code>long</code> و <code>short</code> تحكّمًا أكبر بمقدار المساحة التي تريد حجزها للمتغير، ولكلّ منهما مجال أدنى محدّد في ملف الترويسة <code>&lt;limits.h&gt;</code>، وهو 16 بِت على الأقل لكل من <code>short</code> و <code>int</code>، و32 بتًا على الأقل للنوع <code>long</code>، سواءً كان ذو إشارة signed أو دون إشارة unsigned. وكما ذكرنا آنفًا من الممكن للتنفيذ أن يحجز مقدارًا يزيد على المقدار الأدنى من البتات إذا أراد ذلك، والقيد الوحيد هنا هو أن حدود المجال يجب أن تكون متساويةً أو محسّنة، وألا تحصل على عددٍ أكبر من البتات في متغيرٍ من نوع أصغر موازنةً بنوعٍ أكبر منه، وهي قاعدة منطقية.
</p>

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

<p>
	لتلخيص ما سبق:
</p>

<ul>
<li>
		تتضمن أنواع الأعداد الصحيحة <code>short</code> و <code>long</code> و <code>signed</code> و <code>unsigned</code> و النوع الاعتيادي <code>int</code>.
	</li>
	<li>
		النوع الأكثر استخدامًا وشيوعًا هو النوع الاعتيادي <code>int</code> وهو ذو إشارة إلا في حالة تحديد عكس ذلك.
	</li>
	<li>
		<strong>يمكن</strong> للمتغيرات من نوع <code>char</code> أن تكون ذات إشارة أو عديمة الإشارة حسب تفضيلك، ولكن في حال غياب تخصيصك لها ستُخصّص الحالة الأفضل افتراضيًا.
	</li>
</ul>
<h3>
	طباعة أنواع الأعداد الصحيحة
</h3>

<p>
	يمكننا طباعة هذه الأنواع المختلفة أيضًا باستخدام الدالة <code>printf</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>)، على الرغم من كون القيم الخاصة بها غير مثيرة للاهتمام أو مفيدة في معظم الاستخدامات. نستخدم الرمز <code>c%</code> لطباعة محتويات متغيرات المحارف كما أشرنا سابقًا، كما يمكن طباعة جميع قيم الأعداد الصحيحة بالنظام العشري باستخدام الرمز <code>d%</code> أو <code>ld%</code> لأنواع <code>long</code>، ويوضح الجدول 5.2 المزيد من الرموز المفيدة لطباعة القيم بتنسيقٍ مختلف. لاحظ أنّه في كل حالة تبدأ بالحرف <code>l</code> تُطبع قيمةٌ من نوع <code>long</code>، وهذا التخصيص ليس موجودًا فقط لعرض القيمة الصحيحة بل لتجنُّب السلوك غير المحدد لدالة <code>printf</code> إذا أُدخل الترميز الخاطئ.
</p>

<table>
<thead><tr>
<th>
				التنسيق
			</th>
			<th>
				يُستخدم مع
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				<code>c%</code>
			</td>
			<td>
				<code>char</code> (طباعة المحرف)
			</td>
		</tr>
<tr>
<td>
				<code>d%</code>
			</td>
			<td>
				القيمة العشرية للأنواع <code>signed int</code> و <code>short</code> و <code>char</code>
			</td>
		</tr>
<tr>
<td>
				<code>u%</code>
			</td>
			<td>
				القيمة العشرية للأنواع <code>unsigned int</code> و <code>unsigned short</code> و<code>unsigned char</code>
			</td>
		</tr>
<tr>
<td>
				<code>x%</code>
			</td>
			<td>
				القيمة الست عشرية للأنواع <code>int</code> و<code>short</code> و<code>char</code>
			</td>
		</tr>
<tr>
<td>
				<code>o%</code>
			</td>
			<td>
				القيمة الثمانية للأنواع <code>int</code> و<code>short</code>و<code>char</code>
			</td>
		</tr>
<tr>
<td>
				<code>ld%</code>
			</td>
			<td>
				القيمة العشرية للنوع <code>signed long</code>
			</td>
		</tr>
<tr>
<td>
				<code>lu% lx% lo%</code>
			</td>
			<td>
				كما ذُكر في الأعلى ولكن للنوع <code>long</code>
			</td>
		</tr>
</tbody>
</table>
<p style="text-align: center;">
	[جدول 5.2. المزيد من رموز التنسيق]
</p>

<p>
	سنتكلم على نحوٍ مفصّل حول التنسيق المستخدمة مع الدالة <code>printf</code> لاحقًا.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter2//" rel="external nofollow">Variables and Arithmetic</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%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="">التحويلات ما بين الأنواع في تعابير لغة سي 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>
	<li>
		<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="">إدارة الذاكرة (Memory management) في لغة 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%B9%D8%A7%D8%B4%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D8%B1%D8%B7%D9%8A%D8%A9-%D9%88%D8%AD%D9%84%D9%87%D8%A7-%D9%85%D8%B4%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1013/" 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%AD%D8%A7%D8%AF%D9%8A-%D8%B9%D8%B4%D8%B1-%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%AA%D9%82%D9%8A%D9%8A%D8%AF-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-semaphores-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%B3%D9%8A-c-r1014/" rel="">متغيرات تقييد الوصول (Semaphores) في لغة البرمجة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1611</guid><pubDate>Thu, 28 Jul 2022 16:04:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x628;&#x646;&#x64A;&#x629; &#x627;&#x644;&#x646;&#x635;&#x64A;&#x629; &#x644;&#x628;&#x631;&#x627;&#x645;&#x62C; &#x633;&#x64A; C</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_06/62af71011a42a_----C---.png.678917c5770ef76a48139841e0eba7be.png" /></p>

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

<h2>
	تخطيط البرنامج
</h2>

<p>
	اعتمدت أمثلتنا حتى اللحظة في تنسيقها على المسافات البادئة indentation والأسطر الجديدة newlines، وهذا الأسلوب في التنسيق شائع في لغات البرمجة التي تنتمي إلى عائلة لغة سي، إذ تُعد هذه اللغات لغات "حرة التنسيق free format" وتُستخدم هذه الحرية في كتابة وتنسيق السطور البرمجية بحيث يحسِّن قراءتها ويبرز تسلسل منطقها. تُستخدم محارف المسافات الفارغة space بما فيها مسافات الجدولة tab الأفقية لإنشاء المسافات البادئة في أي مكان دون أن تؤثّر على عمل البرنامج عدا ضمن المعرِّفات identifiers والكلمات المفتاحية. تعمل الأسطر الجديدة على نحوٍ مماثل لعمل المسافات الفارغة ومسافات الجدولة <strong>باستثناء</strong> أسطر أوامر المعالج المسبق، الذي يمتلك بنية سطريّة line-by-line.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3981_7" style="">
<span class="com">/* a long line */</span><span class="pln">
a </span><span class="pun">=</span><span class="pln"> fred </span><span class="pun">+</span><span class="pln"> bill </span><span class="pun">*</span><span class="pln"> </span><span class="pun">((</span><span class="kwd">this</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> that</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> sqrt</span><span class="pun">(</span><span class="lit">3.14159</span><span class="pun">));</span><span class="pln">
</span><span class="com">/* the same line */</span><span class="pln">
a </span><span class="pun">=</span><span class="pln"> fred </span><span class="pun">+</span><span class="pln"> bill </span><span class="pun">*</span><span class="pln">
        </span><span class="pun">((</span><span class="kwd">this</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> that</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln">
        sqrt</span><span class="pun">(</span><span class="lit">3.14159</span><span class="pun">));</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3981_9" style="">
<span class="com">/*
 * Example of the use of line joining
 */</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> IMPORTANT_BUT_LONG_PREPROCESSOR_TEXT \
printf</span><span class="pun">(</span><span class="str">"this is effectively all "</span><span class="pun">);</span><span class="pln">\
printf</span><span class="pun">(</span><span class="str">"on a single line "</span><span class="pun">);</span><span class="pln">\
printf</span><span class="pun">(</span><span class="str">"because of line-joining\n"</span><span class="pun">);</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3981_11" style="">
<span class="com">/* not a good way of folding a string */</span><span class="pln">
printf</span><span class="pun">(</span><span class="str">"</span><span class="typ">This</span><span class="pln"> is a very very very\
</span><span class="kwd">long</span><span class="pln"> string\n</span><span class="str">");</span></pre>

<p>
	سيعمل المثال السابق بالتأكيد، ولكن من المحبّذ استخدام ميزة ضمّ السلاسل النصية string-joining التي قُدّمَت ضمن المعيار عند التعامل مع السلاسل النصية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3981_13" style="">
<span class="com">/* This string joining will not work in Old C */</span><span class="pln">
printf</span><span class="pun">(</span><span class="str">"This is a very very very"</span><span class="pln">
       </span><span class="str">"long string\n"</span><span class="pun">);</span></pre>

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

<p>
	لكن هل انتبهت أن المثالين يحتويان على خطأ؟ لا يوجد هناك أي مسافة فارغة تسبق الكلمة "long"، مما يعني أن خرج البرنامج سيكون "verylong" دون مسافة بين الكلمتين.
</p>

<h2>
	التعليق
</h2>

<p>
	تكلمنا عن التعليق سابقًا وقلنا أن التعليق يبدأ بالمحرفَين "*<em>/"</em> وينتهي بالمحرفين <em>"/*</em>"، ويُترجم التعليق إلى مسافة فارغة واحدة أينما وجد وهو يتبِّع القوانين ذاتها الخاصة بالمسافة الفارغة. من المهم هنا معرفة أن هذا التعليق لا يختفي -كما كان الحال في لغة سي القديمة- ومن غير الممكن وضع التعليق بداخل سلسلة نصية أو <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> ثابت وإلا أصبح جزءًا منهما:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3981_16" style="">
<span class="com">/*"This is comment"*/</span><span class="pln">
</span><span class="str">"/*The quotes mean that this is a string*/"</span></pre>

<p>
	لم تكن لغة C القديمة واضحةً بشأن تفاصيل حذف التعليق، إذ كان من الممكن أن يكون الناتج هنا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3981_18" style="">
<span class="typ">int</span><span class="com">/**/</span><span class="pln">egral</span><span class="pun">();</span></pre>

<p>
	هو حذف التعليق، أي أن المصرف سينظر للتعليمة السابقة بكونها استدعاءً لدالة اسمها <code>integral</code>، لكنه سيُبدَّل التعليق بالاعتماد على معيار سي بمسافة فارغة وستكون التعليمة السابقة مساويةً للتعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3981_20" style="">
<span class="typ">int</span><span class="pln"> egral</span><span class="pun">();</span></pre>

<p>
	التي تصرح عن دالة باسم <code>egral</code> تُعيد قيمةً من نوع <code>int</code>.
</p>

<h2>
	مراحل الترجمة
</h2>

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

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

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			<strong>ملاحظة:</strong> هناك مزيدٌ من المراحل ولكننا ذكرنا أكثر المراحل أهمية
		</p>
	</div>
</blockquote>

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

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

<h3>
	الكلمات المفتاحية
</h3>

<p>
	تحجز لغة سي مجموعةً صغيرةً من <strong>الكلمات المفتاحية</strong> لاستخدامها الخاص، ولا يمكن استعمال هذه الكلمات المفتاحية على أنها معرّفات داخل البرنامج، وهذا أمرٌ شائعٌ في معظم لغات البرمجة الحديثة. قد يتفاجئ بعض مستخدمو لغة سي القديمة بوجود بعض الكلمات المفتاحية الجديدة، وإن كانت هذه الكلمات المفتاحية مُستخدمةٌ مثل معرّفات في برامج سابقة فيجب عليك تغييرها، ولحسن الحظ الانتباه لهذا النوع من الأخطاء والعثور عليها سهل وبسيط، إذ سيخبرك المصرّف أن هناك بعض الأسماء غير الصالحة. يضمّ الجدول التالي الكلمات المفتاحية المُستخدمة في معيار سي، إذ ستلاحظ أن جميع الكلمات لا تبدأ بأحرف كبيرة.
</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>
				struct
			</td>
			<td>
				int
			</td>
			<td>
				double
			</td>
			<td>
				auto
			</td>
		</tr>
<tr>
<td>
				switch
			</td>
			<td>
				long
			</td>
			<td>
				else
			</td>
			<td>
				break
			</td>
		</tr>
<tr>
<td>
				typedef
			</td>
			<td>
				register
			</td>
			<td>
				enum
			</td>
			<td>
				case
			</td>
		</tr>
<tr>
<td>
				union
			</td>
			<td>
				return
			</td>
			<td>
				extern
			</td>
			<td>
				char
			</td>
		</tr>
<tr>
<td>
				unsigned
			</td>
			<td>
				short
			</td>
			<td>
				float
			</td>
			<td>
				const
			</td>
		</tr>
<tr>
<td>
				void
			</td>
			<td>
				signed
			</td>
			<td>
				for
			</td>
			<td>
				continue
			</td>
		</tr>
<tr>
<td>
				volatile
			</td>
			<td>
				sizeof
			</td>
			<td>
				goto
			</td>
			<td>
				default
			</td>
		</tr>
<tr>
<td>
				while
			</td>
			<td>
				static
			</td>
			<td>
				if
			</td>
			<td>
				do
			</td>
		</tr>
</tbody></table>
<p style="text-align: center;">
	[جدول 3.2. كلمات مفتاحية]
</p>

<p>
	الكلمات المفتاحية المُضافة جديدًا التي قد تفاجئ المبرمجين السابقين للغة سي هي: <code>const</code> و<code>signed</code> و<code>void</code> و<code>volatile</code> (على الرغم من وجود <code>void</code> منذ فترة). سيلاحظ بعض القراء شديدي الانتباه أن الكلمات المفتاحية <code>entry</code> و <code>asm</code> و <code>fortran</code> غير موجودة في الجدول، إذ أنها ليست من ضمن المعيار، وسيفتقدها القليل منهم فقط.
</p>

<h3>
	المعرفات
</h3>

<p>
	يُعد <strong>المعرف Identifier</strong> مرادفًا لما نطلق عليه "اسم name"، وتُستخدم المعرفات في لغة سي للدلالة على العديد من الأشياء، فقد لاحظنا استخدامها حتى الآن في تسمية المتغيرات والدوال، ولكنها تُستخدم أيضًا في تسمية مزيدٍ من الأشياء التي لم نراها بعد، مثل:
</p>

<ul>
<li>
		<strong>العناوين labels</strong>
	</li>
	<li>
		<strong>"وسوم" الهياكل tags of structures</strong>
	</li>
	<li>
		<strong>الاتحادات unions</strong>
	</li>
	<li>
		<strong>المعدّدات enums</strong>
	</li>
</ul>
<p>
	قواعد إنشاء معرف بسيطةً جدًا، إذ يمكنك استخدام الأحرف البالغ عددها 52 حرفًا من الأبجدية الإنجليزية (أحرف كبيرة أو صغيرة)، والأرقام العشرة من 0 حتى 9 وأخيرًا الشرطة السفلية "_"، التي يمكن عدّها حرفًا من الأبجدية في حالتنا هذه، لكن هناك قيدٌ واحدٌ ألا وهو أن المعرّف <strong>يجب</strong> أن يبدأ بحرف أبجدي.
</p>

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

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

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

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

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

<p>
	يذكرنا المعيار دائمًا بأنه ينظر إلى استخدام تكافؤ الحالة بين الأحرف الصغيرة والكبيرة إضافةً لحدّ المحارف في تسمية المعرفات على كونها ميزاتٍ قديمة، ومن الممكن أن يلغي المعيار القادم استخدام هذه القيود. لنأمل أن يحصل ذلك قريبًا.
</p>

<h2>
	التصريح عن المتغيرات
</h2>

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

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3981_23" style="">
<span class="com">/*
* A function is only defined if its body is given
* so this is a declaration but not a definition
*/</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> func_dec</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span><span class="pln">

</span><span class="com">/*
* Because this function has a body, it is also
* a definition.
* Any variables declared inside will be definitions,
* unless the keyword 'extern' is used.
* Don't use 'extern' until you understand it!
*/</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> def_func</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">){</span><span class="pln">
     </span><span class="typ">float</span><span class="pln"> f_var</span><span class="pun">;</span><span class="pln">            </span><span class="com">/* a definition */</span><span class="pln">
     </span><span class="typ">int</span><span class="pln"> counter</span><span class="pun">;</span><span class="pln">            </span><span class="com">/* another definition */</span><span class="pln">
     </span><span class="typ">int</span><span class="pln"> rand_num</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span><span class="pln">     </span><span class="com">/* declare (but not define) another function */</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>
	سنستمرّ قدُمًا في القسم التالي ونتكلم عن <strong>نوع</strong> المتغيرات والتعابير.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter2//" rel="external nofollow">Variables and Arithmetic</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%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="">الأنواع الحقيقية والصحيحة في لغة سي 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%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>
</ul>
]]></description><guid isPermaLink="false">1610</guid><pubDate>Mon, 20 Jun 2022 12:30:11 +0000</pubDate></item><item><title>&#x627;&#x644;&#x645;&#x62D;&#x627;&#x631;&#x641; &#x627;&#x644;&#x645;&#x633;&#x62A;&#x62E;&#x62F;&#x645;&#x629; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_06/62af679829aa4_-----C-.png.fefa456c88cf80fb782c0134b0c0a773.png" /></p>

<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>

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

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

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

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

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

<h2>
	الأبجدية الاعتيادية
</h2>

<p>
	تعرّف قلةٌ من لغات <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r662/" rel="">البرمجة </a>أبجديتها أو تلقي بالًا لهذا الأمر، إذ أن هناك افتراضًا مسبقًا بأن أحرف الأبجدية الإنجليزية وخليطًا من علامات الترقيم والرموز ستكون متاحةً في أي بيئة داعمة للّغة، ولكن هذا الافتراض غير محقّقٍ دائمًا. تعاني <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> القديمة من هذه المشكلة بدرجةٍ أقل حدّة، ولكن تخيل إرسال برنامج مكتوب بلغة سي عبر جهاز تلكس Telex أو عن طريق نظام بريد إلكتروني يحتوي على بعض القيود، أتعي أهمية الأمر الآن؟
</p>

<p>
	يوصّف المعيار مجموعتين مختلفتين من المحارف: واحدةٌ تُكتب بها البرامج وأخرى تُنفَّذ بها، وذلك للسماح للأنظمة المختلفة بتصريف البرنامج وتنفيذه بغض النظر عن اختلاف طرق ترميز المحارف لكل نظام. في الحقيقة، الأمر مهمّ فقط في حال استخدامك محارفًا ثابتة constant في المعالج المُسبق preprocessor، إذ من الممكن أن تختلف قيم هذه المحارف عند التنفيذ، وهذا السلوك معرّف عند التنفيذ implementation-defined، فهو موثّق بالتأكيد، ولكن لا تقلق بخصوص هذا الأمر الآن.
</p>

<p>
	يملي المعيار وجود أبجدية مؤلفة من 96 رمزًا للغة سي، وهي:
</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><tbody>
<tr>
<td style="text-align: center;">
				a b c d e f g h i j k l m n o p q r s t u v w x y z
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				0 1 2 3 4 5 6 7 8 9
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				! " # % &amp; ' ( ) * + , - . /
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				: ; &lt; = &gt; ? [ \ ] ^ _ {|} ~
			</td>
		</tr>
<tr></tr>
</tbody></table>
<p style="text-align: center;">
	[جدول 1 أبجدية لغة سي]
</p>

<p>
	اتّضح أن معظم أبجديات الحاسوب الشائعة تحتوي على جميع الرموز اللازمة للغة سي، عدا بعض الحالات الشاذة النادرة مثل المحارف الموجودة في الأسفل، والتي تُعد مثالًا عن محارف أبجدية لغة سي المفقودة من مجموعة المحارف ذات 7 بت لمعيار منظمة المعايير العالمية International Standards Organization المدعوّ ISO 646، وهي مجموعة جزئية من المحارف المُستخدمة في أبجديات الحاسوب على نطاقٍ واسع.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5113_7" style="">
<span class="com"># [ \ ] ^ { | } ~</span></pre>

<p>
	لتضمين هذه الأنظمة التي لا تحتوي على مجموعة المحارف البالغ عددها 96 والمطلوبة لكتابة برامج بلغة سي، حدّد المعيار طريقةً لاستخدام معيار ISO 646 لتمثيل المحارف المفقودة ألا وهي تقنية <strong>ثُلاثيات المحارف trigraphs</strong>.
</p>

<h2>
	ثلاثيات المحارف
</h2>

<p>
	ثلاثيات المحارف Trigraphs هي سلسلةٌ من ثلاثة محارف ضمن المعيار ISO 646، وتُعامل معاملة محرفٍ واحدٍ ضمن أبجدية لغة سي. تبدأ جميع ثلاثيات المحارف بعلامتَي استفهام "??"، ويساعد هذا في الدلالة على أن هناك شيءٌ "خارجٌ عن المألوف" ضمن البرنامج. يوضّح الجدول 2 أدناه جميع ثلاثيات المحارف المُعرّفة ضمن المعيار.
</p>

<table>
<thead><tr>
<th>
				محرف أبجدية سي C
			</th>
			<th>
				ثلاثي المحرف
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				#
			</td>
			<td>
				=??
			</td>
		</tr>
<tr>
<td>
				]
			</td>
			<td>
				)??
			</td>
		</tr>
<tr>
<td>
				[
			</td>
			<td>
				(??
			</td>
		</tr>
<tr>
<td>
				}
			</td>
			<td>
				&gt;??
			</td>
		</tr>
<tr>
<td>
				{
			</td>
			<td>
				&lt;??
			</td>
		</tr>
<tr>
<td>
				\
			</td>
			<td>
				/??
			</td>
		</tr>
<tr>
<td>
				\
			</td>
			<td>
				!??
			</td>
		</tr>
<tr>
<td>
				~
			</td>
			<td>
				-??
			</td>
		</tr>
<tr>
<td>
				^
			</td>
			<td>
				'??
			</td>
		</tr>
</tbody>
</table>
<p style="text-align: center;">
	[جدول 2 ثلاثيات المحارف]
</p>

<p>
	دعنا نفترض مثلًا، أن طرفيتك terminal لا تحتوي على الرمز "#" لكتابة سطر المعالج المُسبق التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5113_9" style="">
<span class="com">#define</span><span class="pln"> MAX     </span><span class="lit">32767</span></pre>

<p>
	عندها، نستطيع استخدام طريقة ثلاثيات المحارف على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5113_11" style="">
<span class="pun">??=</span><span class="pln">define MAX   </span><span class="lit">32767</span></pre>

<p>
	ستعمل ثلاثيات المحارف حتى لو كان المحرف "#" موجودًا، ولكن هذه التقنية موجودةٌ لمساعدتك في الحالات الحرجة ولا يُحبَّذ استخدامها بديلًا عن محارف أبجدية سي دومًا.
</p>

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

<p>
	من الخطأ الاعتقاد أن البرامج المكتوبة بلغة سي، والتي تلقي بالًا كبيرًا لإمكانية تشغيلها على نحوٍ محمول portable وعلى جميع الأنظمة مكتوبةٌ باستخدام ثلاثيات المحارف "إلا في حال ضرورة نقلها إلى نظام يدعم معيار ISO 646 فقط"؛ فإذا كان نظامك يدعم جميع المحارف البالغ عددها 96 محرفًأ، واللازمة لكتابة البرامج بلغة سي، فيجب استخدامها دون الاستعانة بثلاثيات المحارف، إذ وُجدت هذه التقنية لتنفيذها ضمن بيئات محدودة، ومن السهل جدًا كتابة مفسّر يحوِّل برنامجك بين التمثيلين عن طريق فحص كل محرف بمحرفه.
</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> على البرنامج.
</p>

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

<p>
	أُضيف دعم المحارف متعددة البايت multibyte characters إلى المعيار، ولكن ما هو السبب؟
</p>

<p>
	تتضمن نسبةٌ كبيرة من الحوسبة الاعتيادية اليومية بياناتٍ ممثّلة بنص بشكلٍ أو بآخر، وساد الاعتقاد في مجال الحوسبة أنه من الكافي دعم ما يقارب مئة محرف مطبوع فقط، ومن هنا كان عدد المحارف الممثلة لأبجدية سي 96 محرفًا، وذلك بناءً على متطلبات اللغة الإنجليزية وهذا الأمر ليس مفاجئًا بالنظر إلى أن معظم سوق تطوير البرمجيات والحوسبة التجاري كان في الولايات المتحدة الأمريكية. تُعرف مجموعة المحارف هذه باسم <strong>المخزون repertoire</strong> وتتناسب مع 7 أو 8 بتات من الذاكرة، وهذا السبب في أهمية استخدام 8-بت واحدةً لتخزين وقياس للبيانات بصورةٍ أساسية في معيار US-ASCII ومعمارية الحواسيب المصغرة minicomputers والحواسيب الدقيقة microcomputers.
</p>

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

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

<p>
	هناك طريقتان لتوسعة مجموعة المحارف، الأولى عن طريق إضافة عددٍ معين من البايتات (عادةً اثنين) لكل محرف، وهذه هي الطريقة المصممة لدعم محارف أكبر حجمًا في لغة سي؛ أما الطريقة الأخرى فهي باستخدام مخطط ترميز الإدخال بالإزاحة shift-in والخرج بالإزاحة shift-out؛ وهو ترميزٌ شائع في قنوات الاتصال ذات سعة 8-بت. ألقِ نظرةً على سلسلة المحارف التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5113_15" style="">
<span class="pln">a b c </span><span class="pun">&lt;</span><span class="pln">SI</span><span class="pun">&gt;</span><span class="pln"> a b g </span><span class="pun">&lt;</span><span class="pln">SO</span><span class="pun">&gt;</span><span class="pln"> x y</span></pre>

<p>
	إذ يعني المحرف <code>&lt;SI&gt;</code> "بدّل إلى اليونانية" والمحرف <code>&lt;SO&gt;</code> "بدّل مجددًَا إلى الإنجليزية"، ويعرض جهاز العرض المُهيّأ للعمل وفق هذا الترميز النتيجة على النحو التالي: a, b, c, alpha, beta, gamma, x, y. هذه هي الطريقة المُتبعة تقريبًا في معيار shift-JIS الياباني، والاختلاف في ذلك المعيار هو أن المحارف الموجودة ضمن الإدخال بالإزاحة تتألف من مِحرفين يُستخدمان في تمثيل محرفٍ واحدٍ باللغة اليابانية، وهناك العديد من الطرق البديلة التي تستخدم عدة محارف مدخلة بالإزاحة، ولكنها أقل شيوعًا.
</p>

<p>
	يسمح المعيار الآن باستخدام مجموعات المحارف الموسّعة extended character sets، إذ تُستخدم المحارف المُعرفة مُسبقًا والبالغ عددها 96 في كتابة برنامج بلغة سي، ولكن مجموعة المحارف المُوسعة مسموحة في كتابة التعليقات والسلاسل النصية والمحارف الثابتة وأسماء ملفات الترويسة (جميع ما ذُكر يمثل بيانات وليسَ جزءًا من كتابة البرنامج). يضع المعيار بعض القواعد البديهية لوصف طريقة استخدام هذه المجموعات، ولن نكررها هنا، لكن أبرزها هو أن البايت ذو القيمة الصفرية يُفسّر محرفًا <strong>فارغًا null</strong> بغض النظر عن أي حالة إزاحة. هذا المحرف مهم لأن لغة سي ترمز لنهاية السلسلة النصية به وتعتمد عليه العديد من دوال المكتبات. هناك متطلبٌ آخر ألا وهو أن سلاسل المحارف متعددة البايت يجب أن تبدأ وتنتهي ضمن حالة الإزاحة المبدئية.
</p>

<p>
	يصف المعيار النوع char بكونه مناسبًا لتخزين قيمة جميع المحارف ضمن "مجموعة محارف التنفيذ execution character set" التي ستكون مُعرفةً في توثيق نظامك؛ وهذا يعني (في المثال السابق) أن نوع <code>char</code> يمكنه تخزين <code>'a'</code> أو <code>'b'</code> أو محرف الإزاحة إلى اللغة اليونانية بنفسه <code>&lt;SI&gt;</code>، إذ لا يوجد أي فرق في القيم المخزنة بداخل المتغير من نوع <code>char</code> بسبب تقنية الإدخال والإخراج بالإزاحة؛ وهذا يعني أنه لا يمكننا تمثيل <code>'a'</code> على أنه محرف "ألفا" في اللغة اليونانية، وحتى نستطيع تحقيق ذلك يلزمنا أكثر من 8 بتات، وهو أكبر من حجم char في معظم الأنظمة، وهنا يأتي دور نوع المتغير <code>wchar_t</code> الذي قدَّمه المعيار، ولكن يجب تضمين ملف الترويسة <code>&lt;stddef.h&gt;</code> قبل استخدامه، لأن <code>wchar_t</code> معرّفٌ على أنه اسم بديل عن نوعٍ موجودٍ في لغة سي. سنناقش هذا الأمر بتوسع أكبر لاحقًا.
</p>

<p>
	ختامًا، نستطيع تلخيص ما سبق:
</p>

<ul>
<li>
		تتطلب لغة سي مجموعة محارف عددها 96 محرف على الأقل لاستخدامها في محارف الشيفرة المصدرية للبرنامج.
	</li>
	<li>
		لا تحتوي جميع مجموعات المحارف على 96 محرف، بالتالي تسمح ثلاثيات المحارف للمعيار ISO 646 الأساسي كتابة برامج سي في حال الضرورة.
	</li>
	<li>
		أُضيفت المحارف متعددة البايت حديثًا مع المعيار، وتدعم:
		<ul>
<li>
				المحارف متعددة البايت المُرمّزة بالإزاحة Shift-encoded multibyte characters، التي تسمح بحشر المحارف الإضافية ضمن سلاسل محارف "اعتيادية"، بحيث يمكننا استخدام النوع char معها.
			</li>
			<li>
				المحارف الموسّعة wide characters التي تتسع لمساحة أكبر من المحارف الاعتيادية، ولها نوع بيانات مختلف عن النوع char.
			</li>
		</ul>
</li>
</ul>
<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter2//" rel="external nofollow">Variables and Arithmetic</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%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>
	<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%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>
</ul>
]]></description><guid isPermaLink="false">1609</guid><pubDate>Mon, 18 Jul 2022 16:04:00 +0000</pubDate></item><item><title>&#x628;&#x639;&#x636; &#x627;&#x644;&#x628;&#x631;&#x627;&#x645;&#x62C; &#x627;&#x644;&#x628;&#x633;&#x64A;&#x637;&#x629; &#x628;&#x644;&#x63A;&#x629; &#x633;&#x64A; C: &#x627;&#x644;&#x645;&#x635;&#x641;&#x648;&#x641;&#x627;&#x62A; &#x648;&#x627;&#x644;&#x639;&#x645;&#x644;&#x64A;&#x627;&#x62A; &#x627;&#x644;&#x62D;&#x633;&#x627;&#x628;&#x64A;&#x629;</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_06/62af43a2249ed_-----C-(--)-.png.e98612179a26f1fca6d9fb3f5bbeb86b.png" /></p>

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

<h2>
	برنامج لإيجاد الأعداد الأولية
</h2>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1677_7" style="">
<span class="com">/*
*
*
* Dumb program that generates prime numbers.
*/</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">int</span><span class="pln"> this_number</span><span class="pun">,</span><span class="pln"> divisor</span><span class="pun">,</span><span class="pln"> not_prime</span><span class="pun">;</span><span class="pln">

    this_number </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">this_number </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">10000</span><span class="pun">){</span><span class="pln">
            divisor </span><span class="pun">=</span><span class="pln"> this_number </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
            not_prime </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">divisor </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">if</span><span class="pun">(</span><span class="pln">this_number </span><span class="pun">%</span><span class="pln"> divisor </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">){</span><span class="pln">
                            not_prime </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
                            divisor </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="kwd">else</span><span class="pln">
                            divisor </span><span class="pun">=</span><span class="pln"> divisor</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">not_prime </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">"%d is a prime number\n"</span><span class="pun">,</span><span class="pln"> this_number</span><span class="pun">);</span><span class="pln">
            this_number </span><span class="pun">=</span><span class="pln"> this_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="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.1]
</p>

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

<p>
	المشكلة في فحص المساواة هذا وأيّ فحص آخر، هي أن استبدال <code>==</code> بالرمز <code>=</code> هو تعليمة صالحة، ففي الحالة الأولى (استخدام <code>==</code>) تُوازَن القيمتان وتُفحص حالة المساواة، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1677_9" style="">
<span class="kwd">if</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">
</span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">c </span><span class="pun">==</span><span class="pln"> d</span><span class="pun">)</span></pre>

<p>
	كما يمكن استخدام عامل الإسناد <code>=</code> في الموضع ذاته، ولكن لإسناد القيمة الواقعة على يسار الإشارة للمتغيّر على يمينها. تصبح المشكلة مهمةً أكثر إذا كنت معتادًا على<a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r662/" rel=""> البرمجة</a> باللغات التي تستخدم عامل المساواة بصورةٍ مماثلة لما تستخدمه لغة سي لعامل الإسناد، إذ لا يوجد أي شيء يمكنك فعله لتغيير ذلك سوى الاعتياد على هذا الأمر من الآن فصاعدًا، ولحسن الحظ تعطيك بعض المصرّفات الحديثة بعض التحذيرات عندما تلاحظ استخدامًا غريبًا لعامل الإسناد في غير موضعه المعتاد، ولكن يصبح الأمر مزعجًا أحيانًا إذا كان هذا فعلًا ما تريد فعله وليس خطأً.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1677_12" style="">
<span class="kwd">if</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">)</span><span class="pln">
        statement

</span><span class="kwd">if</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">)</span><span class="pln">
        statement
</span><span class="kwd">else</span><span class="pln">
        statement</span></pre>

<p>
	يوضِّح المثال المبين أعلاه أن التعليمة تأتي بنموذجين، إذ يحتوي الأول على الكلمة <code>if</code> والثاني على الكلمة <code>else</code>؛ فإذا كان التعبير صحيحًا وفقًا للنموذج الأول، فستُنفَّذ مجموعة التعليمات الموجودة داخل متن <code>if</code>، أما إذا كان التعبير خاطئًا فلن تُنفّذ؛ بينما تنفّذ التعليمات الموجودة ضمن <code>else</code> وفقًا للنموذج الثاني فقط في حالة كان التعبير خاطئًا (أي تعبير <code>if</code> السابق لها).
</p>

<p>
	يتميز استخدام تعليمة <code>if</code> بمشكلة شائعة، ألقِ نظرةً على المثال التالي وأجِب، هل ستُنفَّذ التعليمة <code>statement-2</code> أم لا؟
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1677_15" style="">
<span class="kwd">if</span><span class="pun">(</span><span class="lit">1</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">if</span><span class="pun">(</span><span class="lit">1</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">
                statement</span><span class="pun">-</span><span class="lit">1</span><span class="pln">
</span><span class="kwd">else</span><span class="pln">
        statement</span><span class="pun">-</span><span class="lit">2</span></pre>

<p>
	الإجابة هي <strong>نعم</strong> ستُنفَّذ التعليمة. لا تركّز تفكيرك على مسافة الإزاحة فهي مضللةٌ غالبًا، فقد تكون تعليمة <code>else</code> تابعةً لتعليمة <code>if</code> الأولى أو الثانية بحسب وصف كلٍّ منهما، لذا علينا اللجوء لقاعدةٍ ما لجعل الأمور أوضح. القاعدة ببساطة هي أن <code>else</code> تابعةٌ لتعليمة <code>if</code> الأقرب (التي تسبق <code>else</code>)، والتي لا تحتوي على تعليمة <code>else</code> مسبقًا. لنجعل البرنامج يعمل كما نريد وفق تنسيق مسافات الإزاحة، علينا إنشاء تعليمة مركبة باستخدام الأقواس المعقوصة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1677_17" style="">
<span class="kwd">if</span><span class="pun">(</span><span class="lit">1</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">if</span><span class="pun">(</span><span class="lit">1</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">
                statement</span><span class="pun">-</span><span class="lit">1</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">else</span><span class="pln">
        statement</span><span class="pun">-</span><span class="lit">2</span></pre>

<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>

<h2>
	عامل القسمة
</h2>

<p>
	يُشار إلى عامل القسمة بالرمز "/"، وعامل باقي القسمة بالرمز "%". تفعل عملية القسمة المتوقّع منها، إلا أن إجراء القسمة على أعداد صحيحة <code>int</code> ستعطي نتيجةً مقربةً باتجاه الحد الأدنى (الصفر)، إذ تعطي العملية <code>5/2</code> مثلًا النتيجة <code>2</code>، وتعطي التعليمة <code>5/3</code> النتيجة <code>1</code>؛ وللحصول على القيمة المقتطعة من الناتج نستخدم عامل باقي القسمة، إذ تعطينا العملية <code>2%5</code> النتيجة <code>1</code>، والعملية <code>3%5</code> النتيجة <code>2</code>. تعتمد إشارة باقي القسمة وناتج القسمة على المقسوم والمقسوم عليه وهي مُعرّفة في المعيار.
</p>

<h2>
	مثال عن تنفيذ عملية الدخل
</h2>

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

<p>
	لا تُعد موازنة قيمة المحارف ما إذا كانت أصغر أو أكبر خيارًا جيّدًا، إذ لا يوجد أي ضمان بأن قيمة <code>a</code> أصغر من قيمة <code>b</code>، مع أن ذلك الأمر محققٌ في معظم الأنظمة، ولكن الضمان الوحيد هنا الذي يقدمه لك المعيار هو أن القيمة ستكون متتابعة من 0 إلى 9. ألقِ نظرةً على المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1677_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">
main</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">

        ch </span><span class="pun">=</span><span class="pln"> getchar</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"> </span><span class="str">'a'</span><span class="pun">){</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">
                        printf</span><span class="pun">(</span><span class="str">"ch was %c, value %d\n"</span><span class="pun">,</span><span class="pln"> ch</span><span class="pun">,</span><span class="pln"> ch</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">
        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.1]
</p>

<p>
	هناك ملاحظتان جديرتان بالاهتمام، هما:
</p>

<ul>
<li>
		سنجد في نهاية كل سطر دخل المحرف <code>n\</code> (محرف ثابت)، وهو المحرف ذاته الذي نستخدمه في دالة <code>printf</code> عندما نريد طباعة سطر جديد. نظام الدخل والخرج الخاص بلغة سي غير مبني على مفهوم الأسطر بل على مفهوم المحارف؛ فإذا كنت تريد التفكير بالأمر من منطلق مفهوم الأسطر، فانظر للمحرف <code>n\</code> على أنه إعلان لنهاية السطر.
	</li>
	<li>
		استخدام <code>c%</code> لطباعة المحرف بواسطة الدالة <code>printf</code>، إذ يسمح لنا هذا الأمر بطباعة المحرف على أنه محرف على الشاشة، بالموازنة مع استخدام <code>d%</code> الذي سيطبع المحرف ذاته ولكن بتمثيله العددي المُستخدم ضمن البرنامج.
	</li>
</ul>
<p>
	إذا جربت تنفيذ هذا البرنامج بنفسك، قد تجد أن بعض الأنظمة لا تمرّر المحرف الواحد تلو الآخر عند كتابته، بل تجبرك على كتابة سطر كاملٍ للدخل أولًا، ثم تبدأ معالجة المحرف الواحد تلو الآخر. قد يبدو الأمر مشوّشًا لبعض المبتدئين عندما يكتبون محرفًا ما ولا يحدث شيء بعد ذلك، وليس للغة سي أي علاقةٍ بذلك، بل يعتمد الأمر على حاسوبك ونظام تشغيله.
</p>

<h2>
	المصفوفات البسيطة
</h2>

<p>
	يكون غالبًا استخدام <strong>المصفوفات arrays</strong> في لغة سي للمبتدئين بمثابة تحدٍ، إذ أن التصريح عن المصفوفات ليس صعبًا، بالأخص المصفوفات أحادية البعد one-dimensional، ولكن سبب الأخطاء هنا هو بدء الدليل index الخاص بها من الرقم 0. للتصريح عن مصفوفة مؤلفة من 5 أعداد من نوع <code>int</code>، نكتب:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1677_21" style="">
<span class="typ">int</span><span class="pln"> something</span><span class="pun">[</span><span class="lit">5</span><span class="pun">];</span></pre>

<p>
	تستخدم لغة سي الأقواس المعقوفة square brackets للتصريح عن المصفوفات، ولا تدعم أيّ مصفوفة لا تقع أدلتها بين 0 وما فوق. العناصر الصالحة في مثالنا هي <code>[something[0</code> إلى <code>[something[4</code>، و <code>[something[5</code> <strong>غير موجود</strong> في المصفوفة وهو عنصر غير صالح.
</p>

<p>
	يقرأ البرنامج التالي المحارف من الدخل، ويرتبها وفقًا لقيمتها العددية، ويطبعها في الخرج مرةً أخرى. ألقِ نظرةً على البرنامج وحاول فهم ما يحصل، إذ لن نتكلم عن <a href="https://academy.hsoub.com/programming/advanced/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r1282/" rel="">الخوارزمية</a> مفصلًا في شرحنا.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1677_23" 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">
main</span><span class="pun">(){</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> ch_arr</span><span class="pun">[</span><span class="pln">ARSIZE</span><span class="pun">],</span><span class="pln">count1</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> count2</span><span class="pun">,</span><span class="pln"> stop</span><span class="pun">,</span><span class="pln"> lastchar</span><span class="pun">;</span><span class="pln">

        lastchar </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
        stop </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">/*
         * Read characters into array.
         * Stop if end of line, or array full.
         */</span><span class="pln">
        </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">stop </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">){</span><span class="pln">
                ch_arr</span><span class="pun">[</span><span class="pln">lastchar</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> getchar</span><span class="pun">();</span><span class="pln">
                </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">ch_arr</span><span class="pun">[</span><span class="pln">lastchar</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">
                        stop </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">else</span><span class="pln">
                        lastchar </span><span class="pun">=</span><span class="pln"> lastchar </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">lastchar </span><span class="pun">==</span><span class="pln"> ARSIZE</span><span class="pun">)</span><span class="pln">
                        stop </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        lastchar </span><span class="pun">=</span><span class="pln"> lastchar</span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln">

        </span><span class="com">/*
         * Now the traditional bubble sort.
         */</span><span class="pln">
        count1 </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">count1 </span><span class="pun">&lt;</span><span class="pln"> lastchar</span><span class="pun">){</span><span class="pln">
                count2 </span><span class="pun">=</span><span class="pln"> count1 </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">while</span><span class="pun">(</span><span class="pln">count2 </span><span class="pun">&lt;=</span><span class="pln"> lastchar</span><span class="pun">){</span><span class="pln">
                        </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">ch_arr</span><span class="pun">[</span><span class="pln">count1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> ch_arr</span><span class="pun">[</span><span class="pln">count2</span><span class="pun">]){</span><span class="pln">
                                </span><span class="com">/* swap */</span><span class="pln">
                                </span><span class="typ">int</span><span class="pln"> temp</span><span class="pun">;</span><span class="pln">
                                temp </span><span class="pun">=</span><span class="pln"> ch_arr</span><span class="pun">[</span><span class="pln">count1</span><span class="pun">];</span><span class="pln">
                                ch_arr</span><span class="pun">[</span><span class="pln">count1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> ch_arr</span><span class="pun">[</span><span class="pln">count2</span><span class="pun">];</span><span class="pln">
                                ch_arr</span><span class="pun">[</span><span class="pln">count2</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> temp</span><span class="pun">;</span><span class="pln">
                        </span><span class="pun">}</span><span class="pln">
                        count2 </span><span class="pun">=</span><span class="pln"> count2 </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
                count1 </span><span class="pun">=</span><span class="pln"> count1 </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        count1 </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">count1 </span><span class="pun">&lt;=</span><span class="pln"> lastchar</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"> ch_arr</span><span class="pun">[</span><span class="pln">count1</span><span class="pun">]);</span><span class="pln">
                count1 </span><span class="pun">=</span><span class="pln"> count1 </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</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.1]
</p>

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

<p>
	على عكس بعض اللغات، لا تُعْلمك لغة سي أنك وصلت إلى نهاية المصفوفة، بل تُنتج ذلك بما يعرف باسم <strong>التصرف غير المحدد undefined behaviour</strong> في البرنامج، وهذا ما يتسبب ببعض الأخطاء الغامضة في برنامجك. يتجنّب المبرمجون الخبراء هذا الخطأ عن طريق اختبار البرنامج المتكرّر للتأكد من عدم حصول ذلك عند تطبيق الخوارزميّة المستخدمة، أو عن طريق فحص القيمة قبل محاولة الحصول عليها من المصفوفة. وتُعد هذه المشكلة من أبرز مصادر أخطاء وقت التشغيل run-time في لغة سي، لقد حذرتك!
</p>

<p>
	خلاصة القول:
</p>

<ul>
<li>
		تبدأ المصفوفات بالدليل 0 دائمًا، ولا يمكنك تجنّب هذا الاصطلاح.
	</li>
	<li>
		تحتوي المصفوفة من الحجم "N" على عناصر من الدليل "0" إلى الدليل "N-1"، والعنصر "N" غير موجود داخل المصفوفة هذه، ومحاولة الوصول إليه هو خطأ فادح.
	</li>
</ul>
<h2>
	مصطلحات
</h2>

<p>
	هناك نوعان من الأشياء المختلفة في البرامج المكتوبة بلغة سي، أشياء تُستخدم لتخزين القيَم، وأشياء تدعى بالدوال، وبدلًا عن الإشارة للشيئين بصورةٍ منفصلة بعبارة طويلة، نعتقد أنه من الأفضل أن نرمز إليهما بتسميةٍ واحدةٍ عامّة ألا وهي "الكائنات objects"، وسنستخدم هذه التسمية كثيرًا في الفصول القادمة، إذ يتبع الشيئان نفس القواعد إلى حدٍّ ما؛ ولكن يجدر الذكر هنا إلى أن معنى المصطلح هذا مختلف عمّا يقصده المعيار، إذ يُستخدم مصطلح "كائن" في المعيار على نحوٍ خاص لوصف منطقة من الذاكرة المحجوزة لتمثيل قيمة، والدالة مختلفة عن هذا التعريف تمامًا. يؤدي هذا لاستخدام المعيار المصطلحين على نحوٍ منفصل وغالبًا ما ستجد العبارة "… الدوال والكائنات …"؛ ونظرًا لأن هذا الاختلاف لا يؤدي لكثيرٍ من الخلط ويحسِّن قراءة النص في كثيرٍ من الحالات، فسنستمرّ في استخدام المصطلح العامّ "كائن" للدلالة على الدوال والقيَم، وسنستخدم المصطلح "دالة" و"كائن بيانات Data object" عندما نريد التمييز بين الاثنين وفقًا للحالة. لذا، قد تجد اختلافًا بسيطًا في المعنى إن كنت تقرأ المعيار.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter1/" rel="external nofollow">Chapter 1 An Introduction to 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%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%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="">إدارة الذاكرة (Memory management) في لغة 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%B9%D8%A7%D8%B4%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D8%B1%D8%B7%D9%8A%D8%A9-%D9%88%D8%AD%D9%84%D9%87%D8%A7-%D9%85%D8%B4%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1013/" 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%AD%D8%A7%D8%AF%D9%8A-%D8%B9%D8%B4%D8%B1-%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%AA%D9%82%D9%8A%D9%8A%D8%AF-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-semaphores-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%B3%D9%8A-c-r1014/" rel="">متغيرات تقييد الوصول (Semaphores) في لغة البرمجة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1608</guid><pubDate>Mon, 11 Jul 2022 16:03:00 +0000</pubDate></item><item><title>&#x628;&#x646;&#x64A;&#x629; &#x628;&#x631;&#x646;&#x627;&#x645;&#x62C; &#x644;&#x63A;&#x629; &#x633;&#x64A; C</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_06/62af3f142933f_----C-.png.027e6c18a103f1ace0a0ecbc2b181e3b.png" /></p>
<p>
	إذا كنت معتادًا على لغات تتبع بنية الكُتل مثل لغة باسكال Pascal، فستجد بنية برنامج لغة سي C مفاجئًا لك؛ وإذا كانت خبرتك في مجال لغات مشابهة للغة فورتران FORTRAN، فستجد البنية مشابهةً لما اعتدت عليه -بالرغم من اختلافها بصورةٍ كبيرة في التفاصيل، وفي الحقيقة استعارت لغة سي من كلا الأسلوبين المُستخدمين بصورةٍ واضحة، ومن أماكن أخرى أيضًا. نتيجةً لأخذ بعض القواعد من مصادر مختلفة، تبدو لغة سي أشبه بنتيجة تزاوج فصيلة كلاب ترير Terrier غير الأنيقة والمعروفة بعنادها وقوتها لكنها متسامحة مع أفراد العائلة. يطلق علماء الأحياء على هذا النوع من الفصائل "القوة الهجينة"، قد يذكرك كلامنا أيضًا بمخلوق كمير Chimera الأسطوري الذي يبدو خليطًا من الخرفان والماعز، قد يمنحنا الحليب والصوف، ولكنه سيزعجنا بثغائه المرتفع ورائحته غير اللطيفة.
</p>

<p>
	إذا نظرنا للأمر عمومًا نلاحظ أن ميزة <a href="https://academy.hsoub.com/programming/c/" rel="">لغة سي C</a> العامة هي بنية البرنامج الموزعة على عدة ملفات، لأنها تسمح <strong>بتصريف منفصل</strong> لهذه الملفات، إذ تسمح لغة سي بتوزيع أجزاء من برنامج مكتمل على عدة <strong>ملفات مصدرية</strong> والتصريف على نحوٍ متفرق عن بعضها بعضًا. مبدأ العمل هنا هو أن جميع عمليات <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> هذه ستعطينا ملفات يمكن <strong>ربطها Linked</strong> سويًّا عن طريق أي محرر ربط، أو محمّل ربط يستخدمه نظامك، ولكن بنية الكتل لبعض لغات البرمجة المشابهة للغة ألغول ALGOL تجعل هذه الطريقة صعبة التنفيذ، نظرًا لأن البرنامج مكتوبٌ بطريقة تجعل منه كتلةً واحدةً مترابطة، إلا أن هناك بعض الطرق للتغلُّب على هذه المشكلة.
</p>

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

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

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="101514" href="https://academy.hsoub.com/uploads/monthly_2022_06/001SeperateCompilation.png.370a36ced8a8c6ef8ba113b00d8235cc.png" rel="" data-fileext="png"><img alt="001SeperateCompilation.png" class="ipsImage ipsImage_thumbnailed" data-fileid="101514" data-unique="rvrymvxl5" src="https://academy.hsoub.com/uploads/monthly_2022_06/001SeperateCompilation.png.370a36ced8a8c6ef8ba113b00d8235cc.png"></a>
</p>

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

<h2>
	الدوال
</h2>

<p>
	تتكون لغة C من مجموعة عناصر تشكل لبنات البناء الأساسية لها، مثل <strong>الدوالّ Functions</strong> وما نطلق عليه تسمية <strong>المتغيرات العامة global variables</strong>، إذ تُسمى هذه العناصر في نقطة ما من البرنامج عند تعريفها، وتحتوي طريقة الوصول لهذه العناصر باستخدام اسمائهم ضمن البرنامج على بعض القواعد، وتُوصف هذه القواعد في المعيار بمصطلح <strong>الربط Linkage</strong>. سنتكلم في الوقت الحالي فقط عن <strong>الربط الخارجي External Linkage</strong> و<strong>انعدام الربط No linkage</strong>، إذ تُدعى العناصر التي يمكن الوصول إليها ضمن البرنامج كاملًا، مثل دوال مكتبة معينة، بعناصر الربط الخارجي، وتُستخدم العناصر عديمة الربط بكثرة أيضًا ولكن الوصول إليها محدودٌ بصورةٍ أكبر.
</p>

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

<p>
	تكافئ الدوال في لغة سي الدوال والبرامج الفرعية في لغة فورتران FORTRAN والدوال والإجراءات في لغة باسكال Pascal وألغول ALGOL، بينما لا تمتلك لغة بيسك BASIC ومعظم طفراتها mutations البسيطة أو لغة كوبول COBOL مقدار الدوال التي تمتلكه لغة سي.
</p>

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

<p>
	هناك دالةٌ ذات اسم مميز في <strong>البيئات المستضافة</strong> ألا وهي دالة "main"، إذ تمثِّل هذه الدالة نقطة بداية البرنامج عند تشغيله، أما في <strong>البيئات المستقلة</strong> فالذي يحدد أولى خطوات البرنامج هي <strong>دالة معرفة مسبقًا Implementation defined</strong>؛ وهذا يعني أنه على الرغم من أن المعيار لا يحدد ما الذي سيحدث، إلا أن سلوك البرنامج يجب أن يكون محدّدًا وموثقًا. يتوقف البرنامج عندما يغادر دالة "main". ألقِ نظرةً على البرنامج البسيط التالي الذي يحتوي على دالتَين:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5703_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">/*
* Tell the compiler that we intend
* to use a function called show_message.
* It has no arguments and returns no value
* This is the "declaration".
*
*/</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> show_message</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">);</span><span class="pln">
</span><span class="com">/*
* Another function, but this includes the body of
* the function. This is a "definition".
*/</span><span class="pln">
main</span><span class="pun">(){</span><span class="pln">
     </span><span class="typ">int</span><span class="pln"> count</span><span class="pun">;</span><span class="pln">

     count </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">count </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">10</span><span class="pun">){</span><span class="pln">
             show_message</span><span class="pun">();</span><span class="pln">
             count </span><span class="pun">=</span><span class="pln"> count </span><span class="pun">+</span><span class="pln"> </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">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">/*
* The body of the simple function.
* This is now a "definition".
*/</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> show_message</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">"hello\n"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[تمرين 1.1]
</p>

<h2>
	شرح تمرين 1.1
</h2>

<h3>
	ما الذي احتواه التمرين السابق؟
</h3>

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

<h3>
	التنسيق والتعليق
</h3>

<p>
	تخطيط برنامج مكتوب بلغة سي ليس مهمًّا للمصرف، ولكنه هام لنقل بعض المعلومات عن البرنامج للقارئ البشري ولتسهيل عملية قراءته، إذ تسمح لك لغة سي باستخدام محرف المسافة space ومسافات الجدولة tabs والسطور الجديدة newline دون أن تؤثر على عمل البرنامج. تُدعى المحارف الثلاث المذكور سابقًا باسم <strong>المسافة الفارغة white space</strong>، ولا يميّز بينها المصرّف لأنها ببساطة تغير من موضع الكلمات دون التأثير "مرئيًّا" على ما يُعرض على جهاز الخرج. يمكن أن نلاحظ المسافة البيضاء في أي مكان من البرنامج عدا وسط <strong>المعرّفات Identifiers</strong> و<strong>السلاسل النصية Strings</strong> و<strong>المحارف الثابتة Character constants</strong>؛ إذ نقصد بالمعرفات هنا اسم الدالة أو كائن Object آخر، لا تشغل بالك بالسلاسل النصية والمحارف الثابتة إذ سنناقشها في الفصول القادمة.
</p>

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

<p>
	يبدأ التعليق في لغة C باستخدام المحرفين "*/" المتلاصقين دون أي فراغ بينهما، ويُعد أي شيء يأتي بعدهما إضافةً للمحرفين "/*" مسافةً فارغةً واحدة. لم يكن الأمر مماثلًا في معيار سي القديم، إذ كانت تنص القاعدة على إمكانية استخدام التعليق في أي مكان يمكن أن تُستخدم فيه المسافة الفارغة، أما الآن يُعد التعليق بحد ذاته مسافةً فارغة؛ التغيير الحاصل طفيف وستكون الأمور أكثر وضوحًا في جزئية لاحقة من هذه السلسلة عندما نناقش <strong>المعالج المُسبق preprocessor</strong>. يجعل تنسيق تعليقات لغة سي بهذا الشكل تضمين تعليق بداخل تعليق آخر غير ممكنٍ، نظرًا لإغلاق أول زوج محارف "*/" في التعليق الثاني كتلة التعليق، وهذا إزعاجٌ بسيط ستعتاد عليه لاحقًا.
</p>

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

<h3>
	تعليمات المعالج المسبق
</h3>

<p>
	التعليمة الأولى في مثالنا السابق هي <strong>موجّه للمعالج المُسبق Preprocessor directive</strong>، إذ تضمَّن مصرّف لغة سي سابقًا مرحلتين، هما <strong>المعالجة المُسبقة</strong> ومرحلة التصريف الاعتيادية؛ إذ تمثّل المعالجة المُسبقة <strong>معالج ماكرو macro processor</strong> تجري بعض عمليات التلاعب النصي البسيطة على البرنامج قبل أن تمرّر النص الناتج إلى المصرّف، وقد أصبحت عملية المعالجة المسبقة جزءًا أساسيًّا من عمل المصرّف ولهذا تُعد جزءًا لا يتجزّأ من اللغة.
</p>

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

<p>
	في التمرين 1.1 يؤدي موجّه المعالجة المسبقة <code>include#</code> إلى تبديل السطر هذا بمحتويات ملف آخر، وفي هذه الحالة اسم الملف يوجد ما بين القوسين <code>&lt;</code> و<code>&gt;</code>، وهذه طريقة شائعة لتضمين محتوى <strong>ملفات الترويسات Header files</strong> القياسية ضمن برنامجك، دون الاضطرار لكتابتها بنفسك. يحتوي ملف <code>&lt;stdio.h&gt;</code> المهم العديد من المعلومات الضرورية التي تسمح لك باستخدام مكتبة الدخل والخرج القياسية، بهدف الحصول على الدخل وإظهار الخرج، فإذا أردت استخدام هذه المكتبة عليك أن <strong>تتأكد</strong> من وجود <code>&lt;stdio.h&gt;</code>. كان معيار سي السابق متساهلًا أكثر بهذا الشأن.
</p>

<h4>
	تعليمات التعريف Define
</h4>

<p>
	تُعد تعليمة <code>define#</code> من الإمكانيات الأخرى والمستخدمة كثيرًا للمُعالج المُسبق، إذ تُستخدم على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5703_11" style=""><span class="com">#define</span><span class="pln"> IDENTIFIER      replacement</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5703_13" style=""><span class="com">#define</span><span class="pln"> PI             </span><span class="lit">3.141592</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> SECS_PER_MIN   </span><span class="lit">60</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> MINS_PER_HOUR  </span><span class="lit">60</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> HOURS_PER_DAY  </span><span class="lit">24</span></pre>

<p>
	واستخدام القيم على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5703_15" style=""><span class="pln">circumf </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">*</span><span class="pln">PI</span><span class="pun">*</span><span class="pln">radius</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">if</span><span class="pun">(</span><span class="pln">timer </span><span class="pun">&gt;=</span><span class="pln"> SECS_PER_MIN</span><span class="pun">){</span><span class="pln">
mins </span><span class="pun">=</span><span class="pln"> mins</span><span class="pun">+</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
        timer </span><span class="pun">=</span><span class="pln"> timer </span><span class="pun">-</span><span class="pln"> SECS_PER_MIN</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_5703_17" style=""><span class="pln">circumf </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">*</span><span class="lit">3.141592</span><span class="pun">*</span><span class="pln">radius</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">if</span><span class="pun">(</span><span class="pln">timer </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">60</span><span class="pun">){</span><span class="pln">
        mins </span><span class="pun">=</span><span class="pln"> mins</span><span class="pun">+</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
       timer </span><span class="pun">=</span><span class="pln"> timer </span><span class="pun">-</span><span class="pln"> </span><span class="lit">60</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أخيرًا، نستطيع تلخيص ما سبق على النحو التالي:
</p>

<ul>
	<li>
		تتعامل تعليمات المعالج المُسبق مع الملفات النصية سطرًا بسطر، على نقيض لغة سي.
	</li>
	<li>
		تُستخدم التعليمات من النوع <code>include#</code> لقراءة محتوًى من ملفٍ معين، عادةً لتسهيل التعامل مع دوال مكتبةٍ ما.
	</li>
	<li>
		تُستخدم تعليمات <code>define#</code> لتسمية الثوابت، وتُكتب الأسماء اصطلاحًا بحروفٍ كبيرة.
	</li>
</ul>

<h2>
	تعريف وتصريح الدالة
</h2>

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

<p>
	نلاحظ وجود ما يُسمى <strong>تصريح الدالة function declaration</strong> بعد تضمين ملف <code>&lt;stdio.h&gt;</code>، الذي يخبر المصرّف أن <code>show_message</code> دالة لا تأخذ أي وسيط ولا تُعيد أي قيمة، ويوضح لنا هذا تغييرًا جرى على المعيار، ألا وهو <strong>النموذج الأولي للدالة function prototype</strong>، وسنناقش هذا الموضوع بتوسّع لاحقًا. ليس من الضروري التصريح عن الدالة مسبقًا، إذ ستستخدم لغة سي بعض القواعد القديمة الافتراضية في هذه الحالة، إلا أنه ينصح بشدة التصريح عن الدالة في البداية.
</p>

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

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

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

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

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

<p>
	إما أن يكون استخدام القيمة <code>void</code> بديهيًّا لك، أو صعب الفهم حسب الطريقة التي تنظر لها للأمر. ففي الحقيقة، يمكننا الدخول في نقاشات فلسفيّة جانبية وغير مثمرة عن كون <code>void</code> يصف نوع قيمةٍ أو لا، لكن أفضل تجنب ذلك. بغض النظر عن رأيك، استخدام النوع <code>void</code> التي تعني: "أنا لا أهتم بأي قيمة ترجعها هذه الدالة (أو لا ترجعها)".
</p>

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

<p>
	يشكّل متن الدالة <strong>تعليمة مركبة compound statement</strong>، وهي مجموعةٌ من التعليمات المُحاطة بأقواس معقوصة <code>{}</code>، على الرغم من وجود تعليمة واحدة فقط إلا أن الأقواس مطلوبة، وعمومًا، تسمح لك لغة سي باستخدام تعليمة مركبة في أي مكان تسمح به عادةً باستخدام تعليمة واحدة بسيطة، وتهدف الأقواس المعقوصة لتحويل عدة تعليمات متتالية إلى تعليمة واحدة.
</p>

<p>
	إذا سألت السؤال المبرَّر "هل يتوجب استخدام الأقواس المعقوصة في كل مكان، إذا كان الهدف منها جمع عدّة تعليمات لتعليمة واحدة؟" الإجابة: نعم، <strong>عليك</strong> استخدام الأقواس المعقوصة، والمكان الوحيد الذي لا تستطيع فيه استخدام تعليمة واحدة عوضًا عن تعليمة مركبة هو عند تعريف دالةٍ ما. بالتالي، أبسط دالة يمكننا إنشاؤها هي دالةٌ فارغة، لا تفعل أي عملٍ إطلاقًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5703_19" style=""><span class="kwd">void</span><span class="pln"> do_nothing</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">){}</span></pre>

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

<p>
	خُلاصة القول:
</p>

<ul>
	<li>
		تُستخدم <strong>التصريحات</strong> في التصريح عن اسم الدالة ونوع القيمة المُعادة ونوع قيم الوسطاء إن وُجدت.
	</li>
	<li>
		<strong>تعريف</strong> الدالة هي تصريح للدالة مع محتواها أيضًا.
	</li>
	<li>
		يُصرَّح عن الدالة التي لا تعيد أي قيمة باستخدام الكلمة <code>void</code>، على سبيل المثال:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5703_22" style=""><span class="pun">;</span><span class="kwd">void</span><span class="pln"> func</span><span class="pun">(</span><span class="com">/* list of arguments */</span><span class="pun">)</span></pre>

<ul>
	<li>
		يجب أن يصرّح عن الدالة التي لا تأخذ أي وسيط باستخدام الكلمة <code>void</code> ضمن لائحة الوسطاء، على سبيل المثال:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5703_24" style=""><span class="pun">;</span><span class="kwd">void</span><span class="pln"> func</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span></pre>

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

<p>
	تُعرَّف السلاسل النصية في لغة سي بأنها سلسلة من المحارف المحتواة داخل علامتي تنصيص على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5703_26" style=""><span class="str">"like this"</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5703_28" style=""><span class="str">"This is a valid string"</span><span class="pln">
</span><span class="str">"</span><span class="typ">This</span><span class="pln"> has a newline in it
and is NOT a valid string</span><span class="str">"</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5703_30" style=""><span class="str">"</span><span class="typ">This</span><span class="pln"> would not be valid but doesn</span><span class="str">'</span><span class="pln">t have \
a newline in it as far as the compiler is concerned</span><span class="str">"</span></pre>

<p>
	الحل الثاني هو باستخدام ميّزة ضم السلاسل النصية، التي تنظر لكل سلسلتين نصيتين متجاورتين على أنهما سلسلةٌ نصيةٌ واحدة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5703_32" style=""><span class="str">"All this "</span><span class="pln"> </span><span class="str">"comes out as "</span><span class="pln">
</span><span class="str">"just one string"</span></pre>

<p>
	بالعودة للمثال 1.1، تُعد السلسلة النصية "n\" مثالًا على ما يُدعى <strong>محرف الهروب escape character</strong> ويمثّل في حالتنا هذه حالة إنشاء سطر جديد، وستطبع الدالة <code>printf</code> محتوى السلسلة النصية على ملف برنامج الخرج، بحيث سيكون الخرج "Hello" متبوعًا بسطرٍ جديد.
</p>

<p>
	يسمح المعيار الجديد باستخدام <strong>المحارف ذات البايتات المتعدّدة multibyte characters</strong> لدعم الأشخاص العاملين ببيئة تستخدم مجموعة محارف أوسع من معيار آسكي ASCII الأميركي، مثل معيار Shift JIS المُستخدم في اليابان. يعرّف المعيار الجديد 96 محرفًا تمثِّل أبجدية لغة سي (المتطرّق لها في مقال أبجدية لغة C)، وفي حال كان نظامك يستخدم مجموعة محارف موسّعة extended، فسيكون المكان الوحيد الذي قد تستخدمها هو بداخل سلسلة نصية، أو متغيرات من نوع محرف، أو ضمن التعليقات وأسماء <strong>ملفات الترويسة Header files</strong>. ينبغي عليك تفقُّد ملفات توثيق نظامك إذا أردت استخدام ميزة دعم المحارف الموسّعة.
</p>

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

<p>
	يحتوي المثال 1.1 على دالتين، هما: دالة <code>show_message</code> ودالة <code>main</code>؛ فإذا صرفنا النظر عن طول دالة <code>main</code> موازنةً مع الدالة <code>show_message</code>، فسنلاحظ أن الدالتين مبنيتان بالشكل نفسه، إذ تحتوي كلا الدالتين على اسمٍ وقوسين <code>()</code> متبوعين بقوس معقوص، وتعليمةٍ مركبة محتواة داخل القوسين المعقوصين تتبٍّع تعريف الدالة. على الرغم من أن الدالة المركبة مختلفةٌ عن الدالة الأخرى، إلا أنك ستجد قوس الإغلاق المعقوص نفسه <code>{</code> الذي يتماشى مع القوس الأول <code>}</code>.
</p>

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

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

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

<p>
	التعليمة الأولى هي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5703_34" style=""><span class="typ">int</span><span class="pln"> count</span><span class="pun">;</span></pre>

<p>
	وهي ليست تعليمة لتنفيذ أمرٍ ما، بل تعلن عن متغيرٍ جديد ضمن البرنامج، واسمه <code>count</code> من نوع "عدد صحيح integer" والكلمة التي تحدد هذا النوع من المتغيرات ضمن برنامج سي مُختصرة إلى الكلمة <code>int</code>. لا تدل جميع أسماء أنواع المتغيرات على نوعها بوضوح في لغة سي، إذ يُستخدم اسم نوع المتغير في بعض الأحيان مثل كلمةٍ مفتاحية مختصرة وفي أحيان أخرى يُكتب كاملًا. لحسن الحظ يمكن تخمين نوع البيانات للكلمة <code>int</code> بقراءتها.
</p>

<p>
	بفضل هذه التعليمة، يعلم المصرّف الآن أن هناك متغيّرًا باسم <code>count</code> لتخزين قيم الأعداد الصحيحة. يجب التصريح عن جميع المتغيرات قبل استخدامها في لغة سي، على نقيض لغة FORTRAN، التي يجب أن يأتي التصريح قبل استخدام المتغير ضمن أي تعليمة.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix" data-gramm="false">
		<p>
			<strong>ملاحظة:</strong> يُعد التصريح عن المتغير <code>count</code> <strong>تعريفًا</strong> عنه في ذات الوقت، وسنناقش الفرق بين الاثنين وسنلاحظ أن الفرق مهم.
		</p>
	</div>
</blockquote>

<h3>
	تعليمة الإسناد
</h3>

<p>
	بالانتقال إلى السطور التالية، نلاحظ <strong>تعليمة الإسناد assignment statement</strong>، وهي التعليمة التي أسندت أول قيمة للمتغير <code>count</code> (القيمة هي صفر في حالتنا هذه). كانت قيمة المتغير <code>count</code> قبل تعليمة الإسناد غير معرّفة undefined وغير آمنة الاستخدام، وربما تتفاجئ بحقيقة أن رمز الإسناد -أي <strong>عامل الإسناد assignment operator</strong>- يُمثَّل بإشارة مساواة واحدة <code>=</code>، وهذا التمثيل مستخدمٌ في معظم لغات البرمجة الحديثة، ولكنه ليس بمشكلة كبيرة.
</p>

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

<h3>
	تعليمة الحلقة التكرارية While
</h3>

<p>
	سنتكلم عن واحدة من تعليمات التحكم بالحلقات، ألا وهي تعليمة <code>while</code> لنلقي نظرةً على شكلها العام:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5703_36" style=""><span class="kwd">while</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">)</span><span class="pln">
        statement</span></pre>

<p>
	هل هذا كل ما هنالك؟ نعم، هذه بنية تعليمة <code>while</code>، الجزء التالي في مثالنا هو:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5703_38" style=""><span class="pln">count </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">10</span></pre>

<p>
	ويدعى باسم <strong>التعبير العلائقي relational expression</strong>، وهو مثال عن إحدى التعبيرات الصالحة الممكن استخدامها في جملة الحلقة التكرارية -مكان <code>expression</code>- ويُتبَع التعبير بتعليمة مركّبة، وهي مثال عن التعليمات الصالحة أيضًا الممكن استخدامها -مكان <code>statement</code>-. وهكذا، نستطيع تشكيل تعليمة <code>while</code> صحيحة.
</p>

<p>
	إذا برمجت برنامجًا في السابق، فستلاحظ أن متن الحلقة سينفَّذ بصورةٍ متكررة طالما أن التعبير <code>count &lt; 10</code> صحيح؛ وإذا أردنا برمجة حلقةٍ منتهية، فعلينا كتابة تعليمة ما تتسبّب بجعل التعبير السابق خاطئًا في مرحلة من المراحل، وهذا موجود في مثالنا فعلًا.
</p>

<p>
	هناك تعليمتان فقط داخل متن الحلقة، إذ تمثِّل الأولى استدعاءً لدالة <code>show_message</code> وتُكتب تعليمة استدعاء الدالة منتهيةً بقوسين <code>()</code> تحتويان ضمنهما لائحة الوسطاء؛ فإذا لم تحتوي الدالة على أي وسيط، نكتب قوسين فارغين؛ وإذا احتوت على وسيط أو عدة وسطاء نكتب ذلك ضمن القوسين على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5703_40" style=""><span class="com">/* call a function with several arguments */</span><span class="pln">
function_name</span><span class="pun">(</span><span class="pln">first_arg</span><span class="pun">,</span><span class="pln"> second_arg</span><span class="pun">,</span><span class="pln"> third_arg</span><span class="pun">);</span></pre>

<p>
	استدعاء الدالة <code>printf</code> هو مثال آخر، وسنشرح المزيد عن هذا الموضوع في جزئية لاحقة من هذه السلسلة.
</p>

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

<h3>
	تعليمة الإعادة return
</h3>

<p>
	التعليمة الأخيرة المتبقية في المثال هي تعليمة <code>return</code>، وتبدو للوهلة الأولى أنها استدعاءٌ لدالة ٍما، ولكن التعليمة تُكتب على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5703_42" style=""><span class="kwd">return</span><span class="pln"> expression</span><span class="pun">;</span></pre>

<p>
	والتعبير <code>expression</code> اختياري، إذ يتبِّع مثالنا تنسيقًا شائعًا ألا وهو وضع التعبير ضمن قوسين، ولكن الأقواس غير ضرورية ولا تؤثر على عمل التعليمة.
</p>

<p>
	تعيد تعليمة <code>return</code> قيمةً من الدالة الحالية الواقعة ضمنها إلى مستدعي caller الدالة؛ فإذا لم تُزوَّد التعليمة بالتعبير <code>expression</code> فستعاد قيمةٌ غير معروفة unknown إلى المستدعي، وهو أمرٌ خاطئ، عدا حالة إعادة <code>void</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5703_44" style=""><span class="typ">int</span><span class="pln"> main</span><span class="pun">(){</span></pre>

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

<p>
	لكن ما الذي تعني القيمة المُعادة من الدالة <code>main</code> وإلى أين تُرسل؟ كانت القيمة تُرسل إلى نظام التشغيل أو الجهة التي شغّلت البرنامج في لغة سي القديمة، وفي البيئات المشابهة لنظام يونيكس UNIX، كانت القيمة "0" تعني "نجاح" العملية، بينما يدلّ على "فشل" العملية أي رقم آخر (غالبًا "1-"). حافظ المعيار عند قدومه على هذا التقليد، إذ يدل "0" على التنفيذ الناجح للبرنامج، ولكن هذا <strong>لا يعني</strong> أن القيمة "0" تُمرّر إلى البيئة المُضيفة، بل تُمرّر القيمة المناسبة للدلالة على نجاح البرنامج ضمن هذا النظام. يسبب هذا الكثير من الالتباس، لذا قد تحبذ استخدام القيمتين المعرّفتين "EXIT_SUCCESS" و"EXIT_FAILURE" ضمن ملف الترويسة <code>&lt;stdlib.h&gt;</code>.
</p>

<p>
	يماثل استخدام التعليمة <code>return</code> ضمن الدالة <code>main</code> استخدام الدالة <code>exit</code> مزوّدًا بالقيمة المُعادة وسيطًا، والفرق هنا أن الدالة <code>exit</code> قد تُستدعى من <strong>أي مكان</strong> ضمن البرنامج، وأن توقف البرنامج في النقطة التي استُدعيت فيها بعد إنجاز بعض العمليات النهائية. إذا أردت استخدام الدالة <code>exit</code>، <strong>يجب</strong> عليك تضمين ملف الترويسة <code>&lt;stdlib.h&gt;</code>، ومن هذه اللحظة فصاعدًا سنستخدم <code>exit</code> بدلًا من استخدام <code>return</code> ضمن الدالة <code>main</code>.
</p>

<h2>
	الملخص
</h2>

<p>
	تُعيد الدالة <code>main</code> قيمةً من نوع <code>int</code>.
</p>

<p>
	يُماثل استخدام التعليمة <code>return</code> ضمن الدالة <code>main</code> استدعاء الدالة <code>exit</code>، الفرق هنا أنه من الممكن استدعاء <code>exit</code> في أي نقطة ضمن البرنامج.
</p>

<p>
	إعادة القيمة 0 أو <code>EXIT_SUCCESS</code> يعني نجاح البرنامج، بينما تُعد أي قيمة أخرى فشلًا للبرنامج.
</p>

<p>
	مع أنّ المثال المُناقش قصير، إلا أنه سمح لنا بمناقشة العديد من مزايا اللغة المهمة، وهي:
</p>

<ul>
	<li>
		بنية البرنامج Program structure.
	</li>
	<li>
		التعليق Comment.
	</li>
	<li>
		تضمين الملفات File inclusion.
	</li>
	<li>
		تعريف الدوال Function definition.
	</li>
	<li>
		التعليمات المركبة Compound statements.
	</li>
	<li>
		استدعاء الدوال Function calling.
	</li>
	<li>
		التصريح عن المتغيرات Variable declaration.
	</li>
	<li>
		العمليات الحسابية Arithmetic.
	</li>
	<li>
		الحلقات التكرارية Looping.
	</li>
</ul>

<p>
	لكننا طبعًا لم نناقش هذه المواضيع بتعمّق بعد.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://publications.gbdirect.co.uk/c_book/chapter1/" rel="external nofollow">Chapter 1 An Introduction to 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%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%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="">إدارة الذاكرة (Memory management) في لغة 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%B9%D8%A7%D8%B4%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D8%B1%D8%B7%D9%8A%D8%A9-%D9%88%D8%AD%D9%84%D9%87%D8%A7-%D9%85%D8%B4%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1013/" 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%AD%D8%A7%D8%AF%D9%8A-%D8%B9%D8%B4%D8%B1-%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%AA%D9%82%D9%8A%D9%8A%D8%AF-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-semaphores-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%B3%D9%8A-c-r1014/" rel="">متغيرات تقييد الوصول (Semaphores) في لغة البرمجة سي C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1607</guid><pubDate>Wed, 06 Jul 2022 16:08:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x641;&#x635;&#x644; &#x627;&#x644;&#x62D;&#x627;&#x62F;&#x64A; &#x639;&#x634;&#x631;: &#x645;&#x62A;&#x63A;&#x64A;&#x631;&#x627;&#x62A; &#x62A;&#x642;&#x64A;&#x64A;&#x62F; &#x627;&#x644;&#x648;&#x635;&#x648;&#x644; (Semaphores) &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629; &#x633;&#x64A; C</title><link>https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%AD%D8%A7%D8%AF%D9%8A-%D8%B9%D8%B4%D8%B1-%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%AA%D9%82%D9%8A%D9%8A%D8%AF-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-semaphores-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%B3%D9%8A-c-r1014/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_10/11.png.644450a8d4fad2f68910348e65b48206.png" /></p>

<p>
	متغيرات تقييد الوصول (Semaphores) طريقةٌ جيدة للتعرف على التزامن، ولكنها ليست مستخدمة على نطاق واسع من الناحية العملية كاستخدام كائنات المزامنة (mutexes) والمتغيرات الشرطية (condition variables)، ومع ذلك توجد بعض مشاكل المزامنة التي يمكن حلها ببساطة باستخدام متغيرات تقييد الوصول، مما يؤدي إلى الوصول إلى حلول صحيحة ودقيقة. تقدّم هذه المقالة واجهة برمجة التطبيقات بلغة C للعمل مع متغيرات تقييد الوصول، وكتابة تطبيق لمتغير تقييد الوصول (semaphore) باستخدام كائنات المزامنة (mutexes) والمتغيرات الشرطية (condition variables).
</p>

<h2>
	POSIX Semaphores
</h2>

<p>
	متغير تقييد الوصول (semaphore) هو بنية بيانات تُستخدم لمساعدة الخيوط أن تعمل مع بعضها البعض دون تداخلٍ فيما بينها، يحدد POSIX القياسي واجهةً لمتغيرات تقييد الوصول، وهي ليست جزءًا من الخيوط Pthreads، ولكن توفّر معظم نظم التشغيل التي تعتمد على يونكس والتي تطبق Pthreads متغيرات تقييد الوصول أيضًا. لمتغيرات تقييد الوصول POSIX نوع هو <code>sem_t</code>، ووضع مغلّف له لجعل استخدامه أسهل كالعادة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6064_7" style="">
<span class="kwd">typedef</span><span class="pln"> </span><span class="typ">sem_t</span><span class="pln"> </span><span class="typ">Semaphore</span><span class="pun">;</span><span class="pln">

</span><span class="typ">Semaphore</span><span class="pln"> </span><span class="pun">*</span><span class="pln">make_semaphore</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> value</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> semaphore_wait</span><span class="pun">(</span><span class="typ">Semaphore</span><span class="pln"> </span><span class="pun">*</span><span class="pln">sem</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> semaphore_signal</span><span class="pun">(</span><span class="typ">Semaphore</span><span class="pln"> </span><span class="pun">*</span><span class="pln">sem</span><span class="pun">);</span></pre>

<p>
	<code>Semaphore</code> هو مرادف للنوع <code>sem_t</code>، ولكنني، يقول الكاتب، وجدت <code>Semaphore</code> أسهل للقراءة وذكّرني الحرف الكبير في أوله بمعاملته ككائن (object) وتمريره كمؤشر (pointer):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6064_12" style="">
<span class="typ">Semaphore</span><span class="pln"> </span><span class="pun">*</span><span class="pln">make_semaphore</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> value</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Semaphore</span><span class="pln"> </span><span class="pun">*</span><span class="pln">sem </span><span class="pun">=</span><span class="pln"> check_malloc</span><span class="pun">(</span><span class="kwd">sizeof</span><span class="pun">(</span><span class="typ">Semaphore</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"> sem_init</span><span class="pun">(</span><span class="pln">sem</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
        perror_exit</span><span class="pun">(</span><span class="str">"sem_init failed"</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> sem</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تأخذ الدالة <code>make_semaphore</code> القيمة الابتدائية لمتغير تقييد الوصول كمعاملٍ لها، وتخصص حيزًا له وتهيئه ثم تعيد مؤشرًا إلى <code>Semaphore</code>. تعيد الدالة <code>sem_init</code> القيمة 0 إذا نجح تنفيذها وتعيد -1 إذا حدث خطأ ما. أحد الأمور الجيدة لاستخدام الدوال المغلّفة هو أنك تستطيع تغليف (encapsulate) شيفرة التحقق من الخطأ، مما يجعل الشيفرة التي تستخدم هذه الدوال أسهل للقراءة. يمكن تطبيق الدالة <code>semaphore_wait</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6064_16" style="">
<span class="kwd">void</span><span class="pln"> semaphore_wait</span><span class="pun">(</span><span class="typ">Semaphore</span><span class="pln"> </span><span class="pun">*</span><span class="pln">sem</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"> n </span><span class="pun">=</span><span class="pln"> sem_wait</span><span class="pun">(</span><span class="pln">sem</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
        perror_exit</span><span class="pun">(</span><span class="str">"sem_wait failed"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	والدالة <code>semaphore_signal</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6064_18" style="">
<span class="kwd">void</span><span class="pln"> semaphore_signal</span><span class="pun">(</span><span class="typ">Semaphore</span><span class="pln"> </span><span class="pun">*</span><span class="pln">sem</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"> n </span><span class="pun">=</span><span class="pln"> sem_post</span><span class="pun">(</span><span class="pln">sem</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
        perror_exit</span><span class="pun">(</span><span class="str">"sem_post failed"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أفضّل، يقول الكاتب، أن أسمي عملية تنبيه الخيط المتوقف بالمصطلح signal على أن أسميها بالمصطلح post على الرغم أن كلا المصطلحين شائعي الاستخدام. يظهر المثال التالي كيفية استخدام متغير تقييد الوصول ككائن مزامنة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6064_20" style="">
<span class="typ">Semaphore</span><span class="pln"> </span><span class="pun">*</span><span class="pln">mutex </span><span class="pun">=</span><span class="pln"> make_semaphore</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">

semaphore_wait</span><span class="pun">(</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
</span><span class="com">// protected code goes here</span><span class="pln">
semaphore_signal</span><span class="pun">(</span><span class="pln">mutex</span><span class="pun">);</span></pre>

<p>
	يجب أن تهيئ متغير تقييد الوصول الذي تستخدمه ككائن مزامنة بالقيمة 1 لتحدد أن كائن المزامنة غير مقفل، أي يستطيع خيطٌ واحد تمرير متغير تقييد الوصول دون توقف. استخدم اسم المتغير <code>mutex</code> للدلالة على أن متغير تقييد الوصول استخدم ككائن مزامنة، ولكن تذكّر أن سلوك متغير تقييد الوصول مختلف عن كائن مزامنة الخيط Pthread.
</p>

<h2>
	المنتجون والمستهلكون مع متغيرات تقييد الوصول (Producers and consumers with
</h2>

<p>
	semaphores) يمكن كتابة حل لمشكلة منتج-مستهلك باستخدام دوال مغلّفة لمتغير تقييد الوصول، حيث يصبح التعريف الجديد للبنية <code>Queue</code> باستبدال كائن المزامنة والمتغيرات الشرطية بمتغيرات تقييد الوصول كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6064_22" style="">
<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="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">array</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> length</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> next_in</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> next_out</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">Semaphore</span><span class="pln"> </span><span class="pun">*</span><span class="pln">mutex</span><span class="pun">;</span><span class="pln">  </span><span class="com">//-- جديد</span><span class="pln">
    </span><span class="typ">Semaphore</span><span class="pln"> </span><span class="pun">*</span><span class="pln">items</span><span class="pun">;</span><span class="pln">  </span><span class="com">//-- جديد</span><span class="pln">
    </span><span class="typ">Semaphore</span><span class="pln"> </span><span class="pun">*</span><span class="pln">spaces</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="typ">Queue</span><span class="pun">;</span></pre>

<p>
	والنسخة الجديدة من الدالة <code>make_queue</code> هي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6064_24" style="">
<span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="pln">make_queue</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> length</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="typ">queue</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Queue</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">Queue</span><span class="pun">));</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">length </span><span class="pun">=</span><span class="pln"> length</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">array </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="pln">length </span><span class="pun">*</span><span class="pln"> </span><span class="kwd">sizeof</span><span class="pun">(</span><span class="typ">int</span><span class="pun">));</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_in </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">queue</span><span class="pun">-&gt;</span><span class="pln">next_out </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">queue</span><span class="pun">-&gt;</span><span class="pln">mutex </span><span class="pun">=</span><span class="pln"> make_semaphore</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">items </span><span class="pun">=</span><span class="pln"> make_semaphore</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">spaces </span><span class="pun">=</span><span class="pln"> make_semaphore</span><span class="pun">(</span><span class="pln">length </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">return</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُستخدم المتغير <code>mutex</code> لضمان الوصول الحصري إلى الطابور، حيث قيمته الابتدائية هي 1 وبالتالي كائن المزامنة غير مقفل مبدئيًا. المتغير <code>items</code> هو عدد العناصر الموجودة في الطابور والذي هو أيضًا عدد الخيوط المستهلكة التي يمكن أن تنفذ الدالة <code>queue_pop</code> دون توقف، ولا يوجد أي عنصر في الطابور مبدئيًا. المتغير <code>spaces</code> هو عدد المساحات الفارغة في الطابور وهو أيضًا عدد الخيوط المنتجة التي يمكن أن تنفّذ الدالة <code>queue_push</code> دون توقف، ويمثل عدد المساحات مبدئيًا سعة الطابور وتساوي <code>length-1</code>. النسخة الجديدة من الدالة <code>queue_push</code> التي تشغّلها الخيوط المنتجة هي كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6064_26" style="">
<span class="kwd">void</span><span class="pln"> queue_push</span><span class="pun">(</span><span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="typ">queue</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> item</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    semaphore_wait</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">spaces</span><span class="pun">);</span><span class="pln">
    semaphore_wait</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">

    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">array</span><span class="pun">[</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_in</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> item</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_in </span><span class="pun">=</span><span class="pln"> queue_incr</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">,</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_in</span><span class="pun">);</span><span class="pln">

    semaphore_signal</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
    semaphore_signal</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">items</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6064_28" style="">
<span class="typ">int</span><span class="pln"> queue_pop</span><span class="pun">(</span><span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="typ">queue</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    semaphore_wait</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">items</span><span class="pun">);</span><span class="pln">
    semaphore_wait</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">

    </span><span class="typ">int</span><span class="pln"> item </span><span class="pun">=</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">array</span><span class="pun">[</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_out</span><span class="pun">];</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_out </span><span class="pun">=</span><span class="pln"> queue_incr</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">,</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_out</span><span class="pun">);</span><span class="pln">

    semaphore_signal</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
    semaphore_signal</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">spaces</span><span class="pun">);</span><span class="pln">

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

<p>
	شُرح هذا الحل باستخدام شيفرة عامة (pseudo-code) في الفصل الرابع من كتاب <em>The Little Book of Semaphores</em>.
</p>

<h2>
	صناعة متغيرات تقييد وصول خاصة
</h2>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6064_30" style="">
<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="typ">int</span><span class="pln"> value</span><span class="pun">,</span><span class="pln"> wakeups</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">Mutex</span><span class="pln"> </span><span class="pun">*</span><span class="pln">mutex</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">Cond</span><span class="pln"> </span><span class="pun">*</span><span class="pln">cond</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="typ">Semaphore</span><span class="pun">;</span></pre>

<p>
	المتغير <code>value</code> هو قيمة متغير تقييد الوصول، ويحصي المتغير <code>wakeups</code> عدد التنبيهات المعلقة (pending signals)، أي عدد الخيوط التي تنبّهت ولكنها لم تستأنف تنفيذها بعد، والسبب وراء استخدام <code>wakeups</code> هو التأكد من أن متغيرات تقييد الوصول الخاصة بك لديها الخاصية 3 المشروحة في كتاب <em>The Little Book of Semaphores</em>. يوفر المتغير <code>mutex</code> الوصول الحصري إلى لمتغيرين <code>value</code> و <code>wakeups</code>، المتغير <code>cond</code> هو المتغير الشرطي الذي تنتظره الخيوط إذا كانت تنتظر متغير تقييد الوصول. تمثل الشيفرة التالية شيفرة التهيئة للبنية <code>Semaphore</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6064_32" style="">
<span class="typ">Semaphore</span><span class="pln"> </span><span class="pun">*</span><span class="pln">make_semaphore</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> value</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Semaphore</span><span class="pln"> </span><span class="pun">*</span><span class="pln">semaphore </span><span class="pun">=</span><span class="pln"> check_malloc</span><span class="pun">(</span><span class="kwd">sizeof</span><span class="pun">(</span><span class="typ">Semaphore</span><span class="pun">));</span><span class="pln">
    semaphore</span><span class="pun">-&gt;</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> value</span><span class="pun">;</span><span class="pln">
    semaphore</span><span class="pun">-&gt;</span><span class="pln">wakeups </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
    semaphore</span><span class="pun">-&gt;</span><span class="pln">mutex </span><span class="pun">=</span><span class="pln"> make_mutex</span><span class="pun">();</span><span class="pln">
    semaphore</span><span class="pun">-&gt;</span><span class="pln">cond </span><span class="pun">=</span><span class="pln"> make_cond</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> semaphore</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	تطبيق متغير تقييد الوصول (Semaphore implementation)
</h2>

<p>
	تطبيقي، يقول الكاتب، لمتغيرات تقييد الوصول باستخدام كائنات المزامنة POSIX والمتغيرات الشرطية كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6064_34" style="">
<span class="kwd">void</span><span class="pln"> semaphore_wait</span><span class="pun">(</span><span class="typ">Semaphore</span><span class="pln"> </span><span class="pun">*</span><span class="pln">semaphore</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    mutex_lock</span><span class="pun">(</span><span class="pln">semaphore</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
    semaphore</span><span class="pun">-&gt;</span><span class="pln">value</span><span class="pun">--;</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">semaphore</span><span class="pun">-&gt;</span><span class="pln">value </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="kwd">do</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            cond_wait</span><span class="pun">(</span><span class="pln">semaphore</span><span class="pun">-&gt;</span><span class="pln">cond</span><span class="pun">,</span><span class="pln"> semaphore</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">semaphore</span><span class="pun">-&gt;</span><span class="pln">wakeups </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln">
        semaphore</span><span class="pun">-&gt;</span><span class="pln">wakeups</span><span class="pun">--;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    mutex_unlock</span><span class="pun">(</span><span class="pln">semaphore</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يجب على الخيط الذي ينتظر متغير تقييد الوصول أن يقفل كائن المزامنة قبل إنقاص قيمة المتغير <code>value</code>، وإذا أصبحت قيمة متغير تقييد الوصول سالبة سيتوقف الخيط حتى يصبح التنبه (wakeup) متاحًا، وطالما الخيط متوقف فإن كائن المزامنة غير مقفل، وبالتالي يمكن أن يتنبه (signal) خيطٌ آخر. شيفرة الدالة <code>semaphore_signal</code> هي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6064_36" style="">
<span class="kwd">void</span><span class="pln"> semaphore_signal</span><span class="pun">(</span><span class="typ">Semaphore</span><span class="pln"> </span><span class="pun">*</span><span class="pln">semaphore</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    mutex_lock</span><span class="pun">(</span><span class="pln">semaphore</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
    semaphore</span><span class="pun">-&gt;</span><span class="pln">value</span><span class="pun">++;</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">semaphore</span><span class="pun">-&gt;</span><span class="pln">value </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">
        semaphore</span><span class="pun">-&gt;</span><span class="pln">wakeups</span><span class="pun">++;</span><span class="pln">
        cond_signal</span><span class="pun">(</span><span class="pln">semaphore</span><span class="pun">-&gt;</span><span class="pln">cond</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    mutex_unlock</span><span class="pun">(</span><span class="pln">semaphore</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يجب على الخيط مرة أخرى أن يقفل كائن المزامنة قبل زيادة قيمة المتغير <code>value</code>، وإذا كانت قيمة متغير تقييد الوصول سالبة فهذا يعني أن الخيوط منتظرة، وبالتالي يزيد تنبيه الخيط قيمة المتغير <code>wakeups</code> ثم ينبه المتغير الشرطي،وعند ذلك قد تتنبه أحد الخيوط المنتظرة ولكن يبقى كائن المزامنة مقفلًا حتى يفك الخيط المتنبه قفله، وعند ذلك أيضًا تعيد أحد الخيوط المنتظرة من الدالة <code>cond_wait</code> ثم تتحقق من أن التنبيه ما زال متاحًا، فإذا لم يكن متاحًا فإن الخيط يعود وينتظر المتغير الشرطي مرة أخرى، أما إذا كان التنبيه متاحًا فإن الخيط ينقص قيمة المتغير <code>wakeups</code> ويفك قفل كائن المزامنة ثم يغادر. قد يوجد شيء واحد ليس واضحًا في هذا الحل وهو استخدام حلقة <code>do...while</code>، هل يمكنك معرفة سبب عدم كونها حلقة <code>while</code> تقليدية؟ وما الخطأ الذي سيحدث؟
</p>

<p>
	المشكلة مع حلقة <code>while</code> هي أنه قد لا يملك هذا التطبيق الخاصية 3، فمن الممكن أن يتنبه الخيط ثم يُشغّل ويلتقط تنبيهه الخاص. من المضمون مع حلقة <code>do...while</code> أن يلتقط أحد الخيوط المنتظرة التنبيه الذي أنشأه خيطٌ ما، حتى إذا شُغّل خيط التنبيه وحصل على كائن المزامنة قبل استئناف أحد الخيوط المنتظرة، ولكن اتضح أنه يمكن أن ينتهك التنبيه الزائف في الوقت المناسب (<a href="https://en.wikipedia.org/wiki/Spurious_wakeup" rel="external nofollow">well-timed spurious wakeup</a>) هذا الضمان.
</p>

<p>
	ترجمة -وبتصرّف- للفصل Semaphores in C من كتاب <a href="http://greenteapress.com/thinkos/" rel="external nofollow">Think OS A Brief Introduction to Operating Systems</a>
</p>
]]></description><guid isPermaLink="false">1014</guid><pubDate>Tue, 06 Oct 2020 10:32:15 +0000</pubDate></item><item><title>&#x627;&#x644;&#x641;&#x635;&#x644; &#x627;&#x644;&#x639;&#x627;&#x634;&#x631;: &#x627;&#x644;&#x645;&#x62A;&#x63A;&#x64A;&#x631;&#x627;&#x62A; &#x627;&#x644;&#x634;&#x631;&#x637;&#x64A;&#x629; &#x648;&#x62D;&#x644;&#x647;&#x627; &#x645;&#x634;&#x627;&#x643;&#x644; &#x627;&#x644;&#x62A;&#x632;&#x627;&#x645;&#x646; &#x628;&#x64A;&#x646; &#x627;&#x644;&#x639;&#x645;&#x644;&#x64A;&#x627;&#x62A; &#x641;&#x64A; &#x644;&#x63A;&#x629; C</title><link>https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%B9%D8%A7%D8%B4%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D8%B1%D8%B7%D9%8A%D8%A9-%D9%88%D8%AD%D9%84%D9%87%D8%A7-%D9%85%D8%B4%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r1013/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_10/10.png.f0eda38436b7960d782d8f1d9d43ee44.png" /></p>

<p>
	يمكن حل العديد من مشاكل التزامن (synchronization) البسيطة باستخدام كائنات المزامنة (mutexes)، ولكن يوجد مشكلة أكبر هي مشكلة منتج-مستهلك (Producer-Consumer problem) التي تُحل باستخدام أداة جديدة هي المتغير الشرطي (condition variable).
</p>

<h2>
	طابور العمل (The work queue)
</h2>

<p>
	تُنظَّم الخيوط في بعض البرامج ذات الخيوط المتعددة لتجري عدة مهام، وتتواصل هذه الخيوط مع بعضها البعض غالبًا عن طريق طابور (queue)، حيث تدعى الخيوط التي تضع بيانات في الطابور بالخيوط المنتجة (producers)، وتدعى الخيوط التي تأخذ بيانات من الطابور بالخيوط المستهلكة (consumers). يمكن أن يوجد خيطٌ يشغّل واجهة المستخدم الرسومية (graphical user interface وتختصر إلى GUI) للاستجابة لأحداث المستخدم في التطبيقات التي لديها واجهة مستخدم رسومية على سبيل المثال، ويمكن أن يوجد خيطٌ آخر يعالج طلبات المستخدم، حيث يمكن أن يضع خيطُ واجهة المستخدم الرسومية الطلبات في طابورٍ ثم يأخذ الخيط المقابل هذه الطلبات ويعالجها. تحتاج تطبيق طابور لدعم هذا التنظيم، بحيث يحافظ تطبيق الطابور على الخيوط (thread safe)، وهذا يعني أنه يستطيع كلا الخيطين (أو أكثر من خيطين في بعض الأحيان) الوصول إلى الطابور في نفس الوقت، وتحتاج أيضًا أن تعالج الحالات الخاصة مثل أن يكون الطابور فارغًا (empty) وأن يكون حجم الطابور منتهٍ عندما يمتلئ (full). سأبدأ، يقول الكاتب، بطابورٍ بسيط لا يحافظ على الخيوط ثم ترى كيف يكون ذلك خاطئًا وكيف يُصحَّح ذلك الخطأ. شيفرة هذا المثال موجودة في المجلد <a data-ss1616960778="1" href="https://github.com/AllenDowney/ThinkOS" rel="external nofollow">queue</a> حيث يتضمن الملف <code>queue.c</code> التطبيق الأساسي للمخزَن الدائري <a data-ss1616960778="1" href="https://en.wikipedia.org/wiki/Circular_buffer" rel="external nofollow">circular buffer</a>. تجد تعريف البنية <code>Queue</code> فيما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_8" style="">
<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="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">array</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> length</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> next_in</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> next_out</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="typ">Queue</span><span class="pun">;</span></pre>

<p>
	<code>array</code> هو المصفوفة التي تتضمن عناصر الطابور وهي أعداد صحيحة (ints) في هذا المثال، ولكنها يمكن أن تكون بنى (structures) تتضمن أحداث المستخدم وعناصر العمل وغير ذلك. <code>length</code> هو طول المصفوفة، و <code>next_in</code> هو دليل (index) المصفوفة الذي يحدد مكان إضافة العنصر التالي في الطابور، أما <code>next_out</code> هو دليل العنصر التالي الذي يجب حذفه من الطابور. تخصص الدالة <code>make_queue</code> حيزًا للبنية <code>Queue</code> وتهيئ حقولها كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_10" style="">
<span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="pln">make_queue</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> length</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="typ">queue</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Queue</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">Queue</span><span class="pun">));</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">length </span><span class="pun">=</span><span class="pln"> length </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">array </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="pln">length </span><span class="pun">*</span><span class="pln"> </span><span class="kwd">sizeof</span><span class="pun">(</span><span class="typ">int</span><span class="pun">));</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_in </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">queue</span><span class="pun">-&gt;</span><span class="pln">next_out </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="typ">queue</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تحتاج القيمة الابتدائية للمتغير <code>next_out</code> بعض الشرح، فبما أن الطابور فارغ مبدئيًا فلا وجود لعنصر تالٍ لحذفه، لذلك يكون المتغير <code>next_out</code> غير صالح (invalid)، وضبط <code>next_out == next_in</code> هي حالة خاصة تحدد أن الطابور فارغ، فيمكن كتابة ما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_12" style="">
<span class="typ">int</span><span class="pln"> queue_empty</span><span class="pun">(</span><span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="typ">queue</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="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_in </span><span class="pun">==</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_out</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكنك الآن إضافة عناصر إلى الطابور باستخدام الدالة <code>queue_push</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_14" style="">
<span class="kwd">void</span><span class="pln"> queue_push</span><span class="pun">(</span><span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="typ">queue</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> item</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">queue_full</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">))</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        perror_exit</span><span class="pun">(</span><span class="str">"queue is full"</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">array</span><span class="pun">[</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_in</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> item</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_in </span><span class="pun">=</span><span class="pln"> queue_incr</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">,</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_in</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا كان الطابور ممتلئًا فإن الدالة <code>queue_push</code> تطبع رسالة خطأ وتغادر، أما إذا كان الطابور غير ممتلئ فتدخِل الدالة <code>queue_push</code> عنصرًا جديدًا ثم تزيد قيمة المتغير <code>next_in</code> باستخدام الدالة <code>queue_incr</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_16" style="">
<span class="typ">int</span><span class="pln"> queue_incr</span><span class="pun">(</span><span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="typ">queue</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">
    </span><span class="kwd">return</span><span class="pln"> </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="pun">%</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">length</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_18" style="">
<span class="typ">int</span><span class="pln"> queue_full</span><span class="pun">(</span><span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="typ">queue</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">queue_incr</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">,</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_in</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_out</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_20" style="">
<span class="typ">int</span><span class="pln"> queue_pop</span><span class="pun">(</span><span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="typ">queue</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">queue_empty</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">))</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        perror_exit</span><span class="pun">(</span><span class="str">"queue is empty"</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"> item </span><span class="pun">=</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">array</span><span class="pun">[</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_out</span><span class="pun">];</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_out </span><span class="pun">=</span><span class="pln"> queue_incr</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">,</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_out</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> item</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وإذا جربت سحب (pop) عنصر من طابور فارغ فستطبع الدالة <code>queue_pop</code> رسالة خطأ وتغادر.
</p>

<h2>
	المستهلكون والمنتجون (Producers and consumers)
</h2>

<p>
	تنشئ الآن بعض الخيوط لتصل إلى هذا الطابور، حيث شيفرة المنتج (producer) هي كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_22" style="">
<span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">producer_entry</span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">arg</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Shared</span><span class="pln"> </span><span class="pun">*</span><span class="pln">shared </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Shared</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">arg</span><span class="pun">;</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> </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="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> QUEUE_LENGTH </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">++)</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        printf</span><span class="pun">(</span><span class="str">"adding item %d\n"</span><span class="pun">,</span><span class="pln"> i</span><span class="pun">);</span><span class="pln">
        queue_push</span><span class="pun">(</span><span class="pln">shared</span><span class="pun">-&gt;</span><span class="typ">queue</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">
    pthread_exit</span><span class="pun">(</span><span class="pln">NULL</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أما شيفرة المستهلك (consumer) هي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_24" style="">
<span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">consumer_entry</span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">arg</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"> item</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">Shared</span><span class="pln"> </span><span class="pun">*</span><span class="pln">shared </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Shared</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">arg</span><span class="pun">;</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> </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="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> QUEUE_LENGTH </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">++)</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        item </span><span class="pun">=</span><span class="pln"> queue_pop</span><span class="pun">(</span><span class="pln">shared</span><span class="pun">-&gt;</span><span class="typ">queue</span><span class="pun">);</span><span class="pln">
        printf</span><span class="pun">(</span><span class="str">"consuming item %d\n"</span><span class="pun">,</span><span class="pln"> item</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    pthread_exit</span><span class="pun">(</span><span class="pln">NULL</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_6227_26" style="">
<span class="typ">pthread_t</span><span class="pln"> child</span><span class="pun">[</span><span class="pln">NUM_CHILDREN</span><span class="pun">];</span><span class="pln">

</span><span class="typ">Shared</span><span class="pln"> </span><span class="pun">*</span><span class="pln">shared </span><span class="pun">=</span><span class="pln"> make_shared</span><span class="pun">();</span><span class="pln">

child</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"> make_thread</span><span class="pun">(</span><span class="pln">producer_entry</span><span class="pun">,</span><span class="pln"> shared</span><span class="pun">);</span><span class="pln">
child</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"> make_thread</span><span class="pun">(</span><span class="pln">consumer_entry</span><span class="pun">,</span><span class="pln"> shared</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">for</span><span class="pln"> </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="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> NUM_CHILDREN</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">
    join_thread</span><span class="pun">(</span><span class="pln">child</span><span class="pun">[</span><span class="pln">i</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_6227_28" style="">
<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="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="typ">queue</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="typ">Shared</span><span class="pun">;</span><span class="pln">

</span><span class="typ">Shared</span><span class="pln"> </span><span class="pun">*</span><span class="pln">make_shared</span><span class="pun">()</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Shared</span><span class="pln"> </span><span class="pun">*</span><span class="pln">shared </span><span class="pun">=</span><span class="pln"> check_malloc</span><span class="pun">(</span><span class="kwd">sizeof</span><span class="pun">(</span><span class="typ">Shared</span><span class="pun">));</span><span class="pln">
    shared</span><span class="pun">-&gt;</span><span class="typ">queue</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> make_queue</span><span class="pun">(</span><span class="pln">QUEUE_LENGTH</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> shared</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تمثل الشيفرة السابقة التي حصلت عليها حتى الآن بدايةً جيدة ولكن لديها بعض المشاكل هي:
</p>

<ul>
<li>
		لا يحافظ الوصول إلى الطابور على الخيوط، حيث يمكن أن تصل خيوط متعددة إلى المتغيرات <code>array</code> و <code>next_in</code> و <code>next_out</code> في نفس الوقت، وهذا يترك الطابور تالفًا وفي حالة غير مستقرة.
	</li>
	<li>
		إذا جُدوِل الخيط المستهلك أولًا فسيجد الطابور فارغًا، وبالتالي يطبع رسالة خطأ وينتهي، لذلك من الأفضل أن يتوقف المستهلك حتى يصبح الطابور غير فارغ. وبالمثل يجب إيقاف المنتج إذا كان الطابور ممتلئًا.
	</li>
</ul>
<p>
	ستُحل المشكلة الأولى في الفقرة القادمة باستخدام <code>Mutex</code>، وستحل المشكلة الثانية في الفقرة التي بعدها باستخدام المتغيرات الشرطية.
</p>

<h2>
	الإقصاء المتبادل (Mutual exclusion)
</h2>

<p>
	يحافظ الطابور على الخيوط باستخدام كائن المزامنة (mutex)، حيث تضيف أولًا المؤشر <code>Mutex</code> إلى بنية الطابور:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_30" style="">
<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="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">array</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> length</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> next_in</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> next_out</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">Mutex</span><span class="pln"> </span><span class="pun">*</span><span class="pln">mutex</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="typ">Queue</span><span class="pun">;</span></pre>

<p>
	ثم تهيئه في الدالة <code>make_queue</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_32" style="">
<span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="pln">make_queue</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> length</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="typ">queue</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Queue</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">Queue</span><span class="pun">));</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">length </span><span class="pun">=</span><span class="pln"> length</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">array </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="pln">length </span><span class="pun">*</span><span class="pln"> </span><span class="kwd">sizeof</span><span class="pun">(</span><span class="typ">int</span><span class="pun">));</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_in </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">queue</span><span class="pun">-&gt;</span><span class="pln">next_out </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">queue</span><span class="pun">-&gt;</span><span class="pln">mutex </span><span class="pun">=</span><span class="pln"> make_mutex</span><span class="pun">();</span><span class="pln"> </span><span class="com">//-- جديد</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ثم تضيف شيفرة التزامن إلى الدالة <code>queue_push</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_34" style="">
<span class="kwd">void</span><span class="pln"> queue_push</span><span class="pun">(</span><span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="typ">queue</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> item</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    mutex_lock</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln"> </span><span class="com">//-- جديد</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">queue_full</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">))</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        mutex_unlock</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln"> </span><span class="com">//-- جديد</span><span class="pln">
        perror_exit</span><span class="pun">(</span><span class="str">"queue is full"</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">array</span><span class="pun">[</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_in</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> item</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_in </span><span class="pun">=</span><span class="pln"> queue_incr</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">,</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_in</span><span class="pun">);</span><span class="pln">
    mutex_unlock</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln"> </span><span class="com">//-- جديد</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يجب قفل <code>Mutex</code> قبل التحقق إذا كان الطابور ممتلئًا أم لا، فإذا كان ممتلئًا يجب فك قفل <code>Mutex</code> قبل المغادرة، وإلا سيتركه الخيط مقفلًا فلا يستطيع أي خيطٍ آخر أن يستأنف عمله. شيفرة التزامن للدالة <code>queue_pop</code> هي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_36" style="">
<span class="typ">int</span><span class="pln"> queue_pop</span><span class="pun">(</span><span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="typ">queue</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    mutex_lock</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">queue_empty</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">))</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        mutex_unlock</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
        perror_exit</span><span class="pun">(</span><span class="str">"queue is empty"</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"> item </span><span class="pun">=</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">array</span><span class="pun">[</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_out</span><span class="pun">];</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_out </span><span class="pun">=</span><span class="pln"> queue_incr</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">,</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_out</span><span class="pun">);</span><span class="pln">
    mutex_unlock</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> item</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لاحظ أن دوال <code>Queue</code> الأخرى والتي هي <code>queue_full</code> و <code>queue_empty</code> و <code>queue_incr</code> لا تحاول قفل كائن المزامنة، فيجب على كل خيط يستدعي هذه الدوال أن يقفل كائن المزامنة أولًا. أصبح الطابور محافظًا على الخيوط باستخدام الشيفرة الجديدة التي أضيفت، ولا يجب أن ترى أخطاء تزامن إذا شغلت هذه الشيفرة، ولكنك سترى أن الخيط المستهلك يغادر أحيانًا لأن الطابور فارغ، أو قد ترى الخيط المنتج يغادر بسبب أن الطابور ممتلئ أو كلا الأمرين معًا، وبالتالي الخطوة القادمة هي إضافة المتغيرات الشرطية (condition variables).
</p>

<h2>
	المتغيرات الشرطية (Condition variables)
</h2>

<p>
	المتغير الشرطي هو عبارة عن بينة بيانات مرتبطة بشرط (condition)، ويسمح المتغير الشرطي بإيقاف الخيوط حتى يتحقق الشرط أو تصبح قيمته true، فقد تتحقق الدالة <code>thread_pop</code> على سبيل المثال فيما إذا كان الطابور فارغًا أم لا، فإذا كان فارغًا تنتظر شرطًا هو (الطابور غير فارغ). وقد تتحقق الدالة <code>thread_push</code> أيضًا فيما إذا كان الطابور ممتلئًا، فإذا كان ممتلئًا تتوقف حتى يصبح غير ممتلئ. تعالج الشيفرة التالية الشرط الأول، حيث تضيف أولًا متغيرًا شرطيًا إلى البنية <code>Queue</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_38" style="">
<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="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">array</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> length</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> next_in</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> next_out</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">Mutex</span><span class="pln"> </span><span class="pun">*</span><span class="pln">mutex</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">Cond</span><span class="pln"> </span><span class="pun">*</span><span class="pln">nonempty</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="typ">Queue</span><span class="pun">;</span></pre>

<p>
	ثم تهيئه في الدالة <code>make_queue</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_40" style="">
<span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="pln">make_queue</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> length</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="typ">queue</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Queue</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">Queue</span><span class="pun">));</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">length </span><span class="pun">=</span><span class="pln"> length</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">array </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="pln">length </span><span class="pun">*</span><span class="pln"> </span><span class="kwd">sizeof</span><span class="pun">(</span><span class="typ">int</span><span class="pun">));</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_in </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">queue</span><span class="pun">-&gt;</span><span class="pln">next_out </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">queue</span><span class="pun">-&gt;</span><span class="pln">mutex </span><span class="pun">=</span><span class="pln"> make_mutex</span><span class="pun">();</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">nonempty </span><span class="pun">=</span><span class="pln"> make_cond</span><span class="pun">();</span><span class="pln"> </span><span class="com">//-- جديد</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا وجدت الطابور فارغًا في الدالة <code>queue_pop</code> لا تغادر بل استخدم المتغير الشرطي لتوقف التنفيذ:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_42" style="">
<span class="typ">int</span><span class="pln"> queue_pop</span><span class="pun">(</span><span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="typ">queue</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    mutex_lock</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">queue_empty</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">))</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        cond_wait</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">nonempty</span><span class="pun">,</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">mutex</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="typ">int</span><span class="pln"> item </span><span class="pun">=</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">array</span><span class="pun">[</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_out</span><span class="pun">];</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_out </span><span class="pun">=</span><span class="pln"> queue_incr</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">,</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_out</span><span class="pun">);</span><span class="pln">
    mutex_unlock</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
    cond_signal</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">nonfull</span><span class="pun">);</span><span class="pln"> </span><span class="com">//-- جديد</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> item</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الدالة <code>cond_wait</code> معقدة، فوسيطها الأول هو المتغير الشرطي والشرط الذي يجب انتظاره في هذه الحالة هو (الطابور غير فارغ)، أما وسيطها الثاني هو كائن المزامنة الذي يحمي الطابور. يفك الخيط قفل كائن المزامنة ثم يتوقف عندما يستدعي الخيط الذي قفل كائن المزامنة الدالةَ <code>cond_wait</code>، وهذا شيء مهم جدًا. إذا لم تقفل الدالة <code>cond_wait</code> كائن المزامنة قبل التوقف فلن يستطيع أي خيطٍ آخر أن يصل إلى الطابور ولن تضاف أي عناصر أخرى إلى الطابور، وبالتالي قد يبقى الطابور فارغًا دائمًا، فيمكن أن يشغّل المنتج بينما يكون المستهلك متوقفًا عند <code>nonempty</code>. تبين الشيفرة التالية ما يحدث عنما يشغّل المنتج الدالة <code>queue_push</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_44" style="">
<span class="kwd">void</span><span class="pln"> queue_push</span><span class="pun">(</span><span class="typ">Queue</span><span class="pln"> </span><span class="pun">*</span><span class="typ">queue</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> item</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    mutex_lock</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">queue_full</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">))</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        mutex_unlock</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
        perror_exit</span><span class="pun">(</span><span class="str">"queue is full"</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">array</span><span class="pun">[</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_in</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> item</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_in </span><span class="pun">=</span><span class="pln"> queue_incr</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">,</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">next_in</span><span class="pun">);</span><span class="pln">
    mutex_unlock</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
    cond_signal</span><span class="pun">(</span><span class="typ">queue</span><span class="pun">-&gt;</span><span class="pln">nonempty</span><span class="pun">);</span><span class="pln"> </span><span class="com">//-- جديد</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تقفل الدالة <code>queue_push</code> المتغير <code>Mutex</code> وتتحقق فيما إذا كان الطابور ممتلئًا أم لا، وعلى فرض أن الطابور ليس ممتلئًا حيث تضيف الدالة <code>queue_push</code> عنصرًا جديدًا إلى الطابور ثم تفك قفل المتغير <code>Mutex</code>، ولكن تقوم هذه الدالة بشيء آخر قبل أن تعيد شيئًا، حيث تنبّه (signals) المتغير الشرطي <code>nonempty</code>، ويحدد تنبيه (Signalling) المتغير الشرطي أن الشرط صحيح (true)، وليس لإشارة التنبيه أي تأثير إذا لم يوجد خيوطٌ تنتظر المتغير الشرطي. إذا وجد خيوط تنتظر المتغير الشرطي فيعود أحد هذه الخيوط إلى العمل ويستأنف تنفيذ الدالة <code>cond_wait</code>، ولكن يجب على الخيط الذي استأنف عمله أن ينتظر المتغير <code>Mutex</code> ويقفله مرة أخرى قبل أن ينهي تنفيذ الدالة <code>cond_wait</code>. عُد الآن إلى الدالة <code>queue_pop</code> وشاهد ما يحدث عندما ينهي الخيط تنفيذ الدالة <code>cond_wait</code>، حيث يعود الخيط مرة أخرى إلى بداية حلقة while ويتحقق من الشرط مرة أخرى. افترض أنه تحقق الشرط أي أن الطابور غير فارغ، فعندما يغادر الخيط المستهلك حلقة while، هذا يؤدي إلى شيئين: (1) تحقق الشرط أي يوجد عنصر واحد على الأقل في الطابور، و (2) قُفِل المتغير <code>Mutex</code> أي أن الوصول إلى الطابور آمن.
</p>

<p>
	تفك الدالة <code>queue_pop</code> قفل كائن المزامنة وتنتهي بعد حذف عنصر من الطابور، سأبين، يقول الكاتب، كيفية عمل شيفرة <code>Cond</code> ولكن يجب أولًا الإجابة عن سؤالين مهمين هما:
</p>

<ul>
<li>
		لماذا توجد الدالة <code>cond_wait</code> ضمن حلقة while بدلًا من وجودها ضمن عبارة if، أي لماذا يجب التحقق من الشرط مرة أخرى بعد انتهاء تنفيذ الدالة <code>cond_wait</code>؟
	</li>
</ul>
<p>
	السبب الرئيسي لإعادة التحقق من الشرط هو إمكانية اعتراض إشارة تنبيه، حيث افترض أن الخيط A ينتظر <code>nonempty</code>، ويضيف الخيط B عنصرًا إلى الطابور ثم ينبه <code>nonempty</code>، فيستيقظ الخيط A ويحاول قفل كائن المزامنة، ولكن قبل أن يقوم الخيط A بذلك، يأتي الخيط الشرير C ويقفل كائن المزامنة ويسحب عنصرًا من الطابور ثم يفك قفل كائن المزامنة، وبالتالي أصبح الطابور فارغًا الآن مرة أخرى، ولكن لا يتوقف الخيط A مرة أخرى، ويستطيع الخيط A قفل كائن المزامنة وينهي تنفيذ الدالة <code>cond_wait</code>، فإذا لم يتحقق الخيط A من الشرط مرة أخرى فقد يحاول سحب عنصر من طابورٍ فارغ، وقد يسبب ذلك خطأ.
</p>

<ul>
<li>
		أما السؤال الثاني الذي يظهر عندما يتعلم الناس المتغيرات الشرطية هو: كيف يعرف المتغير الشرطي الشرطَ الذي يتعلق به؟
	</li>
</ul>
<p>
	هذا السؤال مفهوم لأنه لا يوجد اتصال صريح بين بنية <code>Cond</code> والشرط المتعلق بها، حيث يكون الاتصال مضمّنًا في طريقة استخدامه، وهذه إحدى الطرق للتفكير في ذلك: فالشرط المتعلق ب Cond هو الشي الذي تكون قيمته خاطئة (false) عندما تستدعي الدالة <code>cond_wait</code>، وتكون قيمته صحيحة (true) عندما تستدعي الدالة <code>cond_signal</code>.
</p>

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

<h2>
	تطبيق المتغير الشرطي (Condition variable implementation)
</h2>

<p>
	البنية Cond التي استخدمت في الفقرة السابقة هي مغلّف لنوعٍ يدعى <code>pthread_cond_t</code> المعرّف في واجهة برمجة التطبيقات للخيوط POSIX. البنية Cond شبيهة جدًا بالبنية Mutex والتي هي مغلّفة للنوع <code>pthread_mutex_t</code>، حيث تعريف النوع <code>Cond</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7354_45" style="">
<span class="kwd">typedef</span><span class="pln"> </span><span class="typ">pthread_cond_t</span><span class="pln"> </span><span class="typ">Cond</span><span class="pun">;</span></pre>

<p>
	تخصص الدالة <code>make_cond</code> حيزًا وتهيئ المتغير الشرطي وتعيد مؤشرًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_46" style="">
<span class="typ">Cond</span><span class="pln"> </span><span class="pun">*</span><span class="pln">make_cond</span><span class="pun">()</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Cond</span><span class="pln"> </span><span class="pun">*</span><span class="pln">cond </span><span class="pun">=</span><span class="pln"> check_malloc</span><span class="pun">(</span><span class="kwd">sizeof</span><span class="pun">(</span><span class="typ">Cond</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"> pthread_cond_init</span><span class="pun">(</span><span class="pln">cond</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="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
        perror_exit</span><span class="pun">(</span><span class="str">"make_cond failed"</span><span class="pun">);</span><span class="pln">

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

<p>
	أما الدالتان المغلّفتان للدالتين <code>cond_wait</code> و <code>cond_signal</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6227_48" style="">
<span class="kwd">void</span><span class="pln"> cond_wait</span><span class="pun">(</span><span class="typ">Cond</span><span class="pln"> </span><span class="pun">*</span><span class="pln">cond</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Mutex</span><span class="pln"> </span><span class="pun">*</span><span class="pln">mutex</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"> n </span><span class="pun">=</span><span class="pln"> pthread_cond_wait</span><span class="pun">(</span><span class="pln">cond</span><span class="pun">,</span><span class="pln"> mutex</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
        perror_exit</span><span class="pun">(</span><span class="str">"cond_wait failed"</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"> cond_signal</span><span class="pun">(</span><span class="typ">Cond</span><span class="pln"> </span><span class="pun">*</span><span class="pln">cond</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"> n </span><span class="pun">=</span><span class="pln"> pthread_cond_signal</span><span class="pun">(</span><span class="pln">cond</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
        perror_exit</span><span class="pun">(</span><span class="str">"cond_signal failed"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ترجمة -وبتصرّف- للفصل Condition variables من كتاب <a data-ss1616960778="1" href="http://greenteapress.com/thinkos/" rel="external nofollow">Think OS A Brief Introduction to Operating Systems</a>
</p>
]]></description><guid isPermaLink="false">1013</guid><pubDate>Sat, 10 Oct 2020 13:08:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x641;&#x635;&#x644; &#x627;&#x644;&#x62A;&#x627;&#x633;&#x639;: &#x645;&#x641;&#x647;&#x648;&#x645; &#x627;&#x644;&#x62E;&#x64A;&#x648;&#x637; (Threads) &#x641;&#x64A; &#x639;&#x645;&#x644;&#x64A;&#x629; &#x627;&#x644;&#x645;&#x639;&#x627;&#x644;&#x62C;&#x629;</title><link>https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%AA%D8%A7%D8%B3%D8%B9-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-r1012/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_10/9.png.57441c663332ff8c8f27f154bf3ae7a9.png" /></p>

<p>
	الخيط (Thread) هو نوع معين أو خاص من العمليات، حيث ينشئ نظام التشغيل حيز عناوين جديدًا عند إنشاء عملية، ويتضمن هذا الحيز جزء الشيفرة أو نص البرنامج (text segment) والجزء الساكن (static segment) وجزء الكومة (heap)، وينشئ نظام التشغيل أيضًا خيط تنفيذ (thread of execution) جديدًا يتضمن عداد البرنامج (program counter) وحالة عتاد أخرى واستدعاء المكدس. العمليات التي رأيتها لحد الآن هي عمليات ذات خيط وحيد (single-threaded) أي يوجد خيط تنفيذ واحد فقط يعمل في كل حيز عناوين، وستتعرف على العمليات ذات الخيوط المتعددة (multi-threaded)، أي التي تملك خيوطًا متعددة تعمل في نفس حيز العناوين. تتشارك كل الخيوط بنفس جزء الشيفرة ضمن العملية الواحدة أي أنها تشغّل نفس الشيفرة، ولكن تشغّل هذه الخيوط المختلفة أجزاءً مختلفة من تلك الشيفرة، وتتشارك الخيوط ضمن العملية الواحدة بنفس الجزء الساكن (static segment)، لذلك إذا غيّر أحد الخيوط متغيرًا عامًا (global variable) فإن بقية الخيوط ترى هذا التغيير، ويتشاركون أيضًا بالكومة (heap) لذلك تستطيع الخيوط التشارك بقطع الذاكرة المخصصة ديناميكيًا (dynamically-allocated chunks)، ولكن يكون لكل خيطٍ جزء المكدس الخاص به لذلك تستطيع الخيوط استدعاء دوالٍ دون التداخل مع بعضها البعض، ولا تصل الخيوط عادةً إلى المتغيرات المحلية لخيطٍ آخر، حيث لا تستطيع الوصول إليها في بعض الأحيان.
</p>

<h2>
	إنشاء الخيوط (Creating threads)
</h2>

<p>
	الخيوط القياسية الأكثر شيوعًا والمستخدمة مع C هي خيوط POSIX أو اختصارًا Pthreads. تعرّف خيوط POSIX القياسية نموذج خيط (thread model) وواجهةًَ (interface) لإنشاء الخيوط والتحكم بها، وتوفّر معظم نسخ UNIX تطبيقًا ل Pthreads. يشبه استخدامُ Pthreads استخدامَ معظم مكتبات لغة C حيث:
</p>

<ul>
<li>
		تضمّن ملفات الترويسات (headers files) في بداية برنامجك.
	</li>
	<li>
		تكتب الشيفرة التي تستدعي دوالًا معرّفة باستخدام Pthreads.
	</li>
	<li>
		تربط (link) البرنامج عند تصريفه (compile) مع مكتبة Pthread.
	</li>
</ul>
<p>
	يضمِّن البرنامج ملفات الترويسات التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8487_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;pthread.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;semaphore.h&gt;</span></pre>

<p>
	أول اثنين من ملفات الترويسات السابقة هما تضمين لمكتبات قياسية، أما ملف الترويسات الثالث فيُستخدم من أجل Pthreads، ويُستخدم ملف الترويسات الرابع من أجل متغيرات تقييد الوصول (semaphores). يمكنك استخدام الخيار <code>-l</code> في سطر الأوامر لتصريف البرنامج مع مكتبة Pthread باستخدام الأداة <code>gcc</code> كما يلي:
</p>

<pre class="ipsCode">
gcc -g -O2 -o array array.c -lpthread
</pre>

<p>
	يصرّف الأمر السابق ملفًا مصدريًا يدعى <code>array.c</code> مع معلومات تنقيح الأخطاء (debugging info) والتحسين (optimization) ويربطه مع مكتبة Pthread ثم يولّد ملفًا تنفيذيًا يدعى <code>array</code>.
</p>

<h2>
	إنشاء الخيوط (Creating threads)
</h2>

<p>
	تدعى دالة Pthread التي تنشئ خيوطًا <code>pthread_create</code>، وتُظهر الدالة التالية كيفية استخدامها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5955_7" style="">
<span class="typ">pthread_t</span><span class="pln"> make_thread</span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*(*</span><span class="pln">entry</span><span class="pun">)(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*),</span><span class="pln"> </span><span class="typ">Shared</span><span class="pln"> </span><span class="pun">*</span><span class="pln">shared</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"> n</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">pthread_t</span><span class="pln"> thread</span><span class="pun">;</span><span class="pln">

    n </span><span class="pun">=</span><span class="pln"> pthread_create</span><span class="pun">(&amp;</span><span class="pln">thread</span><span class="pun">,</span><span class="pln"> NULL</span><span class="pun">,</span><span class="pln"> entry</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">shared</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </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">
        perror</span><span class="pun">(</span><span class="str">"pthread_create failed"</span><span class="pun">);</span><span class="pln">
        exit</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">return</span><span class="pln"> thread</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الدالة <code>make_thread</code> هي دالة مغلّفة (wrapper) وكُتبت لجعل الدالة <code>pthread_create</code> سهلة الاستخدام ولتوفير التحقق من الأخطاء (error-checking). نوع القيمة المعادة من الدالة <code>pthread_create</code> هو <code>pthread_t</code> والذي يمكنك التفكير به كمعرّف (id) أو مِقبض (handle) للخيط الجديد. إذا نجح تنفيذ الدالة <code>pthread_create</code> فستعيد القيمة 0 وتعيد الدالة <code>make_thread</code> مقبض الخيط الجديد، وإذا ظهر خطأ فتعيد الدالة <code>pthread_create</code> شيفرة الخطأ وتطبع الدالة <code>make_thread</code> رسالة خطأ وتنتهي. <code>Shared</code> المعامل الثاني للدالة <code>make_thread</code> هو عبارة عن بنية (structure) عُرِّفت لتتضمن القيم المشتركة بين الخيوط، حيث يمكنك تعريف نوع جديد من خلال استخدام عبارة <code>typedef</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5955_9" style="">
<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="typ">int</span><span class="pln"> counter</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="typ">Shared</span><span class="pun">;</span></pre>

<p>
	والمتغير المشترك الوحيد في هذه الحالة هو <code>counter</code>، وتخصص الدالة <code>make_shared</code> حيّزًا للبنية <code>Shared</code> وتهيئ محتوياتها كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5955_11" style="">
<span class="typ">Shared</span><span class="pln"> </span><span class="pun">*</span><span class="pln">make_shared</span><span class="pun">()</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Shared</span><span class="pln"> </span><span class="pun">*</span><span class="pln">shared </span><span class="pun">=</span><span class="pln"> check_malloc</span><span class="pun">(</span><span class="kwd">sizeof</span><span class="pun">(</span><span class="typ">Shared</span><span class="pun">));</span><span class="pln">
    shared</span><span class="pun">-&gt;</span><span class="pln">counter </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"> shared</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لديك الآن بنية بيانات مشتركة وإذا عدتَ إلى الدالة <code>make_thread</code> وتحديدًا المعامل الأول الذي هو عبارة عن مؤشر (pointer) إلى دالة، وتأخذ هذه الدالة مؤشر <code>void</code> وتعيد مؤشر <code>void</code> أيضًا. إذا أصبح نظرك مشوشًا بسبب صيغة تصريح هذا النوع فلست الوحيد في ذلك، على كل حال إن الهدف الأساسي من هذا المعامل هو أن يحدد للدالة مكان بدء تنفيذ الخيط الجديد، وتدعى هذه الدالة <code>entry</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5955_13" style="">
<span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">entry</span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">arg</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Shared</span><span class="pln"> </span><span class="pun">*</span><span class="pln">shared </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Shared</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">arg</span><span class="pun">;</span><span class="pln">
    child_code</span><span class="pun">(</span><span class="pln">shared</span><span class="pun">);</span><span class="pln">
    pthread_exit</span><span class="pun">(</span><span class="pln">NULL</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يجب أن يُصرَّح عن معامل الدالة <code>entry</code> كمؤشر <code>void</code>، ولكنه في هذا البرنامج مؤشرٌ إلى بنية <code>Shared</code> لذلك يمكن تبديل نوعه (typecast) ثم تمريره إلى الدالة <code>child_code</code> التي تقوم بالعمل الحقيقي، حيث تطبع الدالة <code>child_code</code> قيمة المتغير المشترك <code>counter</code> ثم تزيد قيمته كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5955_15" style="">
<span class="kwd">void</span><span class="pln"> child_code</span><span class="pun">(</span><span class="typ">Shared</span><span class="pln"> </span><span class="pun">*</span><span class="pln">shared</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">"counter = %d\n"</span><span class="pun">,</span><span class="pln"> shared</span><span class="pun">-&gt;</span><span class="pln">counter</span><span class="pun">);</span><span class="pln">
    shared</span><span class="pun">-&gt;</span><span class="pln">counter</span><span class="pun">++;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تستدعي الدالةُ <code>entry</code> الدالةََ <code>pthread_exit</code> بعد أن تنتهي الدالة <code>child code</code> وتعيد قيمةً، حيث يمكن أن تُستخدم الدالة <code>pthread_exit</code> لتمرير قيمة إلى الخيط الذي يُضم (join) مع الخيط الحالي، وبالتالي في هذه الحالة لا يبقى شيء للخيط الابن لعمله فتُمرَّر القيمة الخالية <code>NULL</code>، وأخيرًا تنشئ الشيفرة التالية الخيوط الأبناء (child threads) كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5955_17" style="">
<span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
</span><span class="typ">pthread_t</span><span class="pln"> child</span><span class="pun">[</span><span class="pln">NUM_CHILDREN</span><span class="pun">];</span><span class="pln">

</span><span class="typ">Shared</span><span class="pln"> </span><span class="pun">*</span><span class="pln">shared </span><span class="pun">=</span><span class="pln"> make_shared</span><span class="pun">(</span><span class="lit">1000000</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">for</span><span class="pln"> </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_CHILDREN</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">
    child</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"> make_thread</span><span class="pun">(</span><span class="pln">entry</span><span class="pun">,</span><span class="pln"> shared</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	<code>NUM_CHILDREN</code> هو ثابت وقت التصريف (compile-time constant) الذي يحدد عدد الخيوط الأبناء، و <code>child</code> هي مصفوفة مقابض الخيوط (thread handles).
</p>

<h2>
	ضم الخيوط (Joining threads)
</h2>

<p>
	إذا أراد خيطٌ انتظار خيطٍ آخر ليكتمل فإنه يستدعي الدالة <code>pthread_join</code>، وتجد فيما يلي الدالة المغلّفة للدالة <code>pthread_join</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5955_19" style="">
<span class="kwd">void</span><span class="pln"> join_thread</span><span class="pun">(</span><span class="typ">pthread_t</span><span class="pln"> thread</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"> ret </span><span class="pun">=</span><span class="pln"> pthread_join</span><span class="pun">(</span><span class="pln">thread</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="pln"> </span><span class="pun">(</span><span class="pln">ret </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="pln">
        perror</span><span class="pun">(</span><span class="str">"pthread_join failed"</span><span class="pun">);</span><span class="pln">
        exit</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></pre>

<p>
	معامل الدالة المغلّفة هو مقبض الخيط الذي تنتظره ليكتمل، وعمل الدالة المغلّفة هو فقط استدعاء الدالة <code>pthread_join</code> والتحقق من النتيجة. يستطيع أي خيط أن يضم أي خيطٍ آخر، ولكن في النماذج الأكثر شيوعًا ينشئ الخيط الأب (parent thread) كل الخيوط الأبناء ويضمها (join). تجد فيما يلي الشيفرة التي تنتظر الخيوط الأبناء بها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5955_21" style="">
<span class="kwd">for</span><span class="pln"> </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_CHILDREN</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">
    join_thread</span><span class="pun">(</span><span class="pln">child</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تنتظر هذه الحلقات أحد الخيوط الأبناء في كل مرة وذلك حسب ترتيب إنشائها، ولا يوجد ضمان أن تكتمل الخيوط الأبناء في هذا الترتيب ولكن تعمل هذه الحلقة بصورة صحيحة حتى في حال لم يحدث ذلك، فإذا تأخر أحد الخيوط الأبناء فيجب أن تنتظر الحلقة، ويمكن أن تكتمل الخيوط الأبناء الأخرى خلال وقت الانتظار هذا، حيث لا يمكن أن تنتهي هذه الحلقة إلا في حال اكتمال جميع الخيوط الأبناء. يمكنك الاطلاع على المثال ضمن <a href="https://github.com/AllenDowney/ThinkOS" rel="external nofollow">counter/counter.c</a> ثم تصريفه وتشغيله كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8487_30" style="">
<span class="pln">$ make counter
gcc </span><span class="pun">-</span><span class="typ">Wall</span><span class="pln"> counter</span><span class="pun">.</span><span class="pln">c </span><span class="pun">-</span><span class="pln">o counter </span><span class="pun">-</span><span class="pln">lpthread
$ </span><span class="pun">./</span><span class="pln">counter</span></pre>

<p>
	فعند تشغيله مع 5 خيوط أبناء سينتج الخرج التالي:
</p>

<pre class="ipsCode">
counter = 0
counter = 0
counter = 1
counter = 0
counter = 3
</pre>

<p>
	وسينتج خرج آخر عندما تشغله على حاسوبك، وإذا شغلته مرة أخرى سينتج خرج مختلف أيضًا، فماذا يحدث؟
</p>

<h2>
	الأخطاء المتزامنة (Synchronization errors)
</h2>

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

<pre class="ipsCode">
Child A reads 0
Child B reads 0
Child C reads 0
Child A prints 0
Child B prints 0
Child A sets counter=1
Child D reads 1
Child D prints 1
Child C prints 0
Child A sets counter=1
Child B sets counter=2
Child C sets counter=3
Child E reads 3
Child E prints 3
Child D sets counter=4
Child E sets counter=5
</pre>

<p>
	يمكن أن تُقاطَع الخيوط في أماكن مختلفة في كل مرة تشغّل فيها البرنامج، أو قد يختار المجدول خيوطًا مختلفة ليشغّلها، لذلك ستكون سلسلة الأحداث والنتائج مختلفة. افترض أنك تريد فرض بعض الترتيب، أي مثلًا تريد أن يقرأ كل خيط قيمةً مختلفة للمتغير <code>counter</code> ثم يزيدها، وبالتالي تُظهر قيمة المتغير <code>counter</code> عدد الخيوط التي نفّذت الدالة <code>child_code</code>، ويمكنك استخدام كائن المزامنة (mutex) لتطبيق ذلك، حيث كائن المزامنة (mutex) هو عبارة عن كائن (object) يضمن حدوث إقصاء متبادل (mutual exclusion) لكتلة من الشيفرة، أي ينفّذ خيطٌ واحد فقط كتلة الشيفرة في نفس الوقت. كتبتُ، يقول الكاتب، نموذجًا يدعى <code>mutex.c</code> يوفر كائنات المزامنة، ستجد فيما يلي نسخةً من الدالة <code>child_code</code> التي تستخدم كائن المزامنة لتأمين تزامن الخيوط:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5955_23" style="">
<span class="kwd">void</span><span class="pln"> child_code</span><span class="pun">(</span><span class="typ">Shared</span><span class="pln"> </span><span class="pun">*</span><span class="pln">shared</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    mutex_lock</span><span class="pun">(</span><span class="pln">shared</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
    printf</span><span class="pun">(</span><span class="str">"counter = %d\n"</span><span class="pun">,</span><span class="pln"> shared</span><span class="pun">-&gt;</span><span class="pln">counter</span><span class="pun">);</span><span class="pln">
    shared</span><span class="pun">-&gt;</span><span class="pln">counter</span><span class="pun">++;</span><span class="pln">
    mutex_unlock</span><span class="pun">(</span><span class="pln">shared</span><span class="pun">-&gt;</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	حيث يجب على كل خيط أن يقفل (lock) كائن المزامنة قبل أن يصل أي خيطٍ آخر إلى المتغير المشترك <code>counter</code>، وهذا يؤدي إلى حظر كل الخيوط الأخرى من الوصول إلى هذا المتغير. افترض أن الخيط A قفل كائن المزامنة وهو في منتصف الدالة <code>child_code</code>، فإذا وصل الخيط B ونفّذ الدالة <code>mutex_lock</code> يتوقف تنفيذ الخيط B. ينفّذ الخيط A الدالة <code>mutex_unlock</code> عندما ينتهي، وبالتالي يسمح للخيط B متابعة تنفيذه، أي تنفّذ الخيوط الدالة <code>child_code</code> على التوالي بحيث ينفّذها خيطٌ واحدٌ فقط في نفس الوقت، وبالتالي لا يتعارض أي خيط مع الخيوط الأخرى، وإذا شغّلت الشيفرة مع 5 خيوط أبناء سينتج:
</p>

<pre class="ipsCode">
counter = 0
counter = 1
counter = 2
counter = 3
counter = 4
</pre>

<p>
	وهذا هو المطلوب. يجب إضافة كائن المزامنة Mutex إلى البنية Shared لكي يعمل هذا الحل بالصورة الصحيحة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5955_25" style="">
<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="typ">int</span><span class="pln"> counter</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">Mutex</span><span class="pln"> </span><span class="pun">*</span><span class="pln">mutex</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="typ">Shared</span><span class="pun">;</span></pre>

<p>
	وتهيئته في الدالة <code>make_shared</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5955_27" style="">
<span class="typ">Shared</span><span class="pln"> </span><span class="pun">*</span><span class="pln">make_shared</span><span class="pun">(</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">Shared</span><span class="pln"> </span><span class="pun">*</span><span class="pln">shared </span><span class="pun">=</span><span class="pln"> check_malloc</span><span class="pun">(</span><span class="kwd">sizeof</span><span class="pun">(</span><span class="typ">Shared</span><span class="pun">));</span><span class="pln">
    shared</span><span class="pun">-&gt;</span><span class="pln">counter </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
    shared</span><span class="pun">-&gt;</span><span class="pln">mutex </span><span class="pun">=</span><span class="pln"> make_mutex</span><span class="pun">();</span><span class="pln"> </span><span class="com">//-- هذا السطر جديد</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> shared</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	كائن المزامنة (Mutex)
</h2>

<p>
	تعريفي، يقول الكاتب، ل <code>Mutex</code> هو مغلّف لنوعٍ يدعى <code>pthread_mutex_t</code> وهو معرّفٌ في واجهة برمجة التطبيقات للخيوط POSIX، ولإنشاء كائن مزامنة POSIX يجب تخصيص حيزٍ للنوع <code>pthread_mutex_t</code> ثم استدعاء الدالة <code>pthread_mutex_init</code>. إحدى مشاكل واجهة برمجة التطبيقات هذه أن النوع <code>pthread_mutex_t</code> يتصرف كبنية (structure)، لذلك إذا مررته كوسيط سينشئ نسخةً تجعل كائن المزامنة يتصرف بصورة غير صحيحة، ويمكنك تجنب ذلك من خلال تمرير النوع <code>pthread_mutex_t</code> باستخدام عنوانه. تجعل الشيفرة التي كتبتها، يقول الكاتب، الأمور أسهل من خلال تعريف نوعٍ هو النوع <code>Mutex</code> الذي هو عبارة عن اسم للنوع <code>pthread_mutex_t</code> يمكن قراءته بطريقة أسهل:
</p>

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

</span><span class="kwd">typedef</span><span class="pln"> </span><span class="typ">pthread_mutex_t</span><span class="pln"> </span><span class="typ">Mutex</span><span class="pun">;</span></pre>

<p>
	ثم تعريف دالةٍ هي الدالة <code>make_mutex</code> التي تخصص حيّزًا لكائن المزامنة وتهيئته:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5955_29" style="">
<span class="typ">Mutex</span><span class="pln"> </span><span class="pun">*</span><span class="pln">make_mutex</span><span class="pun">()</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Mutex</span><span class="pln"> </span><span class="pun">*</span><span class="pln">mutex </span><span class="pun">=</span><span class="pln"> check_malloc</span><span class="pun">(</span><span class="kwd">sizeof</span><span class="pun">(</span><span class="typ">Mutex</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"> pthread_mutex_init</span><span class="pun">(</span><span class="pln">mutex</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="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
        perror_exit</span><span class="pun">(</span><span class="str">"make_lock failed"</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> mutex</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	القيمة المعادة هي مؤشر يمكن أن تمرره كوسيط دون أن يسبب نسخًا غير مرغوبة. الدوال التي تستخدم لقفل وفك قفل كائن المزامنة هي دوالٌ مغلّفة بسيطة لدوال POSIX:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5955_31" style="">
<span class="kwd">void</span><span class="pln"> mutex_lock</span><span class="pun">(</span><span class="typ">Mutex</span><span class="pln"> </span><span class="pun">*</span><span class="pln">mutex</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"> n </span><span class="pun">=</span><span class="pln"> pthread_mutex_lock</span><span class="pun">(</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
        perror_exit</span><span class="pun">(</span><span class="str">"lock failed"</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"> mutex_unlock</span><span class="pun">(</span><span class="typ">Mutex</span><span class="pln"> </span><span class="pun">*</span><span class="pln">mutex</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"> n </span><span class="pun">=</span><span class="pln"> pthread_mutex_unlock</span><span class="pun">(</span><span class="pln">mutex</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
        perror_exit</span><span class="pun">(</span><span class="str">"unlock failed"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ترجمة -وبتصرّف- للفصل Threads من كتاب <a href="http://greenteapress.com/thinkos/" rel="external nofollow">Think OS A Brief Introduction to Operating Systems</a>
</p>
]]></description><guid isPermaLink="false">1012</guid><pubDate>Mon, 28 Sep 2020 13:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x641;&#x635;&#x644; &#x627;&#x644;&#x62B;&#x627;&#x645;&#x646;: &#x62A;&#x639;&#x62F;&#x62F; &#x627;&#x644;&#x645;&#x647;&#x627;&#x645; (Multitasking) &#x641;&#x64A; &#x627;&#x644;&#x62D;&#x648;&#x627;&#x633;&#x64A;&#x628;</title><link>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%85%D9%86-%D8%AA%D8%B9%D8%AF%D8%AF-%D8%A7%D9%84%D9%85%D9%87%D8%A7%D9%85-multitasking-%D9%81%D9%8A-%D8%A7%D9%84%D8%AD%D9%88%D8%A7%D8%B3%D9%8A%D8%A8-r1011/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_10/8.png.c42d23622f44e8c4062b940806d2ad45.png" /></p>

<p>
	يتضمن المعالج نوى متعددة في العديد من الحواسيب الحالية وهذا يعني أنه يستطيع تشغيل عدة عمليات في نفس الوقت، وكل نواةٍ لديها القدرة على القيام بتعدد المهام (multitasking) أي يمكنها التبديل من عملية لعمليةٍ أخرى بسرعة، وبذلك تخلق وهمًا بوجود عدة عمليات مُشغَّلة في الوقت ذاته. يسمى جزء نظام التشغيل الذي يطبّق تعدد المهام بالنواة (kernel) وهي الجزء الأعمق في نظام التشغيل وتكون محاطةً بالصدفة (shell) سواء كان نظام تشغيل يشبه الجوزة (nut) أو البذرة (seed)، فالنواة (kernel) هي المستوى الأدنى من البرمجيات (software) في نظام التشغيل وتكون هذه النواة محاطةً بطبقات أخرى متعددة، وإحدى هذه الطبقات واجهةٌ (interface) تسمى صدفة (shell) حيث تلاحظ أن الاختصاصيين في علوم الحاسوب يحبون الاستعارات (metaphors).
</p>

<p>
	عمل النواة الأساسي هو معالجة المقاطعات، والمقاطعة هي الحدث الذي يوقِف دورة التعليمة (instruction cycle) القياسية ويسبب قفز تدفق التنفيذ (flow of execution) إلى جزءٍ خاص من الشيفرة يدعى معالج المقاطعة (interrupt handler). للمقاطعة نوعان هما: مقاطعة عتادية (hardware interrupt) ومقاطعة برمجية (software interrupt)، حيث تحدث <strong>المقاطعة العتادية (hardware interrupt)</strong> عندما يرسل جهازٌ ما إشارات إلى المعالج مثل تسبُّب واجهة الشبكة (network interface) بحدوث مقاطعة عند وصول حزمة بيانات (packet of data) أو مثل المقاطعة التي يسببها القرص الصلب (disk drive) عند اكتمال عملية نقل البيانات، وتحوي معظم الأنظمة مؤقتات (timers) تسبِّب مقاطعات عند الفواصل الزمنية المنتظمة (regular intervals) أو بعد انتهاء الوقت المستغرَق (elapsed time). تحدث <strong>المقاطعة البرمجية (software interrupt)</strong> بسبب برنامجٍ قيد التشغيل مثل عدم اكتمال تعليمةٍ لسببٍ ما، فتُنبّه هذه التعليمة مقاطعةً وبالتالي يعالج نظام التشغيل الشرط الخاص بالعملية المُقاطَعة، حيث تُعالَج أخطاء الأعداد العشرية (floating-point errors) مثل خطأ القسمة على صفر (division by zero) باستخدام المقاطعات.
</p>

<p>
	ينشئ برنامج استدعاء نظام (system call) عندما يريد هذا البرنامج الوصول إلى جهاز عتادي، ويشبه استدعاءُ النظام استدعاءَ دالةٍ ولكن بدلًا من القفز إلى بداية الدالة ينفّذ استدعاءُ النظام تعليمةً خاصة، وتنبّهُ هذه التعليمة مقاطعةً مسببةً قفز تدفق التنفيذ إلى النواة، ثم تقرأ النواة معامِلات استدعاء النظام وتجري العمليات المطلوبة ثم تستأنف العملية المُقاطَعة.
</p>

<h2>
	حالة العتاد (Hardware state)
</h2>

<p>
	تتطلب معالجة المقاطعات تعاونًا بين العتاد والبرمجيات، حيث من الممكن وجود تعليمات متعددة قيد التشغيل ضمن المعالج (CPU) وبيانات مُخزَّنة في المسجلات بالإضافة إلى <strong>حالة عتادٍ (hardware state)</strong> أخرى عند حدوث مقاطعة. يكون العتاد عادةً مسؤولًا عن وصول المعالج إلى حالة الاستقرار (consistent state) فيجب أن تكتمل كل تعليمة أو أن تتصرف كأنها لم تبدأ من الأساس أي لا وجود لتعليمةٍ نصفها مكتملٌ على سبيل المثال، والعتاد مسؤولٌ أيضًا عن حفظ عدّاد البرنامج (program counter) ويختصر إلى PC الذي تستخدمه النواة (kernel) لتعرف من أين ستستأنف تنفيذ التعليمات، ثم يستلم معالج المقاطعة (interrupt handler) مسؤولية حفظ بقية حالة العتاد قبل أن يقوم بأي شيء آخر يعدّل حالة العتاد هذه ثم يستعيد حالة العتاد المحفوظة سابقًا قبل استئناف العملية المُقاطعَة، حيث يمكن اختصار سلسلة الأحداث السابقة كما يلي:
</p>

<ol>
<li>
		يحفظ العتاد عدّاد البرنامج في مسجّلٍ خاص عند حدوث المقاطعة ثم يقفز العتاد إلى معالج المقاطعة المناسب.
	</li>
	<li>
		ثم يخزّن معالجُ المقاطعة عدادَ البرامج وحالة المسجل (status register) في الذاكرة إلى جانب محتويات مسجلات البيانات التي من المُخطَّط استخدامها.
	</li>
	<li>
		ثم يُشغّل معالج المقاطعة الشيفرة المطلوبة لمعالجة هذه المقاطعة.
	</li>
	<li>
		يستعيد معالج المقاطعة محتويات المسجلات التي خزّنها سابقًا ثم أخيرًا يستعيد عداد البرنامج للعملية المقاطعة وهذا يؤدّي إلى العودة إلى التعليمة المُقاطَعة.
	</li>
</ol>
<p>
	إذا استخدِمت هذه الآلية بصورة صحيحة فلا يمكن أن تعلم العملية المقاطَعة بحدوث المقاطعة أبدًا إلّا إذا اكتشفت تغيّرًا في الوقت الفاصل بين التعليمات.
</p>

<h2>
	تبديل السياق (Context switching)
</h2>

<p>
	يمكن أن تكون معالجات المقاطعة سريعةً لأنها غير ملزمةٍ بحفظ كامل حالة العتاد وإنما تحفظ المسجلات التي من المخطط استخدامها فقط، ولكن لا تستأنف النواةُ العمليةَ المقاطعة دائمًا عند حدوث مقاطعةٍ ما وبالتالي يكون للنواة حرية التبديل إلى عملية أخرى، وتدعى هذه الآلية بتبديل السياق (context switch). لا تعلم النواة أيّ مسجلات ستستخدمها العملية لذلك يجب أن تحفظ كل المسجلات، ويجب على النواة تصفير البيانات المخزنة في وحدة إدارة الذاكرة (memory management unit) عند التبديل إلى عملية جديدة، حيث يمكن أن يستغرق تحميل بيانات العملية الجديدة إلى الذاكرة المخبئية بعض الوقت بعد تبديل السياق إليها لذلك يكون تبديل السياق بطيئًا نسبيًا فقد يستغرق آلاف الدورات أو عدة ميكرو ثانية. يُسمَح لكل عملية في نظام متعدد المهام أن تُشغَّل لفترة زمنية قصيرة تدعى بشريحة زمنية (time slice) أو حصّة (quantum)، وتضبط النواة مؤقت العتاد (hardware timer) خلال عملية تبديل السياق، وهذا يسبب حدوث مقاطعة عند نهاية الشريحة الزمنية، وبالتالي تستطيع النواة عند حدوث مقاطعةٍ التبديلَ إلى عملية أخرى أو السماح للعملية المقاطَعة أن تستأنف عملها، وجزء نظام التشغيل الذي يقرّر اختيار أحد هذين الخيارين هو المجدول (scheduler).
</p>

<h2>
	دورة حياة العملية (The process life cycle)
</h2>

<p>
	يخصص نظام التشغيل للعملية عند إنشائها بنية بيانات تتضمن معلومات عن هذه العملية وتدعى بينة البيانات هذه بكتلة تحكم العملية (process control block) وتختصر إلى PCB التي تتتبّع حالة العملية (process state)، ويكون للعملية أربع حالات هي:
</p>

<ul>
<li>
		التنفيذ (Running): عند تنفيذ العملية ضمن النواة (core).
	</li>
	<li>
		الاستعداد (Ready): عندما تكون العملية جاهزة للتنفيذ ولكنها لا تُنفَّذ ويجب عليها الانتظار لأن عدد العمليات القابلة للتنفيذ أكبر من عدد الأنوية (cores).
	</li>
	<li>
		الإيقاف (Blocked): إذا كان غير ممكن أن تُنفَّذ العملية لأنها تنتظر حدثًا مستقبليًا مثل اتصال شبكة أو قراءة من القرص الصلب.
	</li>
	<li>
		الاكتمال (Done): إذا اكتمل تنفيذ العملية ولكنها تملك معلومات حالة المغادرة (exit status information) التي لم تُقرَأ بعد.
	</li>
</ul>
<p>
	الأحداث التي تسبب انتقال العملية من حالة إلى أخرى هي:
</p>

<ul>
<li>
		تُنشَأ العملية عندما ينفّذ البرنامج المُشغَّل استدعاء نظام مثل <code>fork</code>، حيث تصبح العملية المنشَأة أو الجديدة في نهاية استدعاء النظام في حالة الاستعداد ثم قد يستأنف المجدول العملية الأصلية التي تسمى العملية الأب (parent) أو يبتدئ المجدول العملية الجديدة التي تسمى العملية الابن (child).
	</li>
	<li>
		تتغير حالة العملية من حالة الاستعداد إلى حالة التنفيذ عندما يبتدئها المجدول أو يستأنفها.
	</li>
	<li>
		تتغير حالة العملية من حالة التنفيذ إلى الاستعداد عندما تُقاطَع العملية ويختار المجدول ألا يستأنفها.
	</li>
	<li>
		إذا نفّذت العملية استدعاء النظام الذي لا يكتمل على الفور وإنما يحتاج وقتًا مثل الطلب من القرص الصلب فتصبح العملية بحالة الإيقاف وعندها يختار المجدول عمليةً أخرى لتنفيذها.
	</li>
	<li>
		إذا اكتملت عمليةٌ ما مثل عملية طلب من القرص الصلب فإنها تسبب مقاطعة، ويحدد معالج المقاطعة العملية المنتظرة لعملية الطلب هذه ويبدّل حالتها من حالة الإيقاف إلى الاستعداد ثم يختار المجدول أن يستأنفها أم لا.
	</li>
	<li>
		إذا استدعت العملية الدالة <code>exit</code> فإن معالج المقاطعة يخزّن شيفرة المغادرة (exit code) في كتلة تحكم العملية (PCB) ثم يغير حالة العملية إلى حالة الاكتمال.
	</li>
</ul>
<h2>
	الجدولة (Scheduling)
</h2>

<p>
	من الممكن وجود مئات العمليات على الحاسوب ولكن معظمها في حالة إيقاف (blocked) ويكون عدد قليل منها في حالة استعداد أو تنفيذ، والمجدول هو الذي يقرر أية عمليةٍ تبدأ التنفيذ أو تستأنف عملها عند حدوث مقاطعة. هدف المجدول الرئيسي هو تقليل وقت الاستجابة (response time) قدر الإمكان على الحاسوب المحمول (laptop) أو على محطة العمل (workstation)، حيث يجب أن يستجيب الحاسوب بسرعة لإجراءات المستخدم. وقت الاستجابة مهمٌ أيضًا في المخدمات (servers) بالإضافة إلى أنه يجب على المجدول زيادة الإنتاجية (throughput) والتي هي عدد الطلبات المنجزة خلال واحدة الزمن، وفي الحقيقة لا يملك المجدول معلومات كثيرة عمّا تفعله العمليات لذلك تعتمد قراراته في اختيار العملية على عدة استنتاجات هي:
</p>

<ul>
<li>
		يمكن أن تكون العمليات محدودةً بموارد مختلفة، فالعملية التي تقوم بعمليات حسابية كثيرة محدودةٌ بالمعالج (CPU-bound) أي أن وقت تشغيل هذه العملية يعتمد على كمية الوقت الذي تأخذه من وقت المعالج، أما العملية التي تقرأ بيانات من الشبكة أو من القرص الصلب فتكون محدودةً بعمليات الإدخال والإخراج (I/O-bound) أي تكون هذه العملية أسرع إذا كان إدخال أو إخراج البيانات أسرع، ولكنها لن تنفّذ أسرع إذا كان وقت المعالج الخاص بها أكبر، ويمكن أن تكون العملية التي تتفاعل مع المستخدم في حالة الإيقاف حيث ستبقى منتظرةً إجراءات المستخدم معظم الوقت. يصنّف نظام التشغيل العمليات أحيانًا تبعًا لسلوكها السابق ويجدولها بناءً على ذلك، فمن المحتمل أن تنفّذ العملية التفاعلية (interactive process) مباشرةً عندما تنتهي من حالة الإيقاف لأن المستخدم ينتظر ردًا منها، بينما تكون العملية المحدودة بالمعالج (CPU-bound) والتي ما زالت تنفّذ منذ مدة طويلة أقلَّ حساسيةً لعامل الوقت.
	</li>
	<li>
		إذا كان من المحتمل أن تُشغَّل العملية لفترة قصيرة ثم تطلب شيئًا يجعلها في حالة إيقاف، فيمكن أن تُشغَّل على الفور لسببين هما: (1) إذا استغرق الطلب بعض الوقت لإكماله فيجب أن يبدأ في أقرب وقت ممكن، و (2) من الأفضل أن تنتظر عملية ذات وقت تنفيذ طويل لفترةٍ قصيرة وليس العكس، بصورة مشابهة افترض أنك تصنع فطيرة تفاح، حيث يستغرق تحضير الطبقة الخارجية للفطيرة 5 دقائق ولكن يجب تركها لتبرد لمدة نصف ساعة ويستغرق تحضير حشوة الفطيرة 20 دقيقة، فإذا حضّرت الطبقة الخارجية أولًا فيمكنك تحضير الحشوة ريثما تبرد الطبقة الخارجية وبالتالي تنهي تحضير الفطيرة خلال 35 دقيقة، أما إذا حضّرت الحشوة أولًا فيستغرق تحضير الفطيرة 55 دقيقة.
	</li>
</ul>
<p>
	تستخدم معظم المجدولات بعض نماذج الجدولة المعتمدة على الأولوية (priority-based scheduling)، حيث يكون لكل عملية أولوية تزيد أو تنقص خلال الوقت ويختار المجدول العملية القابلة للتنفيذ ذات الأولوية العليا، وهناك عدة عوامل لتحديد أولوية العملية هي:
</p>

<ul>
<li>
		تبدأ العملية عادةً برقم أولوية عالٍ نسبيًا لذلك تبدأ التنفيذ بسرعة.
	</li>
	<li>
		إذا طلبت العملية شيئًا ما جعلها في حالة إيقاف قبل انتهاء شريحتها الزمنية ضمن المعالج فمن المحتمل أن تكون عملية تفاعلية مع المستخدم (interactive) أو عملية محدودة بعمليات الإدخال والإخراج (I/O-bound) لذلك يجب أن تصبح أولويتها أعلى.
	</li>
	<li>
		إذا انتهت الشريحة الزمنية الخاصة بالعملية ضمن المعالج ولم ينتهِ تنفيذ هذه العملية فمن المحتمل أن تكون عملية ذات وقت تنفيذ طويل (long-running) ومحدودة بالمعالج (CPU-bound) لذلك يجب أن تصبح أولويتها أقل.
	</li>
	<li>
		إذا توقفت مهمةٌ لمدة طويلة ثم أصبحت بحالة استعداد فيجب أن تحصل على زيادة في الأولوية لتتمكن من الاستجابة على الشيء الذي انتظرته.
	</li>
	<li>
		إذا توقفت العملية A بسبب انتظارها للعملية B وهاتان العمليتان مرتبطتان عن طريق أنبوب (pipe) مثلًا فيجب أن تصبح أولوية العملية B أعلى.
	</li>
	<li>
		يسمح استدعاء النظام <code>nice</code> للعملية بتقليل أولويتها (ولا تسمح بزيادتها) مما يسمح للمبرمجين بتمرير معلومات محددة إلى المجدول.
	</li>
</ul>
<p>
	لا تؤثر خوازرميات الجدولة (scheduling algorithms) كثيرًا على أداء معظم أنظمة التشغيل التي تعمل بأحمال (workloads) عادية فسياسات الجدولة (scheduling policies) البسيطة جيدة كفاية لهذه الأنظمة.
</p>

<h2>
	الجدولة في الوقت الحقيقي (Real-time scheduling)
</h2>

<p>
	الجدولة مهمةٌ جدًا بالنسبة للبرامج التي تتفاعل مع العالم الحقيقي، فقد يضطر البرنامج الذي يقرأ بيانات من الحسّاسات (sensors) والذي يتحكم بالمحركات إلى إكمال المهام المتكررة بالحد الأدنى من التكرار وأن يتفاعل مع الأحداث الخارجية بالحد الأقصى من وقت الاستجابة. يُعبّر عن هذه المتطلبات بالمهام التي يجب إكمالها قبل المواعيد النهائية (deadlines). تدعى جدولة المهام من أجل الوفاء بالمواعيد النهائية بالجدولة في الوقت الحقيقي (real-time scheduling)، ويمكن تعديل أنظمة التشغيل التي تستخدم للأغراض العامة مثل Linux لتتعامل مع الجدولة في الوقت الحقيقي بالنسبة لبعض التطبيقات، وقد تشمل هذه التعديلات ما يلي:
</p>

<ul>
<li>
		توفير واجهات برمجة تطبيقات (APIs) أثرى للتحكم في أولويات المهام.
	</li>
	<li>
		تعديل المجدول لضمان تشغيل العملية ذات الأولوية الأعلى خلال مدة زمنية محددة.
	</li>
	<li>
		إعادة تنظيم معالجات المقاطعة لضمان أكبر وقت لاكتمال العمليات.
	</li>
	<li>
		تعديل الأقفال (locks) وآليات المزامنة الأخرى (synchronization mechanisms) (سنتطرق إليها لاحقًا) للسماح لمهمة ذات أولوية عالية أن تسبق مهمة ذات أولوية أقل.
	</li>
	<li>
		اختيار تطبيق تخصيص الذاكرة الديناميكي الذي يضمن أكبر وقت لاكتمال العمليات.
	</li>
</ul>
<p>
	توفر أنظمة التشغيل في الوقت الحقيقي (real-time operating systems) إمكانيات متخصصة بالنسبة للتطبيقات الأكثر طلبًا وخاصة في المجالات التي تمثل فيها الاستجابة في الوقت الحقيقي مسألةَ حياة أو موت، وتكون هذه الأنظمة ذات تصميم أبسط بكثير من أنظمة التشغيل ذات الأغراض العامة.
</p>

<p>
	ترجمة -وبتصرّف- للفصل Multitasking من كتاب <a href="http://greenteapress.com/thinkos/" rel="external nofollow">Think OS A Brief Introduction to Operating Systems</a>
</p>
]]></description><guid isPermaLink="false">1011</guid><pubDate>Fri, 25 Sep 2020 13:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x641;&#x635;&#x644; &#x627;&#x644;&#x633;&#x627;&#x628;&#x639;: &#x641;&#x647;&#x645; &#x639;&#x645;&#x644;&#x64A;&#x629; &#x627;&#x644;&#x62A;&#x62E;&#x628;&#x626;&#x629; (Caching) &#x641;&#x64A; &#x645;&#x639;&#x645;&#x627;&#x631;&#x64A;&#x629; &#x627;&#x644;&#x62D;&#x627;&#x633;&#x648;&#x628;</title><link>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%A8%D8%B9-%D9%81%D9%87%D9%85-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%AE%D8%A8%D8%A6%D8%A9-caching-%D9%81%D9%8A-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%D9%8A%D8%A9-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8-r995/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_09/7.png.459aa1b5c30791d44ea1d66a728f982a.png" /></p>

<h2>
	كيف ينفذ البرنامج (How programs run)
</h2>

<p>
	يجب أن تفهم كيف تنفّذ الحواسيب البرامج لفهم عملية التخبئة (caching)، وينبغي عليك دراسة معمارية الحاسوب لفهم التخبئة بصورةٍ أعمق. تكون الشيفرة أو النص البرمجي ضمن القرص الصلب (hard disk) أو ضمن SSD عند بدء تشغيل البرنامج، وينشئ نظام التشغيل عمليةً جديدة لتشغيل البرنامج ثم ينسخ المحمِّل (loader) نص البرنامج من التخزين الدائم إلى الذاكرة الرئيسية ثم يبدأ البرنامج من خلال استدعاء الدالة <code>main</code>. تُخزَّن معظم بيانات البرنامج في الذاكرة الرئيسية أثناء تنفيذه، ولكن تكون بعض هذه البيانات موجودةً في مسجّلات (registers) والتي هي وحدات صغيرة من الذاكرة موجودة في المعالج (CPU) وتتضمن هذه المسجلات ما يلي:
</p>

<ul>
<li>
		عداد البرنامج (The program counter) واختصاره PC الذي يتضمن عنوان التعليمة التالية من البرنامج (العنوان في الذاكرة).
	</li>
	<li>
		مسجّل التعليمة (The instruction register) ويختصر إلى IR ويتضمن شيفرة الآلة للتعليمة التي تنفّذ حاليًا.
	</li>
	<li>
		مؤشر المكدس (The stack pointer) واختصاره SP الذي يتضمن عنوان إطار المكدس (stack frame) للدالة الحالية ويحتوي إطار المكدس معاملات الدالة ومتغيراتها المحلية.
	</li>
	<li>
		مسجلات ذات أغراض عامة (General-purpose registers) التي تحتفظ بالبيانات التي يعمل عليها البرنامج حاليًا.
	</li>
	<li>
		مسجل الحالة (status register) أو مسجل الراية (flag register) الذي يتضمن معلومات عن العمليات الحسابية الحالية، حيث يتضمن مسجل الراية عادةً بتًا، ويُضبَط هذا البت إذا كانت نتيجة العملية السابقة صفرًا على سبيل المثال.
	</li>
</ul>
<p>
	ينفّذ المعالج الخطوات التالية عند تشغيل البرنامج وتدعى هذه الخطوات بدورة التعليمة (instruction cycle):
</p>

<ul>
<li>
		الجلب (Fetch): تُجلَب التعليمة التالية من الذاكرة ثم تُخزَّن في مسجّل التعليمة.
	</li>
	<li>
		فك التشفير (Decode): يَفك جزءُ المعالج الذي يدعى وحدة التحكم (control unit) تشفيرَ التعليمة ثم يرسل إشاراتٍ إلى الأجزاء الأخرى من المعالج.
	</li>
	<li>
		التنفيذ (Execute): تسبب إشارات وحدة التحكم ظهور العمليات الحسابية المناسبة.
	</li>
</ul>
<p>
	تستطيع معظم الحواسيب تنفيذ بضع مئات من التعليمات المختلفة تدعى بمجموعة التعليمات (instruction set) ولكن تندرج معظم التعليمات ضمن فئات عامة هي:
</p>

<ul>
<li>
		تعليمات التحميل (Load): تنقل قيمةً من الذاكرة إلى المسجل.
	</li>
	<li>
		التعليمات الحسابية أو المنطقية (Arithmetic/logic): تحمّل المعامَلات (operands) من المسجّلات ثم تجري عمليات رياضية ثم تخزّن النتيجة في مسجّل.
	</li>
	<li>
		تعليمات التخزين (Store): تنقل قيمةً من المسجّل إلى الذاكرة.
	</li>
	<li>
		تعليمات القفز وتعليمات الفرع (Jump/branch): تسبب تغييراتُ عداد البرنامج قفزَ تدفق التنفيذ (flow of execution) إلى موقعٍ آخر من البرنامج. تكون الفروع مشروطة عادةً وهذا يعني أن الفروع تتحقق من رايةٍ ما في مسجل الراية ثم تقفز إلى موقع آخر من البرنامج في حال ضُبطت هذه الراية فقط.
	</li>
</ul>
<p>
	توفر بعضُ مجموعات التعليمات الموجودة ضمن معمارية نظام التشغيل واسعة الانتشار x86 تعليماتٍ تجمع بين عمليةٍ حسابيةٍ وعملية تحميل. تُقرَأ تعليمةٌ واحدةٌ من نص البرنامج خلال كل دورة تعليمة، ويُحمَّل حوالي نصف تعليمات البرنامج أو تخزن بياناتها، وتكمن هنا واحدة من المشاكل الأساسية في معمارية الحاسوب وهي مشكلة عنق زجاجة الذاكرة أو اختناق الذاكرة (memory bottleneck). نواة الحواسيب الحالية قادرةٌ على تنفيذ تعليمةٍ في أقل من 1 نانو ثانية، ولكن يُقدّر الوقت اللازم لنقل بيانات من وإلى الذاكرة بحوالي 100 نانو ثانية، وإذا توجّب على المعالج الانتظار 100 نانو ثانية لجلب التعليمة التالية و100 نانو ثانية أخرى لتحميل البيانات فسيكمل المعالج التعليمات بصورة أبطأ ب 200 مرة مما هو متوقع، لذلك تُعَد الذاكرة في العديد من العمليات الحسابية هي عامل تحديد السرعة ولا يُعَد المعالج كذلك.
</p>

<h2>
	أداء الذاكرة المخبئية (Cache performance)
</h2>

<p>
	الذاكرة المخبئية هي الحل لمشكلة اختناق الذاكرة أو على الأقل حلٌ جزئي لها، حيث أن الذاكرة المخبئية (cache) ذاكرةٌ صغيرة الحجم وسريعة ومتواجدة قرب المعالج على نفس الشريحة عادةً. تحوي الحواسيب الحديثة مستويات متعددة من الذاكرة المخبئية هي: ذاكرة مخبئية ذات مستوى أول (Level 1 cache) وهي الأصغر حجمًا والأسرع حيث يتراوح حجمها بين 1 و 2 ميبي بايت مع وقت وصول 1 نانو ثانية تقريبًا، أما الذاكرة المخبئية ذات المستوى الثاني (Level 2 cache) التي تملك وقت وصول يساوي 4 نانو ثانية تقريبًا، وتملك الذاكرة المخبئية ذات المستوى الثالث وقت وصول يساوي 16 نانو ثانية. يخزّن المعالج نسخةً من القيمة التي يحمّلها من الذاكرة في الذاكرة المخبئية، وإذا حُمّلت تلك القيمة مرةً أخرى يجلب المعالج نسخة هذه القيمة من الذاكرة المخبئية وبالتالي لا يضطر المعالج إلى الانتظار لجلب القيمة من الذاكرة، ولكن من الممكن أن تمتلئ الذاكرة المخبئية وبالتالي يجب إخراج بعض القيم من الذاكرة المخبئية عند إحضار قيم أخرى، لذلك إذا حمّل المعالج قيمةً ثم عاد لتحميلها مرةً أخرى ولكن بعد وقت طويل فقد لا تكون هذه القيمة موجودةً ضمن الذاكرة المخبئية. إن أداء العديد من البرامج محدودٌ بمقدار فعالية الذاكرة المخبئية، فإذا كانت التعليمات والبيانات التي يحتاجها المعالج موجودةً في الذاكرة المخبئية فإن البرنامج يمكن أن ينفّذ بسرعةٍ قريبة من سرعة المعالج الكاملة، بينما إذا احتاج المعالج بياناتٍ غير موجودةٍ في الذاكرة المخبئية مرارًا فسيكون المعالج محدودًا بسرعة الذاكرة. معدّل الإصابة (hit rate) للذاكرة المخبئية الذي يرمز له h هو جزء عمليات الوصول للذاكرة التي تجد البيانات في الذاكرة المخبئية، أما معدل الإخفاق (miss rate) والذي يرمز له m هو جزء عمليات الوصول للذاكرة التي يجب أن تذهب إلى الذاكرة لأنها لم تجد البيانات التي تريدها ضمن الذاكرة المخبئية، فإذا كان وقت إصابة الذاكرة المخبئية هو T<sub>h</sub> ووقت إخفاق الذاكرة المخبئية هو T<span style="font-size: 12px;">m</span> فإن متوسط وقت كل عملية وصولٍ للذاكرة هو: h T<sub>h</sub> + m T<sub>m</sub>، ويمكن تعريف عقوبة الإخفاق (miss penalty) كوقتٍ إضافي لمعالجة إخفاق الذاكرة المخبئية والذي يساوي: T<sub>p</sub> =T<sub>m</sub>-T<sub>h</sub> وبالتالي متوسط وقت الوصول هو: T<sub>h</sub>+mT<sub>p</sub>، يساوي متوسط وقت الوصول تقريبًا T<sub>h</sub> عندما يكون معدل الإخفاق منخفضًا، وبالتالي يؤدي البرنامج عمله وكأن الذاكرة تعمل بسرعة الذاكرة المخبئية.
</p>

<h2>
	المحلية (Locality)
</h2>

<p>
	تحمّل الذاكرة المخبئية عادةً كتلةً أو سطرًا من البيانات الذي يتضمن البايت المطلوب وبعضًا من البايتات المجاورة له وذلك عندما يقرأ البرنامج بايتًا للمرة الأولى، وبالتالي إذا حاول البرنامج قراءة إحدى تلك البايتات المجاورة للبايت المطلوب لاحقًا فستكون موجودةً في الذاكرة المخبئية مسبقًا. افترض أن حجم الكتلة هو 64 بايتًا مثل قراءة سلسلة طولها 64 بايتًا، حيث يمكن أن يقع أول بايت من السلسلة في بداية الكتلة، فستتحمّل عقوبة إخفاق (miss penalty) إذا حمّلت أول بايت ولكن ستوجد بقية السلسلة في الذاكرة المخبئية بعد ذلك، أي يساوي معدل الإصابة 63/64 بعد قراءة كامل السلسلة أي ما يعادل 98%، أما إذا كان حجم السلسلة يعادل كتلتين فستتحمّل عقوبتي إخفاق ويكون معدل الإصابة مساويًا 62/64 أو 97%، ويصبح معدل الإصابة 100% إذا قرأت السلسلة ذاتها مرة أخرى، ولكن سيصبح أداء الذاكرة المخبئية سيئًا إذا قفز البرنامج بصورة غير متوقعة محاولًا قراءة بيانات من مواقع متفرقة في الذاكرة أو إذا كان الوصول إلى نفس الموقع مرتين نادرًا. يدعى ميل البرنامج لاستخدام البيانات ذاتها أكثر من مرة بالمحلية الزمانية (temporal locality)، ويدعى ميل البرنامج لاستخدام البيانات المتواجدة في مواقع قريبة من بعضها بعضًا بالمحلية المكانية (spatial locality)، حيث تقدّم البرامج نوعي المحلية كما يلي:
</p>

<ul>
<li>
		تحوي معظم البرامج كتلًا من الشيفرة بدون تعليمات قفزٍ أو فروع، حيث تنفّذ التعليمات في هذه الكتل تسلسليًا، وبالتالي يملك نموذج الوصول (access pattern) محليةً مكانية (spatial locality).
	</li>
	<li>
		تنفّذ البرامج نفس التعليمات مرات متعددة في الحلقات التكرارية (loop)، وبالتالي يملك نموذج الوصول (access pattern) محليةً زمانية (temporal locality).
	</li>
	<li>
		تُستخدم نتيجة تعليمةٍ ما كمُعامَل (operand) للتعليمة التالية مباشرةً، وبالتالي يملك نموذج وصول البيانات محليةً زمانية (temporal locality).
	</li>
	<li>
		تُخزَّن معامِلات دالةٍ ومتغيراتها المحلية معًا في جزء المكدس عندما ينفّذ البرنامج هذه الدالة، أي يملك الوصول إلى هذه القيم محليةً مكانية (spatial locality).
	</li>
	<li>
		أحد أكثر نماذج المعالجة شيوعًا هو قراءة أو كتابة عناصر مصفوفةٍ تسلسليًا وبالتالي تملك هذه النماذج محليةً مكانية (spatial locality).
	</li>
</ul>
<h2>
	قياس أداء الذاكرة المخبئية (Measuring cache performance)
</h2>

<p>
	أحد برامجي المفضلة، يقول الكاتب، هو البرنامج الذي يتكرر خلال مصفوفة ويقيس متوسط وقت قراءة وكتابة عنصر، ويمكن استنتاج حجم الذاكرة المخبئية وحجم الكتلة وبعض الخصائص الأخرى من خلال تغيير حجم تلك المصفوفة، والجزء الأهم من هذا البرنامج هو الحلقة التكرارية (loop) التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9763_7" style="">
<span class="pln">iters </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">do</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    sec0 </span><span class="pun">=</span><span class="pln"> get_seconds</span><span class="pun">();</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> </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"> index </span><span class="pun">&lt;</span><span class="pln"> limit</span><span class="pun">;</span><span class="pln"> index </span><span class="pun">+=</span><span class="pln"> stride</span><span class="pun">)</span><span class="pln">
        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"> 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">1</span><span class="pun">;</span><span class="pln">

    iters </span><span class="pun">=</span><span class="pln"> iters </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    sec </span><span class="pun">=</span><span class="pln"> sec </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">get_seconds</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> sec0</span><span class="pun">);</span><span class="pln">

</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">sec </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0.1</span><span class="pun">);</span></pre>

<p>
	حيث تعبُر (traverses) حلقة <code>for</code> الداخلية المصفوفة، ويحدد المتغير <code>limit</code> مقدار قيم المصفوفة التي تريد عبورها، ويحدد المتغير <code>stride</code> الخطوة أو عدد العناصر التي يجب تجاوزها في كل تكرار، فمثلًا إذا كانت قيمة المتغير<code>limit</code> هي 16 وقيمة المتغير <code>stride</code> هي 4 فإن الحلقة ستصل إلى القيم التالية: 0 و 4 و 8 و 12. يتتبّع المتغير <code>sec</code> وقت المعالج الذي تستخدمه حلقة <code>for</code> الداخلية، وتنفّذ الحلقة الخارجية حتى يتخطى المتغير <code>sec</code> حاجز 0.1 ثانية والذي هو وقتٌ كافٍ لحساب الوقت الوسطي بدقةٍ كافية. تستخدم الدالة <code>get_seconds</code> استدعاء النظام <code>clock_gettime</code> وتحوّل الوقت إلى ثوانٍ ثم تعيد النتيجة كعدد عشري مضاعف <code>double</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9763_9" style="">
<span class="kwd">double</span><span class="pln"> get_seconds</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"> timespec ts</span><span class="pun">;</span><span class="pln">
    clock_gettime</span><span class="pun">(</span><span class="pln">CLOCK_PROCESS_CPUTIME_ID</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ts</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> ts</span><span class="pun">.</span><span class="pln">tv_sec </span><span class="pun">+</span><span class="pln"> ts</span><span class="pun">.</span><span class="pln">tv_nsec </span><span class="pun">/</span><span class="pln"> </span><span class="lit">1e9</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_3555_7" style="">
<span class="pln">iters2 </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">do</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    sec0 </span><span class="pun">=</span><span class="pln"> get_seconds</span><span class="pun">();</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> </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"> index </span><span class="pun">&lt;</span><span class="pln"> limit</span><span class="pun">;</span><span class="pln"> index </span><span class="pun">+=</span><span class="pln"> stride</span><span class="pun">)</span><span class="pln">
        temp </span><span class="pun">=</span><span class="pln"> temp </span><span class="pun">+</span><span class="pln"> index</span><span class="pun">;</span><span class="pln">

    iters2 </span><span class="pun">=</span><span class="pln"> iters2 </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    sec </span><span class="pun">=</span><span class="pln"> sec </span><span class="pun">-</span><span class="pln"> </span><span class="pun">(</span><span class="pln">get_seconds</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> sec0</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">iters2 </span><span class="pun">&lt;</span><span class="pln"> iters</span><span class="pun">);</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4579_16" style="">
<span class="pln">sec </span><span class="pun">*</span><span class="pln"> </span><span class="lit">1e9</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> iters </span><span class="pun">/</span><span class="pln"> limit </span><span class="pun">*</span><span class="pln"> stride</span></pre>

<p>
	إذا صرّفت البرنامج السابق ثم شغّلته سيظهر الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4579_18" style="">
<span class="typ">Size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4096</span><span class="pln"> </span><span class="typ">Stride</span><span class="pun">:</span><span class="pln"> </span><span class="lit">8</span><span class="pln"> read</span><span class="pun">+</span><span class="pln">write</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.8633</span><span class="pln"> ns
</span><span class="typ">Size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4096</span><span class="pln"> </span><span class="typ">Stride</span><span class="pun">:</span><span class="pln"> </span><span class="lit">16</span><span class="pln"> read</span><span class="pun">+</span><span class="pln">write</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.7023</span><span class="pln"> ns
</span><span class="typ">Size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4096</span><span class="pln"> </span><span class="typ">Stride</span><span class="pun">:</span><span class="pln"> </span><span class="lit">32</span><span class="pln"> read</span><span class="pun">+</span><span class="pln">write</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.7105</span><span class="pln"> ns
</span><span class="typ">Size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4096</span><span class="pln"> </span><span class="typ">Stride</span><span class="pun">:</span><span class="pln"> </span><span class="lit">64</span><span class="pln"> read</span><span class="pun">+</span><span class="pln">write</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.7058</span><span class="pln"> ns</span></pre>

<p>
	إذا كان لديك Python و <code>matplotlib</code> على جهازك فيمكنك استخدام <code>graph_data.py</code> لتحصل على رسمٍ بياني للنتائج كما في الشكل التالي الذي يظهر نتائج البرنامج عندما أشغّله، يقول الكاتب، على Dell Optiplex 7010:
</p>

<p style="text-align: center;">
	<img alt="AverageMissPenaltyAsAFunctionOfArraySizeAndStride.png" class="ipsImage ipsImage_thumbnailed" data-fileid="51123" data-unique="r74fdrtbk" src="https://academy.hsoub.com/uploads/monthly_2020_09/AverageMissPenaltyAsAFunctionOfArraySizeAndStride.png.97904c24c8db6424ecd86ea007516f08.png"></p>

<p>
	لاحظ أن حجم المصفوفة والخطوة (stride) مقدران بالبايت وليس بأعداد عناصر المصفوفة. إذا تمعنت النظر في الرسم البياني السابق سيمكنك استنتاج بعض الأشياء عن الذاكرة المخبئية والتي هي:
</p>

<ul>
<li>
		يقرأ البرنامج من المصفوفة مراتٍ متعددة لذلك يكون لديه كثيرٌ من المحلية الزمانية (temporal locality)، فإذا كانت المصفوفة بكاملها في الذاكرة المخبئية فهذا يعني أن متوسط عقوبة الإخفاق (miss penalty) تساوي 0 تقريبًا.
	</li>
	<li>
		يمكنك قراءة كل عناصر المصفوفة إذا كانت قيمة الخطوة (stride) هي 4 بايتات أي يكون لدى البرنامج كثيرٌ من المحلية المكانية (spatial locality)، وإذا كان حجم الكتلة مناسبًا لتضمين 64 عنصرًا على سبيل المثال فإن معدل الإصابة (hit rate) يساوي 63/64 على الرغم من عدم وجود المصفوفة في الذاكرة المخبئية.
	</li>
	<li>
		إذا كانت الخطوة الواحدة مساويةً لحجم الكتلة أو أكبر منها فإن قيمة المحلية المكانية صفر عمليًا، لأنك تصل إلى عنصرٍ واحد فقط في كل مرة تقرأ فيها كتلةً، أي من المتوقع أن تشاهد الحد الأعلى من عقوبة الإخفاق (miss penalty) في هذه الحالة.
	</li>
</ul>
<p>
	من المتوقع الحصول على أداء جيد للذاكرة المخبئية إذا كانت المصفوفة أصغر من حجم الذاكرة المخبئية أو إذا كانت الخطوة أصغر من حجم الكتلة، بينما يقل الأداء فقط إذا كانت المصفوفة أكبر من الذاكرة المخبئية والخطوة كبيرة. أداء الذاكرة المخبئية في الرسم البياني السابق جيد بالنسبة لجميع الخطوات طالما أن المصفوفة أصغر من 2<sup>22</sup> بايتًا، وبالتالي يمكنك استنتاج أن حجم الذاكرة المخبئية يقارب 4 ميبي بايت ولكن اعتمادًا على المواصفات فإن حجمها هو 3 ميبي بايت. يكون أداء الذاكرة المخبئية جيدًا مع قيم الخطوة 8 و 16 و 32 بايتًا، ولكن يبدأ الأداء بالانخفاض عندما تصبح قيمة الخطوة 64 بايتًا، ويصبح متوسط عقوبة الإخفاق 9 نانو ثانية تقريبًا عندما تصبح الخطوة أكبر، وبالتالي يمكنك استنتاج أن حجم الكتلة يقارب 128 بايتًا. تستخدم العديد من المعالجات الذواكر المخبئية ذات المستويات المتعددة (multi-level caches) والتي تتضمن ذواكر مخبئية صغيرة وسريعة وذواكر مخبئية كبيرة وبطيئة، ويُلاحَظ أن عقوبة الإخفاق تزيد قليلًا عندما يصبح حجم المصفوفة أكبر من 2<sup>14</sup> بايتًا، لذلك من المحتمل أن يكون للمعالج ذاكرةٌ مخبئية حجمها 16 كيلو بايت وبوقت وصول أقل من 1 نانو ثانية.
</p>

<h2>
	برمجة أداء الذاكرة المخبئية (Programming for cache performance)
</h2>

<p>
	تُطبَّق تخبئة الذاكرة في العتاد لذلك لا يتوجّب على المبرمجين معرفة الكثير عنها ولكن إذا عرفت كيفية عمل الذواكر المخبئية فيمكّنك ذلك من كتابة برامجٍ تستخدم الذاكرة المخبئية بفعاليةٍ أكثر، وإذا عملت مع مصفوفة كبيرة على سبيل المثال فمن الممكن أن تعبُر المصفوفة بسرعةٍ مرة واحدة ثم إجراء عملياتٍ متعددة على كل عنصر من المصفوفة وذلك أفضل من عبور المصفوفة مراتٍ متعددة، وإذا تعاملت مع مصفوفة ثنائية الأبعاد (2D array) فيمكن أن تُخزّن هذه المصفوفة كمصفوفة من الصفوف (rows)، وإذا عبرت خلال عناصر المصفوفة فسيكون عبور عناصر هذه المصفوفة من خلال الصفوف (row-wise) أسرع وذلك مع خطوةٍ مساويةٍ لحجم العنصر وهذا أفضل من عبورها من خلال الأعمدة (column-wise) مع خطوة مساوية لطول الصف (row) الواحد. لا تقدّم بنى البيانات المترابطة (Linked data structures) محليةً مكانية وذلك لأنه ليس ضروريًا أن تكون عقد هذه البنى المترابطة متجاورةً في الذاكرة، ولكن إذا خصصت عدة عقد في نفس الوقت فستكون موجودة في مواقع مشتركة من الكومة، وإذا خصصت مصفوفة العقد كلها مرةً واحدةً فستكون في مواقع متجاورة حتمًا. تملك الاستراتيجيات التعاودية (Recursive strategies) مثل خوارزمية الفرز بالدمج (mergesort) سلوك ذاكرةٍ مخبئية جيدًا لأنها تقسّم المصفوفات الكبيرة إلى أجزاء صغيرة ثم تعمل على هذه الأجزاء الصغيرة، وتُضبَط هذه الخوارزميات في بعض الأحيان للاستفادة من سلوك الذاكرة المخبئية. يمكن تصميم خوارزمياتٍ في التطبيقات التي يكون الأداء فيها مهمًا جدًا بحيث تكون هذه الخوارزميات مضبوطة من حيث حجم الذاكرة المخبئية وحجم الكتلة وخصائص عتاد أخرى، ويدعى هذا النوع من من الخوارزميات بالخوارزميات ذات الإدراك حسب الذاكرة المخبئية (cache-aware)، ولكن العائق الأهم لهذا النوع من الخوارزميات هو أنها خاصة بالعتاد (hardware-specific).
</p>

<h2>
	هرمية الذاكرة (The memory hierarchy)
</h2>

<p>
	لا بدّ أنه خطر على بالك السؤال التالي: لماذا لم تُصنّع ذاكرة مخبئية كبيرة وبالتالي تُلغى الذاكرة الرئيسية نهائيًا بما أن الذاكرة المخبئية أسرع بكثير من الذاكرة الرئيسية؟ وجواب هذا السؤال هو وجود سببين أساسيين هما: سبب متعلق بالالكترونيات والآخر سبب اقتصادي، فإن الذواكر المخبئية سريعة لأنها صغيرة وقريبة من المعالج وهذا يقلل التأخير بسبب سعة وانتشار الإشارة، وبالتالي إذا صنعت ذاكرة مخبئية كبيرة فستكون بطيئة حتمًا، وتشغَل الذواكر المخبئية حيّزًا على شريحة المعالج وكلما كانت الشريحة أكبر فسيصبح سعرها أكبر. أما الذاكرة الرئيسية فهي ذاكرة عشوائية ديناميكية (dynamic random-access memory واختصارها DRAM) والتي تستخدم ترانزستورًا ومكثفًا واحدًا لكل بت، وبالتالي يمكن ضغط مزيد من الذاكرة في نفس الحيّز، ولكن هذا التطبيق يجعل الذاكرة أبطأ من الطريقة التي تُطبَّق فيها الذواكر المخبئية. تُحزَم الذاكرة الرئيسية عادةً ضمن وحدة الذاكرة ثنائية الخط (dual in-line memory module) واختصارها DIMM والتي تتضمن 16 شريحة أو أكثر، فشرائح صغيرة متعددة أرخص من شريحة واحدة كبيرة الحجم. وجود تقايض بين السرعة والحجم والكلفة هو السبب الأساسي لصنع الذواكر المخبئية، فإذا وجدت تقنية سريعة وكبيرة ورخيصة للذاكرة فلسنا بحاجة أي شيء آخر. يطبّق نفس المبدأ بالنسبة للتخزين الدائم كما في الذاكرة الرئيسية، فالأقراص من النوع SSD سريعة ولكنها أغلى من الأقراص الصلبة HDD وبالتالي يجب أن تكون أصغر من أجل التوفير في الكلفة، ومحركات الأشرطة (Tape drives) أبطأ من الأقراص الصلبة ولكنها تخزّن كميات كبيرة من البيانات وبسعر رخيص نسبيًا. يظهر الجدول التالي وقت الوصول والحجم والكلفة لكل من هذه التقنيات:
</p>

<table>
<thead><tr>
<th>
				الجهاز (Device)
			</th>
			<th>
				وقت الوصول (Access time)
			</th>
			<th>
				الحجم (Typical size)
			</th>
			<th>
				الكلفة (Cost)
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				المسجل (Register)
			</td>
			<td>
				يقدر ب 0.5 نانو ثانية
			</td>
			<td>
				256 بايت
			</td>
			<td>
				؟
			</td>
		</tr>
<tr>
<td>
				الذاكرة المخبئية (Cache)
			</td>
			<td>
				يقدر ب 1 نانو ثانية
			</td>
			<td>
				2 ميبي بايت
			</td>
			<td>
				؟
			</td>
		</tr>
<tr>
<td>
				الذاكرة العشوائية الديناميكية (DRAM)
			</td>
			<td>
				يقدر ب 100 نانو ثانية
			</td>
			<td>
				4 جيبي بايت
			</td>
			<td>
				10 دولار / جيبي بايت
			</td>
		</tr>
<tr>
<td>
				قرص التخزين ذو الحالة الثابتة (SDD)
			</td>
			<td>
				يقدر ب 10 ميكرو ثانية
			</td>
			<td>
				100 جيبي بايت
			</td>
			<td>
				1 دولار / جيبي بايت
			</td>
		</tr>
<tr>
<td>
				القرص الصلب (HDD)
			</td>
			<td>
				يقدر ب 5 ميلي ثانية
			</td>
			<td>
				500 جيبي بايت
			</td>
			<td>
				0.25 دولار / جيبي بايت
			</td>
		</tr>
<tr>
<td>
				محرك الأشرطة (Tape)
			</td>
			<td>
				دقائق
			</td>
			<td>
				من 1 إلى 2 تيبي بايت
			</td>
			<td>
				0.02 دولار / جيبي بايت
			</td>
		</tr>
</tbody>
</table>
<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>
<p>
	يعتمد عدد وحجم المسجلات على تفاصيل معمارية الحواسيب حيث تملك الحواسيب الحالية 32 مسجلًا ذو أغراضٍ عامة (general-purpose registers) ويخزن كل مسجل منها كلمة (word) واحدة حيث تساوي الكلمة 32 بتًا أو 4 بايتات في حواسيب 32 بت وتساوي 64 بتًا أو 8 بايتات في حواسيب 64 بت، وبالتالي يتراوح الحجم الإجمالي لملف المسجلات بين 100 إلى 300 بايت. من الصعب تحديد كلفة المسجلات والذواكر المخبئية لأنه تُجمَل كلفتهم مع كلفة الشرائح الموجودين عليها، وبالتالي لا يرى المستهلك كلفتهم بطريقة مباشرة، وبالنسبة للأرقام الأخرى الموجودة في الجدول فقد ألقيت النظر، يقول الكاتب، على العتاد النموذجي الموجود في المتاجر الالكترونية لعتاد الحاسوب، وربما ستكون هذه الأرقام غير مستعملة في الوقت الذي ستقرأ فيه هذا المقال ولكنها ستعطيك فكرةً عن الفجوة التي كانت موجودة بين الأداء والكلفة في وقتٍ ما. تشكل التقنيات الموجودة في الجدول السابق هرمية الذاكرة (memory hierarchy) -والذي يتضمن التخزين الدائم (storage) أيضًا- حيث يكون كل مستوى من هذه الهرمية أكبر وأبطأ من المستوى الذي فوقه، وبمعنىً آخر يمكن عدّ كل مستوى هو ذاكرة مخبئية للمستوى الذي تحته، فيمكنك عدّ الذاكرة الرئيسية كذاكرة مخبئية للبرامج والبيانات التي تُخزَّن على أقراص SSD و HHD بصورة دائمة، وإذا عملت مع مجموعات بيانات كبيرة جدًا ومخزنة على محرك أشرطة (tape) فيمكنك استخدام القرص الصلب كذاكرة مخبئية لمجموعة بيانات فرعية واحدة في كل مرة.
</p>

<h2>
	سياسة التخبئة (Caching policy)
</h2>

<p>
	تقدّم هرمية الذاكرة إطار عملٍ للتخبئة (caching) من خلال الإجابة على أربعة أسئلة أساسية عن التخبئة في كل مستوٍ من هذه الهرمية وهي:
</p>

<ul>
<li>
		من ينقل البيانات إلى الأعلى والأسفل ضمن هذه الهرمية؟ يخصص المصرّفُ (compiler) المسجّلَ في قمة هذه الهرمية، ويكون العتاد الموجود على المعالج مسؤولًا عن الذاكرة المخبئية، ينقل المستخدمون البيانات من التخزين الدائم إلى الذاكرة ضمنيًا عندما ينفّذون برامجًا وعندما يفتحون ملفات، ولكن ينقل نظام التشغيل البيانات أيضًا ذهابًا وإيابًا بين الذاكرة والتخزين الدائم، وينقل مسؤولو نظام التشغيل البيانات بين القرص الصلب ومحرك الأشرطة بوضوح وليس ضمنيًا في أسفل هذه الهرمية.
	</li>
	<li>
		ماذا يُنقَل؟ تكون أحجام الكتل صغيرةً في قمة الهرمية وكبيرة في أسفلها، فحجم الكتلة في الذاكرة المخبئية هو 128 بايتًا ويقدّر حجم الصفحات في الذاكرة ب 4 كيبي بايت، ولكن عندما يقرأ نظام التشغيل ملفًا من القرص الصلب فهو بذلك يقرأ عشرات أو مئات الكتل في كل مرة.
	</li>
	<li>
		متى تُنقَل البيانات؟ تُنقَل البيانات إلى الذاكرة المخبئية عندما تُستخدَم للمرة الأولى في معظم أنواع الذواكر المخبئية، ولكن تستخدم العديد من الذواكر المخبئية ما يسمّى بالجلب المسبق (prefetching) والذي يعني تحميل البيانات قبل أن تُطلَب صراحةً. وقد رأيت مسبقًا نموذجًا من الجلب المسبق هو تحميل الكتلة بأكملها عندما يُطلب جزء منها فقط.
	</li>
	<li>
		أين تذهب البيانات في الذاكرة المخبئية؟ لا تستطيع جلب أي شيء آخر إلى الذاكرة المخبئية عندما تمتلئ إلا إذا أخرجت شيئًا ما منها، لذلك يجب أن تبقي البيانات التي ستُستخدم مرة أخرى قريبًا وتستبدل البيانات التي لن تُستخدم قريبًا.
	</li>
</ul>
<p>
	تشكّل أجوبة الأسئلة الأربعة السابقة ما يدعى بسياسة الذاكرة المخبئية (cache policy)، فيجب أن تكون سياسات الذاكرة المخبئية بسيطة في قمة هرمية الذاكرة لأنها يجب أن تكون سريعة وتُطبّق ضمن العتاد، أما في أسفل الهرمية فيوجد وقتٌ أكثر لاتخاذ القرارات حيث تصنع السياسات المصمَّمة جيدًا اختلافًا كبيرًا. معظم سياسات الذاكرة المخبئية قائمةٌ على المبدأ الذي يقول أن التاريخ يعيد نفسه، فإذا ملكت معلومات عن الماضي القريب فيمكنك استخدامها لتنبؤ المستقبل القريب أيضًا، أي إذا اُستخدمت كتلة بيانات مؤخرًا فيمكنك توقع أنها ستُستخدم مرة أخرى قريبًا، وبالتالي يقدّم هذا المبدأ سياسة بديلة تدعى الأقل استخدامًا مؤخرًا (least recently used) واختصارها LRU والتي تحذف كتلة بيانات من الذاكرة المخبئية التي لم تُستخدم مؤخرًا (تعرف على <a href="https://en.wikipedia.org/wiki/Cache_algorithms" rel="external nofollow">خوارزميات الذاكرة المخبئية</a>).
</p>

<h2>
	تبديل الصفحات (Paging)
</h2>

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

<ol>
<li>
		افترض أن العملية A تستدعي الدالة <code>malloc</code> لتخصيص قطعةٍ من الذاكرة، فإذا لم يوجد حيّز حر في الكومة بنفس الحجم المطلوب فإن الدالة <code>malloc</code> تستدعي الدالة <code>sbrk</code> من أجل طلب المزيد من الذاكرة من نظام التشغيل.
	</li>
	<li>
		يضيف نظام التشغيل صفحةً إلى جدول الصفحات الخاص بالعملية A عند وجود صفحةٍ حرّة في الذاكرة الحقيقية منشئًا بذلك مجالًا جديدًا من العناوين الوهمية الصالحة.
	</li>
	<li>
		يختار نظام الصفحات صفحةً ضحية (victim page) تابعة للعملية B وذلك عند عدم وجود صفحات حرة، ثم ينسخ محتوى هذه الصفحة الضحية من الذاكرة إلى القرص الصلب ثم يعدّل جدول الصفحات الخاص بالعملية B ليشير إلى أن هذه الصفحة بُدِّلت (swapped out).
	</li>
	<li>
		يمكن إعادة تخصيص صفحة العملية B للعملية A وذلك بعد نقل بيانات العملية B، حيث يجب أن تصفّر الصفحة قبل إعادة تخصيصها لمنع العملية A من قراءة بيانات العملية B.
	</li>
	<li>
		عندها يستطيع استدعاء الدالة <code>sbrk</code> إرجاع نتيجة وهي إعطاء الدالة <code>malloc</code> حيّزًا إضافيًا في الكومة، ثم تخصص الدالة <code>malloc</code> قطعة الذاكرة المطلوبة وتستأنف العملية A عملها.
	</li>
	<li>
		قد يسمح مجدول نظام التشغيل (scheduler) استئناف العملية B عند اكتمال العملية A أو عند مقاطعتها. تلاحظ وحدة إدارة الذاكرة أن الصفحة التي بُدّلت والتي تحاول العملية B الوصول إليها غير صالحة (invalid) ثم تسبب حدوث مقاطعة.
	</li>
	<li>
		يرى نظام التشغيل أن الصفحة بُدّلت عند استلامه للمقاطعة فيقوم بنقل الصفحة من القرص الصلب إلى الذاكرة.
	</li>
	<li>
		ثم تستطيع العملية B استئناف عملها عندما تُبدَّل الصفحة.
	</li>
</ol>
<p>
	يحسّن تبديل الصفحات من استخدامية (utilization) الذاكرة الحقيقية كثيرًا عندما يعمل جيدًا وبذلك يسمح لعمليات أكثر أن تُشغَّل في حيّز أصغر والسبب هو:
</p>

<ul>
<li>
		لا تستخدم معظم العمليات كامل ذاكرتها المخصصة ولا تنفَّذ أقسامٌ كثيرة من جزء نص البرنامج أبدًا أو قد تنفَّذ مرة واحدة ولا تنفَّذ مرة أخرى، ولكن يمكن تبديل هذه الصفحات بدون أن تسبب مشاكلًا.
	</li>
	<li>
		إذا سرّب البرنامج ذاكرةً فقد يترك حيّزًا مخصصًا وراءه ولا يصل إليه أبدًا مرةً أخرى ولكن يستطيع نظام التشغيل إيقاف هذا التسرب بفعالية عن طريق تبديل هذه الصفحات.
	</li>
	<li>
		يوجد على معظم أنظمة التشغيل عملياتٍ تشبه العفاريت (daemons) التي تبقى خاملة معظم الوقت وتتنبّه أحيانًا لتستجيب للأحداث ويمكن تبديل هذه العمليات عندما تكون خاملة.
	</li>
	<li>
		قد يفتح المستخدم نوافذ متعددة ولكن يكون عدد قليل منها فاعلًا في نفس الوقت وبالتالي يمكن تبديل هذه العمليات غير الفاعلة.
	</li>
	<li>
		يمكن وجود عدة عملياتٍ مشغِّلةٍ لنفس البرنامج بحيث تتشارك هذه العمليات في جزء نص البرنامج والجزء الساكن لتجنب الحاجة إلى إبقاء نسخ متعددة في الذاكرة الحقيقية.
	</li>
</ul>
<p>
	إذا أضفت مجمل الذاكرة المخصصة إلى كل العمليات فهذا سيزيد من حجم الذاكرة الحقيقة بصورة كبيرة ومع ذلك لا يزال بإمكان النظام العمل جيدًا. يجب على العملية التي تحاول الوصول إلى صفحة مبدَّلة أن تعيد البيانات من القرص الصلب والذي يستغرق عدة ميلي ثواني، ويكون هذا التأخير ملحوظًا غالبًا، فإذا تركت نافذةً خاملة لمدة طويلة ثم عدت إليها فستكون بطيئةً في البداية وقد تسمع القرص الصلب يعمل ريثما تُبدّل الصفحات. إن مثل هذه التأخيرات العرضية مقبولة ولكن إذا كان لديك عدة عمليات تستخدم حيّزًا كبيرًا فستتواجه هذه العمليات مع بعضها بعضًا، حيث تطرد العملية A عند تشغيلها الصفحات التي تحتاجها العملية B، ثم تطرد العملية B عند تشغيلها الصفحات التي تحتاجها العملية A، وبالتالي تصبح كلا العمليتين بطيئتين إلى حدٍ كبير ويصبح النظام غير مستجيب، يدعى هذا السيناريو بالتأزّم (thrashing). يمكن أن يتجنّب نظام التشغيل هذا التأزُّم من خلال اكتشاف زيادة في تبديل الصفحات (paging) ثم ينهي أو يوقف عملياتٍ حتى يستجيب النظام مرة أخرى، ولكن يمكن القول أن نظام التشغيل لا يقوم بذلك أو لا يقوم بذلك بصورة جيدة وإنما يترك الأمر أحيانًا للمستخدمين من خلال الحد من استخدامهم للذاكرة الحقيقية أو محاولة استرجاع النظام عند ظهور التأزّم.
</p>

<p>
	ترجمة -وبتصرّف- للفصل Caching من كتاب <a href="http://greenteapress.com/thinkos/" rel="external nofollow">Think OS A Brief Introduction to Operating Systems</a>
</p>
]]></description><guid isPermaLink="false">995</guid><pubDate>Mon, 21 Sep 2020 18:09:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x641;&#x635;&#x644; &#x627;&#x644;&#x633;&#x627;&#x62F;&#x633;: &#x625;&#x62F;&#x627;&#x631;&#x629; &#x627;&#x644;&#x630;&#x627;&#x643;&#x631;&#x629; (Memory management) &#x641;&#x64A; &#x644;&#x63A;&#x629; C</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_09/6.png.305bcae7c885a67e6bc842deb111cfe8.png" /></p>

<p>
	توفّر لغة البرمجة C أربع دوال تخصيصٍ ديناميكي للذاكرة هي:
</p>

<ul>
<li>
		malloc: التي تأخذ وسيطًا نوعه عدد صحيح ويمثّل حجمًا بالبايتات وتعيد مؤشرًا إلى قطعة ذاكرةٍ مخصصة حديثًا حجمها يساوي الحجم المعطى على الأقل، وإذا لم تستوفِ الحجم المطلوب فإنها تعيد قيمة مؤشرٍ خاص هو NULL.
	</li>
	<li>
		calloc: وهي شبيهة بالدالة <code>malloc</code> باستثناء أنها تصفّر قطعة الذاكرة المخصصة حديثًا أيضًا أي أنها تضبط كل قيم بايتات القطعة بالقيمة 0.
	</li>
	<li>
		free: التي تأخذ وسيطًا هو مؤشر إلى قطعة ذاكرةٍ مخصصة سابقًا وتلغي تخصيصها (deallocated) أي تجعل حيز الذاكرة المشغول سابقًا متوفرًا لأي تخصيصٍ مستقبلي.
	</li>
	<li>
		realloc: والتي تأخذ وسيطين هما مؤشرٌ لقطعة ذاكرة مخصصة سابقًا وحجمٌ جديد، أي تخصص قطعة ذاكرة بحجمٍ جديد وتنسخ بيانات القطعة القديمة إلى القطعة الجديدة وتحرّر قطعة الذاكرة القديمة ثم تعيد مؤشرًا إلى قطعة الذاكرة الجديدة.
	</li>
</ul>
<p>
	واجهة برمجة التطبيقات (<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>) لإدارة الذاكرة معرضةٌ للخطأ (error-prone) ولكنها غير متسامحة مع الخطأ في نفس الوقت، فإدارة الذاكرة هي أحد أهم التحديات التي تواجه تصميم أنظمة البرمجيات الكبيرة، وهي أحد أهم الأسباب التي تجعل لغات البرمجة الحديثة توفّر خصائصًا عالية المستوى لإدارة الذاكرة مثل خاصية كنس المهملات (garbage collection).
</p>

<h2>
	أخطاء الذاكرة (Memory errors)
</h2>

<p>
	تشبه واجهة برمجة التطبيقات لإدارة الذاكرة في لغة البرمجة C إلى حدٍ ما Jasper Beardly وهو شخصية ثانوية في برنامج الرسوم المتحركة التلفزيوني The Simpsons الذي ظهر في بعض الحلقات كمعلّمٍ بديل حازمٍ حيث فرض عقوبةً جسدية لكل المخالفات أسماها paddlin. هناك بعض الأمور التي يحاول البرنامج تنفيذها ليستحق بمحاولته تلك هذه العقوبة (paddling)، أي بمعنىً آخر إنها أمورٌ ممنوعة وهي:
</p>

<ul>
<li>
		محاولة الوصول لقطعة ذاكرة لم تُخصّص بعد سواءً للقراءة أو للكتابة.
	</li>
	<li>
		محاولة الوصول إلى قطعة ذاكرة مخصَّصة محررةٌ مسبقًا.
	</li>
	<li>
		محاولة تحرير قطعة ذاكرة لم تُخصّص بعد.
	</li>
	<li>
		محاولة تحرير قطعة ذاكرة أكثر من مرة.
	</li>
	<li>
		استدعاء الدالة <code>realloc</code> مع قطعة ذاكرة لم تُخصّص بعد أو خُصصت ثم حُرّرت.
	</li>
</ul>
<p>
	يمكن أن تجد أن اتباع القواعد السابقة ليس أمرًا صعبًا، ولكن يمكن أن تُخصّص قطعة ذاكرة في جزء من برنامجٍ كبير وتُستخدم في أجزاء أخرى وتُحرّر في جزءٍ آخر من البرنامج، حيث يتطلب التغيير في أحد الأجزاء تغييرًا في الأجزاء الأخرى أيضًا. ويمكن أن يوجد أيضًا العديد من الأسماء البديلة (aliases) أو المراجع (references) التي تشير إلى نفس قطعة الذاكرة المخصصة في أجزاء مختلفة من البرنامج، لذلك يجب ألّا تُحرر تلك القطعة حتى تصبح كل المراجع التي تشير إليها غير مستخدَمة. يتطلب تحقيق ذلك تحليلًا لكل أجزاء البرنامج بعناية، وهو أمرٌ صعب ومخالفٌ لمبادئ هندسة البرمجيات الأساسية. يجب أن تتضمن كل الدوال التي تخصص الذاكرة معلوماتٍ عن كيفية تحرير تلك الذاكرة كجزءٍ من الواجهة الموثقّة (documented interface) في الحالة المثالية، حيث تقوم المكتبات الناضجة (Mature libraries) بذلك جيدًا ولكن لا ترقى ممارسة هندسة البرمجيات الواقعية إلى تلك المثالية. يمكن أن يكون العثور على أخطاء الذاكرة صعبًا لأن أعراض تلك الأخطاء غير متنبأٍ بها مما يزيد الطين بلةً فمثلًا:
</p>

<ul>
<li>
		إذا قرأت قيمةً من قطعة ذاكرةٍ غير مخصصة فقد يكتشف نظام التشغيل الخطأ ثم ينبّه (trigger) عن خطأ وقتٍ تشغيلي والذي يدعى خطأ تجزئة (segmentation fault) ثم يوقف البرنامج، <strong>أو</strong> قد يقرأ البرنامج تلك القطعة غير المخصصة دون اكتشاف الخطأ، وفي هذه الحالة ستُخزّن القيمة التي حصل عليها البرنامج مهما كانت في موقع الذاكرة الذي وصل إليه، ولا يمكن التنبؤ بهذا الموقع لأنه سيتغير في كل مرة يُشغّل بها البرنامج.
	</li>
	<li>
		أما إذا كتبت قيمةً في قطعة ذاكرة غير مخصصة ولم تحصل على خطأ تجزئة فستكون الأمور أسوأ، وسيمر وقتٌ طويل قبل أن تُقرأ تلك القيمة التي كتبتها في موقع غير صالح لعملية أخرى أو جزء ما مسببةً مشاكل، وبالتالي سيكون إيجاد مصدر المشكلة صعبًا جدًا.
	</li>
</ul>
<p>
	ويمكن أن تصبح الأمور أسوأ من ذلك أيضًا، فأحد أكثر مشاكل أسلوب C لإدارة الذاكرة شيوعًا هو أن بنى البيانات المستخدمة لتنفيذ الدالتين <code>malloc</code> و <code>free</code> تُخزّن مع قطع الذاكرة المخصصة غالبًا، لذلك إذا كتبت خارج نهاية قطعة الذاكرة المخصصة ديناميكيًا عن طريق الخطأ فهذا يعني أنك شوّهت (mangle) بنى البيانات تلك. ولن يكتشف النظام المشكلة حتى وقت متأخر وذلك عندما تستدعى الدالة <code>malloc</code> أو الدالة <code>free</code> وبالتالي تفشل هاتان الدالتان بطريقة مبهمة. هناك استنتاجٌ يجب أن تستخلصه من ذلك وهو أن الإدارة الآمنة للذاكرة تتطلب تصميمًا وانضباطًا أيضًا، فإذا كتبت مكتبةً (library) أو نموذجًا (module) يخصّص ذاكرةً فيجب أن توفّر واجهةً (interface) لتحريرها، وينبغي أن تكون إدارة الذاكرة جزءًا من تصميم واجهة برمجة التطبيقات (<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>) منذ البداية. إذا استخدمت مكتبةً تخصص ذاكرةً فيجب أن تكون منضبطًا في استخدامك لواجهة برمجة التطبيقات (<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>)، وإذا وفّرت المكتبة دوالًا لتخصيص وإلغاء تخصيص التخزين فيجب أن تستخدم تلك الدوال وألّا تستدعي الدالتين <code>free</code> و <code>malloc</code> لتحرير قطعة ذاكرة وتخصيصها على سبيل المثال، وينبغي أن تتجنب الاحتفاظ بمراجع متعددة تشير للقطعة ذاتها في أجزاء مختلفة من البرنامج. توجد مقايضة (trade-off) بين الإدارة الآمنة للذاكرة والأداء أي لا يمكننا الحصول على الاثنين معًا بصورة تامة فمثلًا مصدر أخطاء الذاكرة الأكثر شيوعًا هو الكتابة خارج حدود مصفوفة، ويُستخدم التحقق من الحدود (bounds checking) لتلافي هذه المشكلة أي يجب التحقق فيما إذا كان الدليل (index) موجودًا خارج حدود المصفوفة في كل وصولٍ إلى تلك المصفوفة. تُجري المكتباتُ عالية المستوى (High-level libraries) والتي توفّر المصفوفات الشبيهة بالبنى (structures) تحققًا من الحدود على المصفوفات، ولكن لا تجري لغة البرمجة C ومعظم المكتبات منخفضة المستوى (low-level libraries) ذلك التحقق.
</p>

<h2>
	تسريب الذاكرة (Memory leaks)
</h2>

<p>
	يوجد خطأ ذاكرةٍ يمكن أن يستحق عقوبة ويمكن ألّا يستحقها وهو تخصيص قطعة ذاكرةٍ ثم عدم تحريرها نهائيًا وهذا ما يدعى بتسريب الذاكرة (memory leak). تسريب الذاكرة في بعض البرامج أمرٌ عادي فإذا خصص برنامجك ذاكرةً وأجرى حساباتٍ معينة عليها ثم غادر الذاكرة المخصصة، فمن الممكن أن يكون تحرير تلك الذاكرة المخصصة غير ضروري، حيث يلغي نظام التشغيل تخصيص ذاكرة برنامجٍ ما عند مغادرة هذا البرنامج من الذاكرة المخصصة له. وقد يؤدي تحرير الذاكرة مباشرةً أي قبل مغادرة البرنامج لذاكرته إلى الشعور بأن كل الأمور تحت السيطرة ولكنه مضيعةٌ للوقت على الأغلب. ولكن إذا اشتغل البرنامج لوقت طويل وسرّب ذاكرةً فإن مجمل ذاكرته المستخدمة ستزيد بصورةٍ غير محددة، وبالتالي قد تحدث مجموعة من الأمور هي:
</p>

<ul>
<li>
		قد تَنفَد ذاكرة نظام التشغيل الحقيقية (physical memory) وبالتالي سيفشل استدعاء الدالة <code>malloc</code> التالي في أنظمة التشغيل التي لا تملك ذاكرة وهمية (virtual memory)، ثم تعيد الدالة القيمة NULL.
	</li>
	<li>
		بينما تستطيع أنظمة التشغيل التي تملك ذاكرةً وهمية نقلَ صفحات عملية أخرى من الذاكرة إلى القرص الصلب لتخصص حيّز ذاكرة أكبر للعملية المسرّبة.
	</li>
	<li>
		من الممكن أن يوجد حدٌّ لكمية الذاكرة التي تسطيع عمليةٌ ما تخصيصها، وبالتالي تعيد الدالة <code>malloc</code> القيمة NULL عند تجاوز هذا الحد.
	</li>
	<li>
		وقد تملأ عمليةٌ ما حيز العنونة الوهمية الخاص بها أي لا توجد عناوين أخرى لتخصيصها، وبالتالي تعيد الدالة <code>malloc</code> القيمة NULL أيضًا.
	</li>
</ul>
<p>
	إذا أعادت الدالة <code>malloc</code> القيمة NULL ولكنك استمريت في تنفيذ البرنامج وحاولت الوصول إلى قطعة الذاكرة التي اعتقدت أنك خصصتها فستحصل على خطأ تجزئة (segmentation fault)، لذلك من الأفضل أن تتحقق من نتيجة تنفيذ الدالة <code>malloc</code> قبل استخدامها. أحد الخيارات هو أن تضيف شرطًا (condition) بعد كل استدعاء للدالة <code>malloc</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2907_7" style="">
<span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p </span><span class="pun">=</span><span class="pln"> malloc</span><span class="pun">(</span><span class="pln">size</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">p </span><span class="pun">==</span><span class="pln"> NULL</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">"malloc failed"</span><span class="pun">);</span><span class="pln">
    exit</span><span class="pun">(-</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُصرّح عن الدالة <code>perror</code> في ملف الترويسات <code>stdio.h</code> ومهمتها طباعة رسالة خطأ ومعلومات إضافية أيضًا عن آخر خطأ قد ظهر. أما الدالة <code>exit</code> فيصرّح عنها في ملف الترويسات <code>stdlib.h</code> والتي تسبب إنهاء العملية، ويدعى وسيط الدالة برمز الحالة (status code) الذي يحدد طريقة إنهاء العملية، حيث يحدد رمز الحالة 0 أنه إنهاءٌ عادي أما رمز الحالة -1 يدل على وجود خطأ في الشرط، ويوجد رموز حالة أخرى تدل على أنواع أخرى من الأخطاء الموجودة في الشرط. الشيفرة المستخدمة للتحقق من الأخطاء (Error-checking code) مزعجةٌ وتجعل البرنامج صعب القراءة ولكن يمكنك التخفيف من ذلك من خلال استدعاء دوال المكتبة المغلّفة (wrapping library function) وشيفرات التحقق من الأخطاء الخاصة بها في دوالك الخاصة. ستجد مغلّف الدالة <code>malloc</code> الذي يتحقق من القيمة المعادة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2907_9" style="">
<span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">check_malloc</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> size</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"> </span><span class="pun">*</span><span class="pln">p </span><span class="pun">=</span><span class="pln"> malloc</span><span class="pun">(</span><span class="pln">size</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">p </span><span class="pun">==</span><span class="pln"> NULL</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">"malloc failed"</span><span class="pun">);</span><span class="pln">
        exit</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">return</span><span class="pln"> p</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تسرّب معظمُ البرامج الكبيرة مثل متصفحات الويب الذاكرةَ وذلك لأن إدارة الذاكرة أمر صعبٌ جدًا، ويمكنك استخدام أداتي UNIX وهما <code>ps</code> و <code>top</code> لمعرفة البرامج التي تستخدم أكبر قدرٍ من الذاكرة على نظامك.
</p>

<h2>
	التطبيق (Implementation)
</h2>

<p>
	يخصص نظام التشغيل حيّزًا لجزء نص البرنامج (text segment) وللبيانات المخصصة بصورة ساكنة (statically allocated data) وحيزًا آخر لجزء المكدس (stack) وحيزًا أيضًا للكومة (heap) والذي يتضمن البيانات المخصصة ديناميكيًا (dynamically allocated data)، وذلك عند بدء تشغيل عمليةٍ ما. لا تخصص جميعُ البرامج البياناتِ الديناميكية لذلك يمكن أن يكون الحجم الابتدائي للكومة صغيرًا أو صفرًا، حيث تتضمن الكومة قطعةً واحدة حرّة فقط مبدئيًا. تتحقق الدالة <code>malloc</code> عند استدعائها فيما إذا كان هناك قطعةُ ذاكرةٍ حرة وكبيرة كفاية لها، فإذا لم تجد طلبها فإنها تطلب مزيدًا من الذاكرة من نظام التشغيل، حيث تُستخدم الدالة <code>sbrk</code> لهذا الغرض، وتضبط الدالة <code>sbrk</code> نهاية البرنامج (program break) الذي يُعَد مؤشرًا إلى نهاية الكومة. يخصص نظامُ التشغيل صفحاتٍ جديدة من الذاكرة الحقيقية عند استدعاء الدالة <code>sbrk</code> ثم يحدّث جدول صفحات العملية ويضبط نهاية البرنامج، ويستطيع البرنامج استدعاء الدالة <code>sbrk</code> مباشرةً دون استخدام الدالة <code>malloc</code> وإدارة الكومة بنفسه، ولكن استخدام الدالة <code>malloc</code> أسهل كما أنها سريعة التنفيذ وتستخدم الذاكرة بكفاءة في معظم نماذج استخدام الذاكرة. تستخدم معظم أنظمة تشغيل Linux الدالة <code>ptmalloc</code> لتطبيق واجهة برمجة التطبيقات لإدارة الذاكرة (وهذه الواجهة هي الدوال <code>malloc</code> و <code>free</code> و <code>calloc</code> و <code>realloc</code>)، حيث أن الدالة <code>ptmalloc</code> التي كتبها Doug Lea مرتكزةٌ على الدالة <code>dlmalloc</code>. يتوفر <a href="http://gee.cs.oswego.edu/dl/html/malloc.html" rel="external nofollow">بحثٌ قصير</a> يشرح العناصر الأساسية للتطبيق (implementation)، ولكن يجب أن يكون المبرمجون على دراية بالعناصر المهمة التالية:
</p>

<ul>
<li>
		لا يعتمد الوقت التشغيلي للدالة <code>malloc</code> على حجم قطعة الذاكرة ولكنه يعتمد على عدد قطع الذاكرة الحرّة الموجودة. الدالة <code>free</code> سريعة عادةً بغض النظر عن عدد القطع الحرّة. يعتمد وقت التشغيل على حجم القطعة وعلى عدد القطع الحرّة لأن الدالة <code>calloc</code> تجعل جميع قيم بايتات القطعة أصفارًا. الدالة <code>realloc</code> سريعة إذا كان الحجم الجديد أصغر من الحجم الحالي أو إذا كان حيّز الذاكرة متوفرًا من أجل توسيع قطعة الذاكرة الحالية، وإذا لم يتحقق ذلك فيجب على الدالة <code>realloc</code> نسخ البيانات من قطعة الذاكرة القديمة إلى قطعة الذاكرة الجديدة وبالتالي يعتمد وقت التشغيل في هذه الحالة على حجم قطعة الذاكرة القديمة.
	</li>
	<li>
		علامات الحدود (Boundary tags): تضيف الدالة <code>malloc</code> حيّزًا في بداية ونهاية القطعة عند تخصيص هذه القطعة وذلك لتخزين معلومات عن القطعة التي تتضمن حجم القطعة وحالتها (مخصصة أو حرّة) وتدعى هذه المعلومات بعلامات الحدود (Boundary tags)، حيث تستطيع الدالة <code>malloc</code> باستخدام هذه العلامات الانتقال من أية قطعة ذاكرة إلى القطعة السابقة وإلى القطعة التالية من الذاكرة، بالإضافة إلى أن قطع الذاكرة الحرّة تكون موصولة ببعضها بعضًا ضمن لائحة مترابطة مضاعفة (doubly-linked list) حيث تتضمن كل قطعة ذاكرة حرّة مؤشرًا إلى القطعة التي تسبقها ومؤشرًا إلى القطعة التي تليها ضمن لائحة قطع الذاكرة الحرّة. تشكّل علامات الحدود ومؤشرات لائحة القطع الحرة بنى البيانات الداخلية للدالة <code>malloc</code>، وتكون بنى البيانات هذه مبعثرةً مع بيانات البرنامج لذلك يكون من السهل أن يتلفها خطأ برنامجٍ ما.
	</li>
	<li>
		كلفة حيز الذاكرة (Space overhead): تشغَل علامات الحدود ومؤشرات لائحة القطع الحرّة حيزًا من الذاكرة، فالحد الأدنى لحجم قطعة الذاكرة هو 16 بايتًا في معظم أنظمة التشغيل، لذلك ليست الدالة <code>malloc</code> فعّالةً من حيث حيزالذاكرة بالنسبة لقطع الذاكرة الصغيرة جدًا، فإذا تتطلب برنامجك عددًا كبيرًا من بنى البيانات الصغيرة فيكون تخصيصهم ضمن مصفوفات فعالًا أكثر.
	</li>
	<li>
		التجزئة (Fragmentation): إذا خصصت وحررت قطع ذاكرة بأحجام مختلفة فإن الكومة تميل لأن تصبح مجزّأة، وبالتالي يصبح حيز الذاكرة الحر مجزّأ إلى العديد من الأجزاء الصغيرة. تضيّع التجزئة حيز الذاكرة وتبطّئ البرنامج أيضًا من خلال جعل الذواكر المخبئية أقل فعالية.
	</li>
	<li>
		التصنيف والتخبئة (Binning and caching): تُخزّن لائحة القطع الحرة ضمن صناديق (bins) بحيث تكون مرتبة حسب الحجم، حيث تعرف الدالة <code>malloc</code> في أي صندوقٍ تبحث عندما تريد الحصول على قطعةٍ ذات حجم معين. وإذا حررت قطعةً ما ثم خصصت قطعة أخرى بنفس الحجم مباشرةً فستكون الدالة <code>malloc</code> أسرع عادةً.
	</li>
</ul>
<p>
	ترجمة -وبتصرّف- للفصل Memory management من كتاب <a href="http://greenteapress.com/thinkos/" rel="external nofollow">Think OS A Brief Introduction to Operating Systems</a>
</p>
]]></description><guid isPermaLink="false">994</guid><pubDate>Fri, 18 Sep 2020 18:05:00 +0000</pubDate></item></channel></rss>
