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

الأسمبلي في كل مكان حتى في ++c !

مغلق
بدأه Khaled.Alshaya في 28 ديسمبر 2007 · 12 رد · 1,435 مشاهدة · في لغة C و ++C
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم ,,

الحقيقة أحياناً يحتار الشخص الذي يبرمج في ++C, المفروض أن هذه اللغة هي لغة عالية المستوى, و لكنها تبقى دائماً في منتصف الطريق بين اللغات العالية و اللغات المنخفضة المستوى,

لربما كان الكلام السابق نظرياً, و لكنه يظهر في العديد من الأماكن.

سوف أقوم بطرح مثال و سأنتظر الإجابة من أي شخص يريد المشاركة :

تصور أنك تريد كتابة التالي في ++C :

struct Record{
	char x;
	int  y;
};

طبعاً حجم الـ Struct هو مجموع حجم المتغيرات فيه, هذا يعني أن الـ Record في الأعلى حجمه هو حجم المتغير char زائداً حجم المتغير int و هذا يعني 4 + 1 و التي تساوي 5 bytes ...

إلى هنا كل شيء لا غبار عليه,,

قم بتعريف نسخة من الـ Record :

	Record R;

و الآن قم باستخدام sizeof للحصول على حجم الـ Record بالـ Bytes :

	cout << sizeof(R);

جرب الكود و ستكتشف أنه بدلاً من طباعة الحجم الأصلي 5 bytes, سيظهر لك على الشاشة أن حجم الـ Record يساوي 8 bytes !!!!

من أين جاءت الـ 3 bytes هذه ؟؟؟

هذا ما أنتظر إجابته منكم ...

تحياتي ,,

#2

جميل جدا، عندما تجد مشرف يسال :lol: والهدف من السؤال ليس السؤال

Khaled.Alshaya كتب:
من أين جاءت الـ 3 bytes هذه ؟؟؟

لست خبير C أو CPP لكن على ما اعتقد هذا ما يسمى padding bytes لا اعرف الكلمة بالعربية!

والسلام عليكم ورحمة الله

d4baa0.gif
#3

أهلاً بالأخ العزيز :)

بس المشكلة انك حرقت السؤال :D

الحقيقة إجابتك صحيحة, و توقعت أن تكون تأتي الإجابة بسرعة خصوصاً من مبرمجي الأسمبلي :D

عموماً لي عودة لإعطاء فكرة حول الموضوع و متى يحصل هذا الشيء في ++C ,, إلى ذلك الوقت يمكن للجميع أن يضعوا شرحاً أوسع حول الموضوع..

تحياتي ,,

#4

أول مره أنتبه لهذا الأمر ... جزيت خيرا :) .

وهنا نجد نفس المشكله:

http://www.codeguru.com/forum/showthread.php?t=276622

لكن في انتظار شرحك طبعا :) .

دمت بخير أخ خالد .

http://informatic-ar.com منصة تعليمية عربية في علوم الحاسب والبرمجة

https://moalfat.com  للكتب الالكترونية والكورسات التعليمية

Everything we see now is just an engineering solution based on old science

#5

جربت كذا طريقه وكذا نوع من المتغيرات وجدت فعلا ان القياس يظهر غير المتوقع

لكن اذا كانت المتغيرات من نفس النوع النتيجه تكون مطابقه.

tvquran_6.gif

#6
اقتباس
لكن اذا كانت المتغيرات من نفس النوع النتيجه تكون مطابقه

بسبب الـAlignment ؟؟؟

mov eax, dword ptr ds:[0xffdf0308]

jmp dword ptr [eax+0xfc]

#7

حسناً, أعتقد أن الموضوع أصبح مربك :wacko:

لابد من معرفة أن ++C تتبع مواصفات قياسية, و جميع المترجمات تتبع هذه المواصفات, و لكن هذا النوع من المسائل يعتمد على النظام / المعالج / المترجم الذي تستخدمه,

المواصفات القياسية للغة تترك هذا النوع من المشاكل للمترجم لكي يقوم بحله بطريقته الخاصة,

المشكلة تبدأ عند المعالج, رغم أن الـ Byte هو أصغر وحدة تخزين لكنه ليس أصغر وحدة يمكن للمعالج أن يتعامل معها!

على سبيل المثال معظم المعالجات الحديثة تتعامل بنمط 32-bits و هذه المعالجات تحتوي على Registers كل منها 32-bit ,,

لذلك عند كل عملية قراءة, يقوم المعالج بقراءة 4 bytes في كل مرة, لذلك فالتعامل مع النوع int على سبيل المثال في ++C أسرع من التعامل مع النوع char :mellow:

و بالتالي فعناوين المتغيرات يجب أن تكون من مضاعفات الـ 4, و هو في الحقيقة حجم المؤشرات أو عناوين الذاكرة :rolleyes:

الحقيقة أن المشكلة أكبر من ذلك و تتعلق بكيفية تخزين الأعداد في الذاكرة إما big-endian أو little-endian و هذا الموضوع يحتاج إلى شرح طويل

ولكن تخيل أن لدينا المتغير char في الذاكرة يقع عند عنوان فردي :

0
1
2	 'a' // 'a' = 0x41
3
4
5
6
7

إذا أراد المعالج أن يقرأ شيئاً ما, فيمكنه البدء عند العنوان 0 أو 3 و هكذا, فالمعالج إما أن يعتبر أن العدد يبدأ عند العنوان صفر و ينتهي عند العنوان 3 , أو العكس بحيث يكون العنوان 3 هو البداية و الـ 0 هي النهاية, و هذه تعتمد على بنية المعالج,

و تسمى Endianness, فالـ register في المعالج إما أن يكون الـ char فيه أول byte أو آخر byte , أما إن كان في المنتصف كما في حالتنا فستقع المشكلة ,

 a
|_|_|_|_|

		  a
|_|_|_|_|

أما في حالتنا في الأعلى :

	a
|_|_|_|_|

	   a
|_|_|_|_|

هنا تقع المشكلة حيث تكون البيانات في المنتصف!!

الحقيقة وجدت pragma في ++VC تمكننا من تعطيل عملية الـ alignment و هذا كما قلت سابقاً يعتمد على المعالج - معالج intel يسمح بهذا الشيء - و لم أجد طريقة في gcc للغة ++C,

أعرف أن كلامي ليس واضحاً كفاية و لكنه كل ما أعرفه عن هذا الأمر, أتمنى أن يفيدنا الأخوة أكثر ,,

تحياتي ,,

#8

المشكله حتى اذا غيرت فقط ترتيب المتغيرات تتغير القيمه لاحظ المثال التالي:

#include <iostream>
using namespace std;
struct ex1
{
char a;
int b;
char c;
char d;
} s;
struct ex2
{
char a;
char c;
char d;
int b;
} f;
int main(){
 cout<<"sizeof(s) :" <<sizeof(s)  <<endl;
 cout<<"sizeof(f) :"<< sizeof(f)  <<endl;
return 0;
}
النتائج تظهر بالشكل التالي:
CONSOLE

sizeof(s) :12
sizeof(f) :8
Press any key to continue . . .


فلماذا هذا الاختلاف ؟
أما من يريد منع ظهور هذه المشكله فقط يضيف السطر التالي الى اول برنامجه:
#pragma pack(1)
tvquran_6.gif

#9

السلام عليكم,,

أهلاً بك أخ arabi ,,

لاحظ أن عملية الـ alignment تتم عند وجود متغيرات مجموع حجمها أصغر من 4 bytes ,,

في ردي السابق, أعتقد أني خلطت قليلاً بين أمرين :

المترجم يقوم بإضافة الـ bytes عندما تكون المتغيرات المعرفة وراء بعض لا تؤدي إلى عنوان صحيح بالنسبة له, مثالك جيد :

struct ex1
{
char a;
int b;
char c;
char d;
} s;

هنا سيفسرها المترجم كالتالي :

1 + (3) + 4 + 1 + (3) + 1 + (3)

حيث الـ bytes بين الأقواس هي التي يضيفها المترجم لكي يحصل على عنوان سليم,

أما في المثال الثاني :

struct ex2
{
char a;
char c;
char d;
int b;
} f;

هنا سيفسرها كالتالي :

1 + 1 + 1 + (1) + 4

ما بين الأقواس هو ما أضافه المترجم,

أما بالنسبة للـ :

#pragma pack(1)

فهي خاصة بـ ++VC فقط, أما في gcc فتتوفر طريقة لتعطيل الـ alignment في مترجم الـ C فقط و ليس الـ ++C, أضف إلى ذلك أن تعطيل خاصية الـ alignment سيؤدي إلى بطئ عملية القراءة بالنسبة للمعالج ( البطئ غير ملحوظ طبعاً ),

تحياتي ,,

#10

السلام عليكم

المشكلة تتكون من جزئيتين..

الجزئية الاولى هى استهلاك الذاكرة والجزئية الثانية هى السرعة..

بالنسبة للجزئية الاولى ناخذ مثال اخونا خالد

struct Record{
	char x;
	int  y;
};

هنا يحاول الكمبايلر ان يضع المتغيرات فى عناوين قابلة القسمة على 4, لان قرائة 4 بايت ياخذ دورة واحدة, بمعنى ان المعالج يستطيع ان يحضر 4 بايت (word) دفعة واحدة بنفس السرعة التى يستطيع ان يحضر بها بايت واحد..

فى هذا المثال بالذات سرعة احضار البيانات لن تتغير لان محتوى الstruct هو خمسة بايت ولابد انه سوف يستهلك 2 word لكل متغير يعنى السرعة ستكون دورتين لاحضار متغير واحد..

لذلك إذا قمنا بوضع

#pragma pack(1)

سوف يتم استغلال الذاكرة بشكل اكبر دون ان نفقد شيئ فى سرعة الاداء..

مثال اخر

struct Record{
	char x;
	int  y;
int w;
};

هنا اصبح لدينا مشكلة لو وضعنا بداية كل متغير كل متغير فى عنوان قابل القسمة على 4 سوف نخسر 1 بيات مقابل كل متغير, وإذا جعلناها متتالية كلمثال الاول سوف نفقد الكثير من السرعة, وتحديداً نستطيع ان نقول ان سرعة قرائة المتغيرات سوف يستغرق 50% اكثر من ما إذا قمنا بعمل

#pragma pack(4)

طبعاً هذا يعنى انه عندما يتم وضع المتغير رقم 1 يقفز المعالج لعنوان قابل القسمة على 4 ويضع المتغير رقم 2 وهكذا..

والسلام عليكم

تم تعديل هذه المشاركة بواسطة احمد غريب في 28 ديسمبر 2007 في 22:05

لا إله إلا الله محمد رسول الله

busbar : يجب ان تدرك انه هناك حد ادنى للمعرفة المطلوبة قبل البدء في عمل أي شئ.

#11

السلام عليكم ,,

أخيراً وصل الأخ أحمد :rolleyes:

الحقيقة لدي سؤال يحيرني ,,

ما الفرق بين استخدام Zp عند الترجمة, أذكر أني كنت أستخدمها عندما أريد أن أخبر المترجم أن لا يقوم بعمل alignment في الـ Stack,

و استخدام pragma pack في الكود ؟

شكراً لك أخ أحمد على الإضافة القيمة ,,

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 28 ديسمبر 2007 في 22:16

#12

السلام عليكم

اخى خالد بالنسبة لسؤالك فى لغة الاسمبلي واعتقد انه نفس الشيئ بالنسبة لمترجمات السي فهذا النوع من الشفرة يسمى compiler directive ويمكنك ان تضعه إما فى داخل الشفرة او فى سطر الاوامر عند عمل الترجمة.

والسلام عليكم

لا إله إلا الله محمد رسول الله

busbar : يجب ان تدرك انه هناك حد ادنى للمعرفة المطلوبة قبل البدء في عمل أي شئ.

#13

شكراً جزيلاً أخ أحمد,,,

للأسف لم أستخدم ++VC و لأول مرة ألقي نظرة على MSDN :S و يبدو أن شد الرحال إلى microsoft سيتم قريباً :blush: , و خصوصاً مع الـ documentation الفاشل في :mellow: gcc ,,

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 28 ديسمبر 2007 في 22:39

هذا الموضوع مغلق.

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