حجم الأنواع العددية الصحيحة
الأنواع التالية هي أنواع عددية صحيحة:
- 
		
char - الأنواع العددية الصحيحة المُؤشّرة Signed integer types
 - الأنواع العددية الصحيحة غير المُؤشّرة Unsigned integer types
 - 
		
char16_tوchar32_t - 
		
bool - 
		
wchar_t 
	باستثناء sizeof(char) و sizeof(signed char) و sizeof(unsigned char)، الموجودة بين الفقرة  3.9.1.1 [basic.fundamental/1]  والفقرة 5.3.3.1 [expr.sizeof] و sizeof(bool)، والتي تتوقف على التنفيذ بالكامل وليس لها حدّ أدنى، فإنّّ متطلّبات الحد الأدنى لأحجام هذه الأنواع موجود في القسم 3.9.1 [basic.fundamental] من المعيار، وسنوضّحه أدناه.
حجم char
	تحدّد جميع إصدارات معيار C++، في الفقرة 5.3.3.1، أنّ sizeof تعيد القيمة 1 لكلّ من unsigned char و signed char و char (مسألة ما إذا كان النوع char مؤشّرا - signed - أو غير مؤشّر - unsigned - تتعلّق بالتنفيذ).
الإصدار ≥ C++ 14
	النوع char كبير بما يكفي لتمثيل 256 قيمة مختلفة، وهو مناسب لتخزين وحدات رموز UTF-8.
حجم الأنواع العددية الصحيحة المُؤشّرة وغير المُؤشّرة
	تنصّ المواصفات القياسية، في الفقرة 3.9.1.2، أنّه في قائمة أنواع الأعداد الصحيحة القياسية المُؤشّرة، التي تتكون من signed char و short int و int و long int و long long int، فإنّّ كل نوع سيوفّر مساحة تخزينية تكافئ على الأقل المساحة التخزينيّة للنّوع السابق في القائمة. إضافة لذلك، وكما هو مُوضّح في الفقرة 3.9.1.3، كل نوع من هذه الأنواع يقابله نوع صحيح قياسي غير مُؤشّر، هذه الأنواع هي:
- 
		
unsigned char - 
		
unsigned short int - 
		
unsigned int - 
		
unsigned long int - 
		
unsigned long long int 
	هذه الأنواع في النهاية لها نفس حجم ومُحاذاة النوع المؤشّر المقابل بالإضافة إلى ذلك، وكما هو مُوضّح في الفقرة 3.9.1.1، النوع char له نفس متطلّبات signed char و unsigned char فيما يخصّ الحجم والمحاذاة.
الإصدار < C++ 11
	قبل الإصدار C++ 11، لم يكن long long و unsigned long long جزءًا من معيار C++. لكن بعد إدخالهما في لغة C في معيار C99، دعمت العديدُ من المصرّفات النوع long long كنوع عددي صحيح مؤشّر، و unsigned long long كنوع عددي صحيح غير مؤشّر موسّع، له نفس قواعد أنواع C.
يضمن المعيار ما يلي:
1 == sizeof(char) == sizeof(signed char) == sizeof(unsigned char) <= sizeof(short) == sizeof(unsigned short) <= sizeof(int) == sizeof(unsigned int) <= sizeof(long) == sizeof(unsigned long)
الإصدار ≥ C++ 11
<= sizeof(long long) == sizeof(unsigned long long)
لا ينصّ المعيار على حد أدنى للأحجام لكل نوع على حدة. بدلاً من ذلك، لكلّ نوعٍ من الأنواع مجالًا من الحدود الدنيا التي يمكن أن يدعمها، والتي تكون موروثة على النحو المُوضّح في الفقرة 3.9.1.3 من معيار C، في الفقرة 1.2.4.2.1. ويمكن استنتاج الحد الأدنى لحجم نوع ما من ذلك المجال من خلال تحديد الحد الأدنى لعدد البتّات المطلوبة، لاحظ أنّه في معظم الأنظمة قد يكون المجال المدعوم الفعلي لأيّ نوع أكبر من الحد الأدنى، وأنه في الأنواع المُؤشّرة تتوافق المجالات مع مكمّل (complement) واحد، وليس مع مكمّلين اثنين كما هو شائع؛ وذلك لتسهيل توافق مجموعة واسعة من المنصات مع المعايير.
| النوع | النطاق الأدنى | العدد الأدنى المطلوب للبِتَّات | 
|---|---|---|
				signed char
			 | 
			-127 إلى 127 | 8 | 
				unsigned char
			 | 
			0 إلى 255 | 8 | 
				signed short
			 | 
			-32,767 إلى 32,767 | 16 | 
				unsigned short
			 | 
			0 إلى 65,535 | 16 | 
				signed int
			 | 
			-32,767 إلى 32,767 | 16 | 
				unsigned int
			 | 
			0 إلى 65,535 | 16 | 
				signed long
			 | 
			-2,147,483,647 إلى 2,147,483,647 | 32 | 
				unsigned long
			 | 
			0 إلى 4,294,967,295 | 32 | 
الإصدار ≥ C++ 11
| النوع | النطاق الأدنى | العدد الأدنى المطلوب للبِتَّات | 
|---|---|---|
				signed long long
			 | 
			-9,223,372,036,854,775,807 إلى 9,223,372,036,854,775,807 | 64 | 
				unsigned long long
			 | 
			0 إلى 18,446,744,073,709,551,615 | 64 | 
	قد تختلف أحجام الأنواع من تنفيذ لآخر بما أنه يُسمح للأنواع أن تكون أكبر من الحد الأدنى لمتطلّبات الحجم، وأبرز مثال على ذلك تجده في نموذَجي البيانات المخزّنة على 64 بتّة: LP64 و LLP64، إذ أنّه في أنظمة LLP64 (مثل Windows 64-bit)، فإنّ الأنواع ints و long تُخزّن على 32 بتّة (32-bit)، وفي LP64 (مثل 64-bit Linux)، فإنّ int مخزّنة على 32 بتّة، أمّا long فمُخزّنة على 64 بتّة. لهذا من الخطأ افتراض أنّ أنواع الأعداد الصحيحة لها نفس الحجم في جميع الأنظمة.
الإصدار ≥ C++ 11
	إذا كانت هناك حاجة إلى أنواع عددية صحيحة ذات أحجام ثابتة فاستخدم أنواعًا من الترويسة 
- 
		
int8_t - 
		
int16_t - 
		
int32_t - 
		
int64_t - 
		
intptr_t - 
		
uint8_t - 
		
uint16_t - 
		
uint32_t - 
		
uint64_t - 
		
uintptr_t 
الإصدار ≥ C++ 11
حجم char16_t و char32_t
	يتعلق حجم char16_t و char32_t بالتنفيذ كما ينصّ على ذلك المعيار في الفقرة 5.3.3.1، مع الشروط الواردة في الفقرة 3.9.1.5:
- 
		
char16_tكبير بما يكفي لتمثيل أيّ وحدة رمز من UTF-16، كما أنّ له نفس الحجم والإشارة والمحاذاة التي للنوعuint_least16_t، وعليه فحجمه يساوي 16 بتّة على الأقل. - 
		
char32_tكبير بما يكفي لتمثيل أي وحدة رمز في UTF-32، كما أنّ له نفس الحجم والإشارة والمحاذاة التي للنوعuint_least32_t، وعليه فيجب أن يساوي حجمه 32 بتّة على الأقل. 
حجم bool
	يتعلق حجم bool بالتنفيذ، ولا يساوي بالضرورة 1.
حجم wchar_t
	wchar_t، كما هو مُوضّح في الفقرة 3.9.1.5 هو نوع متميّز إذ يمكن أن يمثّل مجال قيمه كل وحدات الرموز (code unit) في أكبر مجموعة محارف موسّعة من بين اللغات المدعومة، وله نفس الحجم والإشارة والمحاذاة لأحد الأنواع العددية الصحيحة الأخرى، والذي يمثّل نوعه الأساسي (underlying type).
	يتعلق حجم هذا النوع بالتنفيذ كما هو مُعرَّف في الفقرة 5.3.3.1، وقد يساوي على سبيل المثال 8 أو 16 أو 32 بتّة على الأقل؛ وإذا كان النظام يدعم اليونيكود فينبغي أن يُخزّن النوع wchar_t على 32 بتّة على الأقل (باستثناء Windows، إذ يُخزّن النّوع wchar_t على 16 بتّة لأغراض التوافق). وهو موروث من معيار C90، في ISO 9899: 1990 الفقرة 4.1.5، لكن مع بعض التعديلات البسيطة.
	ويساوي حجم wchar_t غالبًا 8 أو 16 أو 32 بتّة، وذلك حسب التنفيذ. مثلًا:
- 
		في أنظمة يونكس والأنظمة المشابهة لها، تُخزّن 
wchar_tعلى 32 بتّة، وعادة ما تستخدم في UTF-32. - 
		في ويندوز، تُخزّن 
wchar_tعلى 16 بتّة، وتُستخدم مع UTF-16. - 
		على الأنظمة التي لا تدعم إلّا 8 بتّات فقط، تُخزّن 
wchar_tعلى 8 بتّات. 
الإصدار ≥ C++ 11
	إذا كنت تريد دعم اليونيكود، فإنّه يُوصى باستخدام char لأجل الترميز UTF-8، و char16_t لأجل الترميز UTF-16، و char32_t لأجل الترميز UTF-32، بدلاً من استخدام wchar_t.
نماذج البيانات
يمكن أن تختلف أحجام أنواع الأعداد الصحيحة بين المنصات كما ذكرنا أعلاه، وما يلي هي أكثر النماذج شيوعًا (الأحجام محسوبة بالبتّات):
| النموذج | 
				int
			 | 
			
				long
			 | 
			مؤشر | 
|---|---|---|---|
| LP32 (2/4/4) | 16 | 32 | 32 | 
| ILP32 (4/4/4) | 32 | 32 | 32 | 
| LLP64 (4/4/8) | 32 | 32 | 64 | 
| LP64 (4/8/8) | 32 | 64 | 64 | 
من بين هذه النماذج:
- نظام ويندوز 16-بت استخدم LP32.
 - الأنظمة الشبيهة بيونكس مثل يونكس ولينكس ونظام ماك 10 (Mac OSX) وغيرها، ذات معمارية 32 منها، وكذلك نظام ويندوز 32-بت، تستخدم جميعها ILP32.
 - ويندوز 64-bit يستخدم LLP64.
 - اليونكسات ذات معمارية 64-bit تستخدم LP64.
 
لاحظ أنّ هذه النماذج غير مذكورة في المعيار.
النوع Char قد يكون مُؤشَّرًا أو لا
	لا ينصّ المعيار على وجوب أن يكون النوع char مؤشَّرًا من عدمه، لذا فإنّّ كل مصرّف ينفذه بشكل مختلف، أو قد يسمح بتعديله باستخدام سطر الأوامر.
مجالات الأنواع العددية
	تتعلّق مجالات أنواع الأعداد الصحيحة بالتنفيذ، كما توفّر الترويسة std::numeric_limits<T> الذي يوفّر الحدّين الأدنى والأقصى لقيم جميع الأنواع الأساسية. 
	تفي القيم بالضمانات التي يحدّدها معيار C عبر الترويسات 
- 
		
std::numeric_limits<signed char>::min()- تساويSCHAR_MIN، وهي أصغر من أو تساوي -127. - 
		
std::numeric_limits::max() تساوي SCHAR_MAX، وهي أكبر من أو تساوي 127. - 
		
std::numeric_limits<unsigned char>::max()تساويUCHAR_MAX، وهي أكبر من أو تساوي 255. - 
		
std::numeric_limits<short>::min()تساويSHRT_MIN، وهي أصغر من -32767 أو تساويها. - 
		
std::numeric_limits<short>::max()تساويSHRT_MAX، وهي أكبر من أو تساوي 32767. - 
		
std::numeric_limits<unsigned short>::max()تساويUSHRT_MAX، وهي أكبر من أو تساوي 65535. - 
		
std::numeric_limits<int>::min()تساويINT_MIN، وهي أصغر من -32767 أو تساويها. - 
		
std::numeric_limits<int>::max()تساويINT_MAX، وهي أكبر من أو تساوي 32767. - 
		
std::numeric_limits<unsigned int>::max() تساويUINT_MAX، وهي أكبر من أو تساوي 65535. - 
		
std::numeric_limits<long>::min()تساويLONG_MIN، وهي أصغر من أو تساوي -2147483647. - 
		
std::numeric_limits<long>::max()تساويLONG_MAX، وهي أكبر من أو تساوي 2147483647. - 
		
std::numeric_limits<unsigned long>::max()تساوي ULONG_MAX، وهي أكبر من أو تساوي 4294967295. 
الإصدار ≥ C++11
- 
		
std::numeric_limits<long long>::min()تساويLLONG_MIN، وهي أكبر من أو تساوي -9223372036854775807. - 
		
std::numeric_limits<long long>::max()تساويLLONG_MAX، وهي أكبر من أو تساوي 9223372036854775807. - 
		
std::numeric_limits<unsigned long long>::max()تساويULLONG_MAX، وهي أكبر من أو تساوي 18446744073709551615. 
	بالنسبة للنوع العشري T، فإنّّ max() تمثّل القيمة المنتهية (finite) القصوى، بينما تمثّل min() الحدّ الأدنى للقيمة المُوحّدة الموجبة.
	تمّ توفير بعض الأعضاء الإضافيين للأنواع العشرية، وهي متعلّقة بالتنفيذ أيضًا، ولكنّها تلبّي بعض الضمانات التي يحدّدها المعيار C عبر الترويسة 
- 
		يعيد 
digits10عدد الأرقام العشرية الخاصّة بالدقة. - 
		
std::numeric_limits<float>::digits10تساويFLT_DIG، والتي لا تقلّ عن 6. - 
		
std::numeric_limits<double>::digits10تساويDBL_DIG، والتي لا تقلّ عن 10. - 
		
std::numeric_limits<long double>::digits10تساويLDBL_DIG، والتي لا تقلّ عن 10. - 
		العضو 
min_exponent10هو الحد الأدنى السلبي E بحيث يكون 10 أسّ E طبيعيًّا. - 
		
std::numeric_limits<float>::min_exponent10تساويFLT_MIN_10_EXP، والتي يساوي على الأكثر -37. - 
		
std::numeric_limits<double>::min_exponent10تساويDBL_MIN_10_EXP، والتي تساوي على الأكثر -37. - 
		
std::numeric_limits<long double>::min_exponent10تساويLDBL_MIN_10_EXP، والتي تساوي على الأكثر -37. - 
		العضو 
max_exponent10هو الحد الأقصى E بحيث يكون 10 أسّ E منتهيًا (finite). - 
		
std::numeric_limits<float>::max_exponent10يساويFLT_MIN_10_EXP، ولا يقل عن 37. - 
		
std::numeric_limits<double>::max_exponent10يساويDBL_MIN_10_EXP، ولا يقل عن 37. - 
		
std::numeric_limits<long double>::max_exponent10تساويLDBL_MIN_10_EXP، ولا يقل عن37. - 
		إذا كان العضو 
is_iec559صحيحًا، فإنّّ النوع سيكون مطابقًا للمواصفات IEC 559 / IEEE 754، وبالتالي سيُحدَّد مجاله من قبل المعيار. 
تمثيل قيم الأنواع العشرية
	ينصّ المعيار أن لا تقلّ دقة النوع long double عن دقة النوع double، والذي ينبغي ألّا تقل دقّته عن دقّة النوع float؛ وأنّ النوع long double ينبغي أن يكون قادرًا على تمثيل أيّ قيمة يمثّلها النوع double، وأن يمثّل double أيّ قيمة يمكن أن يمثّلها النوع float، أما تفاصيل التمثيل فتتعلّق بالتنفيذ.
	وبالنسبة لنوع عشري T، فإنّّ std::numeric_limits<T>::radix تحدّد الجذر المُستخدم في تمثيل T، وإذا كانت std::numeric_limits<T>::is_iec559 صحيحة، فإنّّ تمثيل T يطابق أحد التنسيقات المُعرَّفة من قبل معيار IEC 559 / IEEE 754.
التدفق الزائد عند التحويل من عدد صحيح إلى غد صحيح مُؤشّر
	عند تحويل عدد صحيح مؤشّر أو غير مؤشّر إلى نوع عددي صحيح مؤشّر ولا تكون قيمته قابلة للتمثيل في النوع المقصود، فإنّّ القيمة المُنتجة تتعلّق بالتنفيذ. مثلًا، لنفرض أن مجال النوع signed char في هذا التنفيذ يكون [-128,127]، ومجال النوع unsigned char من 0 حتى 255:
int x = 12345; signed char sc = x; // معرفة بالتنفيذ sc قيمة unsigned char uc = x; // مهيأة عند القيمة 57 uc
النوع الأساسي وحجم التعدادات
إذا لم يكن النوع الأساسي (underlying type) لنوع تعدادي مُحدّدا بشكل صريح، فإنّّه سيُعرَّف من قبل التنفيذ.
enum E { RED, GREEN, BLUE, }; using T = std::underlying_type<E>::type; // يعرِّفه التنفيذ
	ومع ذلك، فإنّّ المعيار ينصّ على ألّا يكون نوع التعداد الأساسي أكبر من int إلّا إن لم يكن النوعان int و unsigned int قادريْن على تمثيل جميع قيم التعداد. لذلك في الشيفرة أعلاه، النوع T يمكن أن يكون int أو unsigned int أو short ولكن ليس long long مثلًا. لاحظ أنّ التعداد له نفس حجم نوعه الأساسي (كما يعيده التابع sizeof).
القيمة العددية لمؤشر
	نتيجة تحويل مؤشّر إلى عدد صحيح باستخدام reinterpret_cast تتعلّق بالتنفيذ، لكن "… يُهدف إلى ألا تكون النتيجة مفاجئة للمطوّرين الذين يعرفون نظام العنونة في الجهاز."
int x = 42; int *p = &x; long addr = reinterpret_cast<long> (p); std::cout << addr << "\n"; // طبع رقم عنوان
	وبالمثل، فإنّّ المُؤشر الناتج عن تحويل عدد صحيح يكون أيضًا متعلّقًا بالتنفيذ، والطريقة الصحيحة لتخزين مؤشّر كعدد صحيح هي استخدام النوعين uintptr_t أو intptr_t، انظر المثال التالي حيث لم يكن uintptr_t في C++03 وإنما في C99، كنوع اختياري في الترويسة 
#include <stdint.h> uintptr_t uip;
	الإصدار ≥ C++ 11 يوجد std::uintptr_t اختياري في C++11:
#include <cstdint> std::uintptr_t uip;
	تُحيل C++ 11 إلى C99 لتعريف uintptr_t (المعيار C99، 6.3.2.3):
اقتباسيتميّز النوع العددي الصحيح غير المُؤشّر، بإمكانية تحويل أيّ مؤشّر صالح يشير إلى
voidإليه، ثم إعادة تحويله مرّة أخرى إلى مؤشّر إلىvoid، وتكون النتيجة مساوية للمؤشّر الأصلي.
	بالنسبة لغالبية المنصات الحديثة، يمكنك أن تفترض أنّ مساحة العنونة (address space) مسطحة وأنّ الحسابيات على uintptr_t تكافئ الحسابيات على char *، ومن الممكن أن يجري التنفيذ أيّ تعديل عند تحويل void * إلى uintptr_t طالما أنّه يمكن عكس التعديل عند التحويل من uintptr_t إلى void *.
مسائل تقنية
- 
		في الأنظمة المتوافقة مع XSI (X/Open System Interfaces)، فإنّّ النوعين 
intptr_tوuintptr_tإلزاميان، أمّا في الأنظمة الأخرى فهما اختياريان. - 
		الدوال ليست كائنات ضمن معيار C، ولا يضمن معيار C أنّ 
uintptr_tيستطيع تخزين مؤشّر دالة. كذلك يتطّلب التوافق مع POSIX (2.12.3) أنّ: 
اقتباسيجب أن يكون لجميع أنواع مؤشّرات الدوالّ نفس تمثيل مؤشّر النوع الذي يشير إلى void، وينبغي ألا يغير تحويل مؤشّر دالّة إلى void * التمثيل، ويمكن إعادة تحويل قيمة void * الناتجة عن هذا التحويل إلى نوع مؤشّر الدالّة الأصلي، وذلك باستخدام تحويل صريح، دون الخشية من خسارة أيّ معلومات.
معيار C99 الفقرة 7.18.1:
اقتباسعندما تختلف typedef في غياب أو وجود الحرف الأوّل u، فيجب أن تشير إلى الأنواع المُؤشّرة وغير المُؤشّرة المقابلة كما هو مُوضّح في الفقرة 6.2.5؛ وأيّ تنفيذ يوفّر أحد هذين النوعين ينبغي أن يوفّر أيضًا النوع المقابل لآخر.
	قد يكون uintptr_t مناسبًا إذا كنت تريد العمل على بتّات المؤشّر بشكل قد يتعذّر في حال استخدمت عددًا صحيحًا مؤشّرًا.
عدد البتات في البايت
	البايت (byte) في C++ هو المساحة التي يشغلها كائن char، وعدد البتات في البايت مُحدّد في CHAR_BIT، ومُعرّف في climits ولا يقل عن 8. وفي حين أن عدد بتّات البايت في معظم الأنظمة الحديثة هو 8 وأن أنظمة POSIX تتطلّب أن يساوي CHAR_BIT القيمة 8 بالضبط، إلا أنّ هناك بعض الأنظمة التي يكون فيها CHAR_BIT أكبر من 8، فقد يساوي مثلًا 8 أو 16 أو 32 أو 64 بتّة.
هذا الدرس جزء من سلسلة دروس عن C++.
ترجمة -بتصرّف- للفصل Chapter 71: Implementation-defined behavior من كتاب C++ Notes for Professionals

			
				
			
				
			
				
			
				
			
				
			
				
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.