PDA

View Full Version : سوال: ارایه ای با طول متغییر (مثل ArrayList در C#)



ostovarit
سه شنبه 30 فروردین 1390, 15:49 عصر
با سلام و خسته نباشید
من خوب سرچ می کنم ولی نمیدونم چرا نتیجه نمیده!
می خوام ارایه زیر رو به یک ارایه داینامیک تبدیل کنم:

WCHAR * apd[100];


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

d->nArgs();


اینو نوشتم »

WCHAR * apd[d->nArgs()];


ارور Error 13 error C2466: cannot allocate an array of constant size 0 رو داد.

چه راه حلی هست؟

hi level hdd
سه شنبه 30 فروردین 1390, 17:24 عصر
با سلام
برای گرفتن حافظه پویا باید از تابع malloc استفاده کنی.
char * ptr = (char*)malloc(d->nArgs()*sizeof(char
این تابع مقدار فضایی رو که میخواهید تخصیص میده.
اگه بخواهید مقدار فوق را افزایش بدید از تابع realloc استفاده کنید
حتما بعد کار با آرایه پویا مقدار فوق را آزاد کنید که با تابع free همراه نام اشاره گر در داخل پرانتز استفاده میشه

ostovarit
چهارشنبه 31 فروردین 1390, 08:53 صبح
با سلام و تشکر
قبل از پرسیدن سوال این لینک (http://www.cplusplus.com/reference/clibrary/cstdlib/malloc/)رو و بعد هم پست شما رو خوندم ولی روش استفاد از malloc رو با توجه به نیازم متوجه نمی شم.
من میخوام یک ارایه داینامیک از نوع WCHAR * داشته باشم چطور باید خط زیر رو به یک تابع داینامیک تبدیل کنم؟

WCHAR * apd[100];

hi level hdd
چهارشنبه 31 فروردین 1390, 09:40 صبح
WCHAR *apd = (WCHAR*) malloc (d->nArgs() * sizeof(WCHAR))
داخل تابع malloc طول آرایه با حجم هر خونه رو باید وارد کنی که فکر میکنم WCHAR باید 2 بایت باشه و ضربدر طول آرایه ای که میخواهی، که
از تابعی که خودتون تعریف کردید به هم.ن اندازه میگیره.حالا چرا قبل از تابع malloc از تبدیل WCHAR استفاده کردیم به این دلیل هستش که این تابع
مقدار نا معلوم یا void* برمیگردونه .

ostovarit
شنبه 03 اردیبهشت 1390, 13:18 عصر
راهی به جز malloc وجود داره؟
وقتی از روش malloc ارایه رو تعریف می کنم جلوتر یک کد تبدیل دارم که بهش گیر میده. این کدی که تو اون قسمت دارم :

//APDUs Array
WCHAR * apd[3];
int cc=0;
int CharSet=0;

for(int i=1;i<d->nArgs()+1;i++)
{
//Create HEX Data
CString str = d->readParameter(i);
int nCount = str.GetLength();
CString csHexString;
csHexString.AppendFormat("%d",nCount);
for( int nIdx =0; nIdx < nCount; nIdx++ )
{
int n = str[nIdx];
CString csTemp;
csTemp.Format( _T("%x"), n );
csHexString += csTemp;
}

//if length<10 add 0
if(nCount<10)
csHexString ="0"+csHexString;

//Read apdus characters from apdus.txt
CString PathApduFile;
PathApduFile.Append(CurrentPath);
PathApduFile.Append("\\apdus.txt");
FILE *ApduFile = fopen(PathApduFile,"r+");
CString apduFinal=ReadFirstLine(ApduFile);
apduFinal=apduFinal.Mid(CharSet,8)+csHexString;

//Convert CString to WCHAR
LPSTR p = (LPSTR)(LPCTSTR)apduFinal;
USES_CONVERSION;
apd[cc]=A2W(p);

//next array element ID
cc++;

//next APDU str from apdus.txt
CharSet+=8;
fclose(ApduFile);
}


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

WCHAR *apd = (WCHAR*) malloc (d->nArgs() * sizeof(WCHAR))



در خطی که از ماکرو A2W برای تبدیل استفاده کردم ارور زیر رو میده :
cannot convert from 'LPWSTR' to 'WCHAR'

چه پیشنهاد و راه حلی دارید؟؟؟

با تشکر

mehdi.mousavi
شنبه 03 اردیبهشت 1390, 13:49 عصر
سلام.


USES_CONVERSION رو یک بار، بیرون حلقه بیارید. نیازی نیست هر بار در درون حلقه، این Macro فراخوانی بشه.
کدتون رو Refactor کنید، کدی که نوشته اید مطلقا خوانا نیست. این تابع رو به بخش های کوچکتری تقسیم کنید و توابع ایجاد شده رو فراخونی کنید.
برای ایجاد یک Array بصورت Dynamic (چیزی مثل همون ArrayList در C#)، می تونید از STL یا بطور دقیق تر، از std::vector استفاده کنید. برای این منظور میتونید بدین شکل عمل کنید:

std::vector<std::wstring> items;
items.push_back(L"Item 1");
items.push_back(L"Item 2");



Error ای که بهش اشاره کردید، کاملا گویاست. شما دارید سعی می کنید یک LPWSTR (یا همون WCHAR * رو) به WCHAR تبدیل کنید. چرا؟ چون شما با این خط از کد:

WCHAR *apd = (WCHAR*)malloc(10 * sizeof(WCHAR));

دارید حافظه برای یک Array به طوال 20 byte از heap می گیرید. به بیان دیگه، آرایه ای از 10 تا WCHAR می تونید داشته باشید. پس وقتی دارید اینو می نویسید:

apd[cc] = A2W(p);

دارید میگید که خونه cc ام از اون آرایه رو میخواهید مساوی یک WCHAR * بذارید.... خوب طبیعی هستش که نشه اینکارو کرد. مگر اینکه بنویسید:

apd[cc] = A2W(p)[0];

که اونوقت، اندیس صفرم از p (که یک WCHAR هستش) درون apd[cc] ریخته میشه و ... گمان میکنم با توضیحی که در بالا در مورد vector ها دادم، دیگه نیازی نداشته باشید این کارو کنید.

موفق باشید.

ostovarit
سه شنبه 06 اردیبهشت 1390, 10:46 صبح
سلام.



USES_CONVERSION رو یک بار، بیرون حلقه بیارید. نیازی نیست هر بار در درون حلقه، این Macro فراخوانی بشه.
کدتون رو Refactor کنید، کدی که نوشته اید مطلقا خوانا نیست. این تابع رو به بخش های کوچکتری تقسیم کنید و توابع ایجاد شده رو فراخونی کنید.
برای ایجاد یک Array بصورت Dynamic (چیزی مثل همون ArrayList در C#)، می تونید از STL یا بطور دقیق تر، از std::vector استفاده کنید. برای این منظور میتونید بدین شکل عمل کنید:


std::vector<std::wstring> items;
items.push_back(L"Item 1");
items.push_back(L"Item 2");




Error ای که بهش اشاره کردید، کاملا گویاست. شما دارید سعی می کنید یک LPWSTR (یا همون WCHAR * رو) به WCHAR تبدیل کنید. چرا؟ چون شما با این خط از کد:


WCHAR *apd = (WCHAR*)malloc(10 * sizeof(WCHAR));

دارید حافظه برای یک Array به طوال 20 byte از heap می گیرید. به بیان دیگه، آرایه ای از 10 تا WCHAR می تونید داشته باشید. پس وقتی دارید اینو می نویسید:

apd[cc] = A2W(p);

دارید میگید که خونه cc ام از اون آرایه رو میخواهید مساوی یک WCHAR * بذارید.... خوب طبیعی هستش که نشه اینکارو کرد. مگر اینکه بنویسید:

apd[cc] = A2W(p)[0];

که اونوقت، اندیس صفرم از p (که یک WCHAR هستش) درون apd[cc] ریخته میشه و ... گمان میکنم با توضیحی که در بالا در مورد vector ها دادم، دیگه نیازی نداشته باشید این کارو کنید.

موفق باشید.

من برنامه نویس خیلی خوبی نیستم ولی این پروژه هم نا جوانمردانه سخته ... و از اونجایی که از قبل نوشته شده و من فقط دارم قابلیت هایی رو بهش اضافه میکنم کلی طول کشید تا بفهمم داره چکار میکنه :
الان با وکتور تو dll خودم مشکلی ندارم ولی وقتی متغییر وکتور رو به متد میدم اون طرف نمیتونه این متغییر رو بگیره و استفاده کنه ... WCHAR * میگرفت و مشکلی با این نوع نداره سعی کردم تغییرش بدم که وکتور بگیره ولی امکان تعریف این نوع نیست سر فایلش #include <vector>رو که اضافه میکنم کلا بهش گیر میده و 104 تا ارور میده که یک درمون ایناست:
Error 2 error C2143: syntax error : missing '{' before ':'
Error 3 error C2059: syntax error : ':'
وقتی هم که خط زیر رو مینویسم :

using namespace std;

به کلمه namespace گیر میده و میگه تعریف نشده ....
Error 2 error C2061: syntax error : identifier 'namespace'

Properties های هر دو پروژه رو چک کردم و مشابه هم کردم ... به جز چند مورد که به نظرم ربطی نداره بقیه چیزاش یکیه!
(من از اول سر این پروژه نبودم ولی چیزی که میدونم اینه که این یک برنامه به زبان C بوده ور داشتن اوردن تو ویژال و با هزار بد بختی اینجا سرهمش کردن و اگر زیاد باهاش ور نری کار میکنه)

چه پیشنهادی دارید؟؟؟؟؟

mehdi.mousavi
سه شنبه 06 اردیبهشت 1390, 13:31 عصر
من برنامه نویس خیلی خوبی نیستم ولی این پروژه هم نا جوانمردانه سخته ...

سلام.
متاسفانه یا خوشبختانه، دشوار بودن کد نویسی در C++ همواره بخشی از این زبان برنامه نویسی بوده و عموما نیز به نوع پروژه ارتباطی پیدا نمیکنه. تازه الان شما با ایرادهایی مواجه شده اید که در Compile-Time باهاشون روبرو شده اید، صبر کنید برنامه Release بشه. اونوقت متوجه ایرادهای احتمالی در Runtime میشید که رفع اونها گاهی به صرف زمان بسیار زیادی نیاز داره. در هر حال، هر چی بیشتر کد بنویسید، بیشتر با خصوصیات این زبان آشنا میشید.


الان با وکتور تو dll خودم مشکلی ندارم ولی وقتی متغییر وکتور رو به متد میدم اون طرف نمیتونه این متغییر رو بگیره و استفاده کنه ... WCHAR * میگرفت و مشکلی با این نوع نداره سعی کردم تغییرش بدم که وکتور بگیره ولی امکان تعریف این نوع نیست سر فایلش #include <vector>رو که اضافه میکنم کلا بهش گیر میده و 104 تا ارور میده که یک درمون ایناست Properties های هر دو پروژه رو چک کردم و مشابه هم کردم ... به جز چند مورد که به نظرم ربطی نداره بقیه چیزاش یکیه!

متاسفانه پیدا کردن دلیل این مساله، (مثل خیلی از ایرادهایی که در زبان C++ باهاش مواجه میشیم) ساده نیست و بدین شکل میسر نیستش. اگر نمی تونید از STL استفاده کنید، یکبار دیگه به سراغ همون WCHAR * برگردید. اما این بار درست و حسابی متوجه موضوع بشید، بعد شروع به نوشتن کد کنید. به کد زیر دقت کنید:

int const len = 5;

WCHAR **pp = (WCHAR**)malloc(len * sizeof(*pp));
for(int i = 0; i < len; i++)
{
pp[i] = (WCHAR*)malloc(100 * sizeof(*pp[i]));
}


اینجا من یک WCHAR ** تعریف می کنم. فرض کنید قصد دارم 5 تا WCHAR * نگهداری کنم. پس میام به اندازه نیازم برای WCHAR ** حافظه کنار میذارم (ممکنه چشم هاتون سیاهی بره بخاطر تعداد ستاره ها، اما خوب، باید قدری دووم بیارید تا متوجه موضوع و ایراد اولی که در این پست بهش اشاره کردید بشید). این نیاز چقدره؟ اینقدر:

len * sizeof(*pp)

یعنی 5 * 4 ... یعنی میخواهیم یک آرایه 5 عضوی از WCHAR * ها داشته باشیم. sizeof(WCHAR *) هم میشه 4... حالا یه Level میریم جلو. باید توی یک Loop، بیاییم بگیم این 5 تا خونه ای که کنار گذاشتیم، میخواهیم هر کدومشون آرایه ای از WCHAR ها رو (بطول 100) نگهداری کنن. پس باید حافظه مورد نظر رو برای هر یک بصورت جداگانه Allocate کنیم. اون malloc ای که توی حلقه میبینید، برای اینکار هستش... به این ترتیب، شما آرایه ای با طول متغیر (اینجا، 5) از آرایه ای از WCHAR ها بطول متغیر (اینجا 100) خواهید داشت. حالا توی حلقه هر طور که مایلید میتونید با pp[i] کار کنید و مقادیر مورد نظر رو در اون بریزید، یا با Macro هایی که قبلا خدمتتون عرض کرده بودم اونها رو به CHAR * و ... تبدیل کنید و ... فقط فراموش نکنید که حافظه های گرفته شده رو در پایان کار آزاد کنید.

موفق باشید.

ostovarit
چهارشنبه 07 اردیبهشت 1390, 10:16 صبح
با تشکر زیاد از توضیحاتی که دادید... کامل و مناسب با سطح من بود به نتیجه ای که میخوام رسیدم
و برای آزاد کردن حافظه از متد free استفاده کردم.

- من الان اطلاعات ثابتی رو درون این ارایه میریزم مثلا نام ozhan فامیل ostovar و شماره 9891281 ولی در واقعیت این dll به یک دیتابیس متصل میشه و داده های متفاوتی رو در هر بار call شدن دریافت میکنه که در حال حاضر امکان تست کردنش رو ندارم ... ممکنه که در call شدن همزمان متد داده ها با هم تداخلی داشته باشن؟؟؟

- ما اینجا ماکسیمم طول هر WCHAR رو 100 گرفتیم ... این یعنی حداکثر 100 کاراکتر در هر خانه از ارایه میتونه ذخیره بشه ... پس من باید بیشترین طولی رو که حدس میزنم در هر خانه از ارایه قرار ذخیره کنم رو جایگذاری کنم؟

mehdi.mousavi
چهارشنبه 07 اردیبهشت 1390, 10:33 صبح
با تشکر زیاد از توضیحاتی که دادید... کامل و مناسب با سطح من بود به نتیجه ای که میخوام رسیدم

سلام.
خواهش میکنم. خوشحالم مشکل رفع شد.


و برای آزاد کردن حافظه از متد free استفاده کردم.
درسته.


من الان اطلاعات ثابتی رو درون این ارایه میریزم مثلا نام ozhan فامیل ostovar و شماره 9891281 ولی در واقعیت این dll به یک دیتابیس متصل میشه و داده های متفاوتی رو در هر بار call شدن دریافت میکنه که در حال حاضر امکان تست کردنش رو ندارم ... ممکنه که در call شدن همزمان متد داده ها با هم تداخلی داشته باشن؟؟؟

ببینید. این سوال بر میگرده به اینکه چطور از این آرایه در برنامه استفاده کنید. اگر قرار هستش این آرایه توسط چند Thread بصورت همزمان دستکاری بشه، باید دسترسی به اون Thread-Safe باشه. در غیر اینصورت، نیازی نیست نگران همزمانی باشید.


ما اینجا ماکسیمم طول هر WCHAR رو 100 گرفتیم ... این یعنی حداکثر 100 کاراکتر در هر خانه از ارایه میتونه ذخیره بشه ... پس من باید بیشترین طولی رو که حدس میزنم در هر خانه از ارایه قرار ذخیره کنم رو جایگذاری کنم؟
ترجیحا بله. شما می تونید با استفاده از realloc حافظه جدیدی "مجددا تخصیص بدید"، اما این کارو هرگز توصیه نمی کنم. اگر براتون امکان داره همون بار اول به میزان لازم حافظه مورد نظر رو اختصاص بدید و با همون حافظه کار رو انجام بدید تا نیازی به realloc نباشه.

موفق باشید.