النوع long في C++
النوع long في C++
بالإضافة إلي بيان استخدام متحكمات الخرج , يبين لنا البرنامجين السابقين نوعا جديدا من المتغيرات هو النوع long الذي استخدم لوجود عدد أكثر من طاقة المتغير int , وهو العدد 2,425,785 فالنوع long يمكن أن يحتوي مدي من -2,147,483,648 إلي 2,147,483,647 بينما الحد الأعلي لمدي النوع int هو 32,767 . والنوع long يمكن أن يعرف أيضاً علي أنه long int وهو ما يعني نفس الشئ . والمتغير long يحتل أربعة بايت في الذاكرة , أي ضعف ما يحتله المتغير int , كما هو مبين في الشكل ] أي 32 بتة , لذا فإن المدي هو 2 31 [
شكل المتغير long مختزن في الذاكرة
تعريف الثوابت من النوع long
من الممكن بالنسبة للثوابت أن تعرف علي أنها من هذا النوع باستخدام حرف l بعد قيمتها عند تخصيصها , علي النحو التالي :
Long var = 76781; // assign long constant 7678 to variable long var
ويجب ملاحظة أن الثوابت التي قيمتها أكثر من 65,535 تعتبر من هذا النوع سواء أضيف لها حرف l أم لا .
التعريف المتعدد
لقد استهللنا المتغيرات pop1, pop2, pop3 في نفس تعريفها كما فعلنا مع المتغير char في البرنامج char var , إلا أننا هنا قمنا بتعريف المتغيرات الثلاثة في نفس السطر , وبنفس لفظ التعريف long , مع الفصل بينها بفاصلة عادية . وفي هذا اقتصاد للمساحة في حالة كون مجموعة من المتغيرات من نفس النوع .
ملخص أنواع المتغيرات
تضمنت أمثلتنا إلي الأن أربعة من أنواع من المتغيرات ؛ char, int, float, long , ولا يزال هناك اثنين من المتغيرات , هما double, long double لم نتعرض لهما بعد , فلنتوقف الأن قليلا لكي توجز هذه الأنواع من المتغيرات . ويبين الجدول الكلمات الحاكمة key words المستخدمة لتعريف كل نوع , ومداه , ودرجة دقته (للمتغيرات الكسرية ) , والقدر الذي يشغله من الذاكرة في بيئة الدوس .
جدول المتغيرات الأساسية في لغة السي ++
النوع | الكلمة الحاكمة | المدي | الدقة ( للأعداد الكسرية ) | عدد البتات | |
|
| من | إلي |
|
|
محرفي | Char | -128 | 127 | - | 1 |
عدد صحيح | Int | -23769 | 23767 | - | 2 |
طويل | Long | - | 21474836 | - | 4 |
|
| 21474836 | 47 |
|
|
|
| 48 |
|
|
|
كسري | Float | 38-10x3,4 | 3810x3,4 | 7 | 4 |
مضاعف | Double | 308-10x1,7 | 30810x1,7 | 15 | 8 |
مضاعف |
Long |
-10x1,1 |
0 1x1,1 |
19 |
10 |
يوجد لدينا متغير للمحارف , وهو char , ومتغيران للأعداد الصحيحة , int, long(int) , وثلاثة للأعداد الكسرية , float, double, long double . ونظريا , يوجد متغير رابع يسمي short , ولكنه في بيئة الدوس يطابق المتغير int ولذا فنادرا ما يذكر . على أنه double من الذاكرة ضعف ما يحتله float , ولذلك فهو أكثر مدي ودقة بقدر كبير , وهو يشير إلي أنه " عدد كسري مضاعف الدقة " , وهو يستخدم في حالة كون مدي المتغير float أقل من العدد المطلوب التعامل معه , أو لا يعطي الدقة المطلوبة . ويشغل النوع long double عشر بايتات , وبذلك فهو أوسع مدي وأكثر دقة من النوع double .
ولتتذكر دائما أنه ليس من الكياسة استخدام نوع يشغل من الذاكرة أكبر مما هو مطلوب لمتغيرات البرنامج , حيث إنه كلما زادت المساحة المشغولة من الذاكرة بالمتغيرات , كان تنفيذ البرنامج أبطأ .
الأنواع وحيدة الإشارة
إذا أزلنا الإشارة من نوعي المحارف والأعداد الصحيحة , يمكنك أن تبدأ مداها ابتداء من الصفر , لتشتمل فقط الأعداد الموجبة , وتسمي هذه الأنواع " الأنواع وحيدة الإشارة unsigned data types " . ويسمح لها ذلك أن تشمل أعدادا ضعف ما يسمح به المدي العادي ] لاحظ أن استخدام البتة المستخدمة لتحديد الإشارة لتوسيع المدي يعني ضرب المدي في 2 , مثلا 72 سوف تصبح 82 [ ويبين الجدول التالي مدي هذه الأنواع
جدول الأنواع وحيدة الإشارة
الكلمة الحاكمة | المدي الرقمي | |
Unisigned Char Unsigned Int Long unsigned | أقل
0
0 0 | أعلي
255
65.535 295,4,294,967 |
| |
وتجاوز المدي بالنسبة للمتغيرات ذات الإشارة يمكن أن يؤدي إلي أخطاء غامضة , وهي أخطاء قد يمكن إزالتها لو استخدمنا النوع وحيد الإشارة . ففي المثال التالي , يخزن العدد 25000 مرة كعدد صحيح وحديد الإشارة unsigned , ومرة كعدد صحيح معتاد int , وستجري علي العدد بعض العمليات الحسابية وهو علي الصورتين , ولنلاحظ النتيجة .
Singtest.cpp
signtest.cpp
// signtest.cpp
// tests signed and unsigned integers
#include <iostream.h>
void main()
{
int signedVar = 25000; // signed: -32768 to 32767
unsigned int unsignVar = 25000; // unsigned: 0 to 65535
signedVar = (signedVar * 2) / 3; // calculation exceeds range
unsignVar = (unsignVar * 2) / 3; // calculation within range
cout << "signedVar = " << signedVar << endl; // wrong: -5178
cout << "unsignVar = " << unsignVar << endl; // ok: 16666
}
سوف تفاجأ أن يكون خرج البرنامج قد ظهر خطأ مع المتغير int (-5178) وصحيحا في حالة المتغير unsigned int (16666) . فقد أجري علي نفس العدد عمليتي ضرب في 2 ثم قسمة علي 3 , ولكن في حالة المتغير int سوف يكون ناتج عملية الضرب 50000 أي أكبر من المدي المتاح للمتغير (32767) . ولذا فليس مستغربا أن تكون النتيجة النهائية خاطئة في هذه الحالة .
والدرس المستفاد من هذا المثال هو أنه يجب علي الدوام ملاحظة تناسب المساحة المطلوبة للمتغيرات في الذاكرة مع القيم التي تتخذها خلال تشغيل البرنامج . ( قد تكون النتيجة المبينة في المثال مختلفة لأنواع الأجهزة التي تعمل علي بيئة خلاف الدوس , والتي تعطي مساحة تخزين أكبر للنوع int) .
تحويل النوع
تتسامح لغة السي ++ وكذا السي التقليدية , مع العبارات التي تحتوي علي أكثر من نوع , كما يتبين لك من البرنامج التالي :
Mixed.cpp
mixed.cpp
// mixed.cpp
// shows mixed expressions
#include <iostream.h>
void main()
{
int count = 7;
float avgWeight = 155.5;
double totalWeight = count * avgWeight;
cout << "totalWeight=" << totalWeight << endl;
}
في هذا البرنامج ضرب عدد من نوع int مع أخر من نوع float ليكون حاصل الضرب من نوع double , وتسير ترجمة البرنامج دون مشاكل , فالمترجم يعلم أنه من الطبيعي أن تجري بعض العمليات الرياضية , ومنها عملية الضرب علي أنواع مختلفة من الأعداد .
وكثير من اللغات لا تسمح بذلك , فعندما يحتوي التعبير علي أكثر من نوع , يفترض المترجم أنك قد أرتكبت خطأ , ويعطي بالتالي إشارة بذلك , ليحميك من نفسك . أما السي ++ فتفترض فيك الانتباه لمل تفعل , وتقوم بتنفيذ مطلبك , وهذا هو أحد الأسباب في شعبية لغة السي , فهي تعطيك قدر أكبر من الحرية في العمل , ومن المسئولية في نفس الوقت .
التحويل التلقائي للنوع
لنتبع ما يفعله المترجم حينما يواجه تعبيرا يحتوي علي أنواع مختلفة من المتغيرات . إنه يرتب المتغيرات ترتيبا تصاعديا وفقا للشكل التالي :
النوع | الرتبة |
Long double | الأعلي |
double | |
float | |
long | |
int | |
char | الأدني |
وحينما يصادف المترجم نوعين من المتغيرات في تعبير واحد فإنه يحول المتغير ذا الرتبة الأدني إلي نوع زميله ذي الرتبة الأعلي . وعلي ذلك فإن المتغير count في المثال المعطي يرفع من رتبة النوع int إلي رتبة النوع float , ويخزن في مكان مؤقت لحين إجراء عملية الضرب . ولما كان حاصل الضرب يستخرج من نفس النوع float , فإنه يرفع بالتالي إلي النوع double حتي يمكن تخزينه في المتغير total weight .
ويبين الشكل هذه العملية .
شكل التحويل التلقائي للنوع
هذه التحويلات تتم دون أن يلاحظها أحد , وفي العادة لا يفترض أن تشغل نفسك بها , فلغة السي ++ تغنيك عن ذلك . علي أنه حينما نبدأ في دراسة الكائنات , فإننا في الواقع سوف نضع أنواع البيانات الخاصة بنا , و حتي نتمتع بإمكانية خلط الأنواع المختلفة في نفس التعبير علينا أ نضع روتينيات تتيح عملية التحويل , حيث لن يقوم المترجم بهذه العملية من تلقاء نفسه كما يفعل مع أنواع البيانات الخاصة باللغة .
التحويل القسري للنوع
يقصد بمصطلح التحويل القسري casting " أن يقوم المترجم بنفسه بتحويل النوع , فيعتبر بذلك قد حول النوع "قسرا" بدلا من أن يقوم المترجم بذلك تلقائيا , ففي أي الأحوال يريد المبرمج أن يلجا لهذه الإمكانية ؟
هل تذكر الخطأ الذي حدث في البرنامج signtest.cpp ؟ لقد حدث لكون عملية الضرب , وهي عملية بينية في البرنامج , قد أنتجت قيمة أكبر مما يحتمله النوع int . هذا الخطأ صححناه بتعريف المتغير علي أنه من النوع unsigned int أيضا , في مثل هذه المواقف يمكنك الخروج من المأزق بإستخدام إمكانية التحويل القسري , وإليك مثالا يوضح الفكرة .
Cast.cpp
cast.cpp
// cast.cpp
// tests signed and unsigned integers
#include <iostream.h>
void main()
{
int intVar = 25000; // signed: -32768 to 32767
intVar = (intVar * 10) / 10; // result too large
cout << "intVar = " << intVar << endl; // wrong answer
intVar = 25000;
intVar = ( long(intVar) * 10) / 10; // cast to long
cout << "intVar = " << intVar << endl; // right answer
}
فتري أن حاصل ضرب المتغير intVar في 10 ينتج قيمة 250,000 , وهي قيمة أكبر بكثير مما يمكن للنوع unsigned int من أن يحتويها , ولذلك فقد كان ناتج العملية في النصف الأول من البرنامج خطأ .
وتصحيحا لهذا الوضع , يلزم أن تكون النتيجة من النوع long , وهو نوع له مدي يستغرق تلك القيمة . ولكن لنفرض أن لا تريد أن تغير من تعريف المتغيرات , حفاظا علي حجم البرنامج مثلا , تتيح لك لغة السي حلا أخر , هو أن تغير نوع المتغير int var "قسريا" قبل إجراء عملية الضرب , وذلك بالعبارة :
Long (int var)
التي تراها في صلب البرنامج , أمه تتسبب في إنشاء متغير مؤقت , من النوع long . تخزن فيه نتيجة الضرب , قبل الإنتقال إلي الخطوة التالية , وهي عملية القسمة . وكما تري من خرج البرنامج , فإن نتيجة العملية التي تمت دون تغيير قسري للنوع جاءت خاطئة , بينما الأخري صحيحة .
هذا وتقبل السي ++ الصيغة التالية لمتغير القسري :
(long) int var
بمعني أن يكون القوسان محيطين بالكلمة الحاكمة لتعريف النوع بدلا من اسم المتغير , وهي الصيغة الوحيدة التي تقبلها السي التقليدية , ولكننا في السي ++ نفضل الصيغة الأولي ( والذي يطلق عليها " التعبير الدالي functional notion " لكونه يجعل الكلمة الحاكمة علي صورة الدالة ) .
ولسوف يعطيك المترجم رسالة ''conversion may lose significant'' ومعناها أن التحويل قد يؤدي إلي فقدان أرقام مهمة , وهو يحدث حين يحول المترجم المتغير المحول قسريا إلي صورته الأولي , ] حيث يكون التحول من الرتبة الأعلى إلي الرتبة الأدنى [ , وهو تحذير يتم تجاهله .
تعليقات
إرسال تعليق