# Native Code > برنامه نویسی در Delphi > مباحث عمومی دلفی و پاسکال >  tag چیست؟؟؟

## AbiriAmir

سلام
خاصیت Tag مربوط به اشیا چیست؟؟؟

----------


## hadisalahi2

این خاصیت برای نگهداری یک مقدار Integer  استفاده میشود که برنامه نویس میتونه از اون برای اهداف خاصی 
استفاده کنه.
به عنوان مثال دادن یک شناسه عددی به کامپوننت ها و کنترلهای روی فرم.
فرضا اگه شما به خاصیت Tagیک edit عدد مثلا 18 رو نسبت بدید، میتونید از این به بعد به همون مقدار تگ به اون کنترل دسترسی داشته باشید.
مثلا من شخصا یک کامپوننت درست کردم که عمل درج در جدول رو بر اساس خاصیت Tag موضوعات انجام میده و بر مبنای شماره Tag کنترل ها تشخیص میدم که الان مقدار کدوم کنترل رو میخوام بخونم.
در کل استفاده از این خاصیت بستگی به برنامه نویس داره که چه استفاده ای از اون بکنه.
یا حق

----------


## Mahmood_M

خاصیت تگ برای استفاده ی خاصی نیست ، میشه توش یک مقدار عددی رو قرار داد ( البته با ترفندهایی میشه مقادیر String یا ... رو هم توش گذاشت ) ، با توجه به اسمش نوعی برچسب برای متمایز کردن کامپوننت هست ، مثلا کامپوننت VCLSkin که برای قرار دادن پوسته روی کنترلهای برنامه استفاده میشه یک خاصیت داره به نام DisableTag ، فرض کنید توی این خاصیت یک عدد رو وارد کردید ، حالا هر کنترلی که همین عدد در قسمت Tag مربوط به اون قرار داشته باشه ، پوسته نمی گیره و به حالت عادی نمایش داده میشه ، به عنوان مثال در اینجا کامپوننت VCLSkin به وسیله ی همین خاصیت کامپوننتهایی که نباید پوسته روشون قرار بگیره رو می شناسه ...
استفاده های دیگری هم میشه ازش کرد ، مثلا فرض کنید چندین کامپوننت روی فرم دارید و میخواید که توی یک حلقه روی همشون به غیر از چند کامپوننت خاص یک کاری انجام بشه ! ، می تونید خاصیت تگ مربوط به کامپوننتهای مورد نظر رو یک مقدار مشخص بدید و به جای اینکه در حلقه اسم کامپوننتها و ... رو تک تک چک کنید ، مقدار Tag رو چک کنید ...

پاسخها همزمان شد ، ولی شاید این پاسخ هم مفید باشه ...

موفق باشید ...

----------


## AliReza Vafakhah

> خاصیت تگ برای استفاده ی خاصی نیست ، میشه توش یک مقدار عددی رو قرار داد ( *البته با ترفندهایی میشه مقادیر String یا ... رو هم توش گذاشت* )


خیلی ببخشید چطوری مقدار string را tag ذخیره می کنید ؟

----------


## tdkhakpur

> خیلی ببخشید چطوری مقدار string را tag ذخیره می کنید ؟



k : string;

tag := integer(@k);

----------


## Mahmood_M

> خیلی ببخشید چطوری مقدار string را tag ذخیره می کنید ؟


اشاره گری از String که آرایه ای از کارکتر ها هست در این خاصیت به صورت Integer ذخیره میشه که البته نحوه ی انجامش رو جناب tdkhakpur قرار دادن ...

موفق باشید ...

----------


## vcldeveloper

> tag := integer(@k);


نباید به این شکل بنویسید، برای string باید به شکل زیر عمل کنید:
Self.Tag := Integer(PChar(S));
یعنی باید آدرس اولین کارکتر string ذخیره بشه (PChar این کار رو میکنه)، نه آدرس خودِ string، چون خودِ string یک Pointer هست. کد شما درست عمل نمیکنه، و یک محل نامعلوم از حافظه را میخوانه.

----------


## tdkhakpur

> نباید به این شکل بنویسید، برای string باید به شکل زیر عمل کنید:
> Self.Tag := Integer(PChar(S));
> یعنی باید آدرس اولین کارکتر string ذخیره بشه (PChar این کار رو میکنه)، نه آدرس خودِ string، چون خودِ string یک Pointer هست. کد شما درست عمل نمیکنه، و یک محل نامعلوم از حافظه را میخوانه.


فرقی نداره!
بستگی به برنامه نویس دارد که طرف دیگر از رابط tag به چه صورت استفاده کند!!
بهتره آدرس کل کلاس را با خودمان ببریم تا همه تنظیمات را داشته باشم.
نمونه کد

var
 k : string;
 p : ^string;
begin
//-------------- ارسال
 k := 'hi my friends';
 tag := integer(@k);
 
//-------------- دریافت
 p := Pointer(tag);
 k := p^;
 showmessage(p^);
end;

----------


## vcldeveloper

> بهتره آدرس کل کلاس را با خودمان ببریم تا همه تنظیمات را داشته باشم.


آدرس کل کلاس چیه؟! شما اینجا دارید با یک نوع داده Primitive به نام string کار می کنید. حتی اگر یک شی هم بود، آدرس شی را برای همچین کاری نگهداری می کنند، نه آدرس کلاس مربوط به آن شی را.

نوع داده string یک اشاره گر در Stack هست که به ساختاری در Heap اشاره میکنه. ساختاری که در Heap برای یک string بوجود میاد، Reference-counted هست، و مدیریت آزاد سازی آن با کامپایلر هست. شما در کد خودتان آدرس متغیر string (یعنی اشاره گر موجود در Stack) را ذخیره می کنید. در صورتی که یک  متغیر موجود در Stack با خارج شدن اجرای برنامه از Scope آن، خود به خود آزاد میشه، و اشاره گری به آن شما را به جایی نمیرسونه. اگر به ساختار string شما توسط چند متغیر مختلف ارجاع داده شده باشه، کد شما وابسته به متغیری هست که ارجاع را انجام داده، یعنی اگر اجرای کد از Scope اون متغیر خارج بشه، ساختار String مربوطه میتونه همچنان در حافظه Heap وجود داشته باشه، ولی کد شما متوجه آن نمیشه، چون کد شما بند به متغیر ارجاع دهنده هست، و اون متغیر هم دیگه وجود نداره. به عنوان مثال:

procedure TForm1.Button1Click(Sender: TObject);
var
  LocalStr : string;
begin
 LocalStr := 'hi my friends';
 Self.Tag := integer(@LocalStr);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
 P : ^string;
begin
 P := Pointer(Self.Tag);
 ShowMessage(P^);
end;

در این کد فرضی، متغیرهای LocalStr و GlobalStr هر دو به یک رشته (hi my friends) اشاره می کنند، ولی مقدار متغیر LocalStr در Tag ذخیره شده، و از آنجایی که LocalStr در Button2Click وجود نداره، و در زمان خروج از Button1Click آزاد شده، مقدار ذخیره شده در Tag غیر معتبر هست، در حالی که رشته hi my friends هنوز روی Heap موجود هست.

حالا اگر اون کد به صورت زیر تغییر کنه:


procedure TForm1.Button1Click(Sender: TObject);
var
  LocalStr : string;
begin
 LocalStr := 'hi my friends';
 Self.Tag := integer(PChar(LocalStr));
end;

procedure TForm1.Button2Click(Sender: TObject);
var
 PC : PChar;
begin
 PC := PChar(Self.Tag);
 ShowMessage(PC);
end;
در این صورت، چون مستقیما آدرس ساختار string مربوطه توسط Pchar مورد ارجاع قرار گرفته، رشته hi my friends همچنان در heap معتبر هست، و RTL به خاطر ارجاع PChar به ساختار مربوطه، مقدار Reference-count آن را افزایش داده، و می داند که این رشته جایی در کد همچنان مورد نیاز هست.

----------


## tdkhakpur

> چون مستقیما آدرس ساختار string مربوطه توسط Pchar مورد ارجاع قرار گرفته، رشته hi my friends همچنان در heap معتبر هست، و RTL به خاطر ارجاع PChar به ساختار مربوطه، مقدار Reference-count آن را افزایش داده، و می داند که این رشته جایی در کد همچنان مورد نیاز هست.


خب اگر مشکل دوستمان به اون نقطه ای می رسید که می بایست طول عمر داده را هم در نظز میگرفت بحث چیز دیگری را هم در پی میداشت ولی ایشان طرز ارجاع و ارسال از آدرس به tag را سوال کرده بودند - در حالت کلی میتوان آدرس کلی را هم ارسال نمود که میتواند بهترین حالت برای استفاده کننده از tag را به همراه داشته باشید.
در ضمن من کد بالا را داخل هیچ پروسه ای قرار نداده بودم!

----------


## 1485159

> در این کد فرضی، متغیرهای LocalStr و GlobalStr هر دو به یک رشته (hi my friends) اشاره می کنند، ولی مقدار متغیر LocalStr در Tag ذخیره شده، و از آنجایی که LocalStr در Button2Click وجود نداره، و در زمان خروج از Button1Click آزاد شده، مقدار ذخیره شده در Tag غیر معتبر هست، در حالی که رشته hi my friends هنوز روی Heap موجود هست.


یعنی اینکه بعد از بستن برنامه حافظه به طور کامل آزاد نمیشه؟!
=================
مگه تابع pchar هم آدرس اولین کاراکتر رو در حافظه heap مربوط به scope برنمیگردنه؟
کلا من با تابع pchar آشنایی کامل ندارم، ممنون میشم در مورد کارش توضیح بدین....
ممنون.

----------


## vcldeveloper

> یعنی اینکه بعد از بستن برنامه حافظه به طور کامل آزاد نمیشه؟!


وقتی برنامه بسته بشه، همه حافظه تخصیص داده شده به آن آزاد میشه.




> مگه تابع pchar هم آدرس اولین کاراکتر رو در حافظه heap مربوط به scope  برنمیگردنه؟


PChar تابع نیست، بلکه یک نوع داده هست (مثل Integer، String، و غیره). PChar یک اشاره گر به اولین کارکتر یک String هست.




> کلا من با تابع pchar آشنایی کامل ندارم، ممنون میشم در مورد کارش توضیح  بدین....


قبلا درباره اش چند بار توضیح دادم:
http://www.barnamenevis.org/sh...d.php?t=130721

----------


## مصطفی ساتکی

> نباید به این شکل بنویسید، برای string باید به شکل زیر عمل کنید:
>       کد:
>      Self.Tag := Integer(PChar(S));


 کد شما کار نمی کنه به دلیل زیر:
یک خطای رایجی ممکنه  در تبدیل  متغیر داخلی رشته  به Pchar   باهاش برخورد  کنید اینه که در این شرایط کامپایلر متغیر داخلی در یک data structure  ذخیره می کنه . وقتیکه روتین شما خاتمه پیدا می کنه Pchar هم ناپدید میشه  بخاطراینکه اون یک اشاره گر به حافظه است و نه یک کپی از refrence Counted  رشته . پس کدی که شما قرار دادید اگر این Type cast در این روتین انجام  بگیره عملاً اون آدرس دیگه قابل استفاده نیست .
البته در کدی که جناب  tdkhakpur قرار دادند  هم غلطه بخاطر اینکه با خروج  از روتین مورد نظر بخایم از آدرس استفاده کنیم مدیر حافظه refrence Counted  رو صفر کرده و آدرس به یک فضای نا معلوم اشاره می کنه.البته برای زمانیکه  متغیر رشته به صورت داخلی تعریف شده باشه و اگر به صورت عمومی تعریف شده  باشه این کد درست.




> یعنی باید آدرس اولین کارکتر string ذخیره بشه (PChar این کار رو  میکنه)،  نه آدرس خودِ string، چون خودِ string یک Pointer هست. کد شما درست عمل  نمیکنه، و یک محل نامعلوم از حافظه را میخوانه.


آقای کشاورز کم کم ما داریم از شما  چیزهای جدیدی یاد می گیریم .
Pchar یادگار دلفی 1.0 و یک نقطه ضعف برای دلفی محسوب میشه اون زمان برای  استفاده از API بالجبار به این صورت پیدا شد که حافظه گرفته شده  پس گرفته  نمیشه نه اون دلیلی که شما آوردید آدرس اولش ذخیره می کنه.
در ضمن بجای این نقطه ضعف دلفی می تونید در اکثر موارد که مجبور نیستید می  تونید از Ansistring بجای pchar استفاده کنید که به صورت خودکار مدیریت  حافظه می شه . در مواردی که از مقداری زیادی pchar استفاده کنید حتی ممکن  با خطای memory corruption بشید.

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


procedure SavePosition;
var   s : pchar;
begin
  s := 'Hello';
  tag := longint(s);
end;

procedure UsePosition
begin
 ShowMessage(pchar(tag));
end;

----------


## tdkhakpur

> البته در کدی که جناب tdkhakpur قرار دادند هم غلطه بخاطر اینکه با خروج از روتین مورد نظر بخایم از آدرس استفاده کنیم مدیر حافظه refrence Counted رو صفر کرده و آدرس به یک فضای نا معلوم اشاره می کنه.البته برای زمانیکه متغیر رشته به صورت داخلی تعریف شده باشه و اگر به صورت عمومی تعریف شده باشه این کد درست.


عزیزان من - کجای کد من داخل روتین بود و تشخیص دادید که گلوبال هست یا نیست؟(عجب ها!!!!)
ببینید در حالت کلی چه گلوبال چه لوکال شما میتوانید با آدرس هر شی مانور بدید فقط باید مواظب دامنه متغییر باشید تا به جایی اشاره نکنید که قبلا اون ادرس یا بلوک مورد نظر آزاد شده باشد که این عمل میتواند با خروج از توابع و یا آزاد سازی دستی انجام شود.

----------


## vcldeveloper

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

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




> یک خطای رایجی ممکنه  در تبدیل  متغیر داخلی رشته  به Pchar   باهاش برخورد   کنید اینه که در این شرایط کامپایلر متغیر داخلی در یک data structure   ذخیره می کنه . وقتیکه روتین شما خاتمه پیدا می کنه Pchar هم ناپدید میشه   بخاطراینکه اون یک اشاره گر به حافظه است و نه یک کپی از refrence Counted   رشته .


"_متغیر  داخلی در یک Data structure ذخیره میکنه_"، یعنی چی؟!

PChar دقیقا یک اشاره گر هست، و کاری که انجام میده، دقیقا کاری هست که یک اشاره گر باید انجام بده. اساسا قرار نیست در همچین حالتی، یک کپی از داده ایجاد بشه، بلکه قراره یک ارجاع به یک داده موجود در Tag نگهداری بشه، تا بشه بعدا از طرق Tag به اون داده دسترسی پیدا کرد. پس کپی کردن رشته در همچین شرایطی کار عبثی هست. در ضمن، کامپایلر با دیدن Type-cast به PChar بلافاصله متوجه میشه که باید Reference-counter مربوط به اون رشته را در Heap افزایش بده، در نتیجه، رشته مربوطه توسط کامپایلر آزاد نمیشه.




> آقای کشاورز کم کم ما داریم از شما  چیزهای جدیدی یاد می گیریم .


خوشحالم که شما چیز جدیدی یاد می گیرید!




> Pchar یادگار دلفی 1.0 و یک نقطه ضعف برای دلفی محسوب میشه اون زمان برای   استفاده از API بالجبار به این صورت پیدا شد که حافظه گرفته شده  پس گرفته   نمیشه نه اون دلیلی که شما آوردید آدرس اولش ذخیره می کنه.


PChar در دلفی یک اشاره گر به کارکتر هست، مربوط به دلفی 1.0 هم نیست، عزیز من، PChar از زمان Turbo Pascal وجود داشته! عملکردش هم دقیقا مشابه عملکرد رشته ها در C هست، چون در C نوع داده string وجود نداره، و یک رشته به صورت اشاره گری به  آرایه ایی از کارکترها که آخرین کارکتر آن 0# باشه، نمایش داده میشه. تعریف PChar در دلفی به این شکل هست:
  PChar = ^Char;
که Char در اینجا تا قبل از دلفی 2009 مساوی بود با AnsiChar، و از دلفی 2009 به بعد مساوی شد با WideChar. پس در نسخه ایی مثل دلفی 2010، تعریف دقیق PChar به این صورت هست:
PChar = ^WideChar;
یعنی اشاره گری به یک کارکتر Wide (یک کارکتر دو بایتی).

PChar نه تنها در دلفی یک نقطه ضعف نیست، بلکه یک نقطه قوت هست؛ بیشترین کاربرد PChar در دلفی در فراخوانی توابع API ویندوز هست، چون توابع API ویندوز از C پیروی می کنند، و نوع داده string دلفی در C وجود نداره. برای اینکه رشته هایی مشابه C در دلفی وجود داشته باشه، باید اول بدونید که string در دلفی چطور نگه داری میشه. String در دلفی به صورت یک ساختار آرایه ایی در Heap نگه داری میشه، و هر متغیر String در واقع یک اشاره گر به یک ساختار آرایه ایی در Heap هست. این ساختار را در لینکی در پست قبلی قرار دادم، با تصویر نمایش دادم. به طور خلاصه، در این ساختار آرایه ایی، چند بایت اول برای نگهداری برخی خصیصه های داده مربوطه استفاده میشه، خصیصه هایی مثل اندازه رشته، مقدار Reference-counter، یا مقدار Codepage مربوط به یک AnsiString (در دلفی 2009 و نسخه های بالاتر). این خصیصه ها با اعداد منفی شماره گزاری میشند، چون یک متغیر String در دلفی دقیقا به محل شروع اولین کارکتر رشته در این ساختار آرایه ایی اشاره میکنه، و داده های قبل از آن با اندیس منفی، و داده های بعد از آن با اندیس مثبت بیان میشند. از اولین کارکتر رشته، تا آخرین کارکتر رشته به صورت خانه های یک آرایه در این ساختار قرار می گیرند. بعد از آخرین کارکتر رشته، دلفی به طور خودکار یک کارکتر 0# قرار میده. علت این کار هم سازگاری با C هست. پس stringها در دلفی، هر چند عملکردشان و ساختارشان با رشته ها در C تفاوت داره، ولی از اولین کارکتر رشته تا کارکتر 0# با رشته ها در C سازگار هست؛ پس اگر قرار باشه رشته ایی از دلفی به C انتقال داده بشه، کافیه مثل C یک اشاره گر به اولین کارکتر آن ارجاع بده. این دقیقا همون کاری هست که PChar انجام میده، و به همین دلیل هم در فراخوانی های توابع API ویندوز به کار برده میشه.

نوع داده PChar و نوع داده String در دلفی با هم سازگاری نوع (Type Compatibility) دارند، و شما می تونید به راحتی یک String را به PChar تبدیل نوع (Type Cast) کنید، یا یک PChar را به یک String اختصاص بدید. 

PChar کاربردهای دیگه ایی هم داره، مثلا یکی از کاربردهای PChar تغییر یک رشته بلند، بدون نیاز به کپی آن هست. در دلفی stringها به صورت Copy-on-write عمل می کنند، یعنی وقتی یک ساختار آرایه ایی شکل از یک string در Heap ایجاد شد، هر زمان که یه متغیر string دیگه به همان داده ارجاع بده، دلفی ساختار داده جدیدی روی Heap ایجاد نمیکنه، بلکه متغیر دوم به همان ساختار داده قبلی اشاره میکنه، و مقدار Reference-count آن ساختار یک واحد افزایش پیدا میکنه. زمانی که یکی از این متغیرها مقدارشان تغییر داده شود، دیگه اون متغیر به ساختار داده سابق ارجاع نمی دهد، پس دلفی یک کپی از ساختار داده قبلی بر روی Heap ایجاد می کند، و تغییر مورد نظر را بر روی آن اعمال میکنه. به عنوان مثال:
var
  Str1, Str2 : string;
begin
  Str1 := 'Test';
  Str2 := Str1;
end;

کد فوق دو ساختار داده حاوی عبارت Test ایجاد نمیکنه، بلکه یک ساختار آرایه ایی شکل حاوی Test برای Str1 ایجاد میشه، و Str2 به همان ساختار قبلی اشاره میکنه. حالا اگر مقدار Str2 به این شکل تغییر کنه:
Str2 := Str2 + ' Something else';
اون وقت دیگه Str2 و Str1 به یک ساختار اشاره نمی کنند؛ Str1 به همان ساختار قبلی خودش اشاره میکنه، و مقدار Reference-count آن ساختار یک واحد کاهش پیدا میکنه، و برای Str2 یک ساختار جدید بوجود میاد، و مقدار قبلی + تغییرات مورد نظر، در این ساختار جدید قرار می گیرند. به این تکنیک Copy-on-write گفته میشه.

حالا اگر شما یک رشته بلند داشته باشید، و بخواید مرتبا در آن تغییراتی بدون تغییر سایز رشته انجام بدید (مثلا یک کارکتر را با کارکتر دیگه ایی جایگزین کنید)، با هر تغییر شما، یک بار آن رشته بلند کپی می شود. این کار بسیار زمانبر هست. با استفاده از یک PChar می توانید بدون Copy کردن آن رشته بلند، تغییرات خودتان را در همان محل اعمال کنید.




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


ان شاء الله شما می دونید که اشاره گر (Pointer) چی هست! اشاره گر خودش به تنهایی ظرفی برای داده نیست، بلکه فقط به داده ایی اشاره میکنه. PChar هم ظرفی برای داده نیست، بلکه اشاره گری به آن هست. String یا AnsiString خودشان ظرفی برای داده هستند. یک اشاره گر میتونه به یک String یا AnsiString اشاره کنه. شما برای اینکه با PChar کار کنید، باید ابتدا داده ایی در جایی روی حافظه داشته باشید، تا PChar شما بتونه به آن اشاره کنه. این داده را شما می تونید به شکل مختلفی اختصاص بدید، ممکنه یک Buffer را خودتان با استفاده از توابعی مثل New یا GetMem اختصاص بدید، و خودتان طول عمر آن را کنترل کنید، یا اینکه از string یا Dynamic Array به عنوان بافر مربوطه استفاده کنید. مزیت استفاده از String یا Dynamic Array این هست که مدیریت حافظه این نوع از داده ها به طور خودکار انجام می شود، و برنامه نویس نیازی ندارد که خودش آنها را صراحتا آزاد کند. البته String یک حسن دیگه هم برای استفاده شدن به عنوان بافر داره، اون هم این هست که String با PChar سازگاری نوع داره، پس شما می تونید در کد خودتان با String کار کنید، و در آخرین مرحله، String مربوطه را به PChar تبدیل نوع کنید، و بعد از اتمام کار با PChar، مجددا با آن به عنوان یک String کار کنید.




> در مواردی که از مقداری زیادی pchar استفاده کنید حتی ممکن   با خطای memory corruption بشید.


اگر با اشاره گرها آشنایی داشته باشید، اصولا اشاره گرها چیزی نیستند که هر برنامه نویسی بخواد از آنها استفاده کنه، در واقع اشاره گرها اسباب بازی نیستند! یکی از دلایلی که در بسیاری از برنامه های نوشته شده با C توسط برنامه نویسان نابلد Memory Leak زیاد دیده میشه، و در زبان هایی مثل جاوا یا #C سعی شده برنامه نویس مستقیما با اشاره گرها درگیر نشه، همین هست که اشاره گرها باید به موقع و توسط برنامه نویسان ماهر استفاده شوند. شما از هر نوع اشاره گری، میخواد PChar باشه، یا Pointer بدون نوع، اگر بدون دقت، و در جای نامناسب استفاده کنید، موجب خرابکاری در حافظه میشید. این چیز عجیبی نیست! یکی از کاربران در امضاء خودش نوشته بود C++‎ is not for children (یا چیزی با همین مضمون). من باید بگم Pointers are not for children. اشاره گرها چیزی نیستند که بدون شناخت کافی از آنها، بخواید باهاشون سر و کله بزنید.




> البته اگر یه کمی اطلاعاتون از pchar بیشتر بود با ضعف کامپایلر در مورد   مدیریت حافظه pchar  به صورت متغیر داخلی هم می تونیستید این مشکل رو حل   کنید .


کامپایلر مشکلی در کار با PChar نداره، بلکه برنامه نویسی که از PChar استفاده میکنه، باید بدونه که PChar یک اشاره گر هست، و مدیریت اشاره گرها با خودِ برنامه نویس هست. برنامه نویسی که مثل شما به این حقیقت اشراف نداشته باشه، مسلما در کار با PChar به مشکل بر میخوره، و احتمالا مثل شما مشکل ناقص بودن اطلاعات خودش را به گردن کامپایلر زبان بسته میاندازه! 

اگر خیلی مایل به تکرار این ادعاهای خنده دار درباره PChar هستید، من حاضرم با هم سری به انجمن های Embarcadero یا StackOverflow بزنیم، و شما این ادعاها را آنجا مطرح کنید، و جواب های جالب کاربران با سابقه و شناخته شده دلفی را آنجا با هم مشاهده کنیم. البته اگر قبل از ارسال جواب، کاربران در StacOverflow به ادعای شما به خاطر جوک بودن، امتیاز منفی ندهند، و قبل از ارسال جواب تاپیک بسته نشه!




> مشکل این دوستمون به این طریق  میشه حل کرد.


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

کد شما:

procedure SavePosition;
var   s : pchar;
begin
  s := 'Hello';
  tag := longint(s);
end;

procedure UsePosition
begin
 ShowMessage(pchar(tag));
end;

حالا کد پیشنهاد شده در پست شماره 9 تاپیک:

procedure TForm1.Button1Click(Sender: TObject);
var
  LocalStr : string;
begin
 LocalStr := 'hi my friends';
 Self.Tag := integer(PChar(LocalStr));
end;

procedure TForm1.Button2Click(Sender: TObject);
var
 PC : PChar;
begin
 PC := PChar(Self.Tag);
 ShowMessage(PC);
end;

از تفاوت نام ها و اینجور چیزها صرف نظر می کنم، مدعی شدید که با استفاده از متغیر داخلی (منظورتون متغیر محلی یا Local Variable بود) میشه مسئله رو حل کرد، اما توفیق نداشتید که ببینید در کد ارائه شده در پست شماره 9 اتفاقا از یک متغیر محلی استفاده شده، تا نشان داده بشه که کامپایلر ساختار داده مربوطه در Heap را نگه میداره، هر چند اجرای کد از Scope متغیر محلی مربوطه خارج شده باشه.
همچنین در کد خودتان از LongInt برای Type-cast استفاده کردید، که تغییری در مسئله ایجاد نمیکنه، چون اساسا مشکل کد آقای tdkhakpur استفاده از عملگر @ بود، یعنی آدرس متغیر محلی مربوطه را در Tag ذخیره می کرد، که با خروج اجرای کد از Scope اون متغیر، اون مقدار فاقد اعتبار میشد. البته Type-cast کردن به صورت انجام شده در پست 9 (یعنی ابتدا به PChar و سپس به Integer) نسبت به کار شما این حسن را داره که Forward-compatible هست، یعنی کارکرد PChar به عنوان اشاره گر مشخص هست، کارکرد String هم به عنوان ظرفی برای کارکترها مشخص هست. در حال حاضر پیاده سازی String به صورت اشاره گری به داده ایی در Heap هست، و Type-cast آن به LongInt مشکلی ایجاد نمیکنه، اما اگر پیاده سازی string فرضا به گونه ایی تغییر کنه که دیگه اشاره گر نباشه (هر چند همچین چیزی بسیار غیر محتمل هست)، اون وقت این کد با مشکل مواجه میشه. در هر حال، این بخش اهمیت خاصی نداره، و نکته اون پست همان استفاده نکردن از عملگر @ به آن شکل بود، که جنابعالی عجله داشتید، فرصت مطالعه نداشتید!


درباره PChar در کتاب ها و  سایت های مختلف دلفی توضیح داده شده؛ من به عنوان اشاره، شما رو به مقاله آقای Rudy Velthuis از اعضای TeamB بورلند (سابق) ارجاع میدم: *PChars: no strings attached*
خواستم اول به صفحه مربوط به PChar در Delphi Basics ارجاع بدم، گفتم در شان شما نیست که من شما را به همچین سایت ابتدایی ارجاع بدم (!) اما شاید بعضی از سایر دوستان که مثل شما ادعای فضل آنچنانی ندارند، توضیحات مختصر اون سایت و مثال ساده اش براشون مفید باشه:
http://www.delphibasics.co.uk/RTL.asp?Name=PChar

----------


## 1485159

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

----------


## مصطفی ساتکی

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


باشه ما هیچ بلد نیستیم  وهم رفتارمونم بچگانست. شما که ادعایی خدایی میکنی  چرا لهنت اینطوریه.من هم این زبون دارم می تون مثل شما بهتون جواب بدم  انوقت منم میشم چیزی مثل شما.اگر من اشتباه گفتم بگو اینجا و اینجا اشتباست  برو تست کن.




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


شما هر کاری انجام بدی آزادی به شخصیت خودت لطمه می زنی.قضاوتو میزارم با  دوستان.
اون type-cast شما فضاهایی بوجود میاره که بی استفاده میشه.شما اگه دقت می  کردید من یه متغیر از نوع pchar تعریف کردم ولی در کد شما type-cast صورت  گرفته شما.




> گر با اشاره گرها آشنایی داشته باشید، اصولا اشاره گرها چیزی نیستند  که هر  برنامه نویسی بخواد از آنها استفاده کنه، در واقع اشاره گرها اسباب بازی  نیستند! یکی از دلایلی که در بسیاری از برنامه های نوشته شده با C توسط  برنامه نویسان نابلد Memory Leak زیاد دیده میشه، و در زبان هایی مثل جاوا  یا C#‎‎‎‎‎‎ سعی شده برنامه نویس مستقیما با اشاره گرها درگیر نشه، همین هست که  اشاره گرها باید به موقع و توسط برنامه نویسان ماهر استفاده شوند. شما از  هر نوع اشاره گری، میخواد PChar باشه، یا Pointer بدون نوع، اگر بدون دقت، و  در جای نامناسب استفاده کنید، موجب خرابکاری در حافظه میشید. این چیز  عجیبی نیست! یکی از کاربران در امضاء خودش نوشته بود C++‎‎‎‎‎‎ is not for  children (یا چیزی با همین مضمون). من باید بگم Pointers are not for  children. اشاره گرها چیزی نیستند که بدون شناخت کافی از آنها، بخواید  باهاشون سر و کله بزنید.


شما که با این قضیه آشنا هستی این type-cast ها بخاد بی نهایت بار انجام  بشه حافظه مربوطه چطوری آزاد میشه. این کار صورت نمی گیره و در نهایت خطای  memory corruption صورت می گیره.

من اگر متغیری از نوع pchar تعریف کردم یعنی این امکان رو قرار دادم اگر  user برنامه نویس بخاد حافظه رو ازallocmem کن رشته موردنظر رو با strcopy توش کپی کن و در تو روتین مقصد  بتونه با freemem آزاد کنه.

اگر با اون type-cast که نوشتید می تونید اون حافظه های اشغال شده رو آزاد  کنید لطفاً بفرمائید.
اگر هم گفتم کد جناب tdkhakpur غلطه اگر متغیر رشته بلند local باشه بعد اتمام روتین   Reference-counted  صفر و حافظه مربوط به اون آزاد شده  و اگر هم global تعریف بشه چه لزومی داره از tag استفاده کنیم داده رو تو همون متغیر نگه داری می کنیم.
در ضمن اگر حرفی از من شما رو ناراحت کرد معذرت میخام.

----------


## vcldeveloper

> شما که با این قضیه آشنا هستی این type-cast ها بخاد بی نهایت بار انجام   بشه حافظه مربوطه چطوری آزاد میشه. این کار صورت نمی گیره و در نهایت خطای   memory corruption صورت می گیره.
> 
> من اگر متغیری از نوع pchar تعریف کردم یعنی این امکان رو قرار دادم اگر   user برنامه نویس بخاد حافظه رو ازallocmem کن رشته موردنظر رو با strcopy  توش کپی کن و در تو روتین مقصد  بتونه با freemem آزاد کنه.
> 
> اگر با اون type-cast که نوشتید می تونید اون حافظه های اشغال شده رو آزاد   کنید لطفاً بفرمائید.


اون Type-cast فضای اضافی ایجاد نمیکنه، و Memory Leakایی هم بوجود نمیاره؛ اگر میخواید مطمئن بشید، کافیه ReportMemoryLeak را True کنید، و ببینید که شما اگر هزار بار هم آن کد را اجرا کنید، FastMM حتی یک بار به شما گزارش Memory Leak نمیده.
علتش هم ساده هست، PChar مربوطه اصلا از خودش داده ایی نداره که بخواد آزاد کنه. فقط به یک رشته اشاره میکنه. مدیریت حافظه آن رشته هم با خودِ دلفی هست، پس هر زمان که Reference-count آن رشته صفر بشه، دلفی آن فضا را آزاد میکنه. با آزاد شدن آن رشته، تنها اتفاقی که ممکنه برای اشاره گر شما بیافته، اینه که دیگه به فضای معتبری در حافظه اشاره نداشته باشه. این هم برای همه اشاره گرها صادق هست، شما اگر چند Reference به یک شی هم داشته باشید، وقتی اون شی آزاد میشه، وظیفه Nil کردن اون اشاره گرهایی که به اون شی اشاره می کردند، با شما ست. البته در مثال اون کد، حتی نیاز به Nil کردن هم نیست، چون همون اشاره گر استفاده شده، مانع از اون میشه که Reference-count شما صفر بشه.

----------


## مصطفی ساتکی

پس اگر فضایی نگیره عملا داره به heap اشاره می کنه و نیازی به type-cast مربوط به pchar هم نیست چون اگر این طوری باشه pointer خود دلفی نیز این آدرس به ما میده و مثل رشته های بلند بازهم هیچ تضمینی به ماندگریشون وجود نداره و هر لحظه ممکن override بشه مثلاً شما اگر داده هایی رو به فرض از database لود کنین تو حافظه در حدود 150MG داده بایستی تو heap بشینن و اگر فضای خالی در این محدوده وجود نداشته باشه حتماً کامپایلر از این فضاهایی که ما اشغال کردیم استفاده می کنه . چون این فضای به این اشاره گر اختصاص داده نشده تا ماندگاری دائم داده های که بهشون اشاره می کنن تضمن بشه .
پس نتیجه آخر همون میشه که اگر بخایم تضمینی برای پایداری این داده باشه برای اشاره مورد نظر فضای اختصاص داده بشه و در صورت استفاده کردند از داده بتونیم با آدرسش اون فضای اختصاص یافته به اونو ازش بگیریم.

tag :=integer(pchar(str);
بجاش این رو هم می تونیم بنویسیم.
tag := integer(pointer(str));

----------


## vcldeveloper

> پس اگر فضایی نگیره عملا داره به heap اشاره می کنه


بله، دقیقا به Heap اشاره میکنه؛ چون داده مربوط به String در Heap نگهداری میشه. این رو هم در پست های قبلی گفتم، هم در پست سال 87 که این اشاره کردن به خانه ایی از Heap را برای string و PChar با یک نمودار فرضی نمایش دادم (لینکش در پست شماره 12 همین تاپیک هست).




> بجاش این رو هم می تونیم بنویسیم.


بله، می تونید بنویسید، چون PChar هم یک Pointer هست. تفاوت این دو نوع نوشتن در این هست که وقتی از PChar استفاده می کنید، کد Type-safeتری دارید، ولی وقتی از Pointer که یک اشاره گر بدون نوع (Untyped pointer) است، استفاده می کنید، Type-safety را در کد خودتان رعایت نمی کنید. از نظر کارکرد، کارکرد اون PChar و اون Pointer مشابه هم هست، ولی از نظر اصول کدنویسی، در زبان های برنامه نویسی همواره توصیه میشه که تا حد امکان برنامه نویس از Untyped Pointers استفاده نکنه.

----------


## حسین شهریاری

سلام

همه دوستان ممنون تاپیک خوبی بود !!!همه از جمله خودم استفاده کردند.

ولی تنها چیزی که بنده را متعجب کرد لحن صحبت کردن دوستان با همدیگه بود!!

اینجا فقط و فقط مکان رفع اشکال و تبادل نظر هست.پس به عقیده بنده باید از بهترین و قشنگترین کلمات و عبارات استفاده بشه.

همگی موفق باشید

----------

