رفتن به مطلب
جامعه‌ی برنامه‌نویسان مُدرن ایران
  • 0
قاسم رمضانی منش

اضافه کردن یک کاراکتر به انتهای رشته


سوال

درود بر دوستان عزیز؛

برای اضافه کردن یک کاراکتر به انتهای رشته شاید افراد به این‌صورت عمل کنند :

string[ strlen(string) ] = char;

امّا من خواستم بدون وابسته بودن این قسمت به libc؛ این عمل انجام شود به این‌صورت عمل کردم :

int main (void){
    /* Define variable */
    char c = 'm';
    char* string = NULL;

    /* Allocating memory */
    string = malloc(7);

    /* Copy char into `string` */
    strcpy(string, (char*)"ghase");

    /* Append `c` to end of `string` */
    *((char*)(&string + 1) - 2) = c;
    *((char*)(&string + 1) - 1) = '\0';

    /* Result */
    printf("%s\n", string);

    return EXIT_SUCCESS;
}

امّا با سیگنال SIGSEGV در تابع __strlen_sse2() هنگام فراخوانی printf() مواجه می‌شوم، کلاً حافظهٔ دریافتی از طریق Derefrence کردن string مقادیرش قابل دسترسی نیست (پیغامی که GDB نشان می‌دهد). ولی همچنان می‌توانم به‌اینصورت به مقداردهی که کرده‌ام دسترسی داشته‌باشم :

printf("%c\n", *((char*)(&string + 1) - 2));

الآن چه اتفاقی برای حافظهٔ متغیر string می‌افتد ؟

ویرایش شده در توسط قاسم رمضانی منش
تصحیح حالت رنگ‌آمیزی کدها.

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


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

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

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

  • 1

با سلام!

وقتی با استفاده از این دستور...

 *((char*)(&string + 1) - 2) = c;

اشاره گر string را به یک اشاره گر دیگه که به صورت implicit توسط کامپایلر در زمان کامپایل تعریف میشه( به صورت temp)، کد اسمبلی که تولید میشه به شکل زیر خواهد بود.

test.thumb.png.0200f291817cb60ff4d2f96efbf53684.png

کادر قرمز رنگ شماره 1، بنابراین همانطور که مشاهده میکنید کامپایلر برای نگهداری مقدار temp از ثبات edx استفاده کرده بنابراین بعد از اجرای دستور movzbl که به اختصار move zero-extended byte to long و در instruction بعدی هم مقدار ثبات 8 بیتی dl برابر با آدرس غیر معتبر شده و مقدار اشاره گر string که در eax هست برابر با صفر خواهد شد و در خط بعدی که شما در حال اضافه کردن یک کاراکتر اتمام رشته هستید بدون effect می باشد لذا در این خط خطایی صادر نمی شود ولی هنگامی که شما اشاره گر string را به printf پاس میدید در حقیقت دارید یک اشاره گر تهی را پاس میدید بنابراین یک سیگنال segment faulty دریافت میکنید مبنی بر اینکه یک memory leak رخ داده است.

در هر حال من کاملا متوجه نشدم که شما چرا آفست اشاره گر را به اضافه 1 کردید و مجددا دوتا به عقب برگشتید چون همانطور که مبینید دستور add $0x2 , %eax در دستورات شما باعث میشود که مقدار صفر تولید شود، در صورتی که همانطور که در کادر قرمز رنگ شماره 2 مشاهده میکنید باید توسط دستور lea که به اختصار load effective address خوانده میشه مقدار آفست لازم را در ثبات eax انتقال داد.

بنابراین به نظرم اصلا نیازی به انتقال یک اشاره گر در یک اشاره گر دیگه نیست مگه اینکه دلیل خاصی داره که برمن پوشیده است.

و البته اگر هم میگید که وقتی از این تعریف ...

printf("%c\n", *((char*)(&string + 1) - 2));

استفاده میکنید برنامه رفتار درستی داره! بله رفتار درسته بخاطر اینکه در حال اشاره به اشاره گر temp هست که در ثبات esp در سگمنت text تابع putc وجود داره، بنابراین خطایی نخواهید داشت.

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

والبته می توانید نحوه اضافه کردن کاراکتر را به رشته راهم اینطوری تعریف کنید، منتهی احتمالا یک هشدار از کامپایلر دریافت میکنید....

*((short*)(string+4)) = c;
OR
*((short*)&string[4]) = c;
OR
*(((char*)&string[4])+4) = c;
OR
(((char*)&string[4])[4]) = c;  
  

 

امیدوارم مفید فایده واقع بشه!

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


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

اینجا متوجه آن ۴ واحد اضافه کردن آخری نشدم، شما آدرس خانهٔ چهارم آرایه را تبدیل به *char کردید و سپس چهار واحد اضافه کردید (چرا ؟) و بعد به مقدارَش اشاره کردید.

در حقیقت اشاره به چهارمین عضو آرایه temp هست! البته من خودم هم این روش را نمی پسندم به نظرم از همه این کارها بهتر استفاده از std::copy هست ولی اگر نخوام وابستگی به STL داشته باشم از کست به اشاره گر short استفاده میکنم که بهینه تر هستش !

در 8 ساعت قبل، قاسم رمضانی منش گفته است :

متشکرم از توضیحتان

خواهش میکنم جناب رمضانی منش گرانقدر!

در 8 ساعت قبل، قاسم رمضانی منش گفته است :

اضافه کردن به آدرسی که به کل آرایه اشاره می‌کنه،

اگر هم منظور شما را درست متوجه شده باشم ایندکس شماره 1 آدرس آرایه را نمیده بلکه ایندکس شماره 0 آدرس کلی آرایه را میده!...

*((char*)(&string + 0) - 2) = c;
در 8 ساعت قبل، قاسم رمضانی منش گفته است :

که باعث میشه آدرس بعد از آرایه را به ما بده.

آدرس بعد از آرایه که تعریف کردید را می خواهید!! چه کاری این که رفتار تعریف نشده میشه!

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


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

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

  1.  اضافه کردن به آدرسی که به کل آرایه اشاره می‌کنه، که باعث میشه آدرس بعد از آرایه را به ما بده.
  2.  سپس type اون آدرس را به تبدیل به نوع *char می‌کنم.
  3.  و یک واحد (char) از آن آدرس کم می‌کنم که میشه خونهٔ آخر آرایه ما.

امّا ظاهراً اون اتفاقی که می‌خواستم صورت نگرفته.

درمورد روش‌هایی که پیشنهاد دادید :

در 11 ساعت قبل، فرهاد شیری گفته است :

*(((char*)&string[4])+4) = c;

 

در 11 ساعت قبل، فرهاد شیری گفته است :

(((char*)&string[4])[4]) = c;

اینجا متوجه آن ۴ واحد اضافه کردن آخری نشدم، شما آدرس خانهٔ چهارم آرایه را تبدیل به *char کردید و سپس چهار واحد اضافه کردید (چرا ؟) و بعد به مقدارَش اشاره کردید.

 

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

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


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

در حقیقت اشاره به چهارمین عضو آرایه temp هست!

میشه کاری کرد که اصلاً دست به اشاره‌گر string نزنه ؟ یعنی همان کد بالا ولی هیچ تغییری در محتوای اشاره‌گر string ایجاد نکنه که باعث بشه به ناکجاآباد اشاره‌کنه ؟

در 1 ساعت قبل، فرهاد شیری گفته است :

بلکه ایندکس شماره 0 آدرس کلی آرایه را میده!...

بله درست متوجه شدید، و بعد چون اون اشاره‌گر از نوع کل‌آرایه هست من تبدیلش کردم به نوع *char که بتونم خونه‌های ۱ بایتی را ازش کم کنم؛ که میشه خونهٔ آخر آرایه.

یه کم کامل‌تر بگم :

  • وقتی ما می‌نویسیم arr* یعنی مقدار خانه‌ای که arr به آن اشاره می‌کند را درخواست می‌کنیم (فرض می‌گیریم arr یک []char است).
  • حالا وقتی می‌گوییم arr& یعنی آدرس خانه‌ای که arr به آن اشاره می‌کند را می‌گیریم، با تفاوتش با arr + 2& این است که arr& آدرس کل خانه‌های arr را به ما می‌دهد  به جای آدرس یک خانه.
  • پس وقتی که arr + 1& می‌گوییم درواقع یک همچین چیزی است :
/*
        =================
        | x | x | x | x |
        =================
            \---+---/
                |
   char arr[]: -+
   
        =================
        | x | x | x | x |
        ======|==========
              |
              |
   arr + 1 : -+
  
         =================
         | x | x | x | x |
         =================
               ^
               |
   &arr + 1 : -+
  
     /  =================
     +> | x | x | x | x |
     \  =================
     +--------+
              |
   &arr : ----+
  
  
        ================= -------------...
        | x | x | x | x | |  |  |  |  |...
        ================= -------------...  
                            ^
                 +----------+
                 |
   &arr + 1: ----+
*/

و در انتها وقتی که ما نوع اشاره‌گر را به *char تغییر می‌دهیم و یک واحد کم می‌کنیم، درواقع یک واحد از نوع *char کم‌ می‌شود به جای یک واحد از نوع arr&. و اینطوری می‌توانیم به خانهٔ آخر آرایه به راحتی دسترسی داشته باشیم چرا که آن خانه در دسترس است. امّا اینکه چرا نمی‌توانم مقداری درآن خانه قرار بدم و آن رفتار کامپایلر اتفاق می‌اٌفتد عجیبه.

در 1 ساعت قبل، فرهاد شیری گفته است :

چه کاری این که رفتار تعریف نشده میشه!

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

#define END_OF_ARRAY(type, arr, index) \
    (*((type*) (&arr + 1) - index))

int main (void){
    char string[] = "ghasem is here";
    printf("%c\n", END_OF_ARRAY(char, string, 2));
    printf("%s\n", string);
    return 0;
}

 

ویرایش شده در توسط قاسم رمضانی منش
حذف ستاره‌های اضافه | اصلاح نگارش.

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


لینک به ارسال
به اشتراک گذاری در سایت های دیگر
  • 0
در در 14 شهریور 1398 در 09:53، قاسم رمضانی منش گفته است :

وقتی ما می‌نویسیم arr* یعنی مقدار خانه‌ای که arr به آن اشاره می‌کند را درخواست می‌کنیم (فرض می‌گیریم arr یک []char است).

بله این فرض درست است.در حقیقت دی رفرنس میکنیم.

در در 14 شهریور 1398 در 09:53، قاسم رمضانی منش گفته است :

حالا وقتی می‌گوییم arr& یعنی آدرس خانه‌ای که arr به آن اشاره می‌کند را می‌گیریم، با تفاوتش با arr + 2& این است که arr& آدرس کل خانه‌های arr را به ما می‌دهد  به جای آدرس یک خانه.

بله وقتی که arr& می گوییم یعنی آدرس شروع آرایه ، ولی نمی توانیم به یک اشاره گر temp ویا define شده بگوییم که هم آدرس arr را داشته باش وهم آدرس خودت.

برای اینکه قضیه برای شما روشن تر بشه مثالی میزنم..

دستورات زیر را مشاهده کنید...

char c = 'm';
char* string = NULL;

/* Allocating memory */
string = malloc(7);

/* Copy char into `string` */
strcpy(string, (char*)"ghase");

/* Append `c` to end of `string` */
//*((char*)(&string + 1) - 2) = c;
// *((char*)(&string + 1) - 1) = '\0';

char* tmp = (string + 1) ;
printf("%s\n", tmp);//print string -> hase
printf("%d\n", &tmp);//print address     -> 0x2686628
printf("%d\n", (&tmp+0));//print address -> 0x2686628
printf("%c\n", *(tmp+0));//print char    -> g

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

ولی در صورتی که چنین چیزی تعریف کنید یک رفتار تعریف نشده خواهید داشت...

char* tmp2 = (&string+1 );
printf("%s\n", tmp2);

آنهم به این دلیل که شما دارید یک اشاره گر به اشاره گر میسازید.

بنابراین اگر چنین تعریفی انجام بدید...

char** tmp3 = (&string);
printf("%s\n", *tmp3);//print string -> ghase

قطعا می توانید به آدرس آرایه اشاره کنید!

 

در در 14 شهریور 1398 در 09:53، قاسم رمضانی منش گفته است :

و در انتها وقتی که ما نوع اشاره‌گر را به *char تغییر می‌دهیم و یک واحد کم می‌کنیم، درواقع یک واحد از نوع *char کم‌ می‌شود به جای یک واحد از نوع arr&. و اینطوری می‌توانیم به خانهٔ آخر آرایه به راحتی دسترسی داشته باشیم چرا که آن خانه در دسترس است. امّا اینکه چرا نمی‌توانم مقداری درآن خانه قرار بدم و آن رفتار کامپایلر اتفاق می‌اٌفتد عجیبه.

بنابراین رفتار کامپایلر عجیب نیست، نوع تعریف شما یکم عجیبه!😅

در در 14 شهریور 1398 در 09:53، قاسم رمضانی منش گفته است :

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


#define END_OF_ARRAY(type, arr, index) \
    (*((type*) (&arr + 1) - index))

int main (void){
    char string[] = "ghasem is here";
    printf("%c\n", END_OF_ARRAY(char, string, 2));
    printf("%s\n", string);
    return 0;
}

دقیقا به همین علت دراینجا رفتار تعریف نشده پیش میاد.

در در 14 شهریور 1398 در 09:53، قاسم رمضانی منش گفته است :

میشه کاری کرد که اصلاً دست به اشاره‌گر string نزنه ؟ یعنی همان کد بالا ولی هیچ تغییری در محتوای اشاره‌گر string ایجاد نکنه که باعث بشه به ناکجاآباد اشاره‌کنه ؟

معلومه که نمیشه! چون توکد اسمبلی که مشاهده کردید، در زمانی که محتویات ثبات32 بیتی (edx) را به سگمنت bss. که آرایه string در اونجا تعریف شده انتقال میده مقدار این رجیستر 0 شده بنابراین آرایه شماهم NULL میشه!

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

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


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

😅تشکر بابت توضیحاتتون ممنون؛  اصرارم این بود که فکر می‌کردم اینکار ساده‌ای هست و نیازی نیست که بی‌مورد توابع libc را فراخوانی کنیم. البته به گمانم بشه کارهایی کرد ولی بهتر است که کمی اسمبلی را مطالعه کنم.

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


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

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 کاربر

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

×
×
  • جدید...