رفتن به مطلب
جامعه‌ی برنامه‌نویسان مُدرن ایران

سوال

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

char *doXOR(char *cData1, char * cData2)
{
	char cData[256];
	//

	for (int i = 0; i < 256; ++i)
	{
		cData[i] =cData1[i] ^ cData2[i];
	}
	//
	cData[255] = 0;

	return cData;
}

void usedoXOR()
{
	char cData1[256];
	char cData2[256];
	char cData3[256];
	//
	memset(cData1, 0, sizeof(cData1));
	memset(cData2, 0, sizeof(cData2));
	memset(cData3, 0, sizeof(cData3));

	strcpy(cData1, "In C, the following 6 operators are bitwise operators (work at bit-level)");
	strcpy(cData2, "Typical usage of a right shift operator in C can be seen from the following code.");

	strcpy(cData3, doXOR(cData1, cData2));
}

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

نظر شما چیست.

1 - نوشتن به این شکل مشکل دارد یا نه؟ 

2 - اگر مشکلی دارد آیا نحوه استفاده ما در usedoXOR  از خروجی doXOR به عنوان ورودی strcpy موجب شده تا این مشکل خود را نشان ندهد؟

3 -  آیا نوع کمپایلر و سیستم عامل میتوانند در پاسخ به دو سوال بالا تاثیرگذار باشند.

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر

6 پاسخ به این سوال تا کنون داده شده است

پست های پیشنهاد شده

  • 0
در در 3 مهر 1398 در 09:20، kambiz behnia گفته است :

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


char *doXOR(char *cData1, char * cData2)
{
	char cData[256];
	//

	for (int i = 0; i < 256; ++i)
	{
		cData[i] =cData1[i] ^ cData2[i];
	}
	//
	cData[255] = 0;

	return cData;
}

void usedoXOR()
{
	char cData1[256];
	char cData2[256];
	char cData3[256];
	//
	memset(cData1, 0, sizeof(cData1));
	memset(cData2, 0, sizeof(cData2));
	memset(cData3, 0, sizeof(cData3));

	strcpy(cData1, "In C, the following 6 operators are bitwise operators (work at bit-level)");
	strcpy(cData2, "Typical usage of a right shift operator in C can be seen from the following code.");

	strcpy(cData3, doXOR(cData1, cData2));
}

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

نظر شما چیست.

1 - نوشتن به این شکل مشکل دارد یا نه؟ 

2 - اگر مشکلی دارد آیا نحوه استفاده ما در usedoXOR  از خروجی doXOR به عنوان ورودی strcpy موجب شده تا این مشکل خود را نشان ندهد؟

3 -  آیا نوع کمپایلر و سیستم عامل میتوانند در پاسخ به دو سوال بالا تاثیرگذار باشند.

یک قانون کلی!

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

البته توجه داشته باشید که در تابع شما از حافظه هیپ استفاده نشده است، بنابراین در کد اسمبلی هم از bss. استفاده نشده بنابراین آدرس اشاره گر اکنون مجاز هست!

اگر کد اسمبلی تابع را مشاهده کنید متوجه میشیدید که ثباتی که وظیفه نگهداری آدرس stack متغیر محلی که در سگمنت کد تابع مقدار دهی شده، را داره بلافاصله بعد از برگشت از سگمنت کد به سگمنت تابع strcpy ارسال میشه! و ثباتی هم که وظیفه نگهداری این آدرس راداره از نوع غیر فرار non-volatile می باشد.

ولی اگر به حافظه هیپ اختصاص داده شده در یک تابع نیاز داشتید بهتر هست که تابع فراخوانی کننده مسئول تخصیص و باز پس گیری حافظه باشه! نه تابع فراخوانی شده! به این ترتیب تضمین میدهید که memory leak نخواهید داشت.

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

یک نمونه خیلی خوب Bill Blunden - Memory Management_ Algorithms and Implementation in C, C++ Wordware pub

در صورتی که علاقمند بودید بگید من پی دی اف این کتاب را دارم!

 
 

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر
  • 0
ارسال شده در (ویرایش شده)

درود بر شما؛

در در 3 مهر 1398 در 09:20، kambiz behnia گفته است :

1 - نوشتن به این شکل مشکل دارد یا نه؟ 

بله مشکل دارد، همانطوری که خودتان هم گفتید دارید آدرس یک متغیر محلی را از تابع بر می‌گردانید که این اخطار را هم از سمت کامپایلر هنگام کامپایل آن تابع دریافت می‌کنید ‌:

$ warning: function returns address of local variable

و برنامهٔ شما هم به احتمال زیاد Segmentation Falut داده و از بین می‌رود.

 

در در 3 مهر 1398 در 09:20، kambiz behnia گفته است :

2 - اگر مشکلی دارد آیا نحوه استفاده ما در usedoXOR  از خروجی doXOR به عنوان ورودی strcpy موجب شده تا این مشکل خود را نشان ندهد؟

 

در در 3 مهر 1398 در 09:20، kambiz behnia گفته است :

3 -  آیا نوع کمپایلر و سیستم عامل میتوانند در پاسخ به دو سوال بالا تاثیرگذار باشند.

برنامهٔ شما ممکن است که خروجی درستی نداشته باشد، چراکه دارید از یک حافظه‌ای داده را می‌خوانید که اصلاً وجود ندارد (حافظه آزاد شده است).

  • در کامپایلر MSVC2017 و سیستم‌عامل Windows 7 64bit کد را برای شما کامپایل و بدون مشکل اجرا می‌کند امّا خروجی درستی ندارید.
  • در کامپایلر MinGW و سیستم‌عامل Windows 7 64bit کد را کامپایل و اخطاری که در بالا اشاره کرده‌ام را داده و در هنگام اجرا برنامه با Segmentation Fault رو به رو می‌شود.
  • کامپایلرهای GCC و‌ Clang در سیستم‌عامل ArchLinux اخطار بالا را داده و همانند MinGW عمل می‌کند.
  • کامپایلر TCC در سیستم‌عامل ArchLinux نیز همانند MSVC2017 عمل می‌کند.

 

بهتر است که تابع را به این شکل بازنویسی کنید :

char* doXOR(char* cData1, char* cData2)
{
	char* cData = malloc(256);
  	assert(cData);
	for (int i = 0; i < 255; ++i)
		{ cData[i] =cData1[i] ^ cData2[i]; }
	cData[255] = '\0';
	return cData;
}

و همچنین موقع استفاده :

int main (void)
{
	/* ... */
	char* tmp = doXOR(cData1, cData2);
	strcpy(cData3, tmp);
	printf("%s\n", cData3);
	free(tmp); tmp = NULL;
  
	return EXIT_SUCCESS;
}

 

ویرایش شده در توسط قاسم رمضانی منش
تصحیح خطای نمونه کد.

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر
  • 0
در در 3 مهر 1398 در 09:20، kambiz behnia گفته است :

در برسی کدهای برنامه ای که قبلا نوشته شده بود و ظاهرا در حال حاضر بدرستی کار می کند

کدهای شما درست است و قابل اجرا هستند! به همین دلیل هم بدرستی کار میکنند!

نقل قول

بهتر است که تابع را به این شکل بازنویسی کنید :


char* doXOR(char* cData1, char* cData2)
{
	char* cData = malloc(256);
  	assert(cData);
	for (int i = 0; i < 255; ++i)
		{ cData[i] =cData1[i] ^ cData2[i]; }
	cData[255] = '\0';
	return cData;
}

و همچنین موقع استفاده :


int main (void)
{
	/* ... */
	char* tmp = doXOR(cData1, cData2);
	strcpy(cData3, tmp);
	printf("%s\n", cData3);
	free(tmp); tmp = NULL;
  
	return EXIT_SUCCESS;
}

با سلام

جناب رمضانی منش ، دلیل بهتر بودن کد خودتون را هم بیان کنید؟

 

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر
  • 0
ارسال شده در (ویرایش شده)
در 3 ساعت قبل، فرهاد شیری گفته است :

دلیل بهتر بودن کد خودتون را هم بیان کنید؟

علیکم‌السلام و درود بر شما؛ راستش با توضیحاتی که دادید خیلی گیج شدم و منتظرم که وقتی باشد تا کتابی که پیشنهاد دادید را بخوانم، چون کدهایی که دوستمان ارسال کرده‌اند درواقع برای من اصلاً جواب نداد و Segmentation Fault داده. و تا به چیزی که امروز من یادگرفته‌ام می‌دانم که آدرس یک متغیر local را نباید از تابع برگرداند و این دقیقاً کاری هست که در کد انجام شده و کاری که من کرده‌ام و دلیلی که بهتر دیده‌ام این بوده که حافظه‌ای در Heap  گرفته‌ام و آدرس آن را برگرداندم و این عمل بدون Segmentation Fault کار خود را انجام میدهد. امّا همینطوری که گفتم :

در در 4 مهر 1398 در 11:13، قاسم رمضانی منش گفته است :
  • در کامپایلر MSVC2017 و سیستم‌عامل Windows 7 64bit کد را برای شما کامپایل و بدون مشکل اجرا می‌کند امّا خروجی درستی ندارید.
  • در کامپایلر MinGW و سیستم‌عامل Windows 7 64bit کد را کامپایل و اخطاری که در بالا اشاره کرده‌ام را داده و در هنگام اجرا برنامه با Segmentation Fault رو به رو می‌شود.
  • کامپایلرهای GCC و‌ Clang در سیستم‌عامل ArchLinux اخطار بالا را داده و همانند MinGW عمل می‌کند.
  • کامپایلر TCC در سیستم‌عامل ArchLinux نیز همانند MSVC2017 عمل می‌کند.

اصلاً رفتار کد به درستی مشخص نبود و با توضیحات شما هم چیزی متوجه نشدم.

ویرایش شده در توسط قاسم رمضانی منش
اصلاح غلط کلمات.

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر
  • 0
در 12 ساعت قبل، قاسم رمضانی منش گفته است :

علیکم‌السلام و درود بر شما؛ راستش با توضیحاتی که دادید خیلی گیج شدم و منتظرم که وقتی باشد تا کتابی که پیشنهاد دادید را بخوانم، چون کدهایی که دوستمان ارسال کرده‌اند درواقع برای من اصلاً جواب نداد و Segmentation Fault داده. و تا به چیزی که امروز من یادگرفته‌ام می‌دانم که آدرس یک متغیر local را نباید از تابع برگرداند و این دقیقاً کاری هست که در کد انجام شده و کاری که من کرده‌ام و دلیلی که بهتر دیده‌ام این بوده که حافظه‌ای در Heap  گرفته‌ام و آدرس آن را برگرداندم و این عمل بدون Segmentation Fault کار خود را انجام میدهد. امّا همینطوری که گفتم :

درود بر جناب رمضانی منش گرامی! 

تشکر بابت توضیحی که دادید👏

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر
  • 0
در 20 ساعت قبل، فرهاد شیری گفته است :

کدهای شما درست است و قابل اجرا هستند! به همین دلیل هم بدرستی کار میکنند!

از بابت توضیح در پاسخ اول متشکرم  سعی میکنم کتابی را که معرفی کرده بودید حتما مطالعه کنم. احساس میکنم پاسخ دوم با پاسخ اول در تناقض باشد و بطور کلی استفاده به این شکل درست نباشد. خودم فکر میکنم چون مقدار بازگشتی تابع را مستقیما به دستور copy ارسال کرده ایم و احتمالا در آن از حافظه استک استفاده نشده در عمل دوچار مشکل نشده ایم ولی چنانچه اگر به صورت دیگری استفاده کنیم حتما مشکل حافظه خواهیم داشت مثلا خودم دوباره strcpy را بازنویسی کنم بطوریکه در ابتدا آن اقدام به تعریف یک آرایه 256 تای از char نمودم اگر چه در بدنه آن هیچ استفاده ای از آن نکردم ولی موجب خراب شده داده های مورد نظر من شد و تابع بهصورت درست عمل نکرد و وقتی آن حاظه را از استک نگرفت برنامه کار مورد نظر من را انجام داد.

یکی از دوستان برای چنین مواردی اصطلاح جالبی داشت "برنامه به غلط درست کار میکند."

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

مهمان
پاسخ به این سوال ...

×   شما در حال چسباندن محتوایی با قالب بندی هستید.   حذف قالب بندی

  تنها استفاده از ۷۵ اموجی مجاز می باشد.

×   لینک شما به صورت اتوماتیک جای گذاری شد.   نمایش به عنوان یک لینک به جای

×   محتوای قبلی شما بازگردانی شد.   پاک کردن محتوای ویرایشگر

×   شما مستقیما نمی توانید تصویر خود را قرار دهید. یا آن را اینجا بارگذاری کنید یا از یک URL قرار دهید.


  • کاربران آنلاین در این صفحه   0 کاربر

    هیچ کاربر عضوی،در حال مشاهده این صفحه نیست.

×
×
  • جدید...