سنتطرق في هذا المقال إلى الأنواع الحقيقية والصحيحة في لغة سي C
الأنواع الحقيقية
سيكون من الأسهل التعامل مع الأنواع الحقيقية real أوّلًا، لأن هناك تفاصيل وتعقيدات أقل بخصوصها موازنةً بنوع الأعداد الطبيعية Integers. يقدّم المعيار تفاصيلًا جديدةً بخصوص دقة ونطاق الأعداد الحقيقية، ويمكن أن تجدهم في ملف الترويسة "float.h" الذي سنناقشه بالتفصيل لاحقًا. هذه التفاصيل مهمة جدًا ولكنها ذات طبيعة تقنية للغاية، ولن تُفهم بالكامل غالبًا إلا من قبل مختصّي التحليل العددي.
أنواع الأعداد الحقيقية هي:
-
float
: العدد العشري -
double
: العدد العشري مضاعف الدقة -
long double
: العدد العشري الأدق
يسمح لنا كل واحد من هذه الأنواع بتمثيل الأعداد الحقيقية بطريقة معينة باستخدام الحاسوب؛ فإذا كان هناك نوعٌ واحدٌ لتمثيل الأعداد الحقيقية، فهذا يعني أن تمثيل الأعداد سيكون متماثلًا بغض النظر عن الاستخدام؛ أما إذا كان العدد يتجاوز الثلاثة أنوع، فهذا يعني أن لغة سي لن تستطيع تصنيف أي من الأنواع الإضافية. يُستخدم النوع float
للتمثيل السريع والبسيط للأرقام الصغيرة وهو مشابهٌ للنوع REAL
في لغة فورتران؛ أما double
فيستخدم للدقة الإضافية، و long double
لدقة أكبر من سابقتها.
التركيز الأساسي هنا هو أنّ الزيادة في "دقة" كلٍ من float
و double
و long double
تعطي لكل نوعٍ نطاقًا ودقّة مساويةً للنوع الذي يسبقها على الأقل، فأخذ القيمة من متغير نوع double
مثلًا، وتخزينها في متغير من نوع long double
، يجب أن يمثِّل القيمة ذاتها.
لا توجد هناك أي متطلبات للأنواع الثلاثة من متغيرات الأعداد "الحقيقية" لتختلف في خصائصها، وبالتالي إن لم توفّر الآلة سوى نوع واحدٍ من أنواع متغيرات الأعداد الحقيقية، فيمكن عندئذٍ تمثيل جميع أنواع الأعداد الحقيقية الثلاثة في لغة سي بهذا النوع المتوفِّر. لكن بغض النظر، يجب أن يُنظر إلى هذه الأنواع الثلاثة بأنها مختلفة، وكأنّ هناك فرقٌ بينها حقًأ، وهذا يساعد في نقل البرنامج إلى نظام تختلف فيه هذه الأنواع حقًّا، بحيث لن يظهر لك مجموعةٌ من التحذيرات من المصرّف بخصوص عدم توافق الأنواع التي لم تكُن موجودةً على النظام الأول.
تسمح لغة سي بمزج جميع أنواع البيانات العددية في التعابير بعكس كثيرٍ من لغات البرمجة الصارمة بخصوص قواعد كتابتها، وذلك يضم مختلف أنواع الأعداد الصحيحة إضافةً إلى الأعداد الحقيقية وأنواع المؤشرات؛ وعندما يتضمن التعبير مزيجًا من أنواع الأعداد الحقيقية والصحيحة، سيُستدعى تحويلٌ ضمني يعمل بدوره على معرفة نوع المزيج الكلّي الناتج. هذه القواعد مهمةٌ جدًا وتدعى التحويلات الحسابية الاعتيادية usual arithmetic conversions، ومن المفيد أن تتذكرها، إذ سنشرح كامل هذه القواعد لاحقًا، إلا أننا سننظر في الوقت الحالي إلى الحالات التي تتضمن مزيجًا من float
و double
و long double
ونحاول فهمها.
الحالة الوحيدة التي نحتاج فيها إجراء التحويلات المذكورة هي عندما يُمزج نوعان من البيانات في تعبير، كما في هو موضح في المثال التالي:
int f(void){ float f_var; double d_var; long double l_d_var; f_var = 1; d_var = 1; l_d_var = 1; d_var = d_var + f_var; l_d_var = d_var + f_var; return(l_d_var); }
[مثال 1.2]
نلاحظ في المثال السابق وجود كثيرٍ من التحويلات القسرية، لنبدأ بأسهلها أوّلًا، ولننظر إلى تعيين القيمة الثابتة 1
لكلٍ من المتغيرات الثلاثة. لا بُد من التنويه (كما سيشير القسم الذي يتكلم عن القيم الثابتة constants لاحقًا) إلى أن القيمة 1
هي من نوع int
، أي تمثّل عددًا صحيحًا وليس قيمةً ثابتةً حقيقية، ويحوّل الإسناد قيمة العدد الصحيح إلى نوع العدد الحقيقي المناسب والأسهل للتعامل معه.
التحويلات المثيرة للاهتمام تأتي بعدها، وأولها ضمن السطر التالي:
d_var = d_var + f_var;
ما هو نوع التعبير الذي يتضمن العامل +
؟ الإجابة عن هذا السؤال سهلة ما دمت ملمًّا ببعض القواعد؛ إذ يُحوَّل النوع الأقل دقةً ضمنيًا إلى النوع الأكثر دقةً وتُنجز العملية الحسابية باستخدام هذه الدقة، وذلك عندما يجتمع نوعان من الأعداد الحقيقية في التعبير ذاته. يتضمن المثال السابق استخدام كلٍّ من double
و float
، لذلك تُحوّل قيمة المتغير f_var
إلى النوع double
وتُضاف فيما بعد إلى قيمة النوع double
أي المتغير d_var
، وتكون نتيجة هذا التعبير هي من نوع double
أيضًا، لذا من الواضح أن عملية الإسناد إلى المتغير d_var
صائبة.
عملية الجمع الثانية أكثر تعقيدّا، ولكنها ما زالت سهلة الفهم، إذ تُحوّل قيمة المتغير f_var
وتُجرى العملية الحسابية باستخدام دقة النوع double
، ألا وهي عملية جمع المتغيرين، لكن هناك مشكلة، وهي أن نتيجة عملية الجمع من نوع double
، لكن عملية الإسناد من نوع long double
، ويكون مجدّدًا الحل البديهي في هذه الحالة هو تحويل القيمة الأقل دقة إلى الأكبر دقّة، وهو ما يُجرى ضمنيًّا قبل عملية الإسناد.
الآن بعد أن أخذنا نظرةً سريعةً على الأمثلة السهلة، حان وقت الأمثلة الأكثر صعوبة وهي الحالة التي يتسبب فيها التحويل القسري بتحويل نتيجةٍ بدقةٍ عالية إلى دقةٍ أقل منها، ففي مثل هذه الحالات قد يكون من الضروري خسارة الدقة بطريقة محدّدة من تنفيذ التحويل. ببساطة، يجب أن يحدد التنفيذ طريقة تقريب أو اقتطاعٍ للقيمة، وفي أسوأ الحالات قد يكون نوع الهدف غير قادرٍ على تخزين تلك القيمة الضرورية (على سبيل المثال محاولة جمع أكبر قيمة لعدد إلى نفسه)، وتُعد نتيجة التنفيذ في هذه الحالة غير محددة، والبرنامج يشكو من خطأ ولا يمكنك التنبؤ بتصرفه.
لا ضرر من تكرار فكرتنا السابقة، إذ يقصد المعيار بحالة السلوك غير المحدد undefined behaviour معنى اسمه حرفيًا، وحالما يدخل البرنامج منطقة السلوك غير المحدد، يمكن لأي شيء أن يحدث؛ فمن الممكن إيقاف البرنامج من طرف نظام التشغيل مصحوبًا برسالة معيّنة؛ أو قد يحدث شيء غير مُلاحظ ويستمر البرنامج للعمل باستخدام قيم خاطئة مُخزَّنة في المتغير. منع البرنامج من إبداء أي سلوك غير محدّد تعد من مسؤولياتك، فتوخّ الحذر.
لتلخيص ما سبق:
- تُجرى العمليات الحسابية التي تتضمن نوعين باستخدام النوع الأعلى دقّة منهما.
- قد يتضمن الإسناد خسارة لدقة القيمة في حال كان نوع المتغير الهدف ذو دقة أقل من دقة القيمة التي تُسنَد لهذا المتغير.
- هناك مزيدٌ من التحويلات التي تُجرى عند مزج الأنواع ضمن تعبير واحد، إذ لم نصِف جميعها بعد.
طباعة الأعداد الحقيقية
يمكن استخدام دالة الخرج التقليدي printf
لتنسيق الأعداد الحقيقية وطباعتها، كما يوجد العديد من الطرق لتنسيق هذه الأعداد، ولكننا سنتطرق إلى طريقة واحدة في الوقت الحالي. يوضح الجدول 4.2 التنسيق الموافق لكل نوعٍ من أنواع الأعداد الحقيقية.
النوع | التنسيق |
---|---|
float
|
f%
|
double
|
f%
|
long double
|
Lf%
|
[جدول 4.2. رموز التنسيق للأعداد الحقيقية]
ألقِ نظرةً على المثال التالي لتجربة المعلومة السابقة:
#include <stdio.h> #include <stdlib.h> #define BOILING 212 /* degrees Fahrenheit */ main(){ float f_var; double d_var; long double l_d_var; int i; i = 0; printf("Fahrenheit to Centigrade\n"); while(i <= BOILING){ l_d_var = 5*(i-32); l_d_var = l_d_var/9; d_var = l_d_var; f_var = l_d_var; printf("%d %f %f %Lf\n", i, f_var, d_var, l_d_var); i = i+1; } exit(EXIT_SUCCESS); }
[مثال 2.2]
جرّب المثال السابق على حاسوبك الشخصي، ولاحظ النتائج.
الأنواع الصحيحة
كانت الأنواع الحقيقية أسهل الأنواع، إذ تتصف القوانين الخاصة بالأنواع الصحيحة بتعقيد أكبر، ولكنها ما زالت مفهومة وينبغي تعلُّمها. لحسن الحظ، الأنواع الوحيدة المستخدمة في لغة سي لتخزين البيانات هي الأنواع الحقيقية والصحيحة، إضافةً إلى الهياكل structures والمصفوفات arrays المبنيّة عليهما، إذ لا تحتوي لغة سي على أنواع مميزة للتلاعب بالمحارف، أو التعامل مع القيم البوليانية boolean، وإنما تستخدم الأنواع الصحيحة بدلًا من ذلك، وهذا يعني أنه حالما تفهم الأنواع الصحيحة والحقيقية فأنت تعرف جميع الأنواع.
سنبدأ بالنظر إلى الأنواع المختلفة للأعداد الصحيحة وقوانين التحويل فيما بينها.
الأعداد الصحيحة البسيطة
هناك نوعان من متغيرات الأعداد الصحيحة يطلق عليهما "نكهات flavours"، ويمكن بناء أنواع أخرى انطلاقًا من هذين النوعين كما سنرى لاحقًا، لكن تبقى الأنواع البسيطة int
هي الأساس. النوع الأكثر شهرةً هو العدد الصحيح ذو الإشارة signed أو int
، أما النوع الأقل شهرة هو العدد الصحيح عديم الإشارة أو unsigned int
، ومن المفترض أن تُخزّن القيم في المتغيرات ذات النوع المناسب حسب الآلة التي تشغل البرنامج.
عندما تبحث عن نوع بيانات بسيط لتمثيل عدد صحيح، فإن النوع int
هو الاختيار البديهي لأي استخدام مُتساهل، مثل عدّاد ضمن حلقة تكرارية قصيرة، إذ لا توجد هناك أي قاعدة تحدّد عدد البتّات التي يخزنها نوع int
لقيمةٍ ما، لكنه سيكون دائمًا مساويًا إلى 16 بِت أو أكثر، ويفصِّل ملف الترويسة القياسي "
لم تحتوِ لغة سي القديمة على أية معلومات بخصوص طول متغيّر من نوع int
، ولكن الجميع كان يفترض اصطلاحًا أنها على الأقل 16 بِت. في الحقيقة، لا يحدّد ملف الترويسة "int
، والقيم التي يحددها هي ما بين 32767 و32767- أي 16 بت فما فوق، سواءٌ كانت عملية المتمم الأحادي أو الثنائي الحسابية مستخدمةُ أم لا، وبالطبع لا يوجد هناك أي قيود من توفير نطاق أكبر في أي الطرفين في حال توفرت الطريقة المناسبة.
يتراوح النطاق المحدد وفق المعيار للمتغير من نوع unsigned int
من 0 إلى 65535، مما يعني أن القيمة لا يمكن أن تكون سالبة، وسنتكلم بإسهاب عن هذه النقطة لاحقًا.
إذا لم تعتَد التفكير بعدد البتات لمتغيّر ما، وبدأت بالقلق عمّا إذا سيؤثر ذلك على قابلية نقل البرنامج كون هذه المشكلة مرتبطةً بوضوح بالآلة (أي الحاسوب الذي يشغّل البرنامج)، فقلقك في محلّه. تأخذ لغة سي قابلية نقل البرنامج على محمل الجدّ كما تدلّك على القيم والمجالات الآمنة، وتشجِّعك أيضًا عوامل العوامل الثنائية bitwise operators على التفكير بعدد البتّات في متغيرٍ ما، لأنها تمنحك الوصول المباشر إلى بتات المتغيرالتي تعالجها بصورةٍ منفردة (كل بت على حدى) أو في مجموعات. ونتيجة لذلك تكوَّن لدى مبرمجي لغة سي المعرفة الكافية بخصوص مشكلات قابلية نقل البرنامج، ممّا يتسبب ببرمجة برامج قابلة للنقل، لكننا لا ننفي هنا إمكانية كتابة برامج غير قابلة للنقل إطلاقًا.
متغيرات المحارف
المتغير char
هو النوع الثاني من أنواع الأعداد الصحيحة البسيطة، فهو نوعٌ آخر من int
ولكن بتطبيقٍ مختلف، إذ تُعدّ فكرة تخصيص نوعٍ خاص للتعامل مع المحارف فكرةً جيّدة خاصةً وأن كثيرًا من برامج سي تتعامل بالمحارف، لأن تمثيل القيم باستخدام النوع int
يأخذ كثيرًا من المساحة غير الضرورية لتمثيل المحرف.
يصف ملف ترويسة الحدود "limits" ثلاثة أشياء عن النوع char
ألا وهي:
- عدد البتات 8 على الأقل.
- يمكنها تخزين قيمة 127+ على الأقل.
-
القيمة الدنيا للنوع
char
هي صفر أو أقل، مما يعني أن المجال يتراوح ما بين 0 إلى 127.
يحدِّد تنفيذ المتحول char
فيما إذا كان سيتصرف تصرُّف المتحولات ذات الإشارة signed
أو عديمة الإشارة unsigned
.
باختصار، تحتل متغيرات المحارف مساحةً أقل من المتغيرات الصحيحة int
التقليدية، ويمكن استخدامها لمعالجة المحارف، لكنها تندرج تحت تصنيف الأعداد الصحيحة، ويمكن استخدامها لإجراء العمليات الحسابية كما هو موضح في المثال التالي:
include <limits.h> include <stdio.h> include <stdlib.h> main(){ char c; c = CHAR_MIN; while(c != CHAR_MAX){ printf("%d\n", c); c = c+1; } exit(EXIT_SUCCESS); }
[مثال 3.2]
تشغيل البرنامج في المثال السابق تمرينٌ لك، وربما ستثير النتائج إعجابك. إذا كنت تتسائل عن قيمة CHAR_MIN
وCHAR_MAX
، فاطّلع على ملف الترويسة limits.h
واقرأه.
إليك مثالٌ آخر مثيرٌ للإعجاب حقًا، إذ سنستخدم فيه محارف ثابتة constants، والتي يمكن كتابتها بين إشارتين تنصيص أحاديّة على النحو التالي:
'x'
لأن القواعد الحسابية تُطبَّق هنا، فسيُحوّل المحرف الثابت السابق ليكون من النوع int
، ولكن هذا لا يهم حقًّا لأن قيمة المحرف صغيرة دائمًا ويمكن تخزينها في متغير من نوع char
دون فقدان أي دقة (لسوء الحظ هناك بعض الحالات التي لا ينطبق فيها هذا الكلام، تجاهلها في الوقت الحالي). عندما يُطبع محرف باستخدام الرمز c%
ضمن دالة printf
، يُطبع المحرف كما هو، لكن يمكنك استخدام الرمز d%
إذا أردت طباعة قيمة العدد الصحيح الموافقة لهذا المحرف. لماذا استُخدم الرمز d%
؟ كما ذكرنا سابقًا، النوع char
هو في الحقيقة نوع من أنواع الأعداد الصحيحة.
من المهم أيضًا وجود طريقة لقراءة المحارف إلى البرنامج، وتتكفل الدالة getchar
بهذه المهمة، إذ تقرأ المحارف من الدخل القياسي standard input للبرنامج وتُعيد قيمةً صحيحةً int
موافقة لتخزين هذا المحرف في متغير من نوع char
، تخدم هذه القيمة المُمرّرة من نوع int
غرضين، هما: تمثيل جميع قيم المحارف الممكنة بواسطتها، إضافةً إلى تمرير قيمة إضافية للدلالة على نهاية الدخل. لا يتسع مجال قيم متغير من نوع char
في جميع الحالات لهذه القيمة الإضافية، لذلك يُستخدم النوع int
.
يقرأ البرنامج التالي الدخل ويعدّ الفواصل والنقاط المُدخلة، وعند وصولة لنهاية الدخل يطبع النتيجة.
#include <stdio.h> #include <stdlib.h> main(){ int this_char, comma_count, stop_count; comma_count = stop_count = 0; this_char = getchar(); while(this_char != EOF){ if(this_char == '.') stop_count = stop_count+1; if(this_char == ',') comma_count = comma_count+1; this_char = getchar(); } printf("%d commas, %d stops\n", comma_count, stop_count); exit(EXIT_SUCCESS); }
[مثال 4.2]
هناك ميزتان نستطيع ملاحظتهما من المثال السابق، الأولى هي الإسناد المتعدّد للعدادين، والثانية هي استخدام الثابت المعرّف EOF
؛ وهي قيمة تُمرّر من الدالة getchar
في نهاية الدخل وتمثِّل اختصارًا لكلمة نهاية الملف End Of File، وتكون معرفةً ضمن ملف الترويسة "
لنأخذ مثالًا آخر، وليكن برنامجًا لطباعة جميع الأحرف الأبجدية بأحرف صغيرة إذا كان تنفيذك يحتوي على محارف مخزنة بصورةٍ متتالية، أو طباعة نتيجةٍ مثيرةٍ للاهتمام إذا لم يكن كذلك. لا تقدّم لغة سي العديد من الضمانات بترتيب المحارف داخليًّا، لذلك قد يتسبب هذا البرنامج بنتائج مختلفة ويكون غير محمول.
#include <stdio.h> #include <stdlib.h> main(){ char c; c = 'a'; while(c <= 'z'){ printf("value %d char %c\n", c, c); c = c+1; } exit(EXIT_SUCCESS); }
[مثال 5.2]
يذكرنا هذا المثال مرةً أخرى بأن char
شكلٌ مختلفٌ من أشكال متغيرات الأعداد الصحيحة ويمكن استخدامه مثل أي عدد صحيح آخر، فهو ليس نوع مميّز بقواعد مختلفة.
تصبح المساحة التي يوفرها char
موازنةً مع int
ملحوظةً ومهمةً عندما يُستخدام الكثير من المتغيرات. تستخدم معظم عمليات معالجة المحارف مصفوفات كبيرة منها وليس محرفًا واحدًا أو اثنين فقط، وفي هذه الحالة يصبح الفرق واضحًا بين الاثنين. لنتخيل سويًّا مصفوفةً مؤلفةً من 1024 متغيرًا من نوع int
، تحجز هذه المصفوفة مساحة 4098 بايت (كل بايت 8-بت) من التخزين على معظم الآلات، على افتراض أن طول كل int
هو 4 بايت؛ فإذا كانت معمارية الحاسوب تسمح بتخزين هذه المعلومات بطريقة فعّالة، قد تطبّق لغة سي هذا عن طريق متغيرات من نوع char
بحيث يأخذ كل متغير بايتًا واحدًا، وبذلك ستأخذ المصفوفة مساحة 1024 بايت، مما سيوفّر مساحة 3072 بايت.
لا يهمنا في بعض الحالات إن كان سيوفِّر البرنامج مساحةً أم لا، ولكنه يوفِّرها بغض النظر، ومن الجيد أن تعطينا لغة سي فرصة اختيار نوع المتغير المناسب لاستخدامنا.
المزيد من الأنواع المعقدة
النوعان السابقان الذين تكلمنا عنهما سابقًا بسيطان، سواءٌ بخصوص تصريحهما أو استخدامهما، ولكن دقتهما في التحكم بالتخزين وسلوكهما غير كافيين في استخدامات نظم البرمجة المعقدة. تقدّم لغة سي أنواعًا إضافية من أنواع الأعداد الصحيحة للتغلُّب على هذه المشكلة وتُقسم إلى تصنيفين، الأنواع ذات الإشارة signed
والأنواع عديمة الإشارة unsigned
(بالرغم من هذه المصطلحات كلمات محجوز في لغة سي إلا أن معناها يدلّ على غرضها أيضًا)، والفرق بين النوعين واضح؛ إذ يمكن للأنواع ذات الإشارة أن تكون قيمتها سالبة؛ بينما يكون من المستحيل أن تخزِّن الأنواع عديمة الإشارة قيمةً سالبة، وتُستخدم الأنواع عديمة الإشارة في معظم الأحيان لحالتين، هما: إعطاء القيمة دقةً أكبر، أو عندما نضمن أن المتغير لن يخزن أي قيمٍ سالبة في استخدامه، والحالة الثانية هي الحالة الأكثر شيوعًا.
تملك الأنواع عديمة الإشارة خاصيةً مميزة ألا وهي أنها ليست عرضةً للطفحان الحسابي overflowing عند إجراء العمليات الحسابية، إذ سيتسبب إضافة 1 إلى متغيرٍ من نوعٍ ذي إشارة يخزّن أكبر قيمة يمكن تخزينها بحدوث طفحان، ويصبح سلوك البرنامج نتيجةً لذلك غير محدّد، ولا يحصل هذا الأمر مع المتغيرات من نوعٍ عديم الإشارة، لأنّها تعمل وفق "باقي قسمة واحد زائد القيمة العظمى التي يمكن للمتغير تخزينها على هذه القيمة"، أي باقي قسمة "max+1)/max)"، والمثال التالي يوضح ما نقصده:
#include <stdio.h> #include <stdlib.h> main(){ unsigned int x; x = 0; while(x >= 0){ printf("%u\n", x); x = x+1; } exit(EXIT_SUCCESS); }
[مثال 6.2]
بفرض أن المتغير x
يحتل مساحة 16 بِت، فهذا يعني أن مجال قيمته يترواح بين 0 و 65535، وأن الحلقة التكرارية في المثال ستتكرر لأجل غير مسمّى، إذ أن الشرط التالي محققٌ دائمًا:
x >= 0
وذلك بالنسبة لأي متغير عديم الإشارة.
يوجد ثلاثة أنواع فرعية لكلٍ من الأعداد الصحيحة ذات الإشارة وعديمة الإشارة، هي: short
والنوع الاعتيادي و long
، ونستطيع بعد أخذ هذه المعلومة بالحسبان كتابة لائحة بجميع أنواع متغيرات الأعداد الصحيحة في لغة سي باستثناء نوع تخزين المحرف char
، على النحو التالي:
unsigned short int unsigned int unsigned long int signed short int signed int signed long int
ليس مهمًّا استخدام الكلمة المفتاحية signed
ويمكن الاستغناء عنها في الأنواع الثلاث الأخيرة، إذ أن نوع int
ذو إشارة افتراضيًا، ولكن ينبغي عليك استخدام الكلمة المفتاحية unsigned
إذا أردت الحصول على نتيجة مغايرة لذلك. من الممكن أيضًا التخلِّي عن الكلمة المفتاحية int
من أي تعليمة تصريح شرط أن تحتوي على كلمة مفتاحية أخرى، مثل long
أو short
، وسيُفهم المتغير على أنه int
ضمنيًّا ولكنه أمرٌ غير محبّذ، على سبيل المثال الكلمة المفتاحية long
مساوية للكلمات signed long int
.
يمنحك النوع long
و short
تحكّمًا أكبر بمقدار المساحة التي تريد حجزها للمتغير، ولكلّ منهما مجال أدنى محدّد في ملف الترويسة <limits.h>
، وهو 16 بِت على الأقل لكل من short
و int
، و32 بتًا على الأقل للنوع long
، سواءً كان ذو إشارة signed أو دون إشارة unsigned. وكما ذكرنا آنفًا من الممكن للتنفيذ أن يحجز مقدارًا يزيد على المقدار الأدنى من البتات إذا أراد ذلك، والقيد الوحيد هنا هو أن حدود المجال يجب أن تكون متساويةً أو محسّنة، وألا تحصل على عددٍ أكبر من البتات في متغيرٍ من نوع أصغر موازنةً بنوعٍ أكبر منه، وهي قاعدة منطقية.
أنواع متغيرات المحارف الوحيدة هي signed char
و unsigned char
، ويتمثّل الفرق بين متغيرات من نوع int
و char
في أن جميع متغيرات int
ذات إشارة إن لم يُذكر عكس ذلك، وهذا لا ينطبق على أنواع المحارف char
التي قد تكون ذات إشارة أو عديمة الإشارة اعتمادًا على اختيار المُنفّذ، وعادةً ما يُتخذ القرار بناءً على أسس الكفاءة. يمكنك طبعًا اختيار نوع المتغير قسريًا إذا أردت باستخدام الكلمة المفتاحية الموافقة، ولكن هذه النقطة لا تهمّك إلا في حالة استخدامك لمتغيرات المحارف بنوعها القصير short
لتوفير مساحة التخزين.
لتلخيص ما سبق:
-
تتضمن أنواع الأعداد الصحيحة
short
وlong
وsigned
وunsigned
و النوع الاعتياديint
. -
النوع الأكثر استخدامًا وشيوعًا هو النوع الاعتيادي
int
وهو ذو إشارة إلا في حالة تحديد عكس ذلك. -
يمكن للمتغيرات من نوع
char
أن تكون ذات إشارة أو عديمة الإشارة حسب تفضيلك، ولكن في حال غياب تخصيصك لها ستُخصّص الحالة الأفضل افتراضيًا.
طباعة أنواع الأعداد الصحيحة
يمكننا طباعة هذه الأنواع المختلفة أيضًا باستخدام الدالة printf
، إذ تعمل متغيرات المحارف بنفس الطريقة التي تعمل بها الأعداد الصحيحة الأخرى، ويمكنك استخدام الترميز القياسي لطباعة محتوياتها (أي العدد الذي يمثل المحرف)، على الرغم من كون القيم الخاصة بها غير مثيرة للاهتمام أو مفيدة في معظم الاستخدامات. نستخدم الرمز c%
لطباعة محتويات متغيرات المحارف كما أشرنا سابقًا، كما يمكن طباعة جميع قيم الأعداد الصحيحة بالنظام العشري باستخدام الرمز d%
أو ld%
لأنواع long
، ويوضح الجدول 5.2 المزيد من الرموز المفيدة لطباعة القيم بتنسيقٍ مختلف. لاحظ أنّه في كل حالة تبدأ بالحرف l
تُطبع قيمةٌ من نوع long
، وهذا التخصيص ليس موجودًا فقط لعرض القيمة الصحيحة بل لتجنُّب السلوك غير المحدد لدالة printf
إذا أُدخل الترميز الخاطئ.
التنسيق | يُستخدم مع |
---|---|
c%
|
char (طباعة المحرف)
|
d%
|
القيمة العشرية للأنواع signed int و short و char
|
u%
|
القيمة العشرية للأنواع unsigned int و unsigned short وunsigned char
|
x%
|
القيمة الست عشرية للأنواع int وshort وchar
|
o%
|
القيمة الثمانية للأنواع int وshort وchar
|
ld%
|
القيمة العشرية للنوع signed long
|
lu% lx% lo%
|
كما ذُكر في الأعلى ولكن للنوع long
|
[جدول 5.2. المزيد من رموز التنسيق]
سنتكلم على نحوٍ مفصّل حول التنسيق المستخدمة مع الدالة printf
لاحقًا.
ترجمة -وبتصرف- لقسم من الفصل Variables and Arithmetic من كتاب The C Book.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.