PDA

View Full Version : مقداردهی اولیه آرایه های دینامیکی هنگام تغییر بعد یا سایز



Hadizadeh
سه شنبه 01 فروردین 1385, 15:02 عصر
با سلام و تبریک سال نو - آیا راهی برای مقدار دهی اولیه آرایه های دینامیکی هنگام تغییر سایز آنها توسط دستور SetLength هست؟ به عنوان مثال می خواهیم وقتی که سایز یک آرایه دینامیکی رو تغییر می دهیم، تمام المان های جدید دارای مقدار صفر به صورت پیش فرض شوند؟ توجه کنید که نمی خواهیم از یک حلقه برای این منظور استفاده کنیم!

Inprise
چهارشنبه 02 فروردین 1385, 01:16 صبح
برای استفاده از دلفی : Fillchar در غیر این صورت : ZeroMemory .

Hadizadeh
چهارشنبه 02 فروردین 1385, 13:10 عصر
با تشکر از راهنمایی شما. فانکشن Fillchar برای این مشکل مناسب به نظر می رسه ولی نمی دونم برای آرایه های چند بعدی چه جوری باید ازش استفاده کرد. مثلا در کد زیر می خواهیم کل درایه های آرایه سه بعدی مقدار -1 بگیرند:


var Mat:array of array of array of integer;
SetLength(Mat,256,256,256);
//Initialize the array here- All elements must be set to -1

برای یادآوری تعریف پروسیجر Fillchar رو اینجا نوشتم:


procedure FillChar(var X;Count:Integer;Value:Byte);

البته از فانکشن FillMemory هم میشه استفاده کرد که یک تابع API هست. ولی متاسفانه نحوه استفاده از اونو هم نمی دونم!


VOID FillMemory(
PVOID Destination;//pointer to block to fill
DWORD Length, //size, in bytes, of block to fill
BYTE Fill // the byte value with which to fill
);

Inprise
چهارشنبه 02 فروردین 1385, 19:48 عصر
ZeroMemory‌ رو با توجه به سوال اولت پیشنهاد کردم . اگه مایلی مقادیر به عدد خاصی تغییر کنند باید از FillMemory استفاده کنی ، پارامتر اول : آدرس محل آرایه ات ، دومی سایز و سومی مقدار مورد نظر . ( برای پاس کردن آدرس در دلفی از تابع addr یا کنشگر @ استفاده میشه . سایز رو میتونی از sizeof بگیری )

Hadizadeh
پنج شنبه 03 فروردین 1385, 09:37 صبح
ممنون. ولی من کد زیر رو وقتی اجرا می کنم، جالبه که تابع sizeof مقدار 4 رو برای اون آرایه به اون بزرگی رو برمی گردونه! ثانیا دقت نکرده بودم ، مقدار value عدد منفی نمی تونه باشه!


FillMemory(@Mat,SizeOf(Mat),1);

Hadizadeh
پنج شنبه 03 فروردین 1385, 13:15 عصر
حالا مشکل چیه به نظر شما؟

vcldeveloper
پنج شنبه 03 فروردین 1385, 19:37 عصر
SizeOf(Mat^)

Hadizadeh
جمعه 04 فروردین 1385, 09:33 صبح
ممنون. ولی وقتی کد شما رو هم اجرا می کنم کامپایلر خطای زیر رو می گیره:


Pointer type required

به نظر شما تایپ Mat رو باید چی تعریف کنیم؟

Hadizadeh
جمعه 04 فروردین 1385, 13:12 عصر
آقای کشاورز و آقای Inprise نظرتون چیه؟ کار را کسی کرد که تمام کرد!

vcldeveloper
جمعه 04 فروردین 1385, 19:14 عصر
ببخشید، وقتی شما Mat را از نوع Array تعریف کردید، همون SizeOf(Mat( درسته و جوابی که من دادم درست نیست.
Dynamic Arrays بصورت Pointer نگه داری میشند، اما قاعدتا باید بشه با استفاده از SizeOf اندازه اشون رو بدست آورد (مثل نوع String). اما در مورد کد شما، چون خودتون حجم آرایه را با SetLength تنظیم می کنید، می تونید همون حجم را در FillMemory هم بنویسید.

Hadizadeh
جمعه 04 فروردین 1385, 23:18 عصر
آقای کشاورز ممنون از پاسخ شما.ولی من به جای سایز آرایه Mat عدد 256*256*256*4 رو گذاشتم ولی سیستم هنگ کرد! ببینید در حقیقت من می خوام راه حل سریعتری از روش حلقه های تودرتو برای مقدار دهی چنین آرایه های چند بعدی و بزرگی داشته باشم. حالا نظرتون چیه؟

Hadizadeh
شنبه 05 فروردین 1385, 13:08 عصر
بابا یکی به ما کمک کنه! یعنی این سوال خیلی سخته که میون این همه برنامه نویس حرفه ای داره بی پاسخ می مونه!

Hadizadeh
دوشنبه 07 فروردین 1385, 12:31 عصر
آقای نفیسی دمت گرم. خوشم اومد.اون ستاره هایی که گرفتی نوش جانت. همچنین از آقای کشاورز عزیز و آقای Inprise بزرگ هم ممنونم. مشکل حل شد!
سرعت Fillchar حرف نداره!

Hadizadeh
دوشنبه 07 فروردین 1385, 19:09 عصر
ولی متاسفانه مصل اینکه برای کد زیر مشکل همچنان پا برجاست:


type Lab=record
L,a,b:short integer;
end;
Mat:array of array of array of Lab;
...
begin
SetLength(Mat,256,256,256);
Fillchar(Mat[0,0,0],length(Mat)*sizeof(Mat[0,0,0]),-1);
end;

متاسفانه length(Mat) عدد 256 رو بر می گردونه!

Inprise
سه شنبه 08 فروردین 1385, 03:11 صبح
وقتی یک آرایه بصورت دینامیک رشد میکنه ، معقول ترین روش برای دسترسی به اون ، نگهداری یک اشاره گر به اولین آفست آرایه در حافظه هست ؛ این یک مشکل نیست ، یک راه حله ، و دلفی همینطور عمل میکنه و قاعدتا" وقتی سایز "اشاره گری" به آرایه رو میگیری باید جوابت 4 بایت یا 32 بیت باشه .

برای آخرین کدی که نوشتی : یک آرایه سه بعدی متقارن داریم که هر خانه اش یک ستون سه عضوی از shortint تشکیل شده ، اندازه کل آرایه چقدر هست ؟ روتین length "تعداد" خانه های یک آرایه رو معین میکنه و اگر آرایه چند بعدی بهش پاس کنی فقط تعداد اولین بعد رو برمیگردونه ، که تو کد تو همیشه 256 خواهد بود ؛ علتش هم به وضوح روشنه ؛ این روتین برای رشته ها نوشته شده و بصورت طبیعی رشته های دلفی فقط یک بعد دارند پس تو برای محاسبه اندازه آرایه ات سایز واقعی رو باید خودت محاسبه کنی ؛

Hadizadeh
سه شنبه 08 فروردین 1385, 12:59 عصر
کاملا فرمایش شما صحیح هست. اما من کد زیر رو اجرا کردم، یک استثنای EAccessViolation می ده.


SetLength(Mat,256,256,256);
Fillchar(Mat[0,0,0],sizeof(Lab)*255*255*255,-1);

جالب تر اینکه اگه به جای Mat[0,0,0] در کد بالا فقط از Mat تنها استفاده کنیم ، سیستم هنگ می کنه! یعنی:


SetLength(Mat,256,256,256);
Fillchar(Mat,sizeof(Lab)*255*255*255,-1);

ساده ترین راه حلی که به ذهنم می رسه اینه:


for i := 0 to 255 do
for j := 0 to 255 do
FillChar(Mat[i, j, 0], 256*SizeOf(Lab), -1);

در توجیه کد بالا باید بگم که دلفی با آرایه های دینامیکی چند بعدی به صورت آرایه ای دینامیکی از آرایه های دینامیکی برخورد می کنه و به همین دلیل نمی تونیم با یک بار استفاده از Fillchar مشکل فوق رو حل کنیم. دومین راه حلی که به ذهن کوچیکم می رسه استفاده از یک آرایه یک بعدی با طول کافی جهت نگاشت یک آرایه سه بعدی به اون مطابق کد زیر هست:


Mat = array of Lab;
n1 := 256; // number of elements in 1st dim.
n2 := 256; // number of elements in 2nd dim.
n3 := 256; // number of elements in 3rd dim.
SetLength(Mat, n1*n2*n3);
FillChar(Mat[0], n1*n2*n3*SizeOf(Lab), -1);
.....
function Transform(i, j, k: integer): integer;
begin
Result := (i*n1+j)*n2+k;
end;
.....

Value := Mat[Transform(8, 5, 3)]; // Get Mat[8, 5, 3];

خودتون کد بالا رو یه Trace بکنید می فهمید منظورم چیه! ولی بازهم فکر می کنم برای این مسئله به ظاهر ساده ، راه حل های بهتری وجود داشته باشه. اما نظر شما چیه؟ اگه دلیل بهتری برای اون دو مورد اول دارید، بفرمایید ممنون میشم

BestMan
چهارشنبه 09 فروردین 1385, 10:15 صبح
اقای هادی زاده من هم چنین مشکلی دارم ، اگه به جوابی رسیدید به ما هم بگید!

Hadizadeh
پنج شنبه 10 فروردین 1385, 10:34 صبح
آقای نفیسی ممنونم از اینکه واقعا وقت میگذارید و مسئله رو موشکافی می کنید. از این مسئله متوجه شدم که یک برنامه نویس با تجربه و دقیق هستید چرا که الان هر برنامه نویس دلفی که به قول خودش برنامه نویسه فقط یادگرفته کاموننت دانلود کنه و به کامپونت هاش بنازه در حالیکه برنامه نویس واقعی ، از پایه برنامه نویسی رو فهمیده. به هرحال فکر کنم بهتره با همون دو کد بالا مسئله رو تموم شده فرض کنیم ولی اگه روزی روزگاری شیوه جدیدی یاد گرفتید یا یادگرفتم، اینجا رو فراموش نکنیم. با تشکر

Inprise
پنج شنبه 10 فروردین 1385, 14:47 عصر
دقیقا. اما زمانی به این مساله "مشکل" گفته می شه که کسی بخواد با sizeof طول کل آرایه (یا AnsiString) رو پیدا کنه. در هر صورت منظور من این نبود که "اشکالی" در عملگر sizeof وجود داره.

طبیعیه که برای هر کاری باید از روش یا ابزار مربوط و مناسب استفاده کرد . sizeof برای بعضی از انواع داده ای سایز خود داده و برای برخی سایز اشاره گر مربوطه رو برمیگردونه ؛ آرایه ها و رشته های دلفی از این موارد هستند ، در واقع اصولا" برای محاسبه سایز واقعی این داده ها نباید از sizeof استفاده کرد . این یک امکان Documented هم هست . مثلا" برای محاسبه سایز واقعی اشیاء VCL بهتره از InstanceSize استفاده بشه ، و برای محاسبه سایز واقعی رشته ها و آرایه ها باید از ضرب واحد تعریف آرایه در طولش استفاده کرد ، چون در هر حال هر نوع ارجاع به آرایه و رشته در دلفی ، باعث دسترسی به اشاره گر اون نوع داده ای میشه . این یکی از مزایای بی نظیر دلفی در مقابل سی است . دلفی رو طراحی کرده اند که اینطور کار کنه ، اگه کسی دوست داره بر خلاف قاعده ازش انتظار داشته باشه یا استفاده کنه ، در حال تجربه کردن یک مشکل نیست .

برگردیم به مساله اصلی . گاهی اوقات به نظر میاد موقع جواب دادن باید این پیش فرض که مخاطبین راهنمای محیط مورد نظر رو مطالعه کرده اند رو در نظر نگرفت !

Fillchar هیچ Range Checking ای انجام نمیده ، یعنی هر گونه دسترسی به آفستهای غیر مجاز باعث ایجاد یک خطای دسترسی حافظه میشه که اگر هندل نشه در اغلب موارد اجرای برنامه متوقف میشه . این طبیعیه .

با توجه به اینکه تو در حال استفاده از یک رکورد در یک آرایه چند بعدی هستی استفاده از FillChar دلفی یا FillMemory اغلب جواب مناسبی نمیدن ؛ چون alignment داده ها و Padding زمان اجرا باعث میشه آفستهای مربوط به متغیر تو به همان ترتیبی که FillChar انتظار داره روی حافظه نباشند ( سورس کد FillChar رو ببین اگر سورس VCL رو نصب کردی - کد اسمبلی ساده ای داره که به وضوح روشنه هر چند برای بلوکهای بزرگ حافظه به اندازه کافی خوب عمل میکنه ، اما برای داده های پیچیده میشه بسادگی مواقعی رو تصور کرد که نتونه کار کنه ؛ )

برای حل مشکلت اگر دنبال راه حل دیگری هستی من بهت پیشنهاد میکنم آرایه ای شامل مقدار پیش فرض مورد نظرت در نظر بگیری و هر بار که آرایه مقصد رو دوباره ساختی ، آرایه پیش فرض رو به اون مقصد Move کنی ، که به این ترتیب آرایه مورد نظرت بلافاصله شامل مقادیر پیش فرض میشه ، و البته به این شرط که تو همیشه یه مقدار یا مقادیر قابل شمارشی برای اندازه آرایه هات در نظر داشته باشی ؛ اگر اینطور نباشه این روش هم کمک به خصوصی نمیکنه .

http://www.barnamenevis.org/forum/images/icons/icon4.gif به عنوان یک قانون کلی ، وقتی ما در حال استفاده از Stack هستیم ، قبل از فراخوانی هر تابع ( Call ) آرگومانهای او Push شده اند ، پس امکان دسترسی به تک تک آفستهای یک متغیر ، قبل از فراخوانی تابع مورد نظر وجود داره ؛ اما وقتی از Heap استفاده میکنیم ( اشاره گر ها - داده های دینامیک ) لزوما" چنین قراردادی وجود نداره ؛ پس وقتی با Heap کار میکنیم ، یا دقیقا" در مورد ساختار بلوکهای داده اطلاع داریم و مبتنی بر اون از توابع استفاده میکنیم ، یا با خطای دسترسی غیر مجاز حافظه مواجه میشیم . وقتی با استک کار میکنیم همیشه با یک ردیف منظم از داده سر و کار داریم در حالیکه روی هیپ اوضاع اینطور نیست .

( به عنوان یک توضیح بیشتر میتونی به این نکته دقت کنی که وقتی تو در حال استفاده از دو عدد در یک رکورد داخل یک آرایه دینامیک هستی ، تمام آفستهای اشغال شده متعلق به داده های ورودی تو نیستند ؛ یعنی نباید انتظار داشته باشی که اگر سایز رو 450 محاسبه کردی ، 450 تا از اول بنویسی تا آخر ، و اون موقع تو یک آرایه پرشده و کامل داشته باشی ، به همین دلیل است که برای اشیاء VCL باید از روتین سایز فراهم شده توسط TObject استفاده کنی و برای متغیرهای پیچیده تری مانند آرایه های دینامیک و رشته ها با دقت به ساختار داده ایت سایز رو خودت محاسبه کنی ، در عمل کامپایلر فقط با داشتن هوش انسانی میتونه چنین عملی رو انجام بده که چون مقدور نیست ، طبیعتا" توابع و روتینهائی برای چنین منظوری وجود ندارند . یک روش ساده میتونه همون پیشنهاد Move باشه )

Inprise
جمعه 11 فروردین 1385, 08:34 صبح
سعی کن به جوابها بیشتر دقت کنی شاید کمک کنه متوجه بشی .

Alignment و Padding ای که اینجا در موردش حرف میزنم نه به دلفی مرتبط هستند و نه در اختیار مدیر حافظه دلفی . اگر تو دو بایت بنویسی ، دلفی دوبایت مینویسه و مدیر حافظه هم دوبایت ، اما نهایتا" مدیر حافظه ویندوز دو بایت بهش اضافه میکنه و یک چهار بایتی Aligned رو مینویسه . ( بحث حاضر ارتباطی به Alignment لینکر نداره ) آیا بین دو و چهار تفاوت وجود داره یا نداره ؟ وقتی آرایه از انواع داده ای متفاوت و متعدد تشکیل شده باشه – همونطور که تو نوشته قبلی اشاره کردم – به علت تنظیم و اصلاحاتی که مدیر حافظه ویندوز انجام میده ، ترتیب آفستهای آرایه انطور که به نظر میاد باید باشه نیست ؛ وقتی از Stack استفاده میکنیم ، و فرض کنیم که آرایه ای داریم که میتواند از استک استفاده کند ، چون قبل از فراخوانی هر تابع ، آرگومانها به ترتیب وارد لیست شده اند ، ما یک لیست مرتب از تمامی آفستهای مربوط به آرایه داریم ؛ اما روی هیپ اینطور نیست . آرایه های دلفی روی هیپ زندگی میکنند . یعنی اگر آرایه ای داشته باشیم که دارای اعضای متنوع و انواع داده ای مختلف باشه ، دقیقا همانطور که به نظر میرسه ، مرتب نخواهد بود ؛ به عنوان یک مثال ساده اولین خانه آرایه شامل یک متغیر شش بایتی است ، مدیر حافظه در عمل هشت بایت مینویسه و میره سراغ خانهء بعدی . حالا اگر "بایت به بایت" ( و نه خانه به خانه – که آدرس دهی اش رو خود دلفی مدیریت میکنه ) حرکت کنیم ، بایتهای متعددی وجود دارند که لزوما" حاوی داده های مورد نظر ما نیستند ؛ در این این فضا در محدوده آرایه ما است شکی نیست ، اما محتویات این آفستها ، محتویات آرایه ما نیستند ؛


و با توجه به این توضیحات بدیهی و ابتدائی ، کامپایلر نمیتونه اندازهء واقعی "آرایه" رو در شرایط مذکور محاسبه کنه . ذکر این نکته ضروریه که سایز واقعی آرایه ، مجموع تمامی بایتهائی که در اون محصور شده نیست ، بلکه مجموع بایتهائی است که حاوی داده های اون آرایه هستند .

دلیل اصلی مدیریت داده های پیچیده با اشاره گر همینه . اگر میبینی به اون کلمهء "مشکل" ات گیر دادم به این خاطر است که خواسته یا ناخواسته این یعنی در نظر نگرفتن واقعیت و مزیتی که دلفی ارائه میکنه . داده های پیچیده عموما" در زمان اجرا ساخته و مقدار دهی میشن ؛ در حقیقت ما وقتی با داده های پیچیده کار میکنیم اغلب در حال استفاده از هیپ هستیم و مدیر حافظه مجازی ویندوز نحوهء و ترتیب نوشتن بایتها رو مشخص میکنه . چرا قرار نیست با Sizeof بتونیم بسادگی اندازهء واقعی یک Tbutton رو در زمان اجرا محاسبه کنیم ؟ و چرا روتین InstaceSize برای فرزندان TObject در نظر گرفته شده ؟ Sizeof یک کلید کماکان همان 4 بایت خودمان هست که سایز هندلش هست ؛ سایز واقعی رو چطور میشه محاسبه کرد دلائلش واضحه .

( به عنوان یک تمرین میتونی یک آرایه سه بعدی تعریف کنی که روی یکی از ابعادش یک رکورد با دو عضو داره یکی رشته و دیگری بایت ؛ و بعد صد خانه برای هر بعد تعریف کن و سعی کن با FillMemory با توابعی مثل اون ، به تک تک بایتهای موجود در آرایه مقدار صفر رو نسبت بدی ؛ مهم نیست که تفسیر این صفر چیه ؛ مهم وجود انواع داده مختلف و مشاهده تاثیر عملکرد مدیر حافظه مجازی ویندوز روی این داده هاست ؛ )

Hadizadeh
جمعه 11 فروردین 1385, 08:34 صبح
آقای Inprise من سورس Fillchar رو دیدم، این تابع انتظار داره که کلیه درایه ها یا المنت های یک آرایه به صورت پشت سرهم یا Sequential قرار داشته باشند، اما با آزمایش متوجه شدم که حرف شما مبنی بر پیچیده بودن ساختار یا نحوه alignment رکوردها، درسته. به عنوان مثال اگر همون آرایه Mat سه بعدی خودمان رو در نظر بگیریم، همون طور که قبلا هم گفتم Fillchar می تونه تا درایه 256 بعد اول رو به راحتی و بدون هیچ مشکلی پرکنه ، حالا ما انتظار داریم بعد از آدرس درایه آخر بعد اول ، درایه اول بعد دوم ظاهر بشه یعنی اگه تو مکان 257چیزی بنویسیم باید انتظار داشته باشیم که المان اول بعد دوم باید پر بشه ولی این طور نیست. ولی من متوجه نمیشم چرا دلفی این کار رو مثل زبانهای دیگه نظیر C++ انجام نمی ده؟ گذشته از این پس این نحوه Alignment آرایه ای از رکوردها تو حافظه چه جوریه؟ درضمن می تونید یک مثال از اون روش پیشنهادیتون (استفاده از Move) ارایه کنید؟

Inprise
جمعه 11 فروردین 1385, 08:47 صبح
یک آرایه دینامیک در سی هم وضعیت مشابهی داره . این مساله یکی از مسائل قدیمی و سنتی است . وقتی انواع داده پیچیده میشن ، تنافری بین مدل مدیریت حافظه و انتظارات مدیر حافظهء کامپایلر بوجود میاد . جالبتر اینه که کامپایلرهای مایکروسافت و بورلند فوق العاده معقول و قابل اتکاء عمل میکنن ؛ من گاهی حماقتهای جالبی از جی سی سی دیدم که ذکرشون در موقعیت مناسب میتونه اسباب تفریح باشه .

برای اینکه مدیر حافظه سیستم عامل بتونه از قواعد تبعیت کنه و در عین حال بهینگی هائی رو ایجاد کنه چاره نداره بجز استفاده از Alignment و Padding ؛ از طرف دیگه تو انتظار داری اطلاعاتت پشت سر هم روی یک لیست قرار گرفته باشن تا بتونی بایت به بایت روشون بنویسی ، و در عمل اینطور نیست .

وقتی انواع داده پیچیده میشن بهتره از کلاسها و اشیاء استفاده کنی ؛ میتونی برای کلاس مادر یک روتین محاسبه اندازه درنظر بگیری که با توجه به ساختار نوع داده ایت در زمان اجرا اندازه واقعیش رو محاسبه کنه ، و روتینی برای بازنویسی اش در نظر بگیری ؛ همانطور که فرزندان TObject از امکانات پایه ای او بهره مند هستند . اگر اصرار داری از انواع داده ای ابتدائی دلفی استفاده کنی ، یا باید از راه حل خودت که بهش اشاره کردی استفاده کنی یا از پیشنهاد من . یک آرایه میسازی با این فرض که میدونی قراره سایزش 50 یا 80 یا مجموعه اعداد محدود و قابل شمارشی باشه ؛ مقادیر پیش فرضت رو جایگزین میکنی ؛ هر بار که قرار شد "آرایه اصلی" ات تجدید حیات کنه ، یکی از نسخه های مناسب آرایه پیش فرض رو به اون محل کپی ( Move ) میکنی . بدین ترتیب از ابتدا تا انتهای بایتهای محصور به آرایه پیش فرض در محل آرایه مقصد کپی میشن ؛ و مشکل حل میشه . البته با محدودیتهائی

Hadizadeh
جمعه 11 فروردین 1385, 08:58 صبح
متوجه نمیشم یعنی شما می فرمایید ابتدا در زمان Design Time از کد زیر استفاده کنم:


var Mat:array of array of array of Lab;
SetLength(Mat,10,10,10);
for i:=0,j:=0,k:=0 to 10
Mat[i,j,k]:=-1;

یعنی اول با یک حجم کوچک ،آرایه رو پرکنم بعد هرگاه مثلا خواستم سایز Mat رو به 256*256*256 عوض کنم برم و اون آرایه فوق رو به سایر بلوک های جدید اضافه شده کپی کنم؟
اگه منظورتون این نیست فکر کنم بهترین راه ارایه یه مثال ساده هست. ممنونم1

Hadizadeh
جمعه 11 فروردین 1385, 09:14 صبح
یعنی آقای Inprise فکر کنم پس از این همه بحث مفصل بد نیست که ایده ای رو که به ذهن شما رسیده ، به صورت مصور و واضح و در قالب یه مثال اینجا بگذارید. یعنی یک آرایه دینامیکی از رکورد ها داریم و می خواهیم تمام محتویات اون مقدار مثلا -1 بگیرند و این عمل باید بسیار پر سرعت باشه! پیشنهاد می کنم همون مثال بالا رو تکمیل و تصحیح بفرمایید

Hadizadeh
جمعه 11 فروردین 1385, 18:40 عصر
راستی ، گذشته از این حرفها ، آیا میشه این کار رو تو Design Time انجام داد؟

Hadizadeh
شنبه 12 فروردین 1385, 08:51 صبح
آقای نفیسی، آقای Inprise باید پاسخ شما را بدن چراکه خودشون ایده Alignment و همچنین Padding در مورد متغیرهای پیچیده رو مطرح کردن. اما نظر شما چیه آقای Inprise? به نظرم هیچ پاسخی به اندازه یک مثال عینی نمی تونه موثر باشه!

Inprise
شنبه 12 فروردین 1385, 10:59 صبح
@ Hadizadeh : مثال عینی برای چه چیزی ؟ پیشنهاد Move ؟ توضیحش واضح بود . اگر تعداد "حداکثر" هائی که برای آرایه دینامیکت وجود داره محدود و قابل شمارش هست ، به همون تعداد ، آرایهء پیش فرض بساز که هر کدام دارای مقدار اولیه مورد نظر هستند ؛ حالا بعد از هر بار ساختن یک آرایه دینامیک بسته به اندازه اش ، اول یکی از اون آرایه های پیش فرض رو به محل اون Move میکنی . بیش از حد ساده هست ؛ و البته اگه اندازه آرایه هات به هیچ وجه محدود یا قابل پیش بینی نیست این روش هم به دردت نمیخوره و باید بری سراغ روش قبلی خودت .

@ Naficy : توضیحاتی که ارائه شد حاوی نکات ابتدائی و بدیهی بود ؛ و به نظرم کافی میان. اگر به نظرت این توضیحات قانع کننده نمیان یک راه حل وجود داره . تلاش کن با امکانات دلفی ، ( و یا هر کامپایلر دیگری که بهش علاقه داری - سی به عنوان مثال ) مطابق انچه که گفتی و حاوی تصوراتت بود ، مشکل دوستمون رو حل کنی ؛ یعنی صورت مسئله اش رو همانطور که خواسته براش پیاده سازی کن . به عبارت دیگه عطف به دو جواب قبلیت ، بر این تصوری که مشکل فرد سوال کننده رو میشه به سبکی که توضیح دادی حل کرد ، خوب ، اینکار رو انجام بده . قاعدتا" هم دوستمون خوشحال میشه ، هم ما . یک آرایه دینامیک پیچیده داریم ، حاوی رکورد در یک یا تعدادی از ابعادش ؛ و مایلیم با محاسبه اندازه حقیقی اش ، با فرامینی نظیر Fillchar یا fillmemory و امثالهم ، بایت به بایت مقدار دهی اولیه اش کنیم ؛ و سوالی که بعدش پیش میاد اینه که اگر به تصور تو و منطبق با جواب قبلیت در هر حال هیچ مسئله ای وجود نداره ، چرا تا بحال کدی که مشکل رو به این روش حل میکنه تو این تاپیک منتشر نشده ؟ مشکل کجاست ؟ http://www.barnamenevis.org/forum/images/icons/icon12.gif

Inprise
یک شنبه 13 فروردین 1385, 14:14 عصر
کدهای نوشته شده کار میکنند و مشکلی ندارن ، ضمن اینکه تا بحال کسی دربارهء کار نکردن یک لوپ حرفی نزده . چیزی که بهش دقت نمیشه مفهوم آرایه چند بعدی حاوی داده هائی نظیر رکورد است . در نوشته های قبلی تلاش کردی بگی تک تک عناصر یک آرایه همانطور که انتظار میرود درست پشت سر هم از ابتدا تا انتها در یک لیست قرار دارند و میشه بایت به بایت بهشون مقدار داد ؛ جواب من در نفی این مسئله بود که با مراجعه مجدد قابل رویت هست . چه به دلیل ساختار پیچیدهء نوع داده و عدم توانائی کامپایلر در محاسبه اندازه و ارجاع اشاره گر ، و چه بخاطر مسائل مربوط به مدیریت حافظه ؛ در واقع آنچه که مدعیات صفحات قبل به ذهن متبادر میکنه اینه که امکان داره یک آرایه چند بعدی با تعریفی که در سوال ارائه شد با محاسبه اندازه حقیقی و ارجاع مکانها ، بایت به بایت مقدار دهی بشه ، که در آخرین جوابت به همان چیزی که من تلاش کردم بهت منتقل کنم اشاره کردی ، -- مرسی -- که در عمل چنین چیزی به این شکل امکان پذیر نیست ، و اصولا" درخواست غیر منطقی است . حالا اگر به صفحات قبل برگردی و ببینی که چرا به عبارت "مشکل" در جوابت توجه خاصی نشون دادم متوجه مسیری که خودت لطف کردی و اومدی میشی . حل کردن مسئله با لوپ که طبیعتا" از ابتدا ممکن بود . حتی لازم نبود کسی خلاقیت به خصوصی نشون بده ، فرد سوال کننده به خوبی از پس حل مسئله اش بر میومد . مباحث مطروح حول و حوش بحث اندازه گیری سایز آرایه در حالتهای مختلف و پر کردن بایت به بایت یک آرایه بود که خودت زحمت رو کم کردی و به خوبی توضیح دادی که بدین شکل امکان پذیر نیست . حالا یا باید از لوپ استفاده کرد ، یا در شرایط خاصی با توضیحی که دادم از Move . این مسئله واقعا" ساده و روشنه .

و در انتها ذکر یک نکته خالی از لطف نیست :

نکته ای که بعد باید اضافه کنم اینه که نمی شه با "یک دستور ساده" کل آرایه چند بعدی داینامیک رو مقدار دهی کرد. با یک دستور، تنها می شه بعد آخر آرایه داینامیک رو مقدار دهی کرد. این محدودیت به تعریف و نحوه ذخیره سازی آرایه داینامیک بر می گرده.

FillMemory ویندوز برای مقدار دهی به - لزوما" - آرایه ها تالیف نشده ؛ او آدرسهای دریافتی رو با مقادیر مناسب پر میکنه ، و نه در دلفی ، و نه در هیچ محیط توسعه دیگری امکان پر کردن تمام خانه های مربوط به یک آرایه چند بعدی پیچیده به این سبک و روش نیست . علتش ساختار دادهء پیچیده و مشخصات مدیر حافظه است . فکر میکنم تو اوائل این بحث به این نکته اشاره شد ، پیامهای جالبی در مورد امکان پذیر بودن محاسبه ابعاد و آدرسهای داده های پیچیده ای مانند آرایه های چند بعدی ارسال شد ، نهایتا" به همان بحث اول رسیدیم ، که عدم امکان محاسبه سایز ، به دلیل پیچیدگی خاص نوع داده و سازگار نبودن روتین های محاسبه سایز با شرایط پیچیده ( مثل sizeof ) و ...جزو "مشکلات" محیط یا برنامه نویس نیستند ، جزو منطقی هستند که روشی برای ارتقاء - منطقی - ش وجود نداره ، و اگر میداشت تا بحال ارائه شده بود . اگر این بحث رو از ابتدا مرور کنیم ، کسی موفق نشد سایز یک آرایه دینامیک و پیچیده رو محاسبه کنه ، کسی موفق نشد از اشاره گری به این حافظه اجزاء اون رو مقدار دهی کنه ، و ... . به نظرم به همه این موارد در صفحه دو اشاره شده بود ! ( و الا مقدار دهی یک آرایه با یک لوپ که مسئلهء کودکانه ای هست و محل مناقشه نیست )

و باز همانطور که دفعه قبل گفتم ، این یک مساله قدیمی و نخ نماست ؛ نه مختص ماست و نه دلفی ؛ روتین های محاسبه سایز مانند sizeof برای داده های ساده طراحی شده اند ، توابع عملگر روی اشاره گر ها و بایت های یک نوع داده نیز هم . در حقیقت به سه دلیل تاریخی : 1-عدم امکان محاسبه سایز حقیقی 2-پیچیدگیهای متعدد انواع داده ای دینامیک و چند بعدی 3- و در نتیجه عدم قاعده مندی مقابل فرمولی قابل تدوین ، امکان حل مسئله با روشهائی که بهشون اصرار میشد نیست ، و به همین دلیل است که - عطف به توضیحات صفحه قبلم - برای داده های پیچیده کلاسهای به خصوصی رو در نظر میگیرن که بخشی از اون وظیفه محاسبه اندازه و مقدار دهی اولیه در زمان اجرا و غیره رو بر عهده بگیره ، و این همون رسالتیه که VCL‌بر عهده داره . کدی که نوشته شده ، یک کد ساده آبجکت پاسکال هست که حتی میشه با Free Pascal هم کامپایلش کرد ، کار میکنه ، و هوشمندی به خصوصی نداره ، برای راه حلهای غیر حرفه ای میشه از ایدهء لوپ یا ایدهء Move استفاده کرد ، و راه حل بهتر تالیف کلاسیه که وظایف ابتدائی یعنی اندازه و ارجاع صحیح و مدیریت نمونه ها و ...رو انجام بده . این مساله در دلفی به این نتیجه میرسه ، در سی به این نتیجه میرسه ، و در هر زبان دیگری با مشخصات مشابه به همین نتیجه میرسه ؛

موفق باشید

Hadizadeh
دوشنبه 14 فروردین 1385, 10:14 صبح
دوستان عزیز، بسیار ممنونم از توجه و بحث های مفصل شما. من یک سوال کوچولوی دیگه لابلای همین مباحث مطرح کردم و اونم این بود که آیا میشه همین مسئله رو در Design Time یا به عبارت دیگر در حین کامپایل ، انجام داد؟ البته خودم تلاش کردم ولی جواب منفی بود ولی شک دارم. مثلا شما می دونید یک متغیر رو می تونیم در هنگام تعریفش مقدار دهی اولیه کنیم.حالا همین بحث رو در مورد یک آرایه دینامیکی پیچیده داریم. میشه؟
یک سوال دیگه هم داشتم. استفاده از عبارت SetLength برای آرایه های چند بعدی بزرگ در هنگام Run-Time برای یک کامپیوتر تیپیکال زمانبر هست. یعنی وقتی فانکشن یا پروسیجر دربردارنده این عبارت فراخوانی میشه، یه زمانی گرفته میشه تا مدیرحافظه بلوک درخواستی رو فراهم کنه! حالا اگه از همون اول آرایه رو با ابعاد ثابت و مشخص تعریف کنیم (یک آرایه استاتیک) اون چیزی که به ذهنم می رسه اینه که به محض اجرای برنامه، همون زمانی که قبلا تابع SetLength در Real-Time می گرفت، حالا هم باید گرفته بشه و همچنین حافظه مورد نیاز. ولی به نظر شما این استنباط درسته؟

Inprise
دوشنبه 14 فروردین 1385, 21:42 عصر
توضیح مختصری در مورد رابطه انواع متغیرها و حافظه :

عموما" - از شرایط ویژه بگذریم - متغیرهای یک برنامه در یکی از این دو قسمت حافظه قرار میگیرند : Stack یا Heap . وقتی برنامه اجرا شد ، فضای آدرسی اش شامل بخشهای خاصی است که به اونها Stack ریسمانها یا توابع خاص گفته میشه ، و/یا همینطور یک یا تعدادی Heap . مفهوم Stack در حافظه ، یک لیست مرتب از متغیرهای محلی و مقادیرشون ، آدرس برگشت تابع یا توابع و ...؛ و به Stack مختص به هر تابع ، Stack Frame گفته میشه . اندازه و روش رشد و توسعه Stack توسط مدیر حافظه کامپایلر مشخص میشه که توابع اون هنگام اجرا با فراخوانی توابع سیستم عامل ، حافظه مذکور رو مدیریت میکنن . در مقابل ، Heap [ها] لیستی آزاد ، گاهی غیر متصل از داده ها یا اشاره گر هاست که مکان و اندازه اش توسط مدیر حافظه سیستم عامل مدیریت میشه ، یعنی اگر برنامه نویس درخواست اختصاص 100 خانه حافظه روی هیپ رو ارسال کنه ، لزوما" 100 خانه به او اختصاص داده نمیشه ، در حقیقت اغلب بیشتر از 100 خانه اختصاص داده میشه ، و توضیح خواهم داد که چرا . به هیپ پیش فرض Default Heap گفته میشه و هر سیستم عامل بسته به مورد توابعی برای تولید هیپ های جانبی و دسته بندی نقاط آزاد و قابل توسعه حافظه ارائه میکنه ، مثلا" ویندوز به کمک HeapCreate به برنامه نویس امکان میده هیپ های دیگری هم تولید کنه و ... ( حتی تولید یک هیپ هم تابع انتظارات برنامه نویس نیست ، یعنی چه برنامه نویس و چه مدیر حافظه کامپایلر مورد نظر ، درخواست مشخصی ارسال کنند ، لزومی به تخصیص همانقدر فضا نیست ، خروجی میتونه کاملا" متفاوت باشه و حتی گاهی غیر قابل محاسبه از قبل (http://www.microsoft.com/windows2000/techinfo/reskit/tools/existing/dh-o.asp) - ر.ک MSDN )

برای متغیرها : هر جا که اندازهء یک متغیر بتونه در زمان اجرا تغییر کنه ، یا متغیر بصورت ذاتی دارای اندازه و محدوده مشخصی نباشه ، فضای اختصاص داده شده به اون متغیر ، روی هیپ خواهد بود ، چه برنامه نویس اون حافظه رو صراحتا" تخصیص بده ، چه به این مسئله واقف نباشه و کامپایلر پیچیدگی رو از او مخفی کرده باشه ؛ و هر جا اندازهء متغیر مشخص و معین باشه ، یا دارای محدوده روشنی باشه ، یا در حال تعریف اشاره گری باشیم که در آینده روی هیپ برایش فضا تعریف خواهد شد ، از Stack استفاده میشه . دو قطعه کد ساده مینویسم ، یکی با سی و دیگری با دلفی که معادل هم هستند و حالا میشه بهتر بهش دقت کرد :


void main() {
int y;
char *str;

str = malloc(100);

y = foo(23);
free(str);
}

program project1;
var
y:integer;
str:array of char;
begin
setlength(str,100);
y:=foo(23);
finalize(str);
end;end;
منطق و گرامر کدها مهم نیستند ؛ چیزی که مهمه : y یک عدد 32 بیتی است ، روی Stack تعریف میشه ؛ str عدد 32 بیتی است که قراره بزودی به مکانی روی Heap اشاره کنه ، پس حالا مکانش روی Stack تعریف میشه . مکانی که str بهش اشاره میکنه حاوی 100 خانه از هیپ خواهد بود . مقداری که تابع foo به y میده در مکانش یعنی محلی روی استک ذخیره میشه ، توابع free یا Finalize صد خانه فوق الذکر رو آزاد میکنند ، تابع خاتمه پیدا میکنه ، خروجی تابع به تابع فراخوان ، یا سیستم عامل برمیگرده ، Stack Frame به پایان میرسه ؛ Stack از بین میره . پس Heap بصورت خودکار از بین نمیره ، مگر اینکه کامپایلر به Garbage Collector ای مجهز باشه که اشیاء یا خانه های بدون ارجاع رو حذف کنند ، سی دارای چنین قابلیتی نیست ، دلفی دارای چنین قابلیتی هست ، بصورت محدود ؛ جاوا و دات نت GC های نسبتا" خوبی دارند ، ولی بهر ترتیب ، این یک واجب عینی بر هر برنامه نویس است که اگر حافظه ای رو مصرف کرد ، به بازگرداندن اون حافظه فکر کنه ، حالا یا خودش اینکار رو انجام میده یا کامپایلر بهش کمک میکنه ، ولی فکر نکردن بهش غلطه .


شما می دونید یک متغیر رو می تونیم در هنگام تعریفش مقدار دهی اولیه کنیم.حالا همین بحث رو در مورد یک آرایه دینامیکی پیچیده داریم. میشه؟
یک آرایه دینامیک ، چه ساده چه پیچیده ، یک اشاره گر داره و یک محتوا . هنگام توسعهء کد ، تو اشاره گر رو میبینی ، اشاره گر رو تعریف میکنی ، و اشاره گر رو میشناسی ، و در زمان اجرا محتوای اون آرایه ، و اندازه اش ، مشخض و مکانش معین و مقدار دهی میشه . پس عبارت "آرایهء دینامیک با مقدار پیش فرض" یک مفهوم پارادوکسیکال هست ، مثل آتش سرد . وقتی حرارت در ذات چیزی تعریف شده ، صفت متناقض با ذات رو نمیشه به اون مفهوم متصف کرد . انچه تو تعریف میکنی تنها اشاره گری به آرایه است ، و وقتی در زمان اجرا ، کنترل به تابعی نظیر Setlength رسید ، اندازه و مکان تخصیص حافظه به اون آرایه دینامیک مشخص میشه - طبیعتا" روی هیپ ؛ چنانچه گفته شد - و بعدا" مقادیر به مروز جایگزین میشن . پس جواب سوالت به این شکل منفیه . ما نمیتونیم آرایهء دینامیکی داشته باشیم که مقدار پیش فرض داشته باشه ، چرا که وقتی میگیم آرایهء دینامیک ، یعنی هنوز مشخص نیست این آرایه کجاست و چه اندازه ای داره ، پس چطور میتونیم بهش مقدار یا مقادیری رو اختصاص بدیم ؟ http://www.barnamenevis.org/forum/images/icons/icon7.gif


استفاده از عبارت SetLength برای آرایه های چند بعدی بزرگ در هنگام Run-Time برای یک کامپیوتر تیپیکال زمانبر هست
طبیعیه .
و کمی توضیح در مورد دلفی : برنامه های دلفی مانند هر برنامه Win32 دیگری در زمان اجرا تحت مدیریت مدیر حافظه ویندوز هستند ؛ ممکنه برنامه نویسان دلفی با تعجب در مورد مدیر حافظه دلفی صحبت کنند ، اما این محصول یک بی دقتیه . درسته که دلفی یک "واسط" داره که باعث میشه کدهای مرتبط با حافظهء او - گاهی - مستقیما" به توابع حافظه سیستم عامل ترجمه نشن ، و به نظر بیاد که دلفی حافظه رو خودش مدیریت میکنه ، اما در عمل مسئله به این شکل نیست . مدیر حافظه دلفی که اخیرا" با جایگزین بهینه ای بنام FastMM تعویض شده ، سعی میکنه هم پیچیدگی رو کم کنه و هم یک GC عملی و نسبتا" ساده رو ارائه کنه ، اما :

الف. این مدیر حافظه کماکان از منطق فوق الاشاره تبعیت میکنه
ب. در سطح زیرین باز باید با مدیر حافظه سیستم عامل کار کنه و منطق او روشن و مشخصه .

پس این تصور کلی که دلفی هر کاری که دلش میخواد با حافظه انجام میده و سبک خودش رو در مدیریت داره تصور صحیحی نیست ، وقتی حافظه ای تخصیص داده میشه ، در نهایت این سیستم عامل هست که باید آدرس و محدوده Virtual Page ها رو برگردونه و Padding لازم بدون تردید انجام میشه ، یا وقتی قرار هست متغیری نا متناسب با پهنای حافظه تعریف بشه بی تردید Alignment لازم انجام میشه - مطابق قاعده ؛ و مگر اینکه خلافش صراحتا" درخواست شده باشه - دلفی این میان بخش عمده ای از تعامل مستقیم با مدیر حافظه سیستم عامل رو پوشش میده ، اما چون در نهایت باید با او کار کنه ، هر چه بر او مترتب هست ، بر دلفی نیز هم . تو میتونی از توابع API استفاده کنی ، یک آرایه دینامیک تعریف کنی ( HeapAlloc - VirtualAlloc ) و بدون دخالت مدیر حافظه بورلند باهاش کار کنی ، و میتونی همین کار رو بصورت سنتی انجام بدی ، و در هر حال ، وقتی آرایه دینامیک ات پیچیده باشه ، دیگه مثل یک آرایه خطی ساده ، نمیشه منتظر یک ترتیب خطی روی حافظه بود ، چه بخاطر ساختار پیچیده آرایه ، چه بخاطر تغییر و تحولاتی که موقع تخصیص عناصر ارایه روی فضای مورد نظر رخ میده . تو میتونی شخصا" حساب کنی یک آرایه دو بعدی با رکوردهائی مشخص ، چقدر فضا لازم داره ، و میتونی با هر دو روش فضای مورد نظر رو تخصیص بدی ، و بی تردید در هر دو روش خروجی که خواهی دید با عددی که محاسبه کردی منطق نخواهد بود ، میتونی برای یک Case Study ساده از ابزاری که لینکش داده شد برای مشاهده و تجربه عینی استفاده کنی . امیدوارم این چند خط توضیحات ارائه شده در پاسخهای قبلی رو تکمیل کنه .

حالا چطور میشه کاری کرد که تو خوشحال باشی ؟
زمانبری کد چیزی نیست که برنامه نویس و مشتری رو خوشحال کنه . یک راه حل خوب اینه که با تکیه بر یک معماری معقول ، هزینه تخصیص حافظه برای داده هائی مثل این رو ، در اولین بار اجرا پرداخت کنی ، یعنی مکانهائی رو در اختیار بگیری ، آزادشون نکنی ، و دائما" با مقادیرشون بازی کنی ؛ بدین ترتیب ، هر چند در حافظه صرفه جوئی نکردی ، و زمان اجرای اولیه کد زیاد خواهد بود ، اما لااقل در زمان اجرا ، دائما" برای تخصیص و آزاد سازی حافظه معطل نمیشی . این یک trade-off هست ؛ نمیشه انتظار داشته باشیم برنامه مثل موشک سریع باشه ، و در عین حال کمترین میزان حافظه رو مصرف کنه ، با کمترین کد . همیشه ارتقاء در یک مسیر باعث میشه در مسیر دیگری هزینهء بیشتری پرداخت بشه ؛ میتونه یک آرایه بزرگ از چیزی شبیه به TList که حاوی مقدار زیادی String هست تعریف کنی و همیشه در اختیارت باشه ، و صرفا" با مقادیرش بازی کنی ؛ اگر دوست داری به حافظه لطف کنی ، باید هزینه تخصیص و آزاد سازی رو بپردازی .

موفق باشید

Hadizadeh
سه شنبه 15 فروردین 1385, 08:48 صبح
آقای Inprise اگه لطف کنید یک کم در مورد منطق Padding و Alignment هم توضیح بدید ممنون میشم. شما می فرمایید که برنامه درخواست 6 بایت می ده ولی مدیر حافظه 8 بایت می نویسه، این 2 بایت اضافه شده دقیقا چی هستند و نقش آنها چیست؟ من هم علی رغم توضیحات مفصلی که ارایه دادید ، متاسفانه هنوز روش Move برام کاملا روشن نشده. اگه لطف کنید این دفعه همین سوال اصلی رو با روش پیشنهادی خودتون حل کنید، فکر کنم قضیه حل میشه! و اما برداشت های بنده از این بحث ها به عنوان یک شخص ثالث تا اینجا:
1- آرایه دینامیک در هنگام توسعه کد = قفط یک پوینتر خشک و خالی
2- محل زندگی یک آرایه دینامیک = شهر هیپ
3- نظم موجود در هیپ = هیچ نظم مشخصی وجود نداره
4- المان های یک آرایه دینامیک لزوما پشت سر هم نیستند
5-از قرار معلوم مسئله مقداردهی یک آرایه دینامیک چند بعدی بدون استفاده از حتی یک حلقه اضافی ، امکان پذیر نیست
6- برای هر نوع داده ای ، نباید انتظار داشت که توابع استانداردی نظیر SizeOf، مقدار صحیحی رو برگردوند و لذا یک از دلایلی که اشیاء مختلف خودشون متدهای محاسبه سایز دارن همینه

اگه احساس می کنید یکی از موارد فوق غلط هست، لطفا بنده رو در ارزیابی و استنباط کلی راهنمایی بفرمایید. ممنونم

Inprise
سه شنبه 15 فروردین 1385, 23:32 عصر
چون به بعضی از نکات پیشتر اشاره شده دوباره بهشون جواب نمیدم ؛ و مطالبی ذیل مربوطه به قسمتهای غیر تکراری :

@Hadizadeh : برای مطالعه دقیق یا حتی مرور اجمالی Memory Management‌ روی IA32 جلد سوم مرجع برنامه نویسان سیستم رو از سایت اینتل دریافت کن ؛ هنگام کار با سیستم عامل مورد نظرت کافیه روش رسیدن به اون منطق رو یاد بگیری . در مورد بقیه موارد ، اولا" هیپ نظم داره ، شاید درسته که بگیم ترتیب نداره ، یعنی اونطور که انتظار میره اجزاء رو پشت سر هم نمیگذاره ، لیکن مطابق قواعد خودش منظمه ؛ در بی نظمیه محض که نمیشه انتظار داشت برنامه کار هم بکنه . تلقی ات در مورد مسئله ات صحیحه . بدین شکل بدون لااقل یک لوپ عملی نیست پیشنهاد Copy یا Move هم واقعا" ساده و ابتدائیه . متغیر الف رو داری و با لوپ بهش مقدار میدی ، هر وقت متغیر ب رو تولید کردی بجای اینکه برای او هم لوپ دیگری وجود داشته باشه مقدار اولی رو کپی یا منتقل میکنی . طبیعتا" این مسئله فقط وقتی مفیده که صورت مسئله ات اجازه بده ؛ والا راه منطقی و عملی دیگری وجود نداره .

@Naficy‌ : ابتدای بحث تصور این بود که مورد سوال به یک آرایه تک بعدی برمیگرده که جوابها روشن بود ، و از وقتی صورت سوال مبدل شد به یک آرایه چند بعدی با مشخصات ویژه ای ، بحث به همین مسئله معطوف شد ؛ حالا اگر وسط بحث و سوالی دربارهء یک آرایه چند بعدی ، تو در حال بحث در مورد یک آرایه یک بعدی بودی ، و من در حال اثبات غلط بودن اون نتیجه گیری ، شاید بشه گفت ما یک misunderstanding رو تجربه کردیم ، اما تصادفا" وضعیت خاص ما باعث میشه که بگم اینطور نبوده . یعنی حتی اگر ما در مورد آرایه های تک بعدی هم صحبت کنیم باز تصوراتت غلط هستند . بله ، یک آرایه ساده یک بعدی دارای رکوردهائی حاوی یک عدد و یک بایت ، باز هم align خواهد شد ؛ یعنی بجای اینکه یک آرایه ده عضوی با این مشخصات 50 بایت فضا مصرف کنه ، "کمی بیشتر از" 80 بایت مصرف خواهد کرد ؛ 30 بایتش متعلق به چینش حافظه است و اون - گاهی - کمی بیشترش هم به مولفه های محیطی مدیر حافظه دلفی برمیگرده .
تو حتی اگر آرایه ساده ای متشکل از 6 تا byte بسازی ، باز هم نهایتا" 8 بایت تخصیص داده خواهد شد ، و جالبتر اینه که این قاعدهء مبتنی بر ویندوز ، گاهی بخاطر مشخصه های مدیر حافظه دلفی تغییر میکنه و بجای 2 بایت اضافی ، 4 یا 8 بایت اضافی درج میشه ، تا بلوک ای که قبلا" توسط Delphi MM از هیپ ویندوز دریافت شده ، مصرف بشه و تخصیص حافظه بعدی با بلوک جدید شروع بشه . یعنی حتی اگر مساله بدیهی و سادهء آرایه های چند بعدی و پیچیده رو فراموش کنیم ، با آرایه های ساده روی هیپ هم شرایطی "میتونه" وجود داشته باشه که نگاشت یک به یکی بین محتویاتی که ما میخواهیم ، و خانه هائی که تخصیص داده شده اند ، وجود نداشته باشه . این صفحه متعلق است به یکی از توسعه گران سابق دلفی ، که مقاله قدیمی ولی جالبی در زمینه دیباگ داره ، و در حین بحث به نکات خوبی اشاره کرده که میتونه ابهامها رو تا حد زیادی برطرف کنه : http://www.blong.com/Conferences/BorCon2002/Debugging/3188.htm

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

و بصورت خلاصه در جواب سوالهات : مسئله مورد بحث بدون تردید باید به کمک لوپ حل بشه ، و هیچکدام از توابع مسبوق الذکر موفق نمیشن بصورت بایت به بایت به هدف برسن ، به دلائلی که ذکر شد ؛ و اگر در شرایط خاصی صورت مسئله کمک کنه ، میشه بجای لوپ های متعدد ، یکبار یک آرایه رو مقدار داد و دفعات بعدی تمام فضا رو کپی کرد ، که ممکنه گاهی به زمان اجرا کمک کنه ، و این در هر حال یک راه حل خلاق و خارق العاده نبود .

Hadizadeh
چهارشنبه 16 فروردین 1385, 12:19 عصر
برای یک رکورد، فیلدهای خاص به صورت خاصی algin می شوند. مثلا در حالت اگر کامپایلر روی حالت alignment چهار بایتی باشه، برای فیلدهای خاصی نظیر Integer و غیره، صفر تا سه بایت اضافی قبل از فیلد مورد نظر درج می شه، تا آدرس نسبی فیلد درون رکورد مضربی از 4 یا 2 باشه. (اینکه آدرس قیلد مربوطه به 4 یا 2 align بشه، یا اصلا align نشه بستگی به نوع داده فیلد مروبطه داره) این بایتها به هیچ کاری نمی یان، محتویاتشون اساسا اهمیتی نداره و فقط برای بالا بردن سرعت درج می شوند. توضیح دقیق و کامل این پدیده در راهنمای دلفی موجوده.
می شه این کار رو غیر فعال کرد. $A- یک گزینه کامپایلره که این کار رو کلا غیر فعال می کنه. همینطور اگه هنگام تعریف رکورد، از کلمه کلیدی packed استفاده بشه دیگه این کار برای اون رکورد خاص انجام نمی شه، و فیلدها دقیقا با آدرسهایی پشت سرهم در رکورد ظاهر می شن.

آقای نفیسی واقعا ممنونم از توضیحات شما در مورد بحث Alignment. منتهی همونطور که اشاره کردید اگر از اون دایرکتیو $A- یا از کلمه کلیدی Packed استفاده کنیم، آدرس ها پشت سرهم ذخیره می شوند. خوب این مسئله دوباره من رو به شک انداخت. یعنی اگه از این روش استفاده کنیم ، آیا میشه این بار از Fillchar استفاده کرد؟ البته با فرض اینکه سایز واقعی رو می دونیم! چون تا حالا یکی از بحث های ما این بود که در آرایه های چند بعدی دینامیکی ، معلوم نیست که فلان درایه مثلا بعد دوم در کجا ذخیره میشه. حالا اگه بدونیم که مثلا درایه اول بعد دوم دقیقا بعد از درایه آخر بعد اول قرار داره ، فکر کنم با این فرض، میشه از Fillchar استفاده کرد. اما نظر شما چیه؟
آقای Inprise هر چی سعی کردیم که شما رو متقاعد کنیم که یک مثال از روش Move ارایه بدید، نشد. اگه یک مثال بزنید، من می تونم یک نقص احتمالی پیش پاافتاده این روش رو بیان کنم. گفتم احتمالی ، به دلیل اینکه دقیقا نمی دونم شما چه جوری مسئله جاری این تاپیک رو با اون روش می خواهید حل کنید. به هر حال از شما هم صمیمانه سپاسگذار هستم چرا که باعث شدید دانش من در این زمینه هم بیشتر بشه!

Inprise
چهارشنبه 16 فروردین 1385, 15:17 عصر
و همچنین اینکه مجددا تاکید کردند که روششون نیازی به لوپ نداره، و با توجه به سایر توضیحاتشون، می شه حدس زد که ...
توضیحات ارائه شده در صفحات قبلی از دید خودم کافی و کاملند ؛ یک دلخوری شخصی بخاطر بی دقتی با مزه شماها باقی میمونه که این یکی رو سالهاست بهش عادت کردم ؛ فقط برای یک اشاره کوچک ، اون هم بعد از سه بار تکرار ، یک نگاهی به متنی که این بالا نقل قول کردم بنداز ، و بعد فقط همین دو تیکه رو فقط از جواب قبلیم ببین و تو خود حدیث مفصل بخوان از این مجمل .


تلقی ات در مورد مسئله ات صحیحه . بدین شکل بدون لااقل یک لوپ عملی نیست پیشنهاد Copy یا Move هم واقعا" ساده و ابتدائیه . متغیر الف رو داری و با لوپ بهش مقدار میدی ، هر وقت متغیر ب رو تولید کردی بجای اینکه برای او هم لوپ دیگری وجود داشته باشه مقدار اولی رو کپی یا منتقل میکنی . طبیعتا" این مسئله فقط وقتی مفیده که صورت مسئله ات اجازه بده ؛ والا راه منطقی و عملی دیگری وجود نداره .



و چند خط پائین تر :



و بصورت خلاصه در جواب سوالهات : مسئله مورد بحث بدون تردید باید به کمک لوپ حل بشه ، و هیچکدام از توابع مسبوق الذکر موفق نمیشن بصورت بایت به بایت به هدف برسن ، به دلائلی که ذکر شد ؛ و اگر در شرایط خاصی صورت مسئله کمک کنه ، میشه بجای لوپ های متعدد ، یکبار یک آرایه رو مقدار داد و دفعات بعدی تمام فضا رو کپی کرد ، که ممکنه گاهی به زمان اجرا کمک کنه ، و این در هر حال یک راه حل خلاق و خارق العاده نبود .

http://www.barnamenevis.org/forum/images/icons/icon7.gif

من عموما" از فرصتی که به جوابها اختصاص میدم ، خوشحالم ؛ حتی کم دقتی عمومی که شدیدا" رواج داره مسئله ای ایجاد نمیکنه . اما اگر مایلید از وقت و فرصتتون بهتر استفاده کنید ، دقت بیشتر ، به معنی هزینه کمتر هست .

حالا اگر با دقتی که ازش حرف زده شد ، یک مرور سطحی به محتویات بحث بکنیم :

::sizeof عملگری برای داده های ساده است ؛ برای هر چیزی بیشتر از انواع ابتدائی دلفی ، باید سیاست مشخصی برای محاسبه اندازه وجود داشته باشه ، "اگر" قرار است که این اندازه نقشی در خواندن یا نوشتن بایت به بایت داشته باشه .

::محاسبهء اندازهء واقعی آرایه های چند بعدی پیچیده ای که شامل رکوردهائی ، اشاره گرهائی ، یا رشته هائی ، باشند ، توسط امکانات دلفی امکان پذیر نیست ، و درخواست منطقی هم نیست .