11-dars: Funksiyalar
Dars haqida
Davomiyligi: 90 daqiqa Maqsad: Talaba C tilida funksiya yaratishi, chaqirishi, qiymat qaytarishni bilishi hamda parameter, scope (local/global) va recursion tushunchalarini tushunishi kerak.
1. Funksiya nima?
Funksiya — qayta ishlatish mumkin bo'lgan kod bloki.
int qoshish(int a, int b) {
return a + b;
}
// Chaqirish
int natija = qoshish(5, 3); // 82. Nima uchun funksiya?
Funksiyasiz
int main(void) {
int a = 5, b = 3;
int sum = a + b;
printf("Yig'indi: %d\n", sum);
// har yangi juftlik uchun shu kodni qayta yozish...
}Funksiya bilan
int qoshish(int a, int b) {
return a + b;
}
int main(void) {
printf("%d\n", qoshish(5, 3));
printf("%d\n", qoshish(10, 7));
printf("%d\n", qoshish(100, 200));
}Foydalari:
- DRY (Don't Repeat Yourself) — takrorlanish yo'q
- Modular — bo'lakli
- Test — alohida sinash mumkin
- Reuse — boshqa joyda ishlatish
3. Funksiya tuzilmasi
return_type function_name(parameters) {
// body
return value;
}Misol:
int qoshish(int a, int b) {
int sum = a + b;
return sum;
}Qismlar:
| Qism | Tushuntirish |
|---|---|
int | Return type — qaytaradigan tur |
qoshish | Funksiya nomi |
(int a, int b) | Parametrlar |
{ ... } | Body (kod) |
return sum; | Qiymat qaytarish |
4. main funksiyasi
Har C dasturda main bor:
int main(void) {
// dastur kodi
return 0;
}int— main 0 qaytaradi (success)void— argument yo'qreturn 0— muvaffaqiyatli tugash
5. Misol: kvadrat va kalkulyator funksiyalari
int square(int n) {
return n * n;
}
// square(5) → 25, square(10) → 100Bir nechta funksiya:
#include <stdio.h>
int qoshish(int a, int b) {
return a + b;
}
int ayirish(int a, int b) {
return a - b;
}
int kopaytirish(int a, int b) {
return a * b;
}
int bolish(int a, int b) {
if (b == 0) {
printf("Xato: nolga bo'lish\n");
return 0;
}
return a / b;
}
int main(void) {
int a = 10, b = 3;
printf("Yig'indi: %d\n", qoshish(a, b));
printf("Ayirma: %d\n", ayirish(a, b));
printf("Ko'paytma: %d\n", kopaytirish(a, b));
printf("Bo'linma: %d\n", bolish(a, b));
return 0;
}6. void funksiyalar
Hech narsa qaytarmaydigan funksiya:
void salomlash(void) {
printf("Salom!\n");
}
void chiqar_qator(int n) {
for (int i = 0; i < n; i++) {
printf("%d ", i);
}
printf("\n");
}void return type — funksiya qiymat qaytarmaydi.
void parameter — funksiya hech qanday argument olmaydi:
void salomlash() { ... } // OK
void salomlash(void) { ... } // Yaxshiroq (C uslubi)7. Funksiyani chaqirish: argument vs parameter
int sum = qoshish(5, 3);qoshish— funksiya nomi(5, 3)— arguments (argumentlar)- Qiymat — natija
int qoshish(int a, int b) { // a va b — parameterlar
return a + b;
}
qoshish(5, 3); // 5 va 3 — argumentlar8. Function prototype
#include <stdio.h>
// Prototype — funksiya qayerda yozilishini e'lon qilish
int qoshish(int a, int b);
int main(void) {
printf("%d\n", qoshish(5, 3));
return 0;
}
// Funksiya tanasi (main'dan keyin)
int qoshish(int a, int b) {
return a + b;
}Nima uchun prototype?
C tartibga bog'liq. Funksiya main'dan keyin yozilsa — prototype kerak.
Yoki funksiyalarni main'dan oldin yozing — prototype shart emas.
9. return — qaytarish
int kattaroq(int a, int b) {
if (a > b) {
return a; // funksiya shu yerda tugaydi
}
return b;
}return — funksiya tugaydi va qiymat qaytaradi. Bir funksiyada bir nechta return bo'lishi mumkin — qaysi biri birinchi ishlasa, o'sha qaytadi.
Void return
void chiqar(int n) {
if (n < 0) {
printf("Manfiy raqam\n");
return; // qiymat yo'q, lekin funksiya tugaydi
}
printf("Musbat: %d\n", n);
}10. Misollar: tub son va funksiyalar zanjiri
Tub son tekshirish
#include <stdio.h>
int is_prime(int n) {
if (n < 2) return 0;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) return 0;
}
return 1;
}
int main(void) {
printf("1 dan 30 gacha tub sonlar:\n");
for (int i = 2; i <= 30; i++) {
if (is_prime(i)) {
printf("%d ", i);
}
}
printf("\n");
return 0;
}is_prime — 1 (true) yoki 0 (false) qaytaradi.
Funksiyalar bir-birini chaqiradi
int square(int n) {
return n * n;
}
int sum_of_squares(int a, int b) {
return square(a) + square(b);
}
// sum_of_squares(3, 4) → 9 + 16 = 25sum_of_squares — ichida square chaqiradi.
11. Nomlash va yaxshi funksiya qoidalari
// Yaxshi
int qoshish(int a, int b)
double circle_area(double r)
int is_prime(int n)
void print_menu(void)
// Yomon
int f()
int Add()
int compute_the_sum_of_two_numbers()Qoidalar: snake_case (C standart), aniq nom, qisqa, ish bildiruvchi fe'l bilan.
Yaxshi funksiya
- Bitta vazifa qiladi (Single Responsibility)
- Qisqa — 20–30 qator
- Aniq nom
- 3-4 dan ko'p parameter yo'q
- Side effect'sizroq (faqat olib, qaytaradi)
12. Scope (yashash joyi) nima?
Scope — o'zgaruvchining qayerda ko'rinadigan va qayerda ishlovchi joyi.
int x = 10; // global
void func(void) {
int y = 20; // local
}
int main(void) {
// x ko'rinadi
// y KO'RINMAYDI
}13. Local va global o'zgaruvchilar
Local variable
Local — funksiya ichida e'lon qilingan. Faqat shu funksiyada ko'rinadi.
void func(void) {
int local_var = 100;
printf("%d\n", local_var); // OK
}
int main(void) {
func();
// printf("%d\n", local_var); // XATO! local_var ko'rinmaydi
return 0;
}Global variable
Global — hamma funksiyaning tashqarida e'lon qilingan. Hamma joyda ko'rinadi.
#include <stdio.h>
int global_var = 100; // global
void func1(void) {
printf("Func1: %d\n", global_var); // OK
global_var = 200; // o'zgartirish ham
}
void func2(void) {
printf("Func2: %d\n", global_var); // 200
}
int main(void) {
func1();
func2();
return 0;
}Global'dan ehtiyot
Global o'zgaruvchilar — anti-pattern. Sabab:
- Har funksiya o'zgartirishi mumkin — debugging qiyin
- Test qiyin — funksiya alohida sinab bo'lmaydi
- Kodni tushunish qiyin
Iloji boricha local ishlating, parameter orqali uzating.
14. Block scope
int main(void) {
int x = 10;
{
int y = 20; // faqat bu blokda
printf("%d %d\n", x, y); // OK
}
// printf("%d\n", y); // XATO! y endi yo'q
return 0;
}{} ichida e'lon qilingan o'zgaruvchi — faqat shu blok ichida.
for sikli ichida
for (int i = 0; i < 10; i++) { // i — faqat for ichida
printf("%d\n", i);
}
// printf("%d\n", i); // XATO!15. Variable shadowing
int x = 10; // global
void func(void) {
int x = 100; // local — global'ni "yashiradi"
printf("%d\n", x); // 100 (local)
}
int main(void) {
printf("%d\n", x); // 10 (global)
func();
printf("%d\n", x); // 10 (global, o'zgarmadi!)
return 0;
}Local x global x ni funksiya ichida yashiradi.
16. Parameter passing — pass by value
C'da parameter qiymat orqali (pass by value) uzatiladi. Demak — nusxa uzatiladi.
void increment(int x) {
x = x + 1;
printf("Ichida: %d\n", x);
}
int main(void) {
int a = 10;
increment(a);
printf("Tashqarida: %d\n", a); // 10 (o'zgarmadi!)
return 0;
}Natija:
Ichida: 11
Tashqarida: 10a ning nusxasi x ga uzatiladi. x ni o'zgartirish — a ga ta'sir qilmaydi.
Pass by reference (qisqacha)
C'da haqiqiy pass-by-reference yo'q. Lekin pointer orqali shu effekt:
void increment(int *x) { // pointer
*x = *x + 1;
}
int main(void) {
int a = 10;
increment(&a); // a manzili
printf("%d\n", a); // 11
}Pointer keyingi oylarda batafsil.
17. const parameter va default parameter
Funksiya o'zgartirmasligi kerak bo'lgan parameter:
double doira_yuzasi(const double radius) {
// radius = 5; // XATO! const
return 3.14 * radius * radius;
}const — compiler tomonidan tekshirish uchun.
Default parameter — C'da YO'Q
// XATO — C'da default parameter yo'q
void func(int a, int b = 10) { ... }C++'da bor, C'da yo'q. Function overloading ham yo'q.
18. Recursion (rekursiya) — funksiya o'zini chaqiradi
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}factorial(5):
factorial(5) = 5 * factorial(4)
= 5 * 4 * factorial(3)
= 5 * 4 * 3 * factorial(2)
= 5 * 4 * 3 * 2 * factorial(1)
= 5 * 4 * 3 * 2 * 1
= 120Rekursiya qoidalari
1. Base case (asos) — to'xtatish sharti
if (n <= 1) return 1; // base case2. Recursive case — o'zini chaqirish
return n * factorial(n - 1); // recursiveBase case unutmang
Base case yo'q bo'lsa — cheksiz rekursiya. Stack overflow xatosi.
int bad(int n) {
return n * bad(n - 1); // hech qachon to'xtamaydi!
}19. Rekursiv misollar
Fibonacci
int fib(int n) {
if (n < 2) return n; // base
return fib(n - 1) + fib(n - 2); // recursive
}Fibonacci rekursiv — sekin!
fib(40) — bir necha sekund. fib(50) — bir necha daqiqa.
Sabab — bir xil hisob ko'p marta.
For sikli bilan — millisekund.
Rekursiya — chiroyli, lekin har doim eng tez emas.
GCD (Euclidean)
10-darsda while bilan yozgan Euclidean GCD — rekursiv ko'rinishda:
int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}Bu chiroyli, qisqa va tez rekursiv yechim.
gcd(48, 18):
gcd(48, 18) = gcd(18, 12)
= gcd(12, 6)
= gcd(6, 0)
= 620. Function static variable
void counter(void) {
static int count = 0; // bir marta initialize
count++;
printf("Count: %d\n", count);
}
int main(void) {
counter(); // Count: 1
counter(); // Count: 2
counter(); // Count: 3
return 0;
}static — o'zgaruvchi chaqirishlar orasida saqlanadi.
Local lekin yo'qolmaydi.
21. To'liq misol: Bank hisobi (oddiy)
#include <stdio.h>
double balance = 1000.0; // global (faqat misol uchun)
void show_balance(void) {
printf("Balans: %.2f\n", balance);
}
void deposit(double amount) {
if (amount <= 0) {
printf("Xato: manfiy summa\n");
return;
}
balance += amount;
printf("Qo'shildi: %.2f\n", amount);
}
int withdraw(double amount) {
if (amount <= 0) {
printf("Xato: manfiy summa\n");
return 0;
}
if (amount > balance) {
printf("Xato: yetarli pul yo'q\n");
return 0;
}
balance -= amount;
printf("Olib chiqildi: %.2f\n", amount);
return 1;
}
int main(void) {
show_balance();
deposit(500);
show_balance();
withdraw(200);
show_balance();
withdraw(5000); // xato
show_balance();
return 0;
}Real loyiha
Global o'zgaruvchi — bu yerda misol uchun. Real ishda — struct ishlatiladi (keyinroq).
22. Common pitfalls
1. Return unutish
int qoshish(int a, int b) {
int sum = a + b;
// return yo'q!
}Kompiler warning beradi, lekin kompilyatsiya qiladi. Natija — noma'lum.
2. Return type to'g'rimas
int doira_yuzasi(int r) {
return 3.14 * r * r; // float, lekin int qaytaradi
}
doira_yuzasi(5); // 78 (78.5 emas — kasr tashlanadi!)To'g'ri:
double doira_yuzasi(double r) {
return 3.14 * r * r;
}3. Prototype yo'q
int main(void) {
qoshish(5, 3); // qoshish hali e'lon qilinmagan!
return 0;
}
int qoshish(int a, int b) {
return a + b;
}Kompiler xato beradi. Prototype qo'shing yoki funksiyani main'dan oldin yozing.
Darsdagi topshiriqlar
Topshiriq 1 — Asosiy funksiyalar
functions.c — qoshish, ayirish va kopaytirish nomli uchta funksiya yozing. Har biri ikkita int qabul qilib, natijani return qilsin. mainda har birini chaqirib, natijalarni chiqaring.
Topshiriq 2 — Geometrik shakllar
shapes.c — har bir shakl yuzasi uchun alohida funksiya yozing:
- Doira:
PI * r * r - To'rtburchak:
w * h - Uchburchak:
0.5 * base * height - Trapesiya:
(a + b) * h / 2 - Romb:
d1 * d2 / 2
Hammasi double qaytarsin. main'da har birini sinab, natijani %.2f bilan chiqaring.
Topshiriq 3 — Eng katta / eng kichik
max-min.c — max(a, b) va min(a, b) funksiyalarini yozing. So'ng ularni ishlatib max3(a, b, c) va min3(a, b, c) funksiyalarini quring (funksiya ichida funksiya chaqirish). mainda bir necha misolda sinang.
Topshiriq 4 — Scope mashqi
scope.c:
#include <stdio.h>
int x = 10; // global
void func1(void) {
int x = 100; // local shadowing
printf("Func1 (local x): %d\n", x);
x = 200;
printf("Func1 (local x): %d\n", x);
}
void func2(void) {
printf("Func2 (global x): %d\n", x);
x = 50; // global'ni o'zgartirish
}
int main(void) {
printf("Main (global x): %d\n", x);
func1();
printf("Main (global x): %d\n", x);
func2();
printf("Main (global x): %d\n", x);
return 0;
}Har bir printf nima chiqaradi — natijani oldindan o'ylab daftarga yozing. Keyin kompilyatsiya qilib tekshiring.
Topshiriq 5 — Pass by value
pass-by-value.c:
#include <stdio.h>
void increment(int x) {
x = x + 1;
printf("Ichida: x = %d\n", x);
}
void swap_local(int a, int b) {
int temp = a;
a = b;
b = temp;
printf("Ichida: a = %d, b = %d\n", a, b);
}
int main(void) {
int n = 10;
increment(n);
printf("Tashqarida: n = %d\n\n", n);
int x = 5, y = 15;
swap_local(x, y);
printf("Tashqarida: x = %d, y = %d\n", x, y);
// swap ishlamadi! Sababini izohlang
return 0;
}Topshiriq 6 — Rekursiv funksiyalar
recursive.c — quyidagi funksiyalarni rekursiya bilan (o'zini chaqirish orqali) yozing:
factorial(n)— n!fibonacci(n)— Fibonachchi qatorining n-hadigcd(a, b)— eng katta umumiy bo'luvchi (Evklid usuli)sum_to_n(n)— 1 dan n gacha yig'indi
Har birida base case (to'xtash sharti) borligiga e'tibor bering. mainda sinang.
Topshiriq 7 — Static variable
static-var.c — static lokal o'zgaruvchidan foydalanib ikkita funksiya yozing:
counter()— har chaqirilganda o'sib boradigan hisoblagichni chiqaradi (1, 2, 3...)generate_id()— 1000 dan boshlab har safar navbatdagi ID ni qaytaradi
mainda har birini bir necha marta chaqirib, static oddiy lokal o'zgaruvchidan qanday farq qilishini kuzating.
Topshiriq 8 — GitHub'ga
$ cd ~/c-darslari
$ mkdir 4-oy-dars-11
$ # fayllarni shu joyga
$ git add .
$ git commit -m "feat: dars 11 - functions"
$ git pushAsosiy tushunchalar (lug'at)
| Termin | Qisqacha izoh |
|---|---|
| Function | Qayta ishlatish kod bloki |
| Return type | Qaytaradigan tur |
| Parameter | Funksiya kiruvchi |
| Argument | Chaqirayotgan qiymat |
| Return | Qiymat qaytarish |
| void | Bo'sh, qiymat yo'q |
| Prototype | Funksiya e'lon (oldindan) |
| main() | Asosiy funksiya |
| DRY | Don't Repeat Yourself |
| Single Responsibility | Bir funksiya — bir vazifa |
| Function call | Funksiyani chaqirish |
| Side effect | Funksiyaning tashqi holatni o'zgartirishi |
| Scope | O'zgaruvchi yashash joyi |
| Local variable | Funksiya ichidagi |
| Global variable | Hammasi uchun |
| Block scope | {} ichida |
| Shadowing | Local global'ni yashiradi |
| Pass by value | Nusxa uzatish |
| Pass by reference | Manzil uzatish (pointer) |
| const parameter | O'zgartirib bo'lmaydi |
| static variable | Chaqirishlar orasida saqlanadi |
| Recursion | Funksiya o'zini chaqiradi |
| Base case | To'xtatish sharti |
| Recursive case | O'zini chaqirish |
| Stack overflow | Cheksiz rekursiya xatosi |