PDA

View Full Version : سوال: مشكل در استفاده تابعي از نوع ellipsis



HosseinProgrammer
جمعه 03 آبان 1387, 07:58 صبح
سلام،
تابع MyConCate به تعداد نامشخص پارامتر از نوع رشته ميگيره و كارش الحاق اين رشته هاست. پارامتر اول رشته ايه كه رشته ي ملحق شده توي اون قرار ميگيره. پارامتر آخر هم NULL است.


void MyConCate(char [], ...);

int _tmain(int argc, _TCHAR* argv[])
{
char Str[200];
MyConCate(Str, "String1 ", "String2 ", "String3", NULL);
return 0;
}

void MyConCate(char Str[200], ...)
{
char *ptrStack = &Str[99];
int i = 0;
ptrStack++;
while(*ptrStack != NULL)
{
Str[i] = *ptrStack;
ptrStack++;
i++;
}
Str[i] = NULL;
}


مشكل اينه كه پارامترها پشت سر هم در استك قرار نميگيرند. براي همين نميشه به اونها دسترسي داشت.

Nima_NF
جمعه 03 آبان 1387, 18:38 عصر
در ++C/C شما باید از ماکروهای اختصاص داده شده به خواندن آرگومان ها استفاده کنید یعنی :
va_arg و va_end و va_start

در یونیکس با هدر فایل varargs.h و در ویندوز با هر دوی stdarg.h و stdio.h

در این لینک قبلا با مثالی توضیح داده ام:
http://barnamenevis.org/forum/showthread.php?p=498223

برای توضیحات بیشتر برای هر کدام از این ماکرو ها می توانید از لینک MSDN زیر توضیحات را مشاهده کنید:
Access variable-argument lists (http://msdn.microsoft.com/en-us/library/kb57fad8%28VS.80%29.aspx)

نکته: ضمنا شما می توانید به هدر فایل های آن ماکرو ها بروید و نحوه پیاده سازی آن ها را نیز مشاهده کنید.

HosseinProgrammer
جمعه 03 آبان 1387, 19:25 عصر
ولي اگر به جاي اون رشته ها عدد وارد بشه و جمع اين اعداد رو بخواهيم، مشكل نداره. چرا؟



int MyAdd(int first, ...)
{
int *ptrStack = &first;
int s = 0;
while(*ptrStack)
{
s += *ptrStack;
ptrStack++;
}
return s;
}


اگر مثال رشته اي درست كار نكنه، دليل نداره كه اين يكي درست كار كنه. لطفا در مورد تفاوت هاي اين دو مورد راهنماييم كنيد. ممنون.

HosseinProgrammer
شنبه 04 آبان 1387, 08:30 صبح
توي تالار C# ، سوال ها و بحث هاي تاپيك ها خيلي سريع جواب داده ميشه. ولي چرا اينجا هيچ كس جوابي نميده؟ :ناراحت:

Nima_NF
شنبه 04 آبان 1387, 16:32 عصر
ای کاش حداقل کارهای گفته شده در پست قبلی را انجام می دادید یا کمی آن ها را مطالعه می کردید.

نکته: ضمنا شما می توانید به هدر فایل های آن ماکرو ها بروید و نحوه پیاده سازی آن ها را نیز مشاهده کنید. حتما این کار را انجام دهید تا نحوه بدست آوردن آدرس آرگومان ها را مشاهده کنید.

برای اینکه به پارامتر بعدی بروید همیشه نمی توانید به راحتی از ++ (یا همان به علاوه 1) استفاده کنید. این کار با توجه به معماری سخت افزاری پردازنده ها متفاو ت هست.
مثلا برای x86 به این شکل تعریف شده است:



#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )



ap همان ptrStack است که شما تعریف کردیه اید و آدرس اولین پارامتر در آن قرار می گیرد.
t نیز نوع داده ای است که می خواهید بخوانید مثلا برای رشته *char و برای عدد int قرار می گیرد و سپس با توجه به فرمول _INTSIZEOF محاسبه می شود تا آدرس پارامتر بعدی بدست آید. توجه داشته باشید که اشاره گر ها هر کدام با توجه به نوعشان اندازه متفاوتی دارند.
(در اینجا برای راحتی به جای فرمول های فوق از 4 بایت استفاده می کنیم).

- برای گرفتن آدرس نیز باید تبدیل صریح با reinterpret_cast صورت گیرد.
- برای گرفتن محتویات هر پارامتر نیز ابتدا آن را با ** به صورت ضمنی یعنی اشاره گر به اشاره گر تبدیل می کنیم.
مثال زیر 4 رشته را گرفته و نمایش می دهد:



#include <conio.h>
#include <iostream>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

using namespace std;

void MyConCate(char [], ...);

int main()
{
char Str[200] = "hello";

MyConCate(Str, "String1 ", "String2 ", "String3", NULL);

printf("text:%s", Str);
return 0;
}

void MyConCate(char Str[200], ...)
{
char* ptrStack = &reinterpret_cast<char &>(Str);

int x = 0;
while(x<4)
{
char* tempstr = (*(char **)ptrStack);
printf("text:%s\n", tempstr);

ptrStack += 4; // always not like this
x++;
}
getch();
}

HosseinProgrammer
شنبه 04 آبان 1387, 19:23 عصر
سلام. ممنون از پاسختون. راستش من دقت كردم و پست شما رو خوندم. ولي شايد مشكل از بي سوادي منه (چون زياد چيزي كه بدردم بخوره و دنبالش باشم دستگيرم نشد.)

الان هم دو تا سوال دارم ازتون:


(در اینجا برای راحتی به جای فرمول های فوق از 4 بایت استفاده می کنیم).

1- چرا 4 بايت؟ از كجا به اين نتيجه رسيديد؟ (شايد سوالم پيش پا افتاده باشه! واقعا متوجه نشدم!)

2- به جواب سوال قبليم هم نرسيدم! شما گفتيد كه:

برای اینکه به پارامتر بعدی بروید همیشه نمی توانید به راحتی از ++ (یا همان به علاوه 1) استفاده کنید.
ميخواستم بدونم كه اينكه پارامترها عدد باشه يا رشته، چه فرقي داره كه رشته جواب نميده ولي براي عدد روش ++ جواب ميده؟

ببخشيد كه سرتون رو درد اوردم.
پيشاپيش از جوابتون ممنونم.

Nima_NF
شنبه 04 آبان 1387, 20:56 عصر
1- وقتی شما از عملگر ++ استفاده می کنید به اندازه نوع داده ای که به آن اشاره می کند بایت اضافه می کند. یعنی وقتی ptrStack شما به یک char که فقط یک بایت است اشاره می کند پس با ++ یک بایت به آن اضافه می شود، در حالی که آدرس ها در این نمونه مثال ما 4 بایتی هستند. (اما در هر حال من این عدد را از همان فرمول بدست آوردم)

نکته: در بخش سورس کدها نوشته شده است بر اساس معماری Intel این مقدار به ضریبی از 4 گرد می شود.

برای اینکه برای هر نوعی و هر سیستمی بتوانید این مقدار را محاسبه کنید از فرمول های مشابه ذکر شده در پست قبلی استفاده کنید و خودتان ++ ننویسید. یا کلا از ماکروها ذکر شده استفاده کنید.

2- در مثال int شما ، ptrStack به یک int اشاره می کند (برخلاف مثال قبلی شما که *char برای رشته ها بود)، پس وقتی می نویسید ++ptrStack یعنی 4 واحد به آن اضافه می شود و برعکس رشته ها درست عمل می کند. ضمنا کلا کد نوشته شده برای رشته های شما غلط بود.



int *ptrStack = &first;

char *ptrStack = &Str[99]; // worng code
char* ptrStack = &reinterpret_cast<char &>(Str);

پس با تمام این تفاسیر چه برای *char و چه *int شما باید 4 بایت اضافه کنید تا به آدرس بعدی برود.