المستوى: مبتدئ. المقال ده هيوضّحلك ليه نفس الرقم لما تخزّنه في جهازين مختلفين أو تبعته على الشبكة ممكن يرجع بالمقلوب، وإزاي تتجنّب البَج ده بسطر واحد. مش محتاج خبرة سابقة، بس عارف يعني إيه متغير ورقم.
لو حفظت رقم في ملف على جهاز ورجعت فتحته على جهاز تاني فطلع رقم غريب تمامًا، ده غالبًا مش عطل. ده اسمه Endianness: ترتيب تخزين بايتات الرقم في الذاكرة.
المشكلة باختصار
الرقم الكبير مش بيتخزّن في خانة واحدة. بيتقسّم لبايتات (كل بايت 8 بت). السؤال: مين الأول في الذاكرة، أهم بايت ولا أقل بايت؟ الإجابة بتختلف من معالج للتاني. ده اللي بيخلّي نفس الـ 4 بايتات تتقري كرقمين مختلفين.
مثال يقرّب الفكرة: حرب البيضة في رواية جاليفر
في رواية "رحلات جاليفر"، أهل ليليبوت اتخانقوا حرب كاملة على سؤال سخيف: تكسر البيضة من الطرف العريض (Big End) ولا الطرف الرفيع (Little End). محدش منهم غلط؛ كل فريق ماشي على عادة. المهم إن الكل يتفق على طريقة واحدة عشان الأكل يمشي.
الكمبيوترات بالظبط زي كده. فيه معالجات بتبدأ التخزين من "الطرف الكبير" (أهم بايت الأول)، وفيه اللي بيبدأ من "الطرف الصغير" (أقل بايت الأول). كل واحد شغّال صح لوحده. المشكلة بس لما اتنين بترتيب مختلف يتكلموا مع بعض من غير اتفاق.
المفهوم بدقة: إيه هو الـ Endianness
خد الرقم 0x12345678 (رقم بـ 4 بايتات). البايت الأهم هو 0x12 والأقل هو 0x78. عندك ترتيبين:
- Big Endian: الأهم الأول. الترتيب في الذاكرة:
12 34 56 78. ده هو "ترتيب الشبكة" (network byte order). - Little Endian: الأقل الأول. الترتيب:
78 56 34 12. ده اللي شغّال على معالجات Intel/AMD (x86) وأغلب أجهزة ARM الحديثة.
الافتراض هنا إننا بنتكلم عن تخزين binary للأرقام الصحيحة. النصوص (UTF-8) والـ JSON مش بيتأثروا بالموضوع ده، فهتلاقيه بيظهر بس لما تتعامل مع بايتات خام.
جرّبها بنفسك: كود بايثون شغّال
الكود ده بياخد نفس الرقم ويطبع شكله بالترتيبين، وبعدين يوريك بالظبط إيه اللي بيحصل لو خلطت بينهم:
import struct, sys
n = 0x12345678
big = struct.pack(">I", n) # big endian
little = struct.pack("<I", n) # little endian
print("ترتيب جهازك :", sys.byteorder)
print("big :", big.hex(" ")) # 12 34 56 78
print("little :", little.hex(" ")) # 78 56 34 12
# لو قريت بايتات little endian كأنها big endian:
wrong = struct.unpack(">I", little)[0]
print("الرقم بعد الخلط:", hex(wrong), "=", wrong)
# 0x78563412 = 2018915346
النتيجة الملموسة: نفس الـ 4 بايتات بالظبط. لكن لما تقراهم بالترتيب الغلط، الرقم بيتحوّل من 0x12345678 (305,419,896) لـ 0x78563412 (2,018,915,346). فرق أكتر من مليار ونص، من غير أي رسالة خطأ. ده النوع اللي بيخدعك ساعات في الديباجينج.
سيناريو واقعي: ليه تاخد بالك على الشبكة
تخيّل جهاز x86 (little endian) بيبعت رقم port أو طول رسالة لسيرفر تاني. لو بعت البايتات زي ما هي في ذاكرته، السيرفر اللي بيتوقّع ترتيب الشبكة (big endian) هيقرا رقم تاني خالص. عشان كده بروتوكولات الإنترنت اتفقت على معيار واحد: big endian هو ترتيب الشبكة.
الحل العملي إنك تحوّل قبل الإرسال وبعد الاستقبال. في لغة C بتستخدم htonl() قبل الإرسال وntohl() بعد الاستقبال. في بايثون بتستخدم struct.pack(">I", n) بعلامة > اللي معناها "ابعت big endian دايمًا"، فالكود بيشتغل صح على أي معالج.
الـ trade-offs
- Little Endian: بيسهّل بعض العمليات الحسابية وتحويل أنواع الأرقام (مثلًا تقرا أول بايت كأنه رقم صغير من غير ما تزحلق). بتكسب راحة في المعالج، بتخسر إن الـ memory dump بيبان "مقلوب" وصعب تقراه بعينك.
- Big Endian: بتقراه طبيعي من الشمال زي ما بتكتب الرقم. بتكسب وضوح في الـ dumps وتوافق مع الشبكة، بتخسر إنه مش الافتراضي على أغلب أجهزتك النهارده.
الخلاصة: مفيش ترتيب "أحسن". المكسب الحقيقي إنك تتفق على ترتيب صريح وقت ما البايتات تخرج من جهازك.
متى لا تشغل بالك بده
لو شغلك كله داخل نفس الجهاز/المعمارية، وبتخزّن بياناتك كنصوص أو JSON أو في قاعدة بيانات بتتعامل مع الأرقام بدل البايتات الخام، فالـ Endianness مش هيلمسك. القصة بتبقى مهمة بس في أربع حالات: إرسال binary على الشبكة، كتابة أو قراءة file format ثنائي، قراءة ملف كتبه جهاز تاني، أو الاشتغال على memory dumps. غير كده، سيب المكتبة تتصرّف.
الخطوة التالية
افتح بايثون دلوقتي واكتب سطر واحد: import sys; print(sys.byteorder) عشان تعرف ترتيب جهازك. وبعدها، في أي مكان بتبعت فيه أرقام binary على socket، تأكد إنك مغلّفها بـ struct.pack(">...") أو socket.htonl(). ده بيقفل باب بَج صعب جدًا تلاقيه بعد كده.
المصادر
- Danny Cohen, "On Holy Wars and a Plea for Peace" (IEN 137, 1980؛ IEEE Computer 1981) — أصل مصطلحَي big/little endian والإشارة لرواية جاليفر.
- توثيق بايثون الرسمي — وحدة
structورموز الترتيب>و<: docs.python.org/3/library/struct.html - توثيق بايثون —
sys.byteorder: docs.python.org/3/library/sys.html - RFC 1700 / RFC 791 — تعريف ترتيب بايتات الشبكة (network byte order) كـ big endian.
- صفحات دليل POSIX —
htonl(3)وntohl(3).