السلام عليكم ,,
هذه أول مقالة في هذه السلسة,,
المقالة ليست مرجعاُ معتمداُ بل هي جهد شخصي أتمنى أن يكتمل, و يصبح موضوعاُ مفيداُ في المنتدى و لذلك ستجدني أخاطبك في المقالة :)
عندما بدأنا دراسة البرمجة, كانت احدى و اهم النقاط التي قد يذكرها أي محترف هي أن لغات البرمجة التي نعمل عليها ( اللغات العالية المستوى ), و في حالتنا هذه ++C تخفي عن المبرمج تفاصيل الـ Hardware المملة, و تعطيه أدوات يمكنه استمعالها لإنجاز ما يريد, و طبعاُ أحد أشهر الأمثلة هو التعامل مع الذاكرة, فوحدة التخزين هي الـ Byte فلا يمكن التعامل مع وحدة أقل من الـ Byte . صحيح أن التعامل مع وحدات التخزين بالـ Byte ( و هي بالمناسبة طريقة حفظ البيانات في الذاكرة ) سهل الكثير من الأمور و ألغى تعقيدات لا طائل منها, إلا أنه في نفس الوقت حدد صلاحيات المبرمج! هذا صحيح, فالاستغناء عن شيء له فوائد و مضار و أو عوارض جانبية ,,
ما أردت الإشارة إليه في المقدمة السابقة هو الـ Bit وهو وحدة بناء كل شيء في الحاسب الآلي,
فكل شيء في الحاسب عبارة عن 0 أو 1. إلى النقطة السابقة, أعتقد أنك سمعت هذا الكلام عشرات المرات, و لكني سأسرد ما أتمنى أن يكون مفيداُ ,,
بكل تأكيد تعرف أن الحاسب جهاز رقمي Digital و هناك نوعين من الحاسبات هناك ما يسمى حاسب رقمي و هناك حاسب Analog - الحاسبات الشخصية حاسبات رقمية, و لكن أنا أتحدث بشكل عام عن أنواع الحاسبات -. و لكن ما هو الشيء الرقمي و ما الفرق بينه و بين التماثلي,
طبعاُ تصور أن لديك أن لدينا ثلاثة أجهزة ( المثال خيالي للتوضيح فقط ! ) و الجهاز الأول يخرج منه فرق جهد معين و لنقل 5 و الآخر 5 و الجهاز الثالث يقوم بجمع فرقي الجهد فيعطينا 10 , و هذا هي الطريقة التماثلية,
و لكن انتظر لحظة ,
هل يوجد فرق جهد يساوي 5 بالضبط ! الجواب السليم هو لا, لأن الـ 5 التي حصلنا عليها هي قيمة تقريبية حصلنا عليها من جهاز القياس و بالتالي لو حصلنا على جهاز أدق بكثير لربما أعطانا أن فرق الجهد الناتج هو 5.000001 !!
هنا المشكلة التي واجهت علماء الحاسب في الماضي, فهم لم يستطيعوا أن يفكروا في شيء يعطينا قيمة صحيحة 100 % لأننا بكل بساطة نقيس القيمة عن طريق آلة قياس و كلما زادت الدقة كلما حصلنا على قيم أدق و هكذا إلى ما لا نهاية !!!
لهذا الغرض اخترعنا الترانزيستور !! الترانزيستور بكل بساطة له قدرة على منع مرور الكهرباء حتى يصل فرق الجهد إلى قيمة معينة بعدها يقوم بتوصيل التيار فجأة!!
و لكن هذه القيمة تقريبة كما في المثال السابق !! إذاً ماذا أضاف اختراع الترانزيستور !!
لو فرضنا أن القيمة التي يوصل الترانزيستور الكهرباء بعدها هي 5, وكان لدينا ترانزيستورين و مولد - جهاز إنتاج - فرق الجهد ينتج قيمتين : 10 و 0 ,, إذا اعتبرنا أن الترانزيستور إذا كان يمر فيه فرق جهد 10 يمثل بالشكل التالي " | " و الذي لا يمر فيه تيار أو فرق الجهد كان يساوي صفر يمثل بالشكل التالي " ~ " ,, فيمكن للترانزيستورين أن يعطيانا الاحتمالات التالية من حيث التوصيل أو عدمه :
~~ ~| |~ ||
و هنا لدينا أربعة احتمالات لاثنين من الـ Bits فقط و لو كانوا ثلاثاُ لكان عدد الاحتمالات 8 و هكذا ,,
هذه هي الفكرة,, بدلاُ من الاعتماد على فرق الجهد كقيمة,, قمنا باستعماله كأداة في نظام أعداد جديد اخترعناه و سميناه النظام الثنائي أو Binary System ,, لأنه مكون من حالتين على غرار النظام العشري المكون من عشر احتمالات, أي أننا عندما نمثل أي عدد فإننا يمكننا استعمال كل خانة لتمثيل عشرة احتمالات فقط ,, و هكذا أيضاً في النظام الثنائي كل خانة لها احتمالان إما أن تكون 0 أو 1. لن أدخل هنا في تفاصيل أنظمة الأعداد, فهو موضوع طويل و قد أفرد له مقالاُ كاملاً مع التعريف بعلاقة هذه الأنظمة مع بعضها البعض, و كيفية تحويل الأعداد فيما بينها.
و لكن ما علاقتنا كمبرمجين بتلك الأمور, و خصوصاً أنها أمور متعلقة ببنية الحاسب و لن نستفيد منها عند بناء برنامج لإدارة مخزن على سبيل المثال,, طبعاً التعامل على مستوى الـ Bit لا يستفيد منه إلا المبرمج, و ليس المستخدم!!!
برامجك ستكون في قمة السرعة لأنك التعامل على مستوى الـ Bit يعني استعمال أوامر أولية يوفرها أي معالج, أي أن العملية التي ستتم في الغالب هي عبارة عن تعليمة واحدة في لغة الاسمبلي,, إضافة إلى ذلك فإنها ستمكنك من الولوج إلى مستوى من التحكم بحيث تستطيع تنفيذ بعض المهمات التي تكلف برامجك الكثير من الوقت عن طريق عمليات بسيطة و سهلة و فوق كل ذلك تعطيك سرعة أعلى !!
ذكرنا في بداية الموضوع أن لغات البرمجة العالية المستوى لا توفر لنا التحكم بالبيانات التي لدينا على مستوى الـ Bit بشكل كامل, و لكنها في المقابل توفر لنا مجموعة بسيطة من الأدوات الأولية التي يمكن استخدامها لوحدها أو مع بعضها للخروج بالنتائج المطلوبة, قبل أن أبدأ في الحديث عن معاملات الـ Bit أو لا بد أن أذكر أن اللغات العالية المستوى توفر ما يسمى بالمعاملات المنطقية, و التي هي بكل بساطة عبارة عن محاكاة لبعض العمليات على مستوى الـ Bit و لكن على مستوى الـ Byte أو أكبر من ذلك.
طبعاً اصطلح المبرمجون على :
الإطلاق على العدد الثنائي 0 المصطلح "خاطئ" False و على العدد الثنائي 1 المصطلح "صحيح" True فهذان المصطلحان يمثلان الأعداد في النظام الثنائي - و لنا كلام بعد الانتهاء من شرح المعاملات المنطقية مع هذين المصطلحين .
المعاملات المنطقية كالتالي:
المعامل && :
المعامل && يعتبر حرف العطف "و" في لغة البشر, فلو قلنا "ذهبت للبيت أنا و أخي" ثم أخبرتك أن أخي لم يذهب فأنا أكذب عليك و الجملة التي قلتها سابقاُ غير صحيحة! و كذلك لو قلت لك أن أخي ذهب لكني لم أذهب, أو لم نذهب أنا و أخي ففي الأحول السابقة, تكون جملتي غير صحيحة, الحالة الوحيدة التي يمكن للجملة التي تصف شيئاُ ما أن تكون فيها صحيحة هي أن يكون كل من أخي و أنا قد ذهبنا إلى البيت.
المعامل يستقبل مدخلين و يخرج بنتيجة, كل مدخل على طرف من أطراف المعامل :
AND ( && ) : True && True = True True && False = False False && True = False False && False = False
و كمثال بسيط يمكن استخدام المعامل في عمليات التحقق, فلو أردنا التحقق أن متغيرين منطقيين bool كلاهما يحملان القيمة true,
فيمكننا كتابة التالي :
bool x = true, y = true;
if ( x == true && y = true ){
// Do any thing you want here.
}المعامل || :
المعامل "||" يعتبر "أو" في لغة البشر, فلو قلنا "سأجلس للبرمجة أو تصفح الانترنت"
فلو جلست للبرمجة فقط فكلامك صحيح, أو جلست لتصفح الانترنت فكلامك صحيح أيضاُ, ولو جلست و فعلت الاثنتين لكان كلامك صحيحاُ أيضاُ!!
الحالة الوحيدة التي تكون الجملة التي استخدمناها غير صحيحة, هي عندما لا أجلس لتصفح الانترنت أو البرمجة بل أقوم بشيء آخر!
المعامل يستقبل مدخلين و يخرج بنتيجة, كل مدخل على طرف من أطراف المعامل :
OR ( || ) : True || True = True True || False = True False || True = True False || False = False
المعامل ! :
المعامل "!" هو معامل النفي, فلو نفينا صحة خبر ما فإنه يصبح غير صحيح و معنى ذلك أنه يصبح خاطئاً.
المعامل يستقبل مدخل و يخرج بنتيجة, النتيجة دائماُ تكون عكس المدخل :
NOT ( ! ) : !True = False !False = True
إلى هنا نكون قد انتهينا من المعاملات المنطقية, و لا ننسى أن فهم المعاملات المنطقية يصبح سهلاُ عندما نعرف علاقة الأجزاء - المتغيرات - مع بعضها البعض. و لكن لي تعليق بسيط على true و false, نحن نعرف أن false تساوي 0 و true تساوي 1, و لكن "اصطلح" المبرمجون على أن يكون الصفر يساوي false و ما عدا الصفر يساوي true, و ذلك لأن جميع أنواع البيانات في ++C تأخذ قيم على حسب حجمها و مجال هذه القيم ليس الصفر و الواحد فقط, كـ int - الأعداد الصحيحة - و غيرها.
ما سيلي سوف يتحدث عن معاملات الـBit و لكني أحببت أن أذكر المعاملات المنطقية لتشابه العمل بينهما مع اختلاف المدخلات و المخرجات .
من هذه النقطة سوف نبدأ عملياً تعلم كيفية تطبيق المعاملات التي توفرها ++C, سوف نتعرف على المعاملات واحداُ تلو الآخر, و سيتم شرح المعامل, مع مثال على كيفية عمله.
بدايتنا ستكون عند معامل "الإزاحة" Shifting Operator, المعامل له الشكل التالي ">>" إزاحة لليسار, و "<<" إزاحة لليمين,
مفهوم الإزاحة Shifting مفهوم بسيط جداُ,
لنفرض أن لدينا العدد الثنائي التالي :
10010010
فلو قمنا بعمل إزاحة لليمين 5 خانات - معنى الإزاحة خمس خانات لليمين, اث ان الأعداد الثنائية للرقم سوف تتحرك خمس خانات لليمين - :
10010010 >> 5 = 00000100 10010 -------- ----- /\/\ this part is taken out because of the boundries of the byte.
طبعاُ لاحظ العدد المكون من خمس خانات على يمين العدد الناتج عن الإزاحة, هذا الجزء هو الجزء الضائع لأنه خرج خارج حدود الـ Byte و بالتالي لم يعد موجوداً أصلاُ, أما الخمس خانات الجديدة على اليسار فهي أصفار دائماُ, أي أن هناك جزء ضائع و أصفار متكونة لتعبئة الفراغ المتولد من الإزاحة. هذه ما تفعله الإزاحة إلى اليمين أو إلى اليسار بشكل كامل.
و لا يمكن أن نمر على هذين المعاملين دون الدخول في التفاصيل !! لأنهما يمثلان عملية رياضية أساسية في أنظمة الأعداد, سنتكلم هنا عن الأعداد الصحيحة أي الأعداد بدون كسور, افترض مثلاُ أن لدينا الرقم 20,
و قلت لك اضرب هذا العدد بعشرة, أي 10 * 20 فسيكون الناتج 200 ,,
إذا كنت دقيق الملاحظة فستكون قد اكتشفت أمراُ مهماً و هو أننا قمنا بإزاحة العدد 20 خانة واحدة لليسار , و بمعنى آخر في النظام العشري الضرب في عشرة هو إزاحة لليسار خانة واحدة, و القسمة على عشرة إزاحة لليمين خانة واحدة. و هكذا مع النظام الثنائي فالقسمة على اثنين - اثنين هنا هو أساس النظام أو عدد الرموز التي يستخدمها النظام | 0,1 | و في النظام العشري أساس النظام 10 | 0-9 | - إزاحة لليمين, و الضرب في اثنين إزاحة لليسار خانة واحدة.
طبعاُ كمثال على الإزاحة لليسار ,,
8 >> 2 // 8 >> 2 = 32 // 32 = 8 * ( 2 * 2 )
و كمثال على الإزاحة لليمين ,,
8 << 2 // 8 << 2 = 2 // 2 = 8 / ( 2 * 2 )
الكلام في الأعلى كان مقتصراُ على الأعداد الصحيحة فقط. فلو أننا قمنا بإزاحة العدد 7 إلى اليمين خانة واحدة فسوف نحصل على العدد 3 بدلاُ من 3.5 و ذلك لأن العملية سوف تتم على الشكل التالي ,,
7 >> 1 == 3 // Decimal 00000111 >> 1 == 00000011 // Binary
إلى هنا نكون قد انتهينا من عمليات الإزاحة, طبعاُ لم نعرف أصلاُ ما هي الفائدة من معرفة عمليتي الضرب و القسمة في الأعداد الثنائية بهذه الطريقة بدلاُ من الضرب و القسمة العاديين, حالياُ نريد تعلم النظرية وراء هذا الأشياء فقط أما التطبيقات العملية فسوف تكون مقالات قادمة إن شاء الله.
محطتنا الثانية هي المعامل & أو And Operator. نحن ذكرنا المعامل المنطقي && و هذان الاثنان يتشابهان في العمل, فالأول & يطبق العملية على مستوى الـ Bit أما المعامل && فهو يطبقها على مستوى Byte أو أكبر.
طبعاُ إذا اعتبرنا false تساوي الصفر و true تساوي 1 فإننا سنحصل على التالي :
True & True = True True & False = False False & True = False False & False = False 1 & 1 = 1 1 & 0 = 0 0 & 1 = 0 0 & 0 = 0
ذكرنا أن هذا المعامل يعمل على مستوى الـ Bit ,, و معنى ذلك أنه لو كان لدينا عددين صحيحين, و قمنا بإجراء العملية عليهما فإن كل Bit في العدد الأول سيتم مقارنته بالـ Bit المقابل له في العدد الثاني. مثال :
7 & 5 = 5 111 & 101 ----- 101
عملية المقارنة تمت بين كل Bit في العدد 7 و الـ Bit المقابل له في العدد 5.
محطتنا الثالثة هي عند المعامل | أو Or Operator, و قد قمنا بذكر الأخ التوأم لهذا المعامل في المعاملات المنطقية مع التذكير بأن || تعمل على مستوى Byte أو أكبر, على عكس الأخ الصغير | الذي يعمل على مستوى الـ Bit ,, طريقة عمل هذا المعامل :
True & True = True True & False = True False & True = True False & False = False 1 | 1 = 1 1 | 0 = 1 0 | 1 = 1 0 | 0 = 0
مثال على ذلك :
8 | 7 = 15 1000 | 0111 ------- 1111
لا بد أن نمر الآن على المعامل "~" , هذا المعامل يسمى "المتمم", و كل ما يقوم به هو عكس قيمة الـ Bit فلو كان 0 و طبقنا عليه المعامل فسيصبح 1 و العكس صحيح.
~0 = 1 ~1 = 0
هذا المعامل يأخذ قيمة واحدة فقط ليعطينا المتمم لها,
~(255) = 0 ~(11111111) = 00000000
هذا المعامل مفيد جداُ و سنتكتشف لاحقاُ كيف يمكن الاستفادة منه,.
محطتنا الأخيرة مع المعامل الشهير XOR و في ++C يأتي على الشكل "^" ,
و هذا المعامل يسمى Exclusive OR, هذا المعامل يعطينا True فقط إذا كان المدخلان مختلفين, أما إذا كانا متشابهان فسوف يعطينا False, و هو يعمل كالتالي :
True ^ True = False False ^ False = False True ^ False = True False ^ True = True 1 ^ 1 = 0 0 ^ 0 = 0 1 ^ 0 = 1 0 ^ 1 = 1
و كمثال على ذلك :
15 ^ 8 = 7 1111 ^ 1000 ------- 0111
أشهر مثال يمكن أن يقدم حول هذا المعامل, هي عملية التشفير باستخدامه, طبعاُ لأهمية الموضوع, سوف يكون مشروعنا النهائي في المقالة الثالثة هو هذا المعامل, حيث سنطبق هذا المعامل لتشفير النصوص في ++C .
بكل تأكيد لن نقضي حياتنا البرمجية لهثاُ وراء السرعة و تعقيدات التعامل على مستوى الـ Bit,
و لكننا حقيقة سنستعمل هذه المفاهيم و المعاملات في كثير من الأحيان.
من المستحسن عدم التعامل على مستوى الـ Bit في الحالات العادية, و لكننا في بعض الأحيان سنحتاج إلى زيادة سرعة البرنامج و إلى حفظ المساحة المهدرة دون العمل على مستوى الـ Bit.
إضافة إلى ذلك إذا دخلت عالم الـ Compressing و الخوارزميات المستخدمة فبكل تأكيد ستكون المعاملات في صحبتك طوال الوقت, و أحد أشهر الأمثلة و التي لن نتطرق لها لطولها هي الأعداد الطويلة جداُ أو بتعريف أصح Arbitrary-Precision Number
فأنواع الأعداد في ++C لا يمكنها التعامل مع هذا العدد على سبيل المثال,
59585763636365354745858569695873631
قد يكون التعامل مع هذه الأعداد من الترف البرمجي, و لكنه في الحقيقة حاجة ملحة في التطبيقات الواقعية, فلايمكن تطبيق خوارزمية التشفير RSA دون أرقام أولية بطول الرقم الذي في الأعلى و ربما يتم استخدام أعداد أكبر بكثير. الموضوع جميل جداً و لكن يحتاج إلى الكثير من الجهد لتصميم فئة يمكن الإعتماد عليها, و لكن يوجد الكثير من المكتبات التي يمكن الاعتماد عليها.
المقال القادم سوف يتحدث عن كيفية توظيف هذه المعاملات الصغيرة لتطبيق بعض المهام التي قد تكلف الكثير من وقت المعالج لو تم كتابتها بالطرق العادية.
تحياتي و أنا جاهز للأسئلة :thumb_up:



