[ بستن ]

سیستم وبلاگ پارسی باکسسیستم مدیریت فروش هاست و دامینسایت شخصی محسن داوری برنامه نویس PHPهماهنگی با موتورهای جستجو , رنکینگ گوگل , google pagerank , page rank , seo , search

  انجمن اینترنتی برنامه نویسان  
  برنامه نویسی و رفع اشکال و نرم افزارها  
 
منوی اصلی

بخش ها

نویسندگان

آرشیو

آمار
بازدید امروز : 56
بازدید دیروز : 143 ‍
بازدید این ماه : 3652
بازدید امسال : 12390
بازدید کل : 23454
تعداد پست ها : 87
تعداد لینک های لینکستان : 26
تعداد نظر سنجی های وبلاگ : 2

 
سخن روز
سلام دوستان از امروز برای خواندن مطالب این وب سایت می توانید در خبرنامه ویژه ما عضو شوید تا رمز مطالب برای شما ارسال شود. کلیه دوستان می توانند مقالات و نوشته های خود را در رابطه با علوم کامپیوتری به ایمیل وب سایت ارسال نمایند تا در وب سایت برای نمایش عموم گذاشته شود... خواهشمند است در پایان مقالات خود نام و نام خانوادگی و استان و رشته تحصیلی و سن خود را ذکر نمائید.به مقالات برتر مبلغ ای اهدا خواهد شد. با تشکر _ مدیریت سایت محمد حسن بهجت شماره ارسال مسیج : 09133522892


اشاره گر ها / قسمت 6
 
  • انتقال مقادیر به تابع

      حال ببینیم وقتی مقادیری را به تابع گذر می‌دهیم ، چه اتفاقی رخ می‌دهد . در اینجا برنامه ساده‌ای را ملاحظه می‌کنید که دو مقدار صحیح 4 و 7 را به تابعی به نام gets2  می‌فرستد :

main ( )

  {

     int  x = 4 , y = 7 ;

     gets2 (x , y) ;

   }

void  gets2(xx , yy)    /* print out values of two arguments */

int  xx , yy ;

 {

    printf ("first is %d , second is %d" , xx , yy) ;

  }

این تابع عمل خاصی انجام نمی‌دهد ، فقط دو مقداری را که به آن گذر داده شده است ، چاپ می‌کند ؛ اما تابع مزبور نکته مهمی را نشان می‌دهد :

نکته مهم این است که تابع دو مقدار از برنامه فراخواننده آن دریافت می‌کند و آنها را بطور جداگانه ، یعنی به‌صورت دوبله در فضای حافظه خاص خودش ذخیره می‌کند . حتی تابع می‌تواند به آن دو مقدار ، اسامی متفاوتی (مانند مثال مورد نظر ما) که فقط در تابع مزبور شناخته شده است ، اختصاص دهد ، که در اینجا این اسامی جدید نیز xx و yy است که به جای x و y بکار رفته است (البته می‌توانست همان x و y نیز بکار برده شود) .

شکل زیر این مکانیسم را نمایش می‌دهد . حال این تابع می‌تواند روی متغیرهای جدید xx و yy بدون اینکه تأثیری روی x و y داشته باشد ، هر عملی را انجام دهد ( اگر اسامی یکسان انتخاب می‌شد ، باز هم در شیوة کار تغییری حاصل نمی‌شد ) .

 

 

 

 

  • انتقال اشاره‌گر به تابع

      اشاره‌گرها ، اغلب بعنوان آرگومان ، به یک تابع گذر داده می‌شود . این عمل اجازه می‌دهد که عناصر داده‌های برنامه فراخواننده (که معمولا تابع main است) به‌وسیلة تابع فراخوانده شده ، قابل دستیابی باشند و در داخل تابع فراخوانده شده تغییر یابند و نتیجه در تابع یا برنامه فراخواننده نیز اعمال گردد . این گونه کاربرد اشاره‌گر را گذر دادن آرگومانها به ‌وسیلة آدرس و یا محل نامند .

وقتی که آرگومانها به ‌وسیلة مقدار ، گذر داده می‌شوند . عناصر داده به تابع کپی می‌شوند . بنابراین هر گونه تغییرات اعمال شده در روی آنها در درون تابع یا روتین فراخواننده ، اثر نمی‌گذارد . وقتی که آرگومان بصورت آدرس انتقال می‌یابد (یعنی وقتی که یک اشاره‌گر به یک تابع گذر داده می‌شود) ، در واقع آدرس آن قلم از داده به تابع فرستاده می‌شود . حال محتوای آن آدرس به راحتی هم در درون تابع مزبور و هم در تابع فراخواننده قابل دستیابی است . به‌علاوه هر تغییراتی که روی آن قلم داده انجام پذیرد (یعنی هر گونه تغییراتی که در محتوای آدرس مورد نظر انجام گیرد) هم در تابع فراخوانده شده و هم در برنامه یا تابع فراخواننده تأثیر می‌گذارد و تشخیص داده می‌شود .

دو برنامة نمایش داده شده در مثالهای 1 و 2 دو گونه از تابعی به نام add1 را نشان می‌دهد که آرگومان خود را یک واحد افزایش می‌دهد . مثال 1  متغیر count را با روش فراخوانی با مقدار ، به تابع گذر می‌دهد . تابع add1 آرگومان خود را یک واحد افزایش می‌دهد و مقدار جدید را با دستور return به تابع main برمی‌گرداند . مقدار جدید در تابع main به count نسبت داده می‌شود .در مثال 2 ، برنامه مورد نظر ، متغیر count را با فراخوانی آدرس ، گذر می‌دهد . یعنی آدرس count (نه مقدار آن) به تابع add1 گذر داده می‌شود . تابع add1 یک اشاره‌گر به مقدار صحیح را که در اینجا countptr نامیده شده است  بعنوان آرگومان دریافت می‌کند . تابع add1 مقداری را که با countptr به آن اشاره شده است (یعنی محتوای محلی را که آدرس آن در اشاره‌گر countptr قرار دارد) یک واحد افزایش می‌دهد . این عمل همچنین مقدار count را در main تغییر می‌دهد.

مثال 1.-  افزایش مقدار یک متغیر با بکار بردن فراخوانی با مقدار :

#include <stdio.h>

main ( )

 {

   int  count = 7 ;

   int  add1(int) ;

   printf ("the original value of count is %d\n" , count) ;

   count = add1 (count) ;

   printf ("the new value of count is %d\n" , count) ;

}

int  add1(int c)

 {

    return ++c ;     \* incrementsl variable c */

 }

خروجی برنامه

the original value of count is 7

the new value count is 8

 

مثال 2 -  افزایش مقدار یک متغیر به ‌وسیلة فراخوانی با آدرس :

# include <stdio.h>

main ( )

 {

   int  count = 7 ;

   int  add1 (int *) ;

   printf("the original value of count is %d\n" , count) ;

   add1 (& count) ;

   printf ("the new value of count is %d/n" , count) ;

 }

void  add1 (int  *countptr)

 {

    + + (*countptr) ;       /* increments count in main */

  }

خروجی برنامه

the original value of count is 7

the new value count is 8

 

پارامتر متناظر تابعی که یک آدرس را بعنوان آرگومان دریافت می‌کند ، باید یک اشاره‌گر باشد . مثلاً عنوان تابع add1 در مثال قبلی بصورت زیر است :

void  add1 (int  *countptr)

و بیان می‌کند که add1 آدرس یک متغیر از نوع int را دریافت خواهد کرد و آن را در countptr ذخیره خواهد نمود .

  • انتقال دوطرفه اطلاعات ( بین تابع اصلی و فرعی به کمک اشاره‌گرها )

     وقتی که تابعی ، آدرس متغیری را در برنامه فراخواننده آن بداند ، می‌تواند هم مقادیری را در این متغیرها قرار دهد (یعنی مقادیر آنها را تغییر دهد) و هم مقادیر آن متغیرها را بکار برد . بنابراین به کمک اشاره‌گرها می‌توان مقادیر را هم از برنامه فراخواننده به تابع فراخوانده شده و هم از تابع فراخوانده شده به برنامه فراخواننده آن (درواقع در هر دو جهت) گذر داد . البته قبلاً در مبحث توابع ، انتقال مقادیر به روش  فراخوانی با مقدار  را دیده‌ایم . ولی روش مذکور در بالا ، ما را قادر می‌سازد که بیش از این مقدار را به برنامه یا تابع فراخواننده برگردانیم . و بدین طریق محدودیتی که در برگرداندن  نتایج تابع فرعی به کمک نام آن تابع وجود دارد و در آن فقط می‌توان یک مقدار را با نام تابع برگرداند ، از بین برد . مثال زیر اهمیت و کاربرد آن این موضوع را نشان می‌دهد .

مثال - برنامه‌ای بنویسید که ضرایب معادله درجه دومی را بخواند و سپس با فراخوانده شدن تابع فرعی به نام root  ریشه‌های معادله مزبور محاسبه گردد و به تابع اصلی برگردانده شود . اگر معادله ریشه حقیقی نداشته باشد ، تابع فرعی هر دو ریشه را بعنوان صفر برگرداند . در ضمن اگر معادله ریشه داشته باشد ، ضرایب معادله همراه با ریشه‌های آن در تابع اصلی چاپ شود در غیر اینصورت ضرایب آن همراه با پیغام مناسب چاپ شود .

حل : برنامه مورد نظر در زیر نشان داده شده است :

#include <stdio.h>

# include <math.h>

main ( )

  {

    float  a , b , c , x1 , x2 ;

    scanf ("%f %f %f" , &a , &b , &c) ;

    root (a , b , &x1 , &x2) ;

    if  (x1 = = 0  &&  x2 = = 0)

       printf ("\n %f %f %f no real solution" , a , b , c) ;

    else

        printf ("\n %f %f %f %f %f" , a , b , cx1 , x2) ;

  }

void  root (a , b , c ,  px1 , px2)

float a , b , c , *px1 , *px2 ;

{

   float d , delta ;

   delta = b*b - 4*a*c ;

   if (delta < 0)

    {  *px1 = *px2 = 0 ;

        return ;

    }

  else

     {  d = sqrt (delta) ;

           *px1 = (-b+d) / (2*a) ;

           *px2 = (-b-d) / (2*a) ;

        return ;

     }

  }

در فراخوانی تابع root آدرس متغیرهای x2 , x1 (که باید ریشه‌های معادله را بپذیرد) به تابع مذکور گذر داده می‌شود و سپس در تابع root اگر معادله دارای ریشه‌های حقیقی باشد ، مقادیر آنها به متغیرهای x2 , x1 نسبت داده می‌شود (یعنی در محلهایی که آدرس آنها در متغیر اشاره‌گر px1 و px2 می‌باشد  قرار می‌گیرد) در غیر اینصورت به هر دو متغیر ، مقدار صفر نسبت داده می‌شود . بدین طریق بیش از یک مقدار از تابع فرعی به اصلی برگردانده می‌شود .

  • اشاره‌گرها و آرایه‌ها

      بین اشاره‌گرها و آرایه‌ها رابطه نزدیکی وجود دارد . قطعه برنامه زیر را درنظر بگیرید :

char  str[80] , *p ;

p = str ;

      می‌دانیم که نام آرایه ، همان آدرس اولین عنصر آرایه است . بنابراین دو دستور :

p = &str[0] ; 

p = str ;

هم‌ارز می‌باشند . پس در قطعه برنامه بالا در اشاره‌گر p ، آدرس آرایه (یعنی آدرس اولین عنصر آرایه) قرار داده شده است . حال اگر بخواهیم به پنجمین عنصر در str دسترسی داشته باشیم ، می‌توانیم این کار را با دو طریق زیر انجام دهیم :

str[4]    یا  *(p+4)

هر دو دستور ، پنجمین عنصر را برمی‌گردانند .

همینطور اگر داشته باشیم :

int  a[15] , *p ;

p = &a[0] ;

دو عبارت a[3] و *(p+3)  هم‌ارز بوده و هر دو چهارمین عنصر از 15 عنصر را برمی‌گردانند .

یعنی بطور کلی عنصر :

a[k]

 هم‌ارز  با :

*(p + k)

خواهد بود  و هر دو  محتوای خانه k اُم آرایه a و یا (k+1) اُمین عنصر از مجموعه عناصر مزبور را برمی‌گردانند و همینطور عبارت مزبور هم‌ارز :

*(a+k)

می‌باشد . زیرا a  نام آرایه ، معرف آدرس آغاز آن می‌باشد و (a+k) آدرس خانه k اُم آرایه خواهد بود و در نتیجه عنصر :

*(a + k)

 محتوای خانه k اُم از آرایه مزبور را برمی‌گرداند . بنابراین C ، سه روش برای دستیابی به عناصر آرایه در اختیار ما قرار می‌دهد . اما مهم است بدانیم که دستیابی به عناصر آرایه از طریق اشاره‌گر ، سریعتر از روش استفاده از اندیس است . لذا روش :

*(p + k)

 و همینطور :

*(a + k)

 سریعتر از a[k] عمل می‌کند . بدین لحاظ استفاده از اشاره‌گرها برای دستیابی به عناصر آرایه ، یک روش بسیار متداول در زبان C می‌باشد .

      حال برای تفسیر k در عبارتهای *(a+k)  و  *(p+k)  به برنامه زیر توجه نمایید .

/* prints out values from array */

main ( )

 {

   static int  nums[ ] = {92 , 81 , 70 , 69 , 58} ;

   int  k ;

   for (k = 0 ; k<5 ; k+ +)

      printf (" %d\n " , nums[k]) ;

  }

خروجی برنامه

92

81

70

69

58

 

برنامه مزبور یک برنامه ساده است که در آن برای دستیابی به عناصر آرایه ، از روش متعارف علامتگذاری آرایه استفاده شده است .

حال همان برنامه با روش بکارگیری از اشاره‌گر بصورت زیر خواهد بود :

 

/* uses pointers to print out values from array */

main ( )

 {

   static int nums[ ]= {92 , 81 , 70 , 69 , 58}

   int  k ;

   for (k=0 ; k<5 ; k++)

      printf (" %d\n ", *(nums + k)) ;

  }

شکل زیر نشان می‌دهد که منظور از *(nums+k)   محتوای k خانه ، بعد از nums است  که در آن بزرگی هر خانه مساوی بزرگی داده یا شئی مورد نظر در آرایه برحسب بایت است که در مثال بالا برابر 2 بایت می‌باشد . همچنین در عبارت :

*(nums + k)

نقش اپراتور ستاره را به‌عنوان عملگر غیرمستقیم ملاحظه می‌کنید که محتوای خانه یا آدرس nums+k را در اختیار قرار می‌دهد.

ادامه دارد ...


نوشته شده توسط محمد حسن بهجت | نظرات [2] | لینک به این مطلب |


 
تابلوی گفتمان



نظرسنجی

پیوندها

ثبت نام

خبرنامه ها





خبرنامه ویژه





خبرنامه عمومی


لوگو دوستان

پیوندهای روزانه

 
Copy Right 2007 ParsiBox.com ( Designed By ParsiBox Master Design )