PDA

View Full Version : مقاله: جايگزين تابع strcpy در C



mafia5
دوشنبه 04 شهریور 1387, 21:48 عصر
منم يه برنامه نوشتم كه كار تابع strcpy رو در c انجام ميده.
سخت نيست اما گفتم شايد براي تازه كارا مثل خودم خوب باشه:خجالت:





/*
In ye mesale sade az karie ke tabeye strcpy() anjam mide
www.programer.blogfa.com (http://www.programer.blogfa.com)
*/
#include<stdio.h>
#include<conio.h>
void ABcopy(char str1[5000]);
void output(char str1[5000],char str2[5000]);
void main()
{
char str1[5000];
printf("Please enter your string:");
gets(str1);
clrscr();
ABcopy(str1);
}//end of void main
//\/\/\/\/\/\/\/\/\/\/\/\/\
void ABcopy(char str1[5000])
{
long int j,i;
char str2[5000];
for(i=0,j=0;i<5000,j<5000;i++,j++)
{
str2[j]=str1[i];
}//end of for
output(str1,str2);
}//end of ABcopy
//\/\/\/\/\/\/\/\/\/\/\/\/\
void output(char str1[5000],char str2[5000])
{
puts("your string is:");
puts(str1);
puts("\n\nthe string that we copy your string in is:");
puts(str2);
getch();
clrscr();
}//end of output
//\/\/\/\/\/\/\/\/\/\/\/\/\

oVERfLOW
دوشنبه 04 شهریور 1387, 22:33 عصر
دوست عزیز قصد دلسرد کردن شما رو ندارم
ولی این کد نه جنبه‌ی آموزشی داره و نه جنبه‌ی کاربردی

توابعی که داخل کتابخانه‌ی استاندارد C هستن به بهترین شکل ممکنه بهینه‌‌‌‌سازی شدن و شما نمی‌تونید بهتر از اونا کدی رو توسعه بدید

mafia5
دوشنبه 04 شهریور 1387, 22:53 عصر
درسته

اما جنبه تمريني داره و خواستم تازه كارهايي مثل خودم با درون اين توابع آشنا بشن و فكر كنم مفيد باشه.

ولي حرف شما هم كاملآ درسته.

ممنون

bsng110
چهارشنبه 06 شهریور 1387, 17:07 عصر
هوالحکیم
سلام
من قبلاً یک ذره در مورد رشته ها یک نیم چه مطالبی نوشته بودم. یک سری از توابع رشته ها را هم پیاده سازی کرده بودم که در صفحه http://enghelper.com/forum/viewtopic.php?t=75 لینک فایل آنها وجود دارد ولی اکنون فایل آن خراب شده است ولی امیدوارم که مسئولین سایت، فایل ها را تا چند هفته آینده اصلاح کنند. به هر حال در آنجا سعی کردم کدهای رشته ها را با بهترین کیفیت پیاده سازی کنم. مثلاً strcpy به صورت زیر پیاده سازی شده است:


char *strcpy(char *dest, char *src)
{
char *result = dest;
while(*src)
*dest++ = *src++;
return result;
}

به هر حال بد نیست مطالب زیر را نیز برای دوستان قرار بدهم. به نقل از http://enghelper.com/forum/viewtopic.php?t=33&postdays=0&postorder=asc&start=16 :

ه نام آنکه جان را فکرت آموخت
رشته ها در ++C
صبر کنيد ؟! نگوييد "با رشته ها که آشناييم، پس ولش کن ؟!" . در اينجا مطالب جديدي آمده است که شايد با بعضي از آنها آشنا نباشيد و يا حتي ممکن است با نحوه صحيح عملکرد آنها آشنا نباشيد .
شاید با رشته ها آشنا باشید اما در این قسمت می خواهم مطالب بیشتری راجع به رشته ها به شما بگویم :
رشته ها در اصل یک عبارت متنی می باشند که بین دو عدد علامت (") قرار می گیرند . مثلاً "سلام" یک رشته می باشد . رشته ها در اصل آرایه ای از کاراکتر ها می باشند که در کنار هم دیگر قرار گرفته اند و در انتهای آنها نیز یک کاراکتر بی معنی به نام '0\' قرار می گیرد که بیان کننده انتهای رشته است و کد اسکی آن نیز صفر می باشد و این موضوع در پیاده سازی توابع مربوط به رشته ها به ما خیلی کمک می کند .
توابع زیادی در ارتباط با رشته ها وجود دارد که اعمال زیادی بر روی آنها انجام می دهند و شما نیز به عنوان یک برنامه نویس می توانید تمام این توابع را (بدون استثنا) پیاده سازی کنید . در اینجا بنده به معرفی توابع استاندارد ++C می پردازم :
*) ابتدا به توابع مربوط به کاراکتر ها اشاره ای می کنم که در فایل سرآمد Ctype.h موجود می باشند . اعلان توابع زیر به صورت ;(int function_name(int ch می باشد . خروجی این توابع در اصل یک متغییر bool می باشد اما چونکه در قدیم این متغییر توسط تایپ int پشتیبانی می شده است، اعلان و پیاده سازی توابع توسط int صورت گرفته است بنابراین خروجی توابع در حالت عادی یک عدد غیر صفر بوده و در صورت شکست، صفر (ارزش false) می باشد . ورودی توابع نیز در اصل یک کاراکتر می باشد :
1) تابع isascii : چنانچه ch یک کاراکتر از کد های ascii (از عدد صحیح 0 تا 127) باشد، خروجی true می باشد . توابع دیگر نیز با این پیش فرض عمل می کنند یعنی چنانچه isascii بر قرار بود و شرط خود تابع نیز بر قرار بود، خروجی true است وگرنه چنانچه ch یک کاراکتر isascii نبود، خروجی بدون در نظر گرفتن شرایط دیگر، false است .
2) تابع isalpha : چنانچه ch یک کاراکتر حروف الفبا (از 'A' تا 'Z' یا از 'a' تا 'z') باشد، خروجی true است .
3) تابع isupper : چنانچه کاراکتر ch یک حرف الفبایی انگلیسی بزرگ (از 'A' تا 'Z') باشد، خروجی true است .
4) تابع islower : چنانچه کاراکتر ch یک حرف الفبایی انگلیسی کوچک (از 'a' تا 'z') باشد، خروجی true است .
5) تابع isspace : چنانچه ورودی یک کاراکتر فضای سفید (' ' یا 't\' یا 'v\' یا 'r\' یا 'n\') باشد، خروجی true است . توجه کنید که 'r\' همان Carriage return می باشد و چاپ کردن آن موجب می شود که مکان نما به ابتدای سطر جاری برود . 'n\' همان New Line می باشد و موجب می شود که مکان نما به سطر بعد برود . توجه کنید که در اصل 'n\' فقط موجب می شود که مکان نما به سطر بعد برود و به عبارتی نباید مکان نما به ابتدای سطر بعد برود و این عمل در توابع مربوط به فایل سرآمد conio.h رعایت شده است اما در دیگر فایل های سرآمد یعنی stdio.h و iostream.h چنانچه 'n\' را چاپ نمایید، مکان نما به ابتدای سطر بعد می رود و شاید این امر کمی برای برنامه نویس مشکل بوجود آورد .
6) تابع isdigit : چنانچه کاراکتر ch یک رقم مبنای ده (از کاراکتر '0' تا '9') باشد، خروجی true است .
7) تابع isxdigit : چنانچه کاراکتر ch یک رقم مبنای شانزده (از کاراکتر '0' تا '9' و یا از کاراکتر 'a' تا 'f' و یا از کاراکتر 'A' تا 'F') باشد، خروجی true است .
8 ) تابع isalnum : چنانچه کاراکتر ch یک رقم مبنای ده و یا یک حرف از حروف الفبای انگلیسی (خواه بزرگ یا کوچک) باشد، خروجی true است .
9) تابع iscntrl : چنانچه کاراکتر ch، یک کاراکتر کنترلی (از کد اسکی صفر تا 31 و یا کد اسکی 127) باشد،
10) تابع isprint : چنانچه کاراکتر ch، یک کاراکتر قابل چاپ (از کد اسکی 32 تا 126) باشد، خروجی true است .
11) تابع isgraph : چنانچه کاراکتر ch، یک کاراکتر گرافیکی (مانند کاراکتر های isprint به جز کاراکتر های فضای سفید یا isspace) باشد، خروجی true است .
12) تابع ispunct : چنانچه کاراکتر ch، یک کاراکتر نشانه گذاری (هر یک از کراکتر های فضای خالی (isspace) یا کاراکتر های کنترلی (iscntrl)) باشد، خروجی true است .
نکته جالب اینجاست که توابع معرفی شده در اصل تابع نمی باشند، بلکه یک ماکرو می باشند که خیلی خوب و جالب طراحی شده اند . درک این موضوع که این ماکرو ها چه عملی انجام می دهند حتی برای اساتید نیز مشکل است اما چنانچه سراغ آنها رفته و نتوانستید بفهمید که این ماکرو ها چگونه کار می کنند، می توانید از بنده پرسیده تا جواب شما را بدهم . با توجه به مطالب مذکور، شما می توانید تمام ماکرو های بالا را توسط دستور پیش پردازنده undef تعریف نشده کرده و خود به صورتی که دوست دارید، آنها را در برنامه های خود پیاده سازی کنید . البته نا گفته نماند که دروغ هم نگفته ام چرا که در اصل در این فایل سرآمد هم توابع و هم ماکرو هایی با نام ها و عملکرد بالا وجود دارند که بنا به مقتضیات زمان کامپایل، فقط یکی از آنها به برنامه پیوند می خورند .
الف) توابع مربوط به فایل سرآمد Strting.h :
***) دقت کنید که در ++C، نام تمام توابع استاندارد زبان، با حروف کوچک نوشته می شود . همچنین ابتدای نام توابع زیر با نام str مخفف string شروع می شود .
1) تابع strlen ـ (len مخفف length به معنای طول) : اعلان این تابع به صورت ;(size_t strlen(const char *str می باشد که پارامتر آن رشته مورد نظر و خروجی آن یک تایپ صحیح بدون علامت می باشد که بسته به نیاز می توان آن را به صورت short یا long تعریف کرد اما در اکثر کامپایلر ها به صورت unsigned یعنی unsigned int تعریف شده است که بسته به محیط استفاده از کامپایلر و نوع آن، 2 یا 4 بایتی می باشد . خروجی این تابع توسط دستور typedef تعریف می شود . این دستور به صورت ; تایپ جدید تایپ قدیمtypedef به کار می رود مثلاً برای مثال بالا داریم : ;typedef unsigned size_t . نحوه عملکرد این تابع نیز به این صورت می باشد که این تابع تعداد کاراکتر ها را از ابتدا مکانی که اشاره گر str به آن اشاره می کند می شمارد تا به کاراکتر '0\' برسد . دقت کنید که خود '0\' شمرده نمی شود بنابراین رشته "" یک رشته تهی محسوب شده و طول آن صفر در نظر گرفته می شود . همچنین دقت کنید که رشته "Ali" با آرایه 'A','l','i' متفاوت می باشد چرا که رشته "Ali" در اصل برابر است با آرایه 'A','l','i','\0' .
2) تابع strcpy ـ(cpy مخفف copy به معنای کپی کردن) : اعلان این تابع به صورت ;(char * strcpy(char *dest,const char *src می باشد . این تابع بدون در نظر گرفتن اینکه dest به کجا و چه مقدار از حافظه اشاره می کند، از ابتدا تا انتهای رشته src (حتی '0\') را در dest به ترتیب کپی می کند . خروجی این تابع، همان dest می باشد و از خروجی می توان برای دفعات بعدی استفاده کرد مثلاً با این امکان می توان دستوراتی مانند ;(("int dest_size=strlen(strcpy(dest,"ALI نوشت که در این شبه کد، ابتدا رشته "ALI" در آدرسی که dest به آن اشاره می کند کپی شده و سپس (strlen(dest محاسبه می شود که این امر موجب می شود مقدار 3 در dest_size قرار بگیرد .
3) تابع strncpy : اعلان این تابع به صورت ;(char * strncpy(char *dest,const char *src,size_t maxlen می باشد . این همانند strcpy عمل می کند با این تفاوت که طول رشته src را نیز از ما می گیرد . چنانچه (maxlen<strlen(src باشد این تابع تنها به اندازه maxlen از src را در dest کپی می کند و '0\' نیز در انتهای رشته dest قرار نمی دهد (دقت کنید). چنانچه (maxlen=strlen(src باشد، این تابع شبیه strcpy عمل می کند ولی '0\' را در انتهای رشته dest قرار نمی دهد . چنانچه maxlen=strlen(src)+1 باشد این تابع عیناً مانند strcpy عمل می کند یعنی حتی '0\' را نیز در انتهای رشته قرار می دهد . چنانچه (maxlen>strlen(src باشد، این تابع به اندازه (maxlen-strlen(src یعنی به اندازه اضافه طول خواسته شده از طول src ، مقدار '0\' را در رشته dest کپی می کند . دقت کنید که در این تابع و تابع strcpy و توابعی مشابه که وظیفه کپی کردن یک رشته در آدرس دیگری دارند، این موضوع که آیا dest حافظه لازم را دارد یا خیر، بررسی نمی شود و چنانچه آرایه اول طولی کمتر از طول مورد نظر داشته باشد، در آدرس ادامه آن متغییر های ذخیره می شوند و ممکن است این آدرس مربوط به یک آرایه دیگر، یا حتی یک متغییر int یا float و ... باشد و موجب خراب شدن محتوای آن متغییر ها شود .
4) تابع stpcpy : اعلان و نحوه عملکرد این تابع عیناً شبیه تابع strcpy می باشد تنها تفاوت آن در خروجی می باشد . خروجی این تابع اشاره گر (dest+strlen(src می باشد (یعنی خروجی اشاره گری به کاراکتر تهی انتهای رشته می باشد که از خروجی آن می توان برای چسباندن رشته ای دیگر در انتهای رشته dest استفاده کرد و يا ...) .
5) تابع strxfrm : اعلان این تابع به صورت ;(size_t strxfrm(char *dest,const cahr *source,size_t num می باشد . رفتار این تابع شبیه strncpy می باشد . این تابع حداکثر به طول num-1 کاراکتر از رشته source را در رشته dest کپی می کند و طول رشته کپی شده را بر می گرداند . این تابع بر خلاف strncpy کاراکتر تهی نیز در انتهای رشته dest قرار می دهد به همین دلیل است که حداکثر به طول num-1 کاراکتر را کپی کرده و کاراکتر تهی را نیز به عنوان یک کاراکتر می شمارد به عبارتی می توان گفت عدد num طول آرایه dest می باشد . جهت درک بهتر عملکرد تابع، چهار حالت را در نظر می گیریم . اول اینکه num>=strlen(src)+1 باشد که در این صورت این تابع مانند strcpy عمل می کند و رشته src را به همراه کاراکتر تهی در رشته dest کپی می کند (دقت کنيد که به اندازه مازاد طول خواسته شده بر خلاف strncpy کاراکتر تهي در انتهاي رشته dest قرار نمي دهد). چنانچه num<strlen(src)+1 باشد، این تابع به طول num-1 کاراکتر از رشته src را در رشته dest کپی کرده و در موقعیت [dest[num-1 کاراکتر تهی قرار می دهد . به عبارتی این تابع آرایه dest را به طول num در نظر می گیرد . می دانیم که یک آرایه از اندیس صفر تا num-1 شماره گذاری می شود بنابراین همانطور که گفتم ابتدا کاراکتر تهی در موقعیت num-1 قرار گرفته و سپس یک شمارنده در نظر گرفته و از ابتدای رشته src تا هنگامی که به انتهای آن نرسیده و یا شمارنده از num-1 کوچک تر است، کاراکتر های رشته src را در رشته dest کپی می کند . به نظر من این تابع به نظر خودشان خیلی خوب نوشته شده اما در اصل حواسشان به یک نکته نبوده است ؟! Num یک متغییر بدون علامت است بنابراین فقط در یک نقطه مشکل بوجود می آید ؟! بله نقطه صفر یعنی اگر تابع به صورت ;(strxfrm(dest,"Ali",0 فراخوانده شود، با مشکل مواجه می شود چرا که ابتدا در نقطه 1-0 یعنی نقطه 1- کاراکتر تهی کپی شده و سپس چون شمارنده از 1- کوچکتر نیست تابع متوقف می شود و در اصل تابع یک نقطه غیر مجاز را دستکاری کرده است . خروجی تابع نیز همواره (strlen(src می باشد که واقعاً بی معنی است . بنابراین از من می شنوید چنانچه نیازمند چنین توابعی شدید، خودتان آنها را پیاده سازی کنید بهتر است چرا که حداقل می دانید چه عملی انجام می دهید .
6) تابع strcat ـ (cat مخفف Concatenate به معنای الحاق و پیوست کردن) : اعلان این تابع به صورت ;(char * strcat(char *dest,const char *src می باشد . این تابع src را در انتهای dest قرار می دهد یعنی پس از اجرای این تابع، رشته dest حاوی عبارت اولیه خود به اضافه عبارت src می باشد . مثلاً چنانچه ;{"char s1[20]={"Ali را داشته باشیم، با اجرای تابع ;("strcat(s,"reza رشته s به صورت "Alireza" در می آید . این تابع نیز داشتن حافظه در انتهای dest را بررسی نمی کند . دقت کنید که '0\' از انتهای رشته dest ابتدایی برداشته شده و عیناً رشته src از مکان '0\' به بعد کپی می شود و در پایان نیز کاراکتر '0\' قرار می گیرد به عبارتی دو تابع ;(strcat(dest,src و ;(strcpy(dest+strlen(dest),src یکسان عمل می کنند و حتی می توان در بدنه تابع strcat از همین دستور استفاده کرد .
7) تابع strncat : اعلان این تابع به صورت ;(char * strcat(char *dest,const char *src,size_t maxlen می باشد . این تابع مانند دستور ;(strncpy(dest+strlen(dest),src,maxlen عمل می کند .
8 ) تابع strdup ـ(dup مخفف Duplicate به معنای نسخه برداشتن) : اعلان این تابع به صورت ;(cahr * strdup(const char *str می باشد . این تابع وظیفه تکثیر یک رشته را دارد به عبارتی چنانچه یک رشته داشته باشیم و بخواهیم کپی آن را در یک اشاره گر قرار دهیم، از این تابع استفاده می کنیم . این تابع ابتدا به طول strlen(str) حافظه گرفته و سپس str را در مکانی که حافظه گرفته است، قرار داده و اشاره گر ابتدای این حافظه را بر می گرداند . نکته جالب در این تابع، برگرداندن یک اشاره گر است . شما می دانید که خروجی یک تابع توسط دستور return همه نوع متغییر (اعم از متغییری با تایپ مبنایی یا به صورت ساختار یا شیء) می تواند باشد به جز آرایه اما آرایه ها خود یک اشاره گر می باشند بنابراین می توان گفت خروجی تابع توسط دستور return یک آرایه نیز می تواند باشد که در اصل خروجی تابع اشاره گر ابتدای آرایه می باشد و ما این عمل را در تابع strdup می بینیم و حتی جالب تر اینکه نیاز به دانستن طول آرایه خروجی نیز نداریم زیرا طول یک رشته توسط کاراکتر '0\' مشخص می شود .
9) تابع strset : اعلان این تابع به صورت ;(char * strset(char *str,int ch می باشد . این تابع رشته str را توسط کاراکتر ch پر می کند . شاید بپرسید چرا ch را به صورت int تعریف کرده اند ؟!! دلیل اصلی را باید از خود برنامه نویسان اصلی این توابع بپرسید اما باید بدانیم که یک کاراکتر در یک متغییر صحیح نیز می تواند قرار بگیرد و اگر بعداً متغییر صحیح را به صورت char)ch) یعنی با استفاده از تفسیر دستی استفاده کنیم، مشکلی به وجود نمی آید .
10) تابع strnset : اعلان این تابع به صورت ;(char * strnset(char *str,int ch,int maxlen می باشد . این تابع به اندازه maxlen از رشته str را با کاراکتر ch پر می کند . چنانچه (maxlen>=strlen(str باشد، این تابع مانند strset عمل می کند و به تعداد اضافی maxlen توجهی نمی کند، به عبارتی تا زمانی که به '0\' نرسیده است، عمل می کند و نسبت به کاراکتر '0\' حساس است .
11 و 12) توابع strupr و strlwr ـ(upr و lwr مخفف کلمات Upper و Lower به مفهوم حروف بزرگ و حروف کوچک) : اعلان این توابع به صورت ;(char *function_name(char *str می باشد . خروجی هر دو همان اشاره گر str می باشد . هر دو تابع فقط بر روی حروف الفبای انگلیسی موجود در متن اثر می گذارند و در دیگر کاراکتر ها اثری نمی گذارند . تابع strlwr تمام حروف الفبایی رشته str را به حروف کوچک انگلیسی تبدیل می کند و تابع strupr تمام حروف الفبایی رشته str را به حروف بزرگ انگلیسی تبدیل می کند .
13) تابع strrev ـ(rev مخفف Reverse به معنای معکوس کردن) : اعلان این تابع به صورت ;(char * strrev(char *str می باشد . این تابع رشته str را معکوس کرده و اشاره گر str را بر می گرداند مثلاً رشته "MadAm" به رشته "mAdaM" تبدیل می شود و یا رشته "25" به رشته "52" تبدیل می شود .
14) تابع strtok ـ(tok مخفف Token به معنای نشانه) : اعلان این تابع به صورت ;(char *strtok(char *str_main,const char *srch می باشد . رفتار این تابع بسیار جالب بوده و طرز فکر آن نیز بسیار جالب تر است . فرض کنید از کاربر خواسته باشیم که تاریخ تولد خود را به فرم yyyy/mm/dd وارد کند اما برای جدا کردن سه قسمت می تواند از کاراکتر های دیگری مانند '.' یا '-' یا ',' نیز استفاده کند . حال می خواهیم قسمت های مختلف رشته خوانده شده را جدا کرده تا در مورد هر کدام پردازشی انجام دهیم . فرض کنید که کاربر می تواند روز و ماه و حتی سال را در تعداد رقم کمتری نیز وارد کند یعنی به جای روز هشتم ماه اردیبهشت سال 1358 بتواند هر یک از رشته های 08/02/1358 یا 8/2/58 یا 8/02/1358 یا ... را وارد کند به عبارتی نمی توانیم به جرأت بگوییم مکان پنجم محل کاراکتر جدا کننده است یا مکان سوم و ... . تابع strtok را به صورت ;("/.-,",char *part1=strtok(date صدا می زنیم . در احضار اول این تابع در شته date به دنبال هر یک از کاراکتر های رشته دوم یعنی "/.,-" می گردد . ترتیب کاراکتر های رشته دوم مهم نیست، آنچه مهم است پیدا کردن اولین موقعیت هر کدام از آنها در رشته date می باشد . به محض پیدا کردن آن موقعیت، این تابع به جای اولین کاراکتر رشته دوم، کاراکتر تهی قرار می دهد . مثلاً فرض کنید ما رشته "date="1358/2,-08 را وارد کرده ایم . این تابع در اولین احضار اشاره گری به ابتدای رشته برگردانده و همانطور که گفته شد، اولین کاراکتر '/' را نیز به کاراکتر NULL تبدیل می کند یعنی رشته "1358" را بر می گرداند و آرایه ما در اصل به "08-,2/0\1358" تبدیل می شود . در احضار دوم چنانچه بخواهیم بقیه موقعیت ها را پیدا کنیم باید تابع را به صورت ;(".,/-",char *part2=strtok(NULL صدا بزنیم . دقت کنید که به جای رشته اول، اشاره گر NULL به تابع می فرستیم و رشته دوم را نیز هر چه که خواستیم یعنی حتی می توانیم رشته "/." را بفرستیم و این دفعه تابع بر اساس این رشته جدید عمل می کند . اما ببینیم این دفعه تابع چه عملی انجام می دهد . تابع از موقعیت بعد از رشته "1358" یعنی از کاراکتر '2' در مثال ما، به دوباره به دنبال اولین کاراکتر رشته ".,/-" می گردد و دوباره به جای آن کاراکتر تهی قرار می دهد و اشاره گر به ابتدای رشته جدید را باز می گرداند یعنی رشته "2" را بر می گرداند . همانطور که مشاهده می کنیم کم کم داریم قسمت های مختلف رشته را مد نظر داشتیم، بدست می آوریم . در احضار سوم نیز تابع را به صورت قبل یعنی ;("/,-",char *part3=strtok(0 صدا می زنیم . مثلاً این دفعه چون مطمئن بوده ایم که کاراکتر '.' را وارد نکرده، آن را در رشته دوم قرار ندادیم و به جای NULL نیز از عدد صفر استفاده کرده ایم که برای کامپایلر همان معنی را می دهد . خوب ببینیم این دفعه تابع چه عملی انجام می دهد . همانطور که می دانیم رشته باقیمانده ",08" می باشد . این دفعه تابع از کاراکتر ',' ابتدای رشته گذشته و چون دیگر در رشته کاراکتر دیگری وجود ندارد، رشته "08" را بر می گرداند . چنانچه دفعه چهارم نیز تابع را به صورت ;("/.,-",char *part4=strtok(0 صدا بزنیم، چون دیگر رشته date به انتها رسیده است، اشاره گر NULL یا تهی خروجی تابع خواهد بود . اما یک سوال ؟ چرا برای دفعات دوم به بعد به جای رشته date ما اشاره گر NULL می فرستادیم ؟! اصلاً تابع از کجا می فهمید که باید از کجا به بعد به دنبال قسمت بعدی رشته بگردد ؟! شاید حدس زده باشید ! این تابع از یک متغییر استاتیک استفاده می کند که چون دفعه اول با رشته date تابع را صدا زدیم، مقدار دهی اولیه می شود و دفعات بعد که تابع را با اشاره گر NULL صدا زدیم، به جای رشته date از اشاره گر استاتیک خود استفاده می کند که به مکان بعد از قسمت اول اشاره می کند . مثلاً بعد از احضار اول تابع، اشاره گر استاتیک به رشته "08-,2/" اشاره می کند و یا در احضار چهارم اشاره گر استاتیک به رشته "" یعنی رشته تهی اشاره می کند و به همین دلیل هم NULL بر می گرداند . دیدید که تابعی بسیار جالب با طرز فکر بسیار جالبی بود و از این طرز فکر برای پیاده سازی خیلی از توابع می توانید استفاده نمایید .
***) تا به اینجا با توابعی که در رشته ها دستکاری می کردند آشنا شدیم . از این به بعد بیشتر با توابعی آشنا می شویم که با محتویات رشته کاری نداشته و اعمال چون مقایسه و جستجو را بر روی رشته ها انجام می دهند :
15) تابع strcmp ـ(cmp مخفف Compare به معنای مقایسه کردن) : اعلان این تابع به صورت ;(int strcmp(const char *str1,const char *str2 می باشد . این تابع مقایسه دو رشته را بر عهده دارد . چنانچه دو رشته عیناً یکسان باشند، مقدار صفر را بر می گرداند و چنانچه رشته str1 کوچکتر از رشته str2 باشد، مقداری منفی را بر می گرداند (str1<str2=>str1-str2<0) و چنانچه رشته str1 بزرگ تر از رشته str2 باشد، مقداری مثبت را بر می گرداند (str1>str2=>str1-str2>0) . بعضی ها فکر می کنند به هنگام کوچک تر بودن این تابع مقدار 1- و به هنگام بزرگ تر بودن مقدار 1+ را بر می گرداند ولی اکثر می گویند که مقداری منفی یا مثبت بر می گرداند ولی کمتر کسی می داند که واقعاً این تابع چه مقداری بر می گرداند . برای اینکه بهتر عملکرد این تابع را درک کنید، بنده نحوه پیاده سازی تابع را برای شما شرح می دهم . فرض کنید که از ابتدای دو رشته، حرف به حرف، مقایسه کنیم، چنانچه کاراکتر ها مساوی باشند و به انتهای رشته نرسیده باشیم، به سراغ کاراکتر بعدی می رویم . چنانچه کاراکتر ها مساوی نباشند، تفاضل [str1[i]-str2[i را بر می گردانیم به همین دلیل بود که در بالا نیز نوشته ام str1-str2 و به همین دلیل است که هر دفعه عدد خروجی برای دو رشته متفاوت کوچکتر، متفاوت است چراکه این تابع در اصل مقدار تفاضل دو کد اسکی را بر می گرداند و از اینجا می توان درک کرد که مقایسه این تابع نیز بر اساس کاراکتر های اسکی می باشد بنابراین رشته "Ali" از رشته "ali" کوچکتر است چرا که کد اسکی کاراکتر 'A' از 'a' کوچکتر است و اینجاست که باید به طراحان کد اسکی آفرین گفت چرا که طوری این کاراکتر ها را در کنار یکدیگر قرار داده اند که مقایسه این کاراکتر ها، مانند مقایسه الفبایی معمولی می باشد . مثلاً ما در دفترچه تلفن خود، نام "Ali" را قبل از "ali" قرار می دهیم و این کاراکتر ها نیز به همین صورت قرار گرفته اند و جالب تر اینکه "0Ali" از هر دو آنها کوچک تر است و قبل تر قرار می گیرد چرا که گد اسکی کاراکتر '0' از کد اسکی کاراکتر های 'A' و 'a' کوچکتر است . همچنین نکات جالب دیگری نیز در ارتباط با جدول کاراکتر های اسکی وجود دارد که در درس زبان ماشین و اسمبلی با آنها آشنا می شوید .
16) تابع strncmp : اعلان این تابع به صورت ;(int strncmp(const char *str1,const char *str2,size_t num می باشد . این تابع به طول num کاراکتر از رشته str1 را با رشته str2 مقایسه می کند . چنانچه num=0 باشد، این تابع مقدار صفر را بر می گرداند و چنانچه {(num>=max{strlen(str1),strlen(str2 باشد، مانند strcmp عمل می کند یعنی بیش از طول دو رشته که حافظه غیر مجاز می باشد را، بررسی نمی کند .
17) تابع stricmp : اعلان این تابع به صورت ;(int stricmp(const char *str1,const char *str2 می باشد . این تابع مانند strcmp عمل می کند فقط بین حروف بزرگ و کوچک انگلیسی تفاوتی قائل نمی شود یعنی تمام رشته های "Ali" و "ali" و "ALI" را یکسان می شمارد . البته ماکرویی با نام strcmpi نیز وجود دارد که معادل همین تابع می باشد یعنی به جای ;(stricmp(s1,s2 می توانید ;(strcmpi(s1,s2 نیز بنویسید که دومین دستور یک ماکرو می باشد که دستور اول که یک تابع می باشد را صدا می زند .
18 ) تابع strnicmp : اعلان این تابع به صورت ;(int strnicmp(const char *str1,const char *str2,size_t num می باشد . این تابع نیز مانند stricmp عمل کرده و به طول num کاراکتر مقایسه انجام می دهد . همچنین برای این تابع نیز یک ماکرو معادل با نام strncmpi وجود دارد که می توان به جای ;(strnicmp(s1,s2,num دستور ;(strncmpi(s1,s2,num را به کار برد .
19) تابع strchr ـ(chr مخفف Has Character به معنای کاراکتر داشتن) : اعلان این تابع به صورت ;(char *strchr(const char *str,char search می باشد . این تابع در رشته str به دنبال کاراکتر search می گردد و در صورت پیدا کردن آن کاراکتر، اشاره گر مربوط به کاراکتر در رشته را بر می گرداند و در صورت شکست، اشاره گر NULL یعنی صفر را بر می گرداند .
20) تابع strrchr : اعلان این تابع به صورت ;(char *strrchr(const char *str,char search می باشد . حرف 'r' وسط نام این تابع مخفف right می باشد . این تابع مانند strchr عمل کرده با این تفاوت که رشته را از سمت راست، مورد جستجو قرار می دهد .
21) تابع strstr : اعلان این تابع به صورت ;(char *strstr(const char *StrMain,const char *SubStr می باشد . این تابع چنانچه رشته SubStr را در داخل رشته StrMain یافت، اشاره گر ابتدای رشته یافت شده در رشته StrMain را بر می گرداند و در صورت شکست مقدار NULL را بر می گرداند .
22) تابع strpbrk ـ(pbrk مخفف string poniter to break) : اعلان این تابع به صورت ;(char *strpbrk(const char *str1, const char *str2 می باشد . تابع strtok را به خاطر می آورید ؟! این تابع به همراه دو تابع بعدی ارتباطی با تابع strtok دارند . این تابع در رشته str1 به دنبال هر کدام از کاراکتر های رشته str2 می گردد . چنانچه هر یک از کاراکتر های str2 را یافت، اشاره گر آن موقعیت را بر می گرداند وگرنه اشاره گر NULL بر می گرداند .
23) تابع strspn ـ(spn مخفف Span به معنای محدوده) : اعلان این تابع به صورت ;(size_t strspn(const char *str1, const char *str2 می باشد . این تابع طول رشته ای از سمت چپ از رشته str1 را بر می گرداند که از کاراکتر های str2 تشکیل شده است . مثلاً در احضار ;("num=strspn("lock is bad","cool مقدار 3 در num قرار می گیرد زیرا کاراکتر [str1[3 برابر 'k' می باشد که در رشته str2 وجود ندارد .
24) تابع strcspn : اعلان این تابع به صورت ;(size_t strcspn(const char *str1, const char *str2 می باشد . این تابع مانند strpbrk عمل می کند اما به جای اشاره گر طول رشته قبل از محل پیدا شدن اولین کاراکتر از رشته str2 را بر می گرداند به عبارتی از سمت چپ رشته str2 شمارش کرده و تا زمانی که به هیچ یک از کاراکتر های رشته str2 و کاراکتر تهی نرسیده، به سمت راست رشته حرکت می کند . مثلاً در احضار ;("int num=strcspn("Give thanks to allah","KlmNOpq مقدار 16 در num ذخیره می شود چرا که طول رشته ای از ابتدای str1 که در آن هیچ یک از کاراکتر های str2 وجود ندارد، برابر 16 می باشد (دقت کنید که هر سه تابع مذکور نسبت به بزرگ و کوچک بودن حروف حساس اند) . به عبارتی می توان گفت این تابع مانند strlen عمل می کند با این تفاوت که به جای حساسیت به کاراکتر تهی، به کاراکتر های رشته str2 و کاراکتر تهی حساس است .
25) تابع strerror: اعلان اين تابع به صورت ;(char *strerror(int ErrorNum می باشد . در مورد این تابع قبلاً نیز توضیح داده ام . این تابع شماره خطا را دریافت کرده و پیغام مربوط به خطا را بر می گرداند که می توان آن پیغام را بر روی صفحه نمایش چاپ کرد . شماره خطا را نیز توسط متغییر errno که در فایل های سرآمد errno.h و stdlib.h و stddef.h قرار دارد، می توان دریافت کرد . معمولاً خطا ها در توابع گرافیکی، فایل ها، توابع ریاضی و توابعی مشابه اینها رخ می دهد مثلاً اگر برنامه نتواند مد گرافیکی را تهیه کند و یا فایلی را باز کند و یا خروجی یک تابع ریاضی از محدوده مجاز متغییرش خارج شود و یا آرگومان نا معتبر برای آن فرستاده شود، متغییر errno با شماره رشته متناظر مربوطه اش تنظیم می شود .
ب) توابع مربوط به فایلStdlib.h :
1) توابع مربوط به تبدیل رشته عددی به یک متغییر عددی : گاهی اوقات یک رشته عددی در اختیار داریم و می خواهیم عدد مربوط به آن را در یک متغییر صحیح یا اعشاری ذخیره کنیم . مثلاًٌ فرض کنید از ورودی استاندارد یک عدد را به صورت رشته ای دریافت کرده ایم . برای تبدیل چنین رشته هایی به عدد معادل شان از سه تابع زیر می توان استفاده کرد :
كد:
int atoi(const char *str);
long atol(const char *str);
double atof(const char *str);توابع بالا به کاراکتر های سفید ابتدای رشته حساس نبوده و از آنها صرف نظر می کنند . در مرحله دوم، به کاراکتر '-' ابتدای رشته حساس بوده و عدد منفی نیز تولید می کنند . بعد از کاراکتر منفی نیز باید یک رشته ای از ارقام وجود داشته باشد . مقدار پیش فرض صفر می باشد یعنی به ازای رشته تهی و یا رشته هایی مانند "a" یا "-" یا " " یا "0" خروجی برابر صفر می باشد . چنانچه در هر مرحله کاراکتر نامعتبری شناسایی شود و یا رشته به انتهای خود برسد، ساختن عدد از آن مرحله متوقف می شود، مثلاً برای تابع atoi خروجی تمام رشته های "-2" یا " t -2\ " یا "3. 2- " یا "2.3-" یا "2a6-" یا "4#2-" برابر است با 2- . دقت کنید که ترتیب کاراکتر ها باید به همان صورتی باشد که بیان شد یعنی چنانچه کاراکتر سفید پس از ابتدای رشته و یا علامت '+' یا '-' یافت شود، تبدیل کردن متوقف می شود . در ضمن این توابع سر ریز را بررسی نمی نمایند مثلاً خروجی ;("int x=atoi("123456 غیر قابل پیش بینی است چرا که مقدار 123456 در یک متغییر صحیح جا نشده و سر ریز رخ می دهد . همچنین تابع atof می تواند به صورت نماد علمی نیز به کار برده شود یعنی خروجی رشته هایی به صورت " 2.3e6-" یا " t -23e+05\" یا "2300000-" یا "2300000.0-" نیز برابر است با عدد 2300000.0- . البته تابعی با نام _atold نیز وجود دارد که مانند atof عمل کرده و خروجی آن long double می باشد .
**** ادامه دارد ***

bsng110
چهارشنبه 06 شهریور 1387, 17:08 عصر
ادامه پست قبلی:
2) توابع مربوط به تبدیل یه متغییر عددی به رشته متناظرش :
این دسته از توابع بر عکس توابع قبلی عمل می کنند یعنی یک عدد را دریافت کرده و رشته معادلشان را تولید می کنند . اعلان توابع به صورت زیر می باشد :
كد:
char *itoa(int n, char *str, int radix);
char *ltoa(long n, char *str, int radix);
char *ultoa(unsigned long n, char *str, int radix);عملکرد این توابع بسیار جالب می باشد . عدد n بسته به نوع تابع می تواند صحیح، بزرگ و یا بزرگ بدون علامت باشد . اشاره گر str به رشته ای اشاره می کند که باید معادل رشته ای عدد n در آن ذخیره شود . دقت کنید که '0\' به صورت خودکار ذخیره می شود . خروجی نیز همان رشته str می باشد . بدیهی است که هیچ گاه خروجی رشته تهی نمی باشد . خروجی رشته itoa با در نظر گرفتن '0\' حداکثر به طول 17 می باشد و خروجی ltoa و ultoa با در نظر گرفتن '0\' حداکثر به طول 33 کاراکتر می باشد . radix مبنای عدد n را مشخص می کند که اعداد مجاز از مبنای 2 تا مبنای 36 می باشند و اگر مبنایی خارج از این محدوده وارد کند، (فقط در همین صورت) رشته خروجی برابر رشته تهی خواهد بود . می دانیم که در انگلیسی 10 رقم و 26 حرف وجود دارد که جمع آنها 36 می شود و به همین دلیل است که مبنای مجاز نیز تا 36 در نظر گرفته شده است . مثلاً عدد 57 در مبنای 2 برابر است با "111001" و یا در مبنای 3 برابر است با "2010" و یا در مبنای 4 برابر است با "321" و یا در مبنای 10 برابر است با "57" و یا در مبنای 36 برابر است با "1l" که توسط تابع toupper به "1L" تبدیل می شود . اما در مورد اعداد منفی اوضاع کمی فرق می کند . در مورد اعداد منفی در مبنای ده، یک عدد کاراکتر '-' در ابتدای رشته اضافه می شود . در مورد اعداد منفی در مبنای دو، چهار، هشت، شانزده و 32، به ترتیب مکمل دو، چهار، هشت، شانزده و 32 عدد در رشته ذخیره می شود که بعضاً به خصوص در مورد مبنا های هشت و 32 در ابتدای رشته یک کاراکتر '1' نیز قرار می دهد که حکماً می خواهد منفی بودن عدد را نشان دهد اما در مورد مبنا های دیگر هیچ کدام از توابع فوق عملکرد خوبی ندارند چرا که بعضاً در ابتدای رشته کاراکتر '1' را قرار می دهند ولی باز هم رشته حاصل با مکمل n ام عدد در مبنای n مساوی نمی باشد ؟! جالب تر اینکه در هیچکدام از مراجع حتی همین مقدار نیز در مورد این توابع توضیح داده نشده است و بنده با تست کردن مقادیر مختلف به این نتایج رسیده ام ؟!!!
3) اگر توانستید حدس بزنید ساده ترین راه برای نوشتن تابع ftoa چیست ؟! دقت کنید که این تابع باید بتواند عدد را به صورت نماد علمی نیز بنویسد . البته برای نوشتن این تابع فقط حالت مبنای ده را در نظر بگیرید .
ج) اما علاوه بر توابعی که در بالا اشاره شد دو دسته دیگر از توابع در فایل سرآمد String.h موجود اند . یک دسته که ابتدای نام توابع آنها پیش وند f_ وجود دارد مثلاً fstrcpy_ و ... که این توابع رفتاری عیناً شبیه توابعی که بدون پیشوند f_ هستند دارند با این تفاوت که پارامتر ها و خروجی های آنها اشاره گر هايی از نوع far می باشند و این موضوع به نوع کامپایلر ها بستگی دارد مثلاً در پروژه های چند فایل، گاهی ممکن است به دلایلی کامپایلر یک اشاره گر را با آدرس کامل segment-offset ذخیره کند که در این صورت تابع strcpy عمل نکرده و به شما خطا خواهد داد که ممکن است با تبدیل نام آن به fstrcpy_ مشکل بر طرف شود . اما دسته دیگری از توابع نیز وجود دارد که به جای str از پیشوند mem استفاده می کنند . این توابع برای کپی و یا اتصال و مقایسه و ... حافظه ها به کار می روند و بدیهی است که چون در حافظه دیگر انتهای آن توسط کاراکتر به خصوصی مشخص نمی شود، یک پارامتر سوم به توابع مذکور اضافه می شود که طول حافظه را مشخص می کند . این توابع در اصل، در فایل سرآمد mem.h قرار دارند و در اصل نیز باید در آنجا قرار می داشتند، اما به دلایلی یک کپی از آنها در این فایل سرآمد نیز قرار داده شده است و در بعضی از مراجع ++C آمده است که سرعت این توابع بیشتر از توابع متناظر رشته ایشان می باشد . (شاید به این دلیل که در پیاده سازی توابع مربوط به رشته ها از آنها کمک گرفته شده است) . نسخه هایی از این توابع نيز با پیشوند f_ وجود دارد که مانند توابع مشابه قبلی عمل می کنند، تفاوتشان فقط در نوع اشاره گر های ورودی و خروجی می باشد . به توابع زیر توجه کنید :
1- تابع memcpy : اعلان این تابع به صورت ;(void *memcpy(void *dest,const void *src,size_t n می باشد . این تابع مانند strncpy عمل می کند و n بایت از حافظه به آدرس src را در حافظه به آدرس dest کپی می کند . دقت کنید که * void یک تایپ مبنایی می باشد و معرف یک اشاره گر به هر جایی از حافظه می باشد به عبارتی * char یعنی اشاره گری به حافظه ای که در آن کاراکتر ذخیره شده است و * int یعنی اشاره گر به حافظه ای که در آن اعداد int ذخیره شده اند اما * void یعنی اشاره گر به حافظه ای که معلوم نیست در آن چه عبارتی ذخیره شده است بنابراین حتماً به هنگام استفاده از آن ابتدا باید آن را تفسیر کرده و سپس استفاده نمود . مثلاً فرض کنید ;void *ptr و ;char str[10]={"AliReza"},*pstr,ch را در اختیار داریم . جهت مقدار دهی ptr کافیست دستور ;ptr=str را بنویسیم . چنانچه بخواهیم اشاره گر pstr را با استفاده از ptr مقدار دهی کنیم پس از تفسیر ptr انتساب را انجام می دهیم یعنی دستور ;pstr=(char *)ptr را می نویسیم . چنانچه بخواهیم به کاراکتر های str دسترسی داشته باشیم (مثلاً کاراکتر شماره یک یعنی 'l' را در متغییر ch ذخیره کنیم) می توانیم از هر یک از دستورات ;[ch=((char *)ptr)[1 و یا ;(ch=*((char *)ptr+1 استفاده کنیم . نکته دیگر اینکه با استفاده از این تابع می توان یک آرایه از هر نوع را کپی کرد . مثلاً فرض کنید آرایه ای به طول size از تایپ type با نام a در اختیار داشته باشیم و بخواهیم آن را در آرایه b از همان نوع که طول کافی دارد، کپی کنیم . برای این منظور می توان از دستور ;((memcpy(b,a,size*sizeof(type استفاده کرد . دقت کنید که خروجی تابع memcpy اشاره گر dest می باشد که جهت انتساب آن باید از عمل قالب ریزی یا تفسیر مجدد استفاده کرد مثلاً : ;((int *iptr=memcpy(b,a,5*sizeof(int . همچنین نکته دیگر در ارتباط با این تابع نحوه تفسیر آن به هنگام کپی است ؟! گفتیم که به هنگام استفاده از * void ها باید آنها را تفسیر کرد، در توابع ...mem حافظه ها به صورت char تفسیر می شوند چرا که n معرف طول بر حسب بایت می باشد .
2- تابع memmove : اعلان این تابع به صورت ;(void *memmove(void *dest,const void* src,size_t n می باشد . این تابع نیز مانند strncpy عمل می کند اما نکته جالبی در ارتباط با توابع strncpy و memcpy وجود دارد . چنانچه ;{"char str[20]={"0123456789 را در اختیار داشته باشیم و هر یک از دستورات ;(strncpy(str+2,str,5 یا ;(memcpy(str+2,str,5 را اجرا کنیم، حدس می زنید حاصل رشته str چه خواهد بود : "0101010789" ؟! چرا ؟! ابتدا بیایید به مبدأ کپی نگاهی بیاندازیم . می خواهیم از ابتدای رشته به طول پنج کاراکتر در مقصد کپی کنیم یعنی رشته "01234" را در مقصد می خواهیم کپی کنیم . اما مقصد کجاست ؟! در وسط همین رشته یعنی در موقعیت کاراکتر '2' به بعد بنابراین پس از کپی شدن اولین حرف از پنج حرف، رشته مبدأ به "01034" تغییر می کند (رشته مقصد : "0103456789" دقت کنید که رشته های مبدأ و مقصد تداخل دارند و به عبارتی یکی می باشند ولی برای اینکه راحت تر بتوانید عملکرد تابع را تشخیص دهید، بنده در هر لحظه رشته های مبدأ و مقصد را به صورت مجزا نشان خواهم داد) و پس از کپی شدن دومین حرف، رشته مبداً به "01014" تبدیل می شود (رشته مقصد : "0101456789" ) و پس از کپی شدن سومین حرف، رشته مبدأ به "01010" تبدیل می شود (رشته مقصد : "0101056789" ) و از این به بعد رشته مبدأ دیگر تغییری نمی کند بنابراین چهارمین و پنجمین حرف در رشته مقصد کپی می شوند که نتیجه رشته "0101010789" خواهد بود . این طرز عملکرد توابع strncpy و memcpy بدیهی است اما شاید شما بخواهید که این گونه عملکرد تابع را تغییر دهید یعنی هنگامی که می خواهید رشته ای را در رشته ای دیگر کپی کنید، مطمئن باشید که رشته مبدأ کپی می شود نه رشته ای دیگر بنابراین می توانید از تابع memmove استفاده کنید . این تابع حتی برای آرایه های با تداخل نیز درست عمل می کند ؟! فکر می کنید چگونه ؟! راحت است : ابتدا یک کپی از مبدأ در یک آرایه موقتی ذخیره کرده و سپس آن آرایه موقتی را در مقصد کپی می کند که در اینصورت مطمئن است که تداخل مبدأ و مقصد مشکلی ایجاد نمی کند . خروجی نیز مانند memcpy و strncpy همان dest می باشد که مجدداً باید قالب ریزی شود .
3- تابع memccpy : اعلان این تابع به صورت ;(void *memccpy(void *dest,const void* src,int ch,size_t n می باشد . این تابع نیز مانند strncpy و memcpy عمل می کند اما به کاراکتر ch حساس است یعنی چنانچه در هنگام کپی آرایه مبدأ به مقصد به کاراکتر ch برخورد کند، ابتدا ch را در مقصد کپی کرده و سپس عمل کپی را متوقف کرده و اشاره گری به موقعیت بعد از کاراکتر ch در آرایه مقصد بر می گرداند . تابع stpcpy را به خاطر می آورید ؟! به احتمال زیاد با استفاده از این تابع به صورت زیر پیاده سازی شده است :
كد:
char * stpcpy(char *dest,const char *src)
{ return (char *)memccpy(dest,src,'\0',(unsigned long)-1);
}چند نکته در ارتباط با تابع مذکور : یادآوری که '0\' و کد اسکی صفر یکی می باشند بنابراین بنده از هر دو استفاده کرده ام که نشان دهم تفاوتی بین این دو نمی باشد و طول کپی را نیز در تابع memccpy، به اندازه ماکزیمم مقدار unsigned long در نظر گرفته ام که تابع نسبت به طول حساس نباشد بلکه نسبت به کاراکتر تهی حساس باشد . برگردیم سر تابع memccpy : اما چنانچه این تابع کاراکتر ch را در آرایه مبدأ پیدا نکرد، به طول n از آرایه مبدأ در آرایه مقصد کپی کرده و اشاره گر تهی بر می گرداند که حاکی از پیدا نکردن کاراکتر ch در آرایه مبدأ می باشد . دقت کنید، با وجود اینکه ch یک متغییر int است اما نمی توانید از آن برای متغییر های short و int و یا long و ... استفاده کنید بلکه فقط حق استفاده از آن به صورت یک متغییر char را دارید چرا که در این تابع به هنگام بررسی ch، آن را با یک متغییر کاراکتری مقایسه می کنند چرا که گفتم * void را به صورت * char تفسیر کرده و کپی می کند .
4 و 5 و 6- توابع memset و memcmp و memicmp : اعلان این توابع به صورت ;(void *memset(void *s,int c,size_t n و ;(void *memcmp(const void *s1, const void *s2,size_t n و ;(void *memicmp(const void *s1, const void *s2,size_t n می باشد . نحوه عملکرد عیناً شبیه strset و strcmp و stricmp می باشد فقط به جای اینکه انتهای آرایه با کاراکتر تهی مشخص شود، طول آرایه را به تابع می دهیم .
بحث مفصلی بود ...
امیدوارم که خسته نشده باشید ...
بنده که واقعاً خسته شدم چرا که چند هفته است مشغول نوشتن این مطلب می باشم ...
امیدوارم که لذت برده باشید ...
البته باید از مدیریت سایت نیز تشکر کنم چرا که ایشان فرمودند بهتر است در تابستان، دوستان را با توابع ++C آشنا کنید و بنده نیز در همین راستا سعی دارم که توابع زیادی را معرفی کنم ...
در پناه حق
خداحافظ

anubis_ir
چهارشنبه 06 شهریور 1387, 22:08 عصر
در باب امنيت كد نويسي و توابع ذكر شده در بالا:
استفاده از توابع زير منسوخ شده است: (چون امكان حملات buffer overflow را به شدت تسهيل مي‌كنند)
strcpy ، بجاي آن ازstrncpy استفاده كنيد.
strcat ، بجاي آن از strncat استفاده كنيد.
و ...

http://www.freebsd.org/doc/en/books/developers-handbook/secure-bufferov.html