Home » #C » نوشتن تابع در زبان برنامه نویسی C

نوشتن تابع در زبان برنامه نویسی C

برنامه‌هایی که تاکنون نوشته شده، فقط شامل یک تابع هستید به نام main بود. در برنامه های طولانی و پیپچیده که شامل چندین بخش منطقی و مستقل از هم هستند، بهتر است برای هر قسمت منطقی، برنامه جداگانه ای نوشت. برنامه‌ای که برای هر یک از بخش‌ها نوشته می‌شود، تابع نام دارد. در واقع تابع، واحدی از برنامه است که کار به خصوصی را انجام میدهند. در بعضی از توابع عملکرد خاصی دارند، مانند تابع printf. بعضی دیگر از توابع مقداری به برنامه بر می گردانند. برای مثال  strlen  طول یک رشته را بر میگرداند.
توابع، مارا از تکرار برنامه های یکسان بی نیاز می سازد. اگر عملی قرار است چندین بار در طول برنامه تکرار شود، یکبار تابع مناسب برای آن مینویسیم و بار دیگر که نیاز پیدا کردیم آن را صدا میزنیم. می توان یک تابع را در برنامه های گوناگون استفاده کنیم. برنامه نویس بر حسب ضرورت می‌تواند توابعی را بنویسد و در برنامه مورد استفاده قرار دهد. در این آموزش روش نوشتن توابع در زبان برنامه نویسی C، را مطرح خواهیم کرد. قبل از پرداختن به چگونگی نوشتن توابع، اهمیت توابع  در برنامه نویسی C، را مورد بررسی قرار می دهیم.

نوشتن تابع در زبان برنامه نویسی C

۱- اهمیت نوشتن توابع در زبان برنامه نویسی

استفاده از توابع در برنامه‌نویسی مزایایی دارد که برخی از آن‌ها به ترتیب زیر هستند:

  • خرد کردن برنامه به اجزای کوچک‌تر جهت ساده‌تر شدن حل مساله
  • طراحی، تست و اشکال زدایی اجزای برنامه به طور مستقل
  • امکان استفاده دوباره از برنامه به طور مستقل
  • انجام پروژه به صورت تیمی
  • افزایش خوانایی برنامه و درک راحت‌تر آن
  • ساده‌تر شدن اشکال یابی برنامه
  • کاهش حجم برنامه
  • صرفه جویی در وقت

نوشتن تابع در زبان برنامه نویسی C۲- نوشتن توابع
برای نوشتن تابع باید اهداف تابع مشخص باشد. تابع چه وظیفه ای برعهده دارد، ورودی های تابع چیست، و خروجی های تابع کدامند. با دانستن این موارد نوشتن تابع بسیار راحت خواهد بود. هر تابع دارای دو جنبه می باشد:
تعریف تابع: مجموعه ای از دستورات که عملکرد تابع را مشخص میکند.
فراخوانی تابع: دستوراتی که تابع را فراخوانی می کند. فراخوانی تابع با نام آن انجام می‌شود.

نکته: قانون نامگذاری برای توابع، از قانون نامگذاری برای متغیرها پیروی می‌کند.

۳- تعریف توابع

نوع تابع: یکی از انواع void ،char ،double ،float ،int. یکی از انواع تعریف شده توسط کاربر. (یکی از انواع موجود در زبان c یا انواع تعریف شده توسط کاربر است.  اگر تابع بخواهد مقداری را به تابع فراخوان برگرداند، آن مقدار در نام تابع قرار می گیرد. چون هر مقداری در زبان c دارای نوع است. لذا تابع نیز دارای نوع است. اگر تابع هیچ مقداری را به برنامه فراخوان برنگرداند، نوع تابع void خواهد بود.)
عنوان تابع: همانند قوانین نامگذاری متغیرها، وسیله ای برای دسترسی و استفاده از تابع.
پارامتر های تابع: متغیر هایی برای انتقال مقادیر از برنامه اصلی به یک تابع. (پارامتر ها اطلاعاتی هستند که بین تابع فراخوان و تابع فراخوانی شونده هستند. هنگام فراخوانی تابع ، پارامتر ها از برنامه فراخوان به تابع ارسال می شوند. اگر تعداد پارامتر ها بیش از یک عدد باشد با کاما از هم جدا شوند. در لیست پارامتر ها، نوع هر یک از پارامترها نیز مشخص می شود. پارامتر ها متغیر هایی هستند که هنگام تعریف تابع، جلوی نام تابع و در داخل پرانتز قرار گرفته و تعریف می شوند. پارامترها به عنوان متغیری در داخل تابع قابل استفاده هستند. اگر تابعی آرگومان است، به جایی لیست آرگومان ها، کلمه void را قرار دهید.)
بدنه تابع: مجموعه دستوراتی که عملیات مربوط به تابع را انجام می دهند.

نکته: تعریف تابع در داخل تابع دیگر مجاز نمی باشد.

نوشتن تابع در زبان برنامه نویسی C

۴- الگوی تابع

الگوی تابع مشخص می کند که تابع چگونه باید فراخوانی شود

;  (لیست پارامترها)   نام تابع   <نوع تابع>

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

نکته: هیچ تابعی نمی تواند از متغیرهای سایر توابع استفاده کند، مگر اینکه از طریق پارامتر ها منتقل شوند.

۵- فراخوانی توابع

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

  1. فراخوانی تابع با مقدار(call by value)
  2. فراخوانی تابع با ارجاع(call by reference)

در فراخوانی تابع با مقدار یک کپی از مقدار آرگومان‌های تابع روی پارامترهای آن نوشته می‌شود، بنابراین تغییر کردن آرگومان تاثیری روی مقدار پارامتر نمی‌گذارد.

float tempconv(float centigrade);
 
float c, f;
 
void main(void){
	c = 38.7;
	f = tempconv(c);
}
 
float tempconv(float centigrade){
	float i;
	i = 1.8 * centigrade + 32;
	return i;
}

 * در مثال بالا تابع tempconv تبدیل دما برحسب سانتیگراد به فارنهایت را انجام می دهد، و مقدار دما را بر می گرداند. اما حالت دیگری هم در زبان c وجود دارد که در آن بدنه تابع مستقیما دستری به آرگومان‌ها داشته باشد. این قابلیت مفیدی است که به آن فراخوانی با ارجاع می‌گویند. در این روش آدرس آرگومان روی پارامتر کپی می‌شود. در بدنه تابع از آدرس ارسال شده برای دسترسی به آرگومان استفاده می‌شود، به این ترتیب تغییرات ایجاد شده در متغیر پارامتر، روی آرگومان نیز تاثیر می‌گذارد. به مثال زیر توجه کنید.

void tempconv(float centigrade, float *farenheit, float *kelvin);
 
float c, f, k;
 
void main(void){
	c = 38.7;
	tempconv(c, &f, &k);
}
 
void tempconv(float centigrade, float *farenheit, float *kelvin){
	*farenheit = 1.8 * centigrade + 32;
	*kelvin = centigrade + 273.15;
}

نکته: در روش فراخوانی با مقدار، تابع می‌تواند یک یا چند ورودی و تنها یک خروجی داشته باشد؛ اما در بسیاری از کاربردها لازم است تابع چندین خروجی داشته باشد.

۵-۱- فراخوانی تابع با مقدار

در فراخوانی توسط مقدار، آرگومانهای ارسالی توسط تابع فراخواننده، در پارامترهای متناظر تابع فراخوانی شده، کپی می گردند. بنابراین تابع فراخوانی شده عملیات خود را برروی یک کپی از آرگومانهای ارسالی انجام می دهد. درنتیجه، در صورت انجام هرگونه تغییری برروی این کپی توسط تابع فراخوانی شده، متغیر اصلی در تابع فراخواننده تغییر نخواهد کرد. مزیت این نوع فراخوانی در این است که می توان بدون هیچ نگرانی از تغییر ناخواسته متغیرها، آنها را به هر تابعی ارسال کرد، چرا که فقط یک کپی از آنها به تابع ارسال می شود. در زبان C در حالت عادی فراخوانی تابع توسط مقدار صورت می پذیرد. برای روشن شدن موضوع به مثال زیر توجه کنید:

void test(int a, int b) {
printf(“Function test : a=%d and b=%d \n”,a,b);
a ++ ;
b *= 2;
printf(“Function test : a=%d and b=%d \n”,a,b);
}
void main() {
int x, y;
x = 10;
y = 8;
printf(“Function main : x=%d and y=%d \n”,x,y);
test(x,y) ;
printf(“Function main : x=%d and y=%d \n”,x,y);
}

خروجی کد بالا:

Function main : x=10 and y=8
Function test : a=10 and b=8
Function test : a=11 and b=16
Function main : x=10 and y=8

همانطور که می بینید، گرچه پارامترهای aو b در داخل تابع test تغییر کرده اند، اما پس از بازگشت به تابع فراخواننده یعنی، main آرگومانهای متناظر به آنها یعنی xو  y همچنان همان مقادیر اولیه خود را دارا هستند. شکل زیر نحوه کار را نشان می دهد.
 فراخوانی تابع با مقدارهمانطور که گفته شد، این نحوه فراخوانی حالت پیش فرض در زبان C بوده ومعمولا نیز برنامه نویسان ترجیح می دهند از این روش برای ارسال آرگومانها به توابع استفاده نمایند، چرا که متغیرها را در برابر تغییرات ناخواسته در هنگام ارسال به توابع دیگر حفظ می کنند. اما گاهی لازم است که تابع فراخوانی شده بتواند مقدار متغیرهای دریافتی را تغییر دهد، و این تغییرات در متغیرهای اصلی ارسال شده از طرف تابع فراخواننده نیز اعمال شود. در اینصورت باید از روش فراخوانی توسط ارجاع استفاده نماییم.

۵-۲- فراخوانی تابع با ارجاع

در این روش فراخوانی، خود آرگومانهای اصلی، به تابع فراخوانی شده ارسال می گردد. در نتیجه، هرگونه تغییری در پارامترهایی تابع فراخوانی شده، مقدار آرگومانهای اصلی در تابع فراخوانی کننده را نیز تغییر خواهد داد. در C اولیه، تنها راه فراخوانی توابع توسط ارجاع، استفاده از متغیرهای اشاره گر بود، اما در استاندارد جدید C ،می توان این کار را به روش ساده تری و با استفاده از متغیرهای ارجاعی انجام داد. یک متغیر ارجاعی، در حقیقت یک نام مترادف و یا یک جایگزین برای یک متغیر دیگر است. برای تعریف یک متغیر ارجاعی از علامت & پس از نوع متغیر مورد ارجاع استفاده می کنیم. بعنوان مثال به نمونه زیر توجه کنید:
 

#include <stdio.h>
void main() {
int a = 10;
int &r = a;
r ++;
printf("a=%d and r=%d \n",a,r);
a ++;
printf("a=%d and r=%d \n",a,r);
}

خروجی کد بالا:

a = 11 and r = 11
a = 12 and r = 12

مثال فوق نشان می دهد که در حقیقت a و r یک متغیر هستند و هرگونه تغییری در هریک از این دو، دیگری را نیز تغییر خواهد داد.
برای ارسال آرگومانها توسط ارجاع، کافی است پارامترهای تابع مورد نظر را بصورت متغیر ارجاعی تعریف نماییم. در اینصورت، این پارامترها در حقیقت یک ارجاع به آرگومانهای ارسالی خواهند بود و در نتیجه هرگونه تغییری در آنها، آرگومانهای اصلی در تابع فراخوانی آننده را نیز تغییر خواهد داد. بعبارت بهتر، در این روش بجای آنکه یک آپی از آرگومانها در پارامترها قرار گیرد، خود آرگومانها به تابع فراخوانی شده ارسال خواهند شد. بعنوان مثال، همان تابع قسمت قبلی را که بصورت فراخوانی توسط مقدار عمل می کرد، مجددا به روش فراخوانی با ارجاع باز نویسی می کنیم تا تفاوت این دو مشخص گردد:

void test(int &a, int &b) {
printf(“Function test : a=%d and b=%d \n”,a,b);
a ++ ;
b *= 2;
printf(“Function test : a=%d and b=%d \n”,a,b);
}
void main() {
int x, y;x = 10;
y = 8;
printf(“Function main : x=%d and y=%d \n”,x,y);
test(x,y) ;
printf(“Function main : x=%d and y=%d \n”,x,y);
}

خروجی کد بالا:

Function main : x=10 and y=8
Function test : a=10 and b=8
Function test : a=11 and b=16
Function main : x=11 and y=16

همانگونه که مشاهده می کنید، تغییر پارامترهای a و b در داخل تابع test باعث شده آه آرگومانهای متناظر آنها در تابع فراخواننده، یعنی x و y نیز تغییر نمایند. شکل زیر نحوه کار را نشان می دهد.

فراخوانی تابع با ارجاع

چه موقع از فراخوانی توسط ارجاع استفاده کنیم؟

فراخوانی توسط ارجاع در دو مورد کاربرد دارد، که هریک را با ذکر یک مثال توضیح می دهیم:

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

*برنامه ای بنویسید که ضرایب یک معادله درجه دوم را دریافت و ریشه های آن را محاسبه وچاپ نماید.

#include <stdio.h>
#include <math.h>
int equation(int a, int b, int c, float &x1, float &x2) {
float delta;if (a==0) return(0) ;
delta = b*b – ۴*a*c ;
if (delta < 0) return(0) ;
else if (delta == 0) {
x1 = -b / (2*a) ;
return(1) ;
}
else {
delta = sqrt(delta) ;
x1 = (-b + delta) / (2*a) ;
x2 = (-b – delta) / (2*a) ;
return(2) ;
}
}
void main() {
int resultNo, coef1, coef2, coef3;
float result1,result2 ;
printf("Please enter coefficients: ");
scanf("%d %d %d", &coef1, &coef2, &coef3);
resultNo = equation(coef1,coef2,coef3,result1,result2);
if (resultNo == 0)
printf("There is no answer! \n");
else if (resultNo==1)
printf("There is 1 answer, x=%f \n", x1);
else printf("There are 2 answers, x1=%f and x2=%f \n", x1, x2);
}

  • در مواردی که آرگومان ارسالی به تابع، همزمان نقش ورودی و خروجی را دارا باشد. یعنی آرگومان علاوه بر آنکه مقداری را به تابع ارسال می آند، ممکن است مقداری را به تابع فرا واننده نیز بازگرداند. مثال زیر این موضوع را روشن می کند.
    * برنامه ای که و عدد را از کاربر دریافت و با استفاده از یک تابع، مقدار آن دو را جابجا و چاپ کند.

#include <stdio.h>
void swap(int &a, int &b) {
int temp;
temp = a;
a = b;
b = temp;
}
void main() {
int x, y;printf("enter two numbers : ");
scanf("%d %d",&x,&y);
printf("x = %d and y = %d \n", x, y);
swap(x,y);
printf("x = %d and y = %d \n", x, y);
}

خروجی کد بالا:

enter two numbers : 8 12
x = 8 and y = 12
x = 12 and y = 8

دقت کنید که در برنامه فوق، چنانچه آرگومانها بصورت مقدار به تابع swap ارسال می شد، پس از بازگشت از تابع، مقادیر متغیرهای x و y هیچ تغییری نکرده و همان مقادیر قبلی خود را دارا بودند.

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

بعنوان مثال فراخوانیهای زیر برای تابع swap مجاز نیستند.

swap(8,10) ; // compile error
swap(x+5, y); // compile error

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

Add a Comment

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *