تشخیص تفاوت توابع کتابخانهای و توابع تعریفشده توسط برنامهنویس
آشنایی با ساختار تعریف تابع و معنی نوع بازگشتی void
نوشتن و فراخوانی توابع دارای پارامتر و مقدار بازگشتی
استفاده از هدر <cmath> و پیادهسازی مثالهای کلاسیک فاکتوریل، عدد اول و ترکیب
تابع چیست؟
وقتی برنامهای طولانی میشود، خواندن، رفع اشکال و نگهداری آن سخت میگردد. راهحل، تقسیم برنامه به قطعات کوچکتر و مستقل است که هر کدام یک کار مشخص انجام میدهند. به این قطعه کدِ دارای نام، تابع (Function) میگویند.
یک تابع مانند یک دستگاه ساده است: ورودی دریافت میکند، روی آن پردازش انجام میدهد و در نهایت یک نتیجه تولید میکند. مزایای استفاده از تابع:
جلوگیری از تکرار کد (کد یکبار نوشته و چندبار فراخوانی میشود)
خوانایی و نگهداری بهتر برنامه
تستپذیری آسانتر هر بخش بهصورت جداگانه
تیممحور بودن کار برنامهنویسی
نمودار مفهومی تابع
در سادهترین حالت، یک تابع را میتوان با سه مرحله نشان داد: دریافت داده، پردازش در بلوک f، و بازگرداندن نتیجه.
flow.svg
دو نوع تابع در C++
توابع در C++ به دو دسته کلی تقسیم میشوند:
۱. توابع کتابخانهای (Library Functions): توابعی که از پیش توسط کامپایلر و کتابخانه استاندارد فراهم شدهاند. کافی است هدر مربوطه را #include کنیم و آنها را فراخوانی کنیم. مثال:
sqrt, sin, cos, pow از هدر <cmath>
strlen, strcpy, strcmp از هدر <cstring>
swap از هدر <utility> (یا <algorithm>)
rand, srand از هدر <cstdlib>
۲. توابع تعریفشده توسط برنامهنویس (User-defined Functions): تابعی که خودمان از صفر تعریف میکنیم تا منطق خاصی را پیادهسازی کند. در این درس تمرکز ما روی این دسته است.
ساختار کلی تعریف تابع
هر تابع از چهار بخش تشکیل میشود:
main.cppcpp
نوع بازگشتی (Return Type): نوع دادهای که تابع به فراخواننده برمیگرداند. اگر تابع مقداری برنگرداند، از کلمهکلیدی void استفاده میکنیم.
نام تابع: شناسهای که با آن تابع را صدا میزنیم (مثل Sum, fact, prime).
پارامترها: متغیرهایی که مقدارشان از سمت فراخواننده به تابع داده میشود؛ داخل پرانتز نوشته میشوند.
بدنه: بلوکی که عملیات تابع در آن انجام میشود و معمولاً با return خاتمه مییابد.
تابع جمع دو عدد
این مثال کلاسیک، نحوه تعریف و استفاده از یک تابع ساده با مقدار بازگشتی را نشان میدهد. پارامترهای x و y بهصورت مقداری (by value) ارسال میشوند؛ یعنی یک کپی از مقدار آرگومان ساخته میشود و تغییر آن داخل تابع روی متغیر اصلی تأثیر ندارد.
تعریف و فراخوانی تابع Sumcpp
$خروجی برنامه
lotfan do adad vared konid: 7 5
jame do adad = 12
bazham mostaghim: 12
نوع بازگشتی void
گاهی میخواهیم تابعی بنویسیم که فقط کاری انجام میدهد و نیازی به بازگرداندن مقدار ندارد. در این حالت نوع بازگشتی را void قرار میدهیم و از دستور return; (بدون مقدار) یا صرفاً پایان بدنه استفاده میکنیم.
وقتی پارامترها بهصورت مقداری پاس داده شوند، هر تغییری که داخل تابع روی آنها انجام شود، روی متغیر اصلیِ درون main اثر نمیگذارد، زیرا تابع با یک کپی کار میکند.
تابع void و عدم تأثیر روی متغیر اصلی
در مثال زیر تابع F پارامتر x را صفر میکند. اما چون x یک کپی از n در main است، مقدار n تغییری نمیکند و خروجی همچنان ۱۰ خواهد بود.
نشان دادن اینکه پارامتر مقداری یک کپی استcpp
$خروجی برنامه
meghdare n ba'd az seda kardane F = 10
توابع ریاضی کتابخانهای با <cmath>
برای استفاده از توابع ریاضی مانند sqrt (رادیکال)، sin (سینوس)، cos (کسینوس) و pow (توان)، باید هدر <cmath> را اضافه کنیم. این توابع معمولاً با نوع double کار میکنند، پس متغیرها را هم double در نظر بگیرید تا دقت از دست نرود.
main.cppcpp
$خروجی برنامه
yek adad vared konid: 9
sqrt(a) = 3
sin(a) = 0.412118
a ^ 2 = 81
در حال باز کردن ویرایشگر…
مثال کلاسیک: تابع فاکتوریل
فاکتوریل عدد n (نماد n!) برابر است با حاصلضرب اعداد ۱ تا n:
n! = 1 × 2 × 3 × ... × n
بهعلاوه 0! = 1 تعریف میشود. این مسئله، مثال آموزشی معروفی برای نوشتن تابع با حلقه و مقدار بازگشتی است.
پیادهسازی تابع fact (روش حلقهای)
متغیر p را با مقدار اولیه ۱ مقداردهی میکنیم و در هر دور حاصلضرب را در i قرار میدهیم. در پایان مقدار p را برمیگردانیم.
محاسبه فاکتوریل با حلقه forcpp
$خروجی برنامه
yek adad vared konid: 6
6! = 720
چرا از long long استفاده کردیم؟
فاکتوریل بهسرعت بزرگ میشود. مثلاً 13! = 6,227,020,800 است که از محدوده int (تا حدود ۲ میلیارد) فراتر میرود. استفاده از long long اجازه میدهد تا 20! بهدرستی محاسبه شود. برای اعداد بزرگتر باید از آرایه ارقام یا کتابخانههای اعداد بزرگ استفاده کنید.
مثال کلاسیک: تابع بررسی عدد اول
عددی اول است که دقیقاً دو مقسومعلیه داشته باشد: ۱ و خودش. پس کافی است تعداد مقسومعلیهها را بشماریم. بهینهتر اینکه فقط تا جذر n بررسی کنیم، چون مقسومعلیهها زوج هستند (اگر i مقسومعلیه n باشد، n/i هم مقسومعلیه است).
پیادهسازی تابع prime (بهینهشده)
تابع prime یک bool برمیگرداند: true اگر اول باشد و false در غیر این صورت. برای اعداد کوچکتر از ۲ مستقیماً false برمیگردد.
بررسی اول بودن عدد با حلقه تا sqrt(n)cpp
$خروجی برنامه
yek adad vared konid: 17
17 aval ast.
خطای رایج در تابع prime: تقسیم بهجای باقیمانده
در جزوه اصلی، شرط بررسی بخشپذیری بهاشتباه با عملگر / (تقسیم) نوشته شده بود، در حالی که باید با عملگر % (باقیمانده) نوشته شود.
n / i نتیجه تقسیم را میدهد که تقریباً هیچوقت صفر نمیشود (مگر n = 0).
n % i باقیمانده تقسیم را میدهد که دقیقاً همان چیزی است که برای بررسی بخشپذیر بودن نیاز داریم.
نسخه اشتباه جزوه
کد دارای باگcpp
نسخه اصلاحشده و استاندارد
کد صحیحcpp
محاسبه ترکیب C(n,r)
فرمول ترکیب r به n عبارت است از:
C(n, r) = n! / (r! × (n-r)!)
با داشتن تابع fact از قبل، میتوانیم ترکیب را بهسادگی محاسبه کنیم. دقت کنید پرانتزها را درست بگذارید! اولویت عملگرها در C++ باعث میشود عبارت fact(n)/fact(r)*fact(n-r) بهاشتباه بهصورت (fact(n)/fact(r)) * fact(n-r) محاسبه شود، نه فرمول ترکیب.
برنامه محاسبه ترکیب با فراخوانی fact
توجه به پرانتز دور fact(r) * fact(n - r) ضروری است تا اولویت ضرب و تقسیم درست اعمال شود.
استفاده از تابع فاکتوریل در ترکیبcpp
$خروجی برنامه
n va r ra vared konid: 5 2
C(5,2) = 10
مثال کاربردی: تولید اعداد تصادفی
برای تولید اعداد تصادفی از تابع rand() استفاده میکنیم. تابع srand(time(NULL)) با استفاده از زمان سیستم، دانه تصادفی را تنظیم میکند تا در هر بار اجرا اعداد متفاوتی تولید شود. هدرهای لازم: <cstdlib> و <ctime>.
main.cppcpp
$خروجی برنامه
10 adad tasadofi:
42 7 83 19 55 91 3 67 28 74
در حال باز کردن ویرایشگر…
خطاهای رایج در نوشتن تابع
فراموش کردن return در تابعی که نوع بازگشتی غیر void دارد → خطای کامپایل یا رفتار تعریفنشده.
عدم تطابق نوع بازگشتی با مقدار واقعی return (مثلاً تابع int که string برمیگرداند).
حساسیت به بزرگی/کوچکی حروف: در جزوه اصلی متغیر p تعریف شده اما در حلقه از P بزرگ استفاده شده (P = P * i). در C++ این دو متغیر متفاوت هستند و باعث خطای «Undefined identifier» میشود.
فراموش کردن هدرها: مثلاً استفاده از sqrt بدون #include <cmath> یا استفاده از rand بدون <cstdlib>.
اشتباه گرفتن عملگرهای / و % در بررسی بخشپذیری.
اشتباه در اولویت عملگرها هنگام نوشتن فرمولهای پیچیده؛ همیشه از پرانتز استفاده کنید.
تابعی بهنام `int square(int n)` بنویسید که مجذور (توان دوم) عدد ورودی را برگرداند. در `main` آن را برای عدد ۹ فراخوانی کرده و چاپ کنید.
نمایش راهنما
بدنه تابع فقط `return n * n;` است. فراموش نکنید تابع را قبل از `main` تعریف کنید.
02
تمرین•حل کامل دارد
تابع `bool isEven(int n)` بنویسید که اگر `n` زوج باشد `true` و در غیر این صورت `false` برگرداند. در `main` عددی را از ورودی بگیرید و زوج یا فرد بودن آن را چاپ کنید.
نمایش راهنما
از عملگر باقیمانده `%` استفاده کنید: `n % 2 == 0` یعنی زوج است.
03
تمرین•حل کامل دارد
تابعی بهنام `int maxOfThree(int a, int b, int c)` بنویسید که بزرگترین عدد میان سه عدد را برگرداند. در `main` سه عدد از ورودی بگیرید و بیشینه را چاپ کنید.
نمایش راهنما
میتوانید از `std::max` استفاده کنید، اما بهتر است خودتان با چند `if` پیاده کنید: ابتدا `m = a`، سپس اگر `b > m` آنگاه `m = b` و همینطور برای `c`.
04
تمرین•حل کامل دارد
تابع بازگشتی یا حلقهای `int gcd(int a, int b)` بنویسید که ب.م.م (بزرگترین مقسومعلیه مشترک) دو عدد را با الگوریتم اقلیدسی محاسبه کند. در `main` آن را برای ۲۴ و ۳۶ تست کنید.
نمایش راهنما
الگوریتم اقلیدسی: تا وقتی `b != 0`، مقدار `(a, b)` را با `(b, a % b)` جایگزین کنید. در پایان `a` جواب است.
// نکات-کلیدی۶
01تابع یک بلوک کد مستقل با نام است که ورودی میگیرد، پردازش میکند و مقدار بازگشتی تولید میکند.
02پارامترها بهصورت **مقداری (by value)** ارسال میشوند مگر اینکه با `&` بهصورت ارجاعی تعریف شوند؛ در این صورت تابع با یک کپی کار میکند.
03نوع بازگشتی `void` یعنی تابع مقداری برنمیگرداند و معمولاً برای انجام یک کار جانبی استفاده میشود.
04برای توابع ریاضی مثل `sqrt` و `sin` باید هدر `<cmath>` اضافه شود و بهتر است از متغیرهای `double` استفاده کنید.
05C++ به بزرگی و کوچکی حروف حساس است؛ `p` و `P` دو متغیر کاملاً متفاوتاند.
06در فرمولهای پیچیده مثل ترکیب، همیشه از پرانتز استفاده کنید تا اولویت عملگرها را کنترل کنید.
گام بعدی پیشنهادی
ارجاع، ارسال آرایه به تابع و معکوس رشته
متغیرهای ارجاعی، تابع swap و ارسال آرایههای یک و دو بُعدی
1#include<iostream>2usingnamespace std;34// تعریف تابع Sum که جمع دو عدد صحیح را برمیگرداند5intSum(int x,int y){6return x + y;// بازگرداندن حاصلجمع7}89intmain(){10int a, b;11 cout <<"lotfan do adad vared konid: ";12 cin >> a >> b;1314int z =Sum(a, b);// فراخوانی و ذخیره نتیجه در z15 cout <<"jame do adad = "<< z << endl;16 cout <<"bazham mostaghim: "<<Sum(a, b)<< endl;// فراخوانی مستقیم17return0;18}
1#include<iostream>2usingnamespace std;34voidF(int x){5 x =0;// فقط روی کپی تغییر میکند6}78intmain(){9int n =10;10F(n);11 cout <<"meghdare n ba'd az seda kardane F = "<< n << endl;12return0;13}
1#include<iostream>2#include<cmath>3usingnamespace std;45intmain(){6double a;7 cout <<"yek adad vared konid: ";8 cin >> a;910double r1 =sqrt(a);// جذر a11double r2 =sin(a);// سینوس a (a بر حسب رادیان)12double r3 =pow(a,2);// a به توان ۲1314 cout <<"sqrt(a) = "<< r1 << endl;15 cout <<"sin(a) = "<< r2 << endl;16 cout <<"a ^ 2 = "<< r3 << endl;17return0;18}
1#include<iostream>2usingnamespace std;34longlongfact(int n){5longlong p =1;6for(int i =1; i <= n; i++)7 p = p * i;8return p;9}1011intmain(){12int n;13 cout <<"yek adad vared konid: ";14 cin >> n;15 cout << n <<"! = "<<fact(n)<< endl;16return0;17}
1#include<iostream>2#include<cmath>3usingnamespace std;45boolprime(int n){6if(n <2)returnfalse;7for(int i =2; i * i <= n; i++){8if(n % i ==0)// اگر بخشپذیر بود، اول نیست9returnfalse;10}11returntrue;// اگر هیچ مقسومی پیدا نشد، اول است12}1314intmain(){15int n;16 cout <<"yek adad vared konid: ";17 cin >> n;18if(prime(n))19 cout << n <<" aval ast."<< endl;20else21 cout << n <<" aval nist."<< endl;22return0;23}
1boolprime(int n){2int p =0;3for(int i =1; i <= n; i++)4if(n / i ==0)// اشتباه! باید % باشد5 p++;6if(p ==2)return1;7return0;8}
1boolprime(int n){2if(n <2)returnfalse;3for(int i =2; i * i <= n; i++)4if(n % i ==0)// صحیح: عملگر باقیمانده5returnfalse;6returntrue;7}
1#include<iostream>2usingnamespace std;34longlongfact(int n){5longlong p =1;6for(int i =1; i <= n; i++)7 p *= i;8return p;9}1011longlongcomb(int n,int r){12if(r <0|| r > n)return0;13returnfact(n)/(fact(r)*fact(n - r));// پرانتزگذاری صحیح14}1516intmain(){17int n, r;18 cout <<"n va r ra vared konid: ";19 cin >> n >> r;20 cout <<"C("<< n <<","<< r <<") = "<<comb(n, r)<< endl;21return0;22}
1#include<iostream>2#include<cstdlib>3#include<ctime>4usingnamespace std;56intmain(){7srand(time(NULL));// تنظیم دانه بر اساس ساعت8 cout <<"10 adad tasadofi:"<< endl;9for(int i =0; i <10; i++)10 cout <<(rand()%100)+1<<" ";// اعداد بین ۱ تا ۱۰۰11 cout << endl;12return0;13}