الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

[ تمت الإجابة ]أنا متلخبط بخصوص Little-Endian و Big-Endian

بدأه مستر برمجة في 21 أغسطس 2013 · 9 رد · 1,810 مشاهدة · في الأسئلة المجابة
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم

 

أنا عندى مشكلة بخصوص Little-Endian و Big-Endian , دائما الاثنين يتم قراءتهم بالعكس, فى البداية هعرض ترتيب كل نوع وبعدين هشرح المشكلة .

 

  • ترتيب البايتات بتاعة الـ Big-Endian

280px-Big-Endian.svg.png

 

  • ترتيب البايتات بتاعة Little-Endian

280px-Little-Endian.svg.png

 

المشكلة هى أن عندى النظامين بيتم قراءتهم بالعكس بمعنى لو فرضنا عندنا رقم 0x1020

 

عند قراءة الرقم السابق من ملف يستخدم نظام Big-Endian يتم قراءتة بهذا الشكل (0x2010)

أيضا قراءة الرقم نفسة من ملف يستخدم نظام Little-Endian بيتم قراءتة بهذا الشكل (0x2010)

يعنى النظامين لازم أعكسهم عشان يبقى (0x1020) .

 

عن تجربتى أنا قمت بقراءة أول 2 بايت من ملف من نوع JPEG, وقام بقراءتهم بهذا الشكل (0xd8ff) مع العلم أن الترتيب الصحيح هو (0xffd8), لكن هذا طبيعى لان هذا النوع من الملفات يتم قراءتة بالمعكوس, لانة يستخدم النوع Big-Endian .

 

تجربة أخرى, قمت بقراءة أول 2 بايت من ملف من نوع BMP, وقام بقراءتهم بهذا الشكل (0x4d42) مع العلم أن فى موقع Wikipedia كاتب أول بايتين بهذا الشكل (0x424d), لكن أنا لما قريتهم من الملف طلعو بالعكس, وهذا يستخدم النوع Little-Endian .

 

طيب أية سبب المشكلة دى, النوعين بيتم قراءتهم معكوسين .

تم تعديل هذه المشاركة بواسطة مستر برمجة في 21 أغسطس 2013 في 09:26

1

||||||||||||||||||||||||||||||||||||


||||||||||||||||||||||||||||||||||||


egypt_coa.gif


||||||||||||||||||||||||||||||||||||


||||||||||||||||||||||||||||||||||||

#2

الأمر ملتبس عليك، إنس أمر القراءة و الكتابة و تعال نأخذ الموضوع من بدايته.

 

أى مجموعة من البايت يمكن وضعهم فى الذاكرة بإسلوب معين، الإسلوب ده إسمه Endianness، و فى الشرح التالي سنرى كيف يتم تمثيل الرقم  12345678h داخل الذاكرة.

 

يوجد مصطلحين تحتاج لأن تكون على دراية بهم و هم Least Significant و Most Significant و يمثلوا بداية و نهاية سلسلة معينه فمثلا إذا قلت Least Significant Bit فأنت تعني أول بت داخل سلسلة من البتتات و بالمثل إذا قلت Least Significant Byte فأنت تعني أول بايت فى سلسلة من البايت.

 

عندما نتكلم عن قيمة رقميه فبدايتها هو قيمة خانة الأحاد و نهايتها هو قيمة أخر خانه بالرقم، بالمثل مع الرقم الذى سنستخدمه فى الشرح الأن حيث البايت الأول هو 78h و اسمه Least Significant Byte و الرقم 12h هو البايت الأخير و أسمه Most Significant Byte، هذه التسمية مطلقة و لا علاقة لها بالذاكرة.

 MSB             LSB
+----+----+----+----+
| 12 | 34 | 56 | 78 |
+----+----+----+----+

نظام Little-Endian

 

هذا النظام يتبع إسلوب Least Significant In Least Memory و هذا يعني أن موقع أول بايت فى السلسله يكون داخل أول بايت فى الذاكره و هكذا وصولا لأخر بات فى السلسله ليتم وضعه فى أخر مكان داخل الذاكره الخاصه بالسلسلة.

       MSB             LSB
        3     2    1    0
      +----+----+----+----+
VALUE | 12 | 34 | 56 | 78 |
      +----+----+----+----+


    LOC  VALUE         MEM  LOC
        +----+      +----+
LSB  0  | 78 |  ->  | 78 |   0
        +----+      +----+
     1  | 56 |  ->  | 56 |   1
        +----+      +----+
     2  | 34 |  ->  | 34 |   2
        +----+      +----+
MSB  3  | 12 |  ->  | 12 |   3
        +----+      +----+


        LSB             LSB
         0     1    2    3
       +----+----+----+----+
MEMORY | 78 | 56 | 34 | 12 |
       +----+----+----+----+

لاحظ اننا نتحدث بوحدة البايت أى 8بت و إذا اردنا التحدث بوحدة 16بت سيصبح الشكل السابق كالتالي:

       MSW      LSW
        1         0
      +------+------+
VALUE | 1234 | 5678 |
      +------+------+


    LOC  VALUE            MEM   LOC
        +------+      +------+
LSW  0  | 5678 |  ->  | 5678 |   0
        +------+      +------+
MSW  1  | 1234 |  ->  | 1234 |   1
        +------+      +------+


        LSW        LSW
         0          1
       +------+------+
MEMORY | 5678 | 1234 |
       +------+------+

نظام Big-Endian

 

هذا النظام يتبع إسلوب Most Significant in Least Memory، و هذا يعني أن موقع أخر بايت فى السلسله يكون داخل أول بايت فى الذاكره و هكذا وصولا لأول بايت فى السلسله ليتم وضعه فى أخر مكان داخل الذاكره الخاصه بالسلسلة.

       MSB             LSB
        3     2    1    0
      +----+----+----+----+
VALUE | 12 | 34 | 56 | 78 |
      +----+----+----+----+


    LOC  VALUE         MEM  LOC
        +----+      +----+
LSB  0  | 78 |  ->  | 78 |   3
        +----+      +----+
     1  | 56 |  ->  | 56 |   2
        +----+      +----+
     2  | 34 |  ->  | 34 |   1
        +----+      +----+
MSB  3  | 12 |  ->  | 12 |   0
        +----+      +----+


        LSB             LSB
         0     1    2    3
       +----+----+----+----+
MEMORY | 12 | 34 | 56 | 78 |
       +----+----+----+----+

و إذا كانت الوحدة 16بت بدلا من 8بت يصبح الشكل السابق كالتالي:

       MSW      LSW
        1         0
      +------+------+
VALUE | 1234 | 5678 |
      +------+------+


    LOC  VALUE            MEM   LOC
        +------+      +------+
LSW  0  | 5678 |  ->  | 5678 |   1
        +------+      +------+
MSW  1  | 1234 |  ->  | 1234 |   0
        +------+      +------+


        LSW        LSW
         0          1
       +------+------+
MEMORY | 1234 | 5678 |
       +------+------+

مثال تطبيقي

 

المثال التالي يظهر لك النتيجة بشكل عملي على أى من النظامين little او big.

 

فى الكود التالي الماكرو USE_BIG ضع قيمته بواحد ليطبع لك النتيجة كما ستكون داخل big endian و اجعل قيمته بصفر لتطبع النتيجة كما ستكون داخل little-endian.

#include <iostream>
using std::cout;
using std::hex;
using std::endl;

#define USE_BIG 0

typedef unsigned int   uint32;
typedef unsigned short uint16;
typedef unsigned char  uint8;

union number
{
    uint32 a;

    uint8  b[sizeof(uint32)];

    uint16 c[sizeof(uint32) / sizeof(uint16)];
};

int main()
{
    number t;
    t.a = 0x12345678;

    const uint32 u32_32 = sizeof(uint32);
    const uint32 u32_16 = sizeof(uint32) / sizeof(uint16);

    cout << "memory fetch for 8-bits:\n";
    for(int i=0, c=u32_32-1; i<u32_32; ++i, --c)
    {
        cout << "  b[" << i << "] = " << hex << (uint32)t.b[USE_BIG?c:i] << endl;
    }
    cout << endl;

    cout << "memory fetch for 16-bits:\n";
    for(int i=0, c=u32_16-1; i<u32_16; ++i, --c)
    {
        cout << "  c[" << i << "] = " << hex << t.c[USE_BIG?c:i] << endl;
    }
}

القراءة و الكتابة

 

عملية ترميز محتويات ملف/الذاكرة دائما النظام يقوم بها تلقائيا - أو بالأصح المعالج - تبعا لإعداداته.

 

المشكله تظهر مثلا عندما تكون داخل نظام Little-Endian و تقوم بقراءة ملف كتب داخل Big-Endian حينها ما تحتاج لفعله هو قراءة وحدة بيانات مفهومه ثم عكسها و هكذا. فمثلا الرقم 12345678h داخل big-endian سيتم ترميزه كالتالي:

        LSB             LSB
         0     1    2    3
       +----+----+----+----+
MEMORY | 12 | 34 | 56 | 78 |
       +----+----+----+----+

و عندما تقوم بقراءة هذه القيمه من داخل Little Endian سيتم تحميلها بالذاكرة بنفس الشكل، المشكله ستحدث عندما تقوم بتحويل هذه السلسلة لأصلها حينها ستصبح 78563412h و هذا خاطئ لذا ستحتاج لعكسها بإستخدام الكود التالي:

#define REV(i) ((i << 24) | ((i & 0xFF00) << 8) | ((i >> 8) & 0xFF00) | (i >> 24))
uint32 a = 0x78563412;
uint32 b = REV(a); // b = 0x12345678

أو يمكنك إستخدام الكود التالي و عام ليعمل على اى نوع بيانات أولي:

template <typename t>
inline void rev_order(t& value)
{
    union u_t
    {
        t e1;
        uint8 e2[sizeof(t)];
    } *tmp = (u_t*)&value;

    int len = sizeof(t) >> 1;
    int frm = 0;
    int to  = sizeof(t) - 1;

    do
    {
        uint8 c = tmp->e2[frm];
        tmp->e2[frm++] = tmp->e2[to];
        tmp->e2[to--] = c;
    } while(--len);
}

مثال لإستخدامه:

uint32 x = 0x78563412;
rev_order<uint32>(x); // x = 0x12345678

و الله ولي التوفيق

4

مدونتي: C++ Tips and Tricks

#3

ملاحظة بسيطة أخى فى القطعة التالية فية خطأ غير مقصود منك أرجو تعديلة :

يوجد مصطلحين تحتاج لأن تكون على دراية بهم و هم Least Significant و Most Significant و يمثلوا بداية و نهاية سلسلة معينه فمثلا إذا قلت Least Significant Bit فأنت تعني أول بت داخل سلسلة من البتتات و بالمثل إذا قلت Least Significant Byte فأنت تعني أول بايت فى سلسلة من البايت.

 

أعتقد أن كنت تقصد

قلت Least Significant Bit فأنت تعني أول بت داخل سلسلة من البتتات و بالمثل إذا قلت Most Significant Byte فأنت تعني أخر بايت فى سلسلة من البايت.

تم تعديل هذه المشاركة بواسطة مستر برمجة في 21 أغسطس 2013 في 23:43

||||||||||||||||||||||||||||||||||||


||||||||||||||||||||||||||||||||||||


egypt_coa.gif


||||||||||||||||||||||||||||||||||||


||||||||||||||||||||||||||||||||||||

#4
اقتباس
عندما نتكلم عن قيمة رقميه فبدايتها هو قيمة خانة الأحاد و نهايتها هو قيمة أخر خانه بالرقم، بالمثل مع الرقم الذى سنستخدمه فى الشرح الأن حيث البايت الأول هو 78h و اسمه Least Significant Byte و الرقم 12h هو البايت الأخير و أسمه Most Significant Byte، هذه التسمية مطلقة و لا علاقة لها بالذاكرة.
اقتباس

هل معنى ذلك أنة يتم قراءة مجموعة البتات من اليمين إلى الشمال (هل هذا الافتراضى عند التعامل مع البتات فى موضوع التعامل مع الملفات).

لاهذه الوسيلة المطلقه لقراءة القيم و يتم إستخدامه كمرجع مع اى اسلوب لترميز القيم فى الذاكرة و قد ذكرت لك منهم إثنين و هم Little-endian و Big-endian و يوجد أخر لم أذكره أسمه Middle-endian.

 

 

اقتباس

ملاحظة بسيطة أخى فى القطعة التالية فية خطأ غير مقصود منك أرجو تعديلة :

يوجد مصطلحين تحتاج لأن تكون على دراية بهم و هم Least Significant و Most Significant و يمثلوا بداية و نهاية سلسلة معينه فمثلا إذا قلت Least Significant Bit فأنت تعني أول بت داخل سلسلة من البتتات و بالمثل إذا قلت Least Significant Byte فأنت تعني أول بايت فى سلسلة من البايت.

 

 

عرفت مكان الخطأ، و لكن ما هو؟

 

 

و الله ولي التوفيق

مدونتي: C++ Tips and Tricks

#5

جزاك الله كل خير أخى C++er على تعبك معايا.

 

لكن للاسف لازالت لم أفهم الموضوع بشكل 100%, الموضوع ملخبط شواية .

وبالذات موضوع (القراءة والكتابة).

 

طيب نعطى مثال أخر

يعنى لو العدد كما هو مكتوب فى موقع الويكيبيديا (0x424D)

وهذا العدد داخل ملف BMP

ويستخدم هذا الملف أسلوب Little-endian

والمعالج الخاص بجهازى intel, وشركة intel تستخدم أسلوب Little-endian أيضا .

  • فكيف يكون شكل هذا الرقم على جهازى عند قراءتة, وكيف أكتبة أيضا إذا أردت إضافة بيانات داخل ملف bmp ؟

تم تعديل هذه المشاركة بواسطة مستر برمجة في 21 أغسطس 2013 في 23:48

||||||||||||||||||||||||||||||||||||


||||||||||||||||||||||||||||||||||||


egypt_coa.gif


||||||||||||||||||||||||||||||||||||


||||||||||||||||||||||||||||||||||||

#6
اقتباس

المشكلة هى أن عندى النظامين بيتم قراءتهم بالعكس بمعنى لو فرضنا عندنا رقم 0x1020

 

  عند قراءة الرقم السابق من ملف يستخدم نظام Big-Endian يتم قراءتة بهذا الشكل (0x2010)  

أيضا قراءة الرقم نفسة من ملف يستخدم نظام Little-Endian بيتم قراءتة بهذا الشكل (0x2010)

يعنى النظامين لازم أعكسهم عشان يبقى (0x1020) .

 

 

جزء الـbig-endian غير صحيح، لو كان في الملف الرقم 0x1020 فعند قراءته ستحصل أيضاً على 0x1020 وليس 0x2010.

 

هذا تقريباً تعريف وتلخصيل للـ

 

الرقم 0x1122 اذا كتبته لملف وظهر في الملف (الملف صورة لما في الذاكرة) 0x22 0x11، فهذا يعني أن لديك little-endian (ماتراه، معكوس).

 

الرقم 0x1122 اذا كتبته لملف وظهر في الملف (الملف صورة لما في الذاكرة) 0x11 0x22، فهذا يعني أن لديك big-endian (ماتراه، هو ماتحصل عليه).

 

-----------------------------------------------------------------------------------------------

 

عند القراءة والكتابة بين حاسوبين يستخدمان نفس الترتيب:

 

في الـlittle-endian لديك 0x1122 عند كتابته سيظهر 0x22 0x11 عند اعادة قراءته سيعود مرة أخرى 0x1122 (نفس الرقم).

 

في big-endian لديك 0x1122 عند كتابته سيظهر 0x11 022 عند اعادة قراءته سيعود مرة أخرى 0x1122 (نفس الرقم).

 

-----------------------------------------------------------------------------------------------

 

عند القراءة والكتابة بين حاسوبين يستخدمان عكس الترتيب:

 

لديك 0x1122 كتبتها من حاسوب little-endian لملف سيظهر 0x22 0x11 عندما يقرأها صديقك الذي يستخدم حاسوب big-endian سيظهر لديه الرقم 0x2211 (رقم مختلف).

 

لديك 0x1122 كتبتها من حاسوب big-endian لملف سيظهر 0x11 0x22 عندما يقرأها صديقك الذي يستخدم حاسوب little-endian سيظهر لديه أيضاً الرقم 0x2211 (رقم مختلف).

 

إذاً قلب البيانات لازم فقط عند التخاطب بين حاسوبين يستخدمان ترتيب مختلف.

تم تعديل هذه المشاركة بواسطة Mr.B في 22 أغسطس 2013 في 00:15

2
#7

هذا ما حدث بالفعل أخى Mr.B ما هو موجود فى الفقرة التالية.

 

اقتباس

لديك 0x1122 كتبتها من حاسوب big-endian لملف سيظهر 0x11 0x22 عندما يقرأها صديقك الذي يستخدم حاسوب little-endian سيظهر لديه أيضاً الرقم 0x2211 (رقم مختلف).

 

ذكر فى موقع Wikipedia أن اول 2 بايت (0x424D) هى قيمة للتأكد من أن هذا الملف هو ملف BMP.

وعند فتح ملف الـ BMP فى محرر Hex editor يظهر الـ 2 بايت بهذا الشكل (0x424D), لكن عندما أقوم بقراءة هذة القيمة من الملف, تظهر بهذا الشكل (0x4D42), إذن وبناء على الفقرة السابقة اللى أنت كتبتها أن هذا الملف يستخدم Big-Endian وليس Little-Endian كما ذكر موقع Wikipedia أن ملفات الـ BMP تستخدم نظام الـ Little-Endian .

 

إذن المشكلة عند مين بالظبط, هل الخطأ عندى ولا الخطأ عند Wikipedia .

تم تعديل هذه المشاركة بواسطة مستر برمجة في 22 أغسطس 2013 في 01:44

||||||||||||||||||||||||||||||||||||


||||||||||||||||||||||||||||||||||||


egypt_coa.gif


||||||||||||||||||||||||||||||||||||


||||||||||||||||||||||||||||||||||||

#8

البيانات ظهرت بالعكس (رأيته في 424D ثم قرأته كرقم ظهر 0x4D42) هذا يعني أن حاسوبك little-endian، تذكر:

 

ترتيب بايتات الرقم عكس ترتيبها في الملف -> little-endian

ترتيب بايتات الرقم يطابق ترتيبها في الملف -> big-endian

تم تعديل هذه المشاركة بواسطة Mr.B في 22 أغسطس 2013 في 02:03

#9
اقتباس
ذكر فى موقع Wikipedia أن اول 2 بايت (0x424D) هى قيمة للتأكد من أن هذا الملف هو ملف BMP.

موقع Wikipedia  لم يذكر أن اول 2 بايت هم 0x424D و لكن ذكر ان اول 2 بايت هم 0x42 0x4D و الفرق كبير بينهم حيث الأول هو قيمة رقمية و الثاني هو سلسلة من البايت، هذه السلسلة عند قرائتها من الملف بنظام Little-Endian تعطيك القيمة 0x4D42

 

 

و الله ولي التوفيق

مدونتي: C++ Tips and Tricks

#10

أخى Mr.B

 

بالفعل أخى أنا أعرف أن جهازى هو Little-Endian, وهذا لا أريد أن أثبتة.

لكن أنا أريد أن أثبت بناء على ما قلتة

 

اقتباس

 

لديك 0x1122 كتبتها من حاسوب big-endian لملف سيظهر 0x11 0x22 عندما يقرأها صديقك الذي يستخدم حاسوب little-endian سيظهر لديه أيضاً الرقم 0x2211 (رقم مختلف).

 

أن ملف الـ BMP اللى قرات منة البيانات يستخدم Big-Endian وليس Little-Endian كما تقول أيضا ويكيبيديا أن ملفات BMP تستخدم Little-Endian, لان لو كان Little-Endian كان قراءة بشكل سليم وليس معكوس, مثل ما قلت سابقا فى الفقرة التالية      

 

اقتباس

 

في الـlittle-endian لديك 0x1122 عند كتابته سيظهر 0x22 0x11 عند اعادة قراءته سيعود مرة أخرى 0x1122 (نفس الرقم).

 

هل كلامى صح أن ملف الـ BMP اللى قرات منة البيانات يستخدم Big-Endian وليس Little-Endian ؟

 

 

========================================================

 

 

 

شكرا أخى C++er على هذة الملحوظة

 

لكن هل يوجد فرق لو قراتهم 2 بايت مع بعض أو قراتهم بايت بايت.

 

يا ريت لو سمحت توضح لى الفرق

 

  • لو وجت قيمة بهذا الشكل

0x424D

  • ولو وجت قيمة بهذا الشكل

0x42 0x4D

 

كيف اتعامل مع كل واحدة منهم فى القراءة والكتابة (لان أنا لما شوفت القيمة أعتقدت أن الشكلين زى بعض) . لذلك أرجو التوضيح بشكل أكثر تفصيلا .

تم تعديل هذه المشاركة بواسطة مستر برمجة في 22 أغسطس 2013 في 03:04

||||||||||||||||||||||||||||||||||||


||||||||||||||||||||||||||||||||||||


egypt_coa.gif


||||||||||||||||||||||||||||||||||||


||||||||||||||||||||||||||||||||||||

مواضيع مشابهة