PDA

View Full Version : سوال: مشکل در درک نحوه خروجی یک برنامه ؟؟



ehp_kz6597
شنبه 01 مرداد 1390, 10:18 صبح
سلام
یه تست کوچیک کردم ولی نتیجش منو درگیر خودش کرد
دردوحالت trace کردم
1- یه کد ساده نوشتم و توش یه متغیر از نوع int تعریف کردم
2- تو همون کد یه متغیر دیگه از نوع string تعریف کردم
و بدون مقدار دهی اولیه مقدارهاشون رو نمایش دادم

نتیجه :
در حالت 1: یه عدد که احتمالا شماره اون خونه از حافظه است که برای متغیراختصاص داده
درحالت 2 : این عدد صفر شد !!!

چرا درحالت دوم این اتفاق افتاد ؟

BORHAN TEC
شنبه 01 مرداد 1390, 10:41 صبح
متغیر سراسری است یا محلی؟ آیا این متغیر به عنوان یک شمارنده حلقه عمل می کند؟
به هر حال اگر کد را به صورت کامل قرار دهید بهتر می توان بر روی این مسئله بحث کرد. :چشمک:

ehp_kz6597
شنبه 01 مرداد 1390, 17:46 عصر
يه کد خيلي ساده بود
چون کد رو ذخيره نکرده بودم دوباره نوشتم (تو عکسهای جدید معلومه ) و اينبار به يه چيز جالب تر برخوردم :متعجب:

جواب در سه حالت زیر متفاوت است :
اگه روي btn کليک کنم : 1635841
space رو بزنم : 1636097
اگه Enter بزنم : 1

از همه مهمتر اینکه این کد رو با Delphi7 تست کردم در هر سه حالت یک عدد بدست اومد :متفکر:

Felony
شنبه 01 مرداد 1390, 19:44 عصر
متغییری که شما تعریف کردید یک متغییر محلی هست ، متغییرهای محلی ( عددی ) به صورت خودکار مقدار دهی نمیشن و ممکنه داخلشون هر چیزی باشه ...

ehp_kz6597
یک شنبه 02 مرداد 1390, 09:07 صبح
متغییری که شما تعریف کردید یک متغییر محلی هست ، متغییرهای محلی ( عددی ) به صورت خودکار مقدار دهی نمیشن و ممکنه داخلشون هر چیزی باشه ...
احتمالا شما پست بعدی رو ندیدید

اون چیزی که شما میگی ، ازش اطلاع دارم ولی در مورد زیر چه دلیلی وجود داره ؟؟؟؟


جواب در سه حالت زیر متفاوت است :
اگه روي btn کليک کنم : 1635841
space رو بزنم : 1636097
اگه Enter بزنم : 1

از همه مهمتر اینکه این کد رو با Delphi7 تست کردم در هر سه حالت یک عدد بدست اومد

Felony
یک شنبه 02 مرداد 1390, 10:46 صبح
اون چیزی که شما میگی ، ازش اطلاع دارم ولی در مورد زیر چه دلیلی وجود داره ؟؟؟؟
اون چيزي که شما ميبيني غير عادي نيست و دلیلش هم چندان پیچیده نیست ؛

کامپايلر از ثبات ebx براي نگهداري مقدار متغيير a استفاده ميکنه ، تا زماني که جناب عالي روي دکمه کليک نکردي و رويداد OnClick اجرا نشده متغيير a هم تعريف نشده پس به ثبات ebx تا زماني که روي دکمه کليک نکردي نيازي نداره ؛ تا قبل از اينکه روي دکمه کليک کني و رويداد OnClick دکمه رو صدا بزني Message Handler شئ Button از همين ثبات ebx براي نگهداري اشاره گر مربوط به خودش استفاده ميکنه ، بعد از اينکه يک تابع API توسط شئ Button فراخواني شد ( جناب عالي روي دکمه کليک کردي یا Enter و ... رو زدی ) اين مقدار ( اشاره گر مربوط به Message Handler دکمه ) در ثبات eax جايگزين ميشه تا مقدار فعلی از بین نره و ebx آماده براي استفاده متغيير a بشه ، چون متغييرهاي محلي به صورت خودکار مقدار دهي اوليه نميشن مقدار قلبي ثبات reset نميشه و ebx حاوي اشاره گر مربوط به Button ميمونه ؛ حالا در آخرین بار که Message Handler شئ Button از ebx استفاده کرده ممکنه هر پیغامی پردازش شده باشه و هر چیزی در ebx درج کرده باشه ، مثلا اون مقدار 1 که با زدن Enter نمایش داده میشه مقدار برگشتی در Message Handler شئ Button هست که پشت صحنه VCL بعد از اجرای روتین های خودش اون کد رو تولید میکنه و در ebx قرار میده و در آخر هم شما به a مقدار اولیه ندادی و در حقیقت ebx رو مقدار دهی نکردی پس آخرین مقدار داخل ebx به شما نمایش داده میشه .

حالا وقتی شما روی دکمه کلیک میکنی یا هر کار دیگه ای ، Message Handler یک پیغام دیگه دریافت و پردازش میکنه و پشت صحنه VCL و کامپایلر ممکنه هر کاری انجام بده و ممکنه هر چیز دیگه ای داخل ebx باشه ، OK ؟

یه نمونه کد هم نوشتم که بفهمی چی میگم :

var
a: Integer;
begin
asm
mov a, ebx;
end;
ShowMessage(IntToStr(a));
ShowMessage(IntToStr(Integer(Button1)));
end;

کد بالا مقدار ثبات ebx رو میریزه تو متغییر a و نمایشش میده و بعد از اون هم مقدار اشاره گر مربوط به Button1 که کد رو توش نوشتی نشون میده ، جفتشون یکیه درسته ؟ این ثابت میکنه که Button1 از ebx برای نگهداری اشاره گر مربوط به خودش استفاده میکنه .

اگر میخوای دقیقا ببینی پشت VCL چی میگذره ، از منوی Tools گزینه Option رو انتخاب و به قسمت Library برو ، در این قسمت پوشه مربوط به کتابخانه های VCL رو Add کن تا وقتی کد رو debug میکنی سورس VCL هم دیباگ بشه ، البته Debug کردن سورس VCL کار بسیار وقت گیر و پیچیده ای هست .

- پوشه مربوط به کتابخانه های VCL در نسخه های مختلف دلفی ممکنه در زیر پوشه های متفاوتی قرار داشته باشه ، آدرس این پوشه برای Delphi XE :


C:\Program Files (x86)\Embarcadero\RAD Studio\8.0\source\vcl

موفق باشید .

Mahmood_M
یک شنبه 02 مرداد 1390, 15:52 عصر
اون چيزي که شما ميبيني غير عادي نيست و دلیلش هم چندان پیچیده نیست ؛
تحلیل این موضوع به این سادگی ها نیست !

کامپايلر از ثبات ebx براي نگهداري مقدار متغيير a استفاده ميکنه
متغیرهای Local در Stack ذخیره می شن ، رجیستر ebx و اشاره گر ebp برای ذخیره و بازیابی مقادیر متغیرها از Stack استفاده می شن ( به کمک اشاره گر Stack یا esp )
وقتی شما چند متغیر رو به صورت Local تعریف میکنید ، این متغیرها با استفاده از اشاره گر ebp به Stack فرستاده میشن و برای هر کدوم یک خانه ی حافظه با اختلاف 4 بایت ( 32 بیت ) درنظر گرفته میشه ، اگر شما یک متغیر دو بایتی ( مثل WORD ) هم تعریف کنید ، باز هم یک خانه ی حافظه ی 4 بایتی براش درنظر گرفته میشه ، دلیل این موضوع DWord-Aligned بودن حافظه هست ، در مورد DWord-Aligned قبلا بحث شده (http://barnamenevis.org/showthread.php?209318-منظور-از-كلمه-packed-چيست؟)
البته متغیرهای پیچیده تر مثل String یا Array یا ... در Heap ذخیره می شن و فقط اشاره گری از اونها در Stack قرار میگیره
مکان این متغیرها در یک Stack Frame و زیر هم هست ، با فاصله ی 4 بایت ، مکان این متغیرها هم توسط ebp مشخص میشه ، مثلا اگر دستور زیر رو اجرا کنید :

procedure TForm1.BitBtn1Click(Sender: TObject);
var
a, b, c : Integer;
begin
asm
mov a, ebx;
mov b, ebx;
mov c, ebx;
end;
end;

و در هنگام Debug کردن قسمت CPU > Entire CPU رو مشاهده کنید ، برای کدهای بالا مقدار Assembly زیر رو می بینید :


Unit1.pas.31: mov a, ebx;
004A689A 895DFC mov [ebp-$04],ebx
Unit1.pas.32: mov b, ebx;
004A689D 895DF8 mov [ebp-$08],ebx
Unit1.pas.33: mov c, ebx;
004A68A0 895DF4 mov [ebp-$0c],ebx

ما در کد خودمون مقدار EBX رو به هر 3 متغیر نسبت دادیم ، و در کد تولید شده هم مقدار EBX در خانه ی حافظه ی EBP با اختلاف 4 بایت برای هر متغیر قرار گرفت
برای درک این موضوع باید با Stack Frame آشنا باشید ، و بدونید که چه مقادیری در یک Stack Frame ذخیره میشه و آدرس یک متیغر در Stack چطور مشخص میشه
...
اگر مقدار متغیرهای تعریف شده برابر با ebx باشه ، مقدار همشون در شروع کار باید یکسان باشه ، که اینطور نیست

حالا وقتی شما روی دکمه کلیک میکنی یا هر کار دیگه ای ، Message Handler یک پیغام دیگه دریافت و پردازش میکنه و پشت صحنه VCL و کامپایلر ممکنه هر کاری انجام بده و ممکنه هر چیز دیگه ای داخل ebx باشه ، OK ؟

...

یه نمونه کد هم نوشتم که بفهمی چی میگم :
کدی که در اینجا (http://stackoverflow.com/questions/6800996/result-in-simple-code-when-variables-are-uninitialized) براتون به عنوان نمونه گذاشته شده با کدی که خودتون اینجا قرار دادید فرق میکنه !
در اونجا از دو Procedure جدا استفاده شده ، کدی که شما قرار دادید مقدار مشابهی برای دو پیغام نمایش نمیده ، چون اونها به یک خانه از حافظه اشاره نمی کنند ، بلکه در زیر همدیگه با یک خانه اختلاف قرار دارند ، وقتی از دو Procedure جدا استفاده میشه برای هر Procedure مقادیر Stack دوباره مقداردهی میشه و به خاطر همینه که نتیجه یکسانی نمایش داده میشه ، این موضوع ربطی به اینکه متغیر مقدار ebx رو نشون میده نداره ، بلکه مقدار ebx برابر با اولین خانه ی Stack هست و اولین متغیری که تعریف میشه هم مقدارش درون اولین خانه ی Stack قرار میگیره

..............................

راستش من در دلفی 7 و 2010 امتحان کردم ولی توی هیچکدوم همچین مشکلی ندیدم ! ، شما از کدوم نسخه ی دلفی استفاده می کنید ؟ ، از چه ویندوزی استفاده میکنید ؟
همچین چیزی اصلا منطقی نیست ، مگر اینکه این یک باگ باشه یا اینکه در جریان نمایش پیغام Stack ( خانه ی مربوط به متغیر در Stack ) دستکاری بشه و مقدارش تغییر کنه

Felony
یک شنبه 02 مرداد 1390, 16:12 عصر
کدی که در اینجا براتون به عنوان نمونه گذاشته شده با کدی که خودتون اینجا قرار دادید فرق میکنه !
جایی نگفته بودم که اون کد رو اینجا قرار دادم !


این موضوع ربطی به اینکه متغیر مقدار ebx رو نشون میده نداره ، بلکه مقدار ebx برابر با اولین خانه ی اول Stack هست و اولین متغیری که تعریف میشه مقدار برابر با اولین خانه ی Stack هست
کدی که در بالا نوشتم دقیقا مقدار یکسانی تو سیستم من نمایش میده ، این هم که میگید مقدار ebx برابر با اولین خانه stack هست و اینکه اولین خانه stack برابر با اولین متغییری که تعریف میشه پس کد زیر باید نتیجه ای متفاوت داشته باشه :

procedure TForm1.Button1Click(Sender: TObject);
var
anInt: Integer;
a: Integer;
begin
asm
mov a, ebx;
end;
ShowMessage(IntToStr(a));
ShowMessage(IntToStr(Integer(Button1)));
end;

ولی نتیجه همچنان یکسان هست .

منظورتون همین بود یا من اشتباه متوجه شدم ؟


راستش من در دلفی 7 و 2010 امتحان کردم ولی توی هیچکدوم همچین مشکلی ندیدم ! ، شما از کدوم نسخه ی دلفی استفاده می کنید ؟ ، از چه ویندوزی استفاده میکنید ؟
دلفی XE و ویندوز 7 نسخه 64 بیت .


همچین چیزی اصلا منطقی نیست ، مگر اینکه این یک باگ باشه
منطقی نیست ولی همینطوره که تو پست اول ذکر شده ، من کل تاپیک های Embarcadero رو زیر و رو کردم ، کسی همچین باگی گزارش نداده ، به همین دلیل بود که به نظرم رسید شاید این یک مشکل نباشه و تو SOF پرسیدمش ، همونطور که میبینید اونجا هم همه همون نظر اولیه من و شما رو داشتن ( هر چیزی ممکنه داخل متغییری که مقدار دهی اولیه نشده باشه ) ولی یکی از کاربران اون پاسخی که دیدی داد و من با بررسی دیدم زمان Debug همون اتفاق میافته .

در هر صورت دارم میرم سوال رو تو Embarcadero مطرح کنم ببینم خودشون چی میگن ...

Mahmood_M
یک شنبه 02 مرداد 1390, 17:52 عصر
کدی که در بالا نوشتم دقیقا مقدار یکسانی تو سیستم من نمایش میده
احتمالا به خاطر 64 بیتی بودن ویندوز و 32 بیتی بودن کامپایلر دلفی هست
احتمالا هر دو مقادیر توسط کامپایلر در یک خانه ی حافظه قرار می گیرن ، در غیر اینصورت جواب یکسانی نباید داشته باشن و ندارن !

این هم که میگید مقدار ebx برابر با اولین خانه stack هست و اینکه اولین خانه stack برابر با اولین متغییری که تعریف میشه پس کد زیر باید نتیجه ای متفاوت داشته باشه
اون دستور که در سیستم من نتیجه ی متفاوتی داره ، ولی منظور این بود که دستور زیر دو پیغام یکسان رو نمایش میده :

procedure TForm1.Button1Click(Sender: TObject);
var
I1, I2 : Integer;
begin
asm
mov I1, ebx;
end;
ShowMessage(IntToStr(I2));
ShowMessage(IntToStr(I1));
end;

البته اول یا دوم بودن متغیرها ! به نحوه ی استفادشون هم بستگی داره ، برای درک بهتر کد اسمبلی تولید شده برای دستور بالا :

Unit1.pas.31: mov I1, ebx;
004B3274 895DFC mov [ebp-$04],ebx
Unit1.pas.33: ShowMessage(IntToStr(I2));
004B3277 8D55F8 lea edx,[ebp-$08]
004B327A 8BC3 mov eax,ebx
004B327C E8FFEEF5FF call IntToStr
004B3281 8B45F8 mov eax,[ebp-$08]
004B3284 E8CF4DFBFF call ShowMessage
Unit1.pas.34: ShowMessage(IntToStr(I1));
004B3289 8D55F4 lea edx,[ebp-$0c]
004B328C 8B45FC mov eax,[ebp-$04]
004B328F E8ECEEF5FF call IntToStr
004B3294 8B45F4 mov eax,[ebp-$0c]
004B3297 E8BC4DFBFF call ShowMessage
Unit1.pas.35: end;
004B329C 33C0 xor eax,eax

ابتدا توسط دستور mov ، مقدار ebx ریخته میشه در خانه ی [ ebp - 4 ]
برای دستور ShowMessage اول که مقدار I2 رو نمایش میده ، دستور LEA مقدار edx رو مربوط می کنه به خانه ی حافظه ی [ ebp - 8 ] ( یعنی مقادیری که به edx فرستاده میشن ، میرن به خانه ی ebp - 8 ) ، بعد توسط دستور mov مقدار ebx درون رجیستر eax قرار می گیره تا به عنوان ورودی دستور IntToStr استفاده بشه ، خروجی دستور در edx قرار می گیره که همون خانه ی حافظه ی [ ebp - 8 ] هست ، بعد مقدار این خانه ی حافظه درون eax قرار میگیره تا به عنوان ورودی ShowMessage استفاده بشه
برای دستور ShowMessage ای که مقدار I1 رو نشون میده همین عملیات انجام میشه ، اما به جای ebx از خانه ی حافظه ی زیری ( 4 بایت پایینتر ) یعنی [ ebp - 4 ] استفاده میشه و برای مقدار خروجی ( یا edx ) از خانه ی پایینتر از [ ebp - 8 ] ، یعنی [ ebp - 12 ] ( مقدار C در مبنای 16 برابر هست با 12 در مبنای 10 ) استفاده میشه
یعنی کامپایلر ebx رو به عنوان خانه ی اول برای متغیر I2 که ابتدا استفاده شد در نظر گرفت و خانه ی پایینی یعنی [ ebp - 4 ] رو برای متغیر I1 که بعد از متغیر I2 استفاده شد در نظر گرفت ، درواقع EBX آدرسش میشه EBP و متغیرها در خانه های پایینتر به فاصله ی 4 بایت قرار می گیرند


هر چیزی ممکنه داخل متغییری که مقدار دهی اولیه نشده باشه
این جمله درست نیست ! ، من همچین چیزی نگفتم ، مقدارش به Stack و خانه ی حافظه ای که درش قرار داره وابسته هست

با توجه به موارد بالا مشکلی که به وجود میاد اصلا منطقی نیست ، مگر اینکه در سیستم های 64 بیت قضیه جور دیگه ای باشه یا کامپایلر 32 بیتی با ویندوز 64 بیتی سازگار نیست

Felony
یک شنبه 02 مرداد 1390, 18:19 عصر
به قول geek قوانین مورفی داره سر به سرمون میزاره !


اون دستور که در سیستم من نتیجه ی متفاوتی داره ، ولی منظور این بود که دستور زیر دو پیغام یکسان رو نمایش میده :
دستوری که نوشتید در سیستم من 2 پیغام کاملا متفاوت رو نمایش میده !


این جمله درست نیست ! ، من همچین چیزی نگفتم ، مقدارش به Stack و خانه ی حافظه ای که درش قرار داره وابسته هست
اون مقداری که نمایش داده میشه چیه ؟ مگه مقدار قبلی اون خانه از حافظه نیست ؟ مگه شما تو حالت عادی میدونی تو Stack چی بوده ؟ به همین دلیل گفتم هر چیزی ممکنه توش باشه چون ممکنه اشاره گر اون متغییر هر جایی از حافظه ست بشه و ما نمیدونیم اون مقدار قبلا مربوط به چی بوده !


با توجه به موارد بالا مشکلی که به وجود میاد اصلا منطقی نیست ، مگر اینکه در سیستم های 64 بیت قضیه جور دیگه ای باشه یا کامپایلر 32 بیتی با ویندوز 64 بیتی سازگار نیست
بهرته بگیم با منطق فعلی ما سازگار نیست ؛
فکر کنم همونطور که گفتی این قضیه در معماری 64 بیت متفاوت باشه یا ... ، هر چی هست به نظر باگ نمیاد چون همچین باگی میتونه خیلی خطرناک ظاهر بشه .

واجب شد سوال رو تو Embarcadero بپرسم ، فعلا که بانک اطلاعاتیش مشکل داره ، اولین فرصت این کار رو میکنم ببینم چی میشه ، شما هم اگر به موضوع جدیدی برخوردی لطفا همین جا اعلام کن ، این موضوع زیادی جالبه ...

@ کاش خدا بیامرز اینپی الان اینجا بود ... !