11-bo‘lim
Funksiyalar
Funksiya e'lon qilish va aniqlash, argumentlar qiymat bo'yicha uzatilishi, o'zgaruvchi ko'rinish sohasi, static, rekursiya va stek.
Ushbu bo‘lim mundarijasi
Funksiya - biror vazifani bajaradigan, nom berilgan kod bo'lagi. Katta dasturni kichik, tushunarli qismlarga bo'lish uchun ishlatiladi.
Funksiya tuzilishi #
#include <stdio.h>
int qoshish(int birinchi, int ikkinchi) {
return birinchi + ikkinchi;
}
double aylana_yuzasi(double radius) {
return 3.14159 * radius * radius;
}
void salomlash(const char *ism) {
printf("Salom, %s!\n", ism);
/* void - hech narsa qaytarmaydi */
}
int main(void) {
printf("7 + 5 = %d\n", qoshish(7, 5));
printf("Yuza = %.2f\n", aylana_yuzasi(3.0));
salomlash("Husanboy");
return 0;
}
7 + 5 = 12
Yuza = 28.27
Salom, Husanboy!
Prototip - oldindan e'lon qilish #
C kodni yuqoridan pastga o'qiydi. Funksiya ishlatilishidan oldin kompilyator uni bilishi kerak.
#include <stdio.h>
/* prototiplar - funksiyalar haqida oldindan xabar */
int qoshish(int a, int b);
int kopaytirish(int a, int b);
int main(void) {
printf("%d\n", qoshish(3, 4));
printf("%d\n", kopaytirish(3, 4));
return 0;
}
/* aniqlanishi - keyinroq */
int qoshish(int a, int b) {
return a + b;
}
int kopaytirish(int a, int b) {
return a * b;
}
7
12
Prototipsiz ikki yo'l bor:
- Barcha funksiyalarni
maindan oldin yozish - lekin katta faylda bu noqulay; - Prototiplarni yuqoriga yozib, aniqlanishini pastga qoldirish.
Ikkinchi usul afzal: fayl boshida barcha funksiyalar ro'yxati ko'rinadi,
main esa yuqorida turadi - o'quvchi darhol dastur nima qilishini
tushunadi.
Prototipda parametr nomlari shart emas, lekin ular hujjat vazifasini bajaradi:
int qoshish(int, int); /* to'g'ri */
int qoshish(int birinchi, int ikkinchi); /* yaxshiroq */
Argumentlar nusxa bo'lib uzatiladi #
#include <stdio.h>
void ozgartirishga_urinish(int son) {
son = 100;
printf(" Funksiya ichida : %d\n", son);
}
int main(void) {
int qiymat = 5;
printf("Chaqiruvdan oldin : %d\n", qiymat);
ozgartirishga_urinish(qiymat);
printf("Chaqiruvdan keyin : %d\n", qiymat);
return 0;
}
Chaqiruvdan oldin : 5
Funksiya ichida : 100
Chaqiruvdan keyin : 5
Funksiya argumentning nusxasini oladi. Nusxani o'zgartirish aslini o'zgartirmaydi.
Buni chetlab o'tishning yagona yo'li - ko'rsatkich uzatish. Buni keyingi bo'limda ko'ramiz.
Istisno: massiv uzatilganda nusxa olinmaydi - massiv nomi manzilga aylanadi. Shuning uchun funksiya massivni o'zgartira oladi.
Massivni funksiyaga uzatish #
#include <stdio.h>
void nolla(int massiv[], int soni) {
for (int i = 0; i < soni; i++) {
massiv[i] = 0;
}
}
int yigindi(const int massiv[], int soni) {
int jami = 0;
for (int i = 0; i < soni; i++) {
jami += massiv[i];
}
return jami;
}
int main(void) {
int ballar[5] = {85, 92, 78, 95, 88};
printf("Yig'indi: %d\n", yigindi(ballar, 5));
nolla(ballar, 5);
printf("Nollangandan keyin: %d\n", yigindi(ballar, 5));
return 0;
}
Yig'indi: 438
Nollangandan keyin: 0
const bilan niyatingizni bildiringint yigindi(const int massiv[], int soni);
const kompilyatorga «bu funksiya massivni o'zgartirmaydi» deb aytadi.
Agar ichkarida tasodifan o'zgartirsangiz, kompilyator to'xtatadi.
Bu funksiyani chaqiruvchi uchun ham hujjat: nomidanoq ma'lum bo'ladiki, ma'lumot xavfsiz.
void chiqar(int massiv[]) {
int soni = sizeof(massiv) / sizeof(massiv[0]); /* NOTO'G'RI */
}
Funksiya ichida massiv - bu massiv emas, ko'rsatkich. sizeof
ko'rsatkich hajmini (odatda 8) beradi, massiv hajmini emas.
Shuning uchun hajmni alohida parametr sifatida uzatish shart:
void chiqar(const int massiv[], int soni);
Bu C ning eng ko'p tanqid qilinadigan tomonlaridan biri.
Ko'rinish sohasi (scope) #
#include <stdio.h>
int global_hisob = 0; /* butun faylda ko'rinadi */
void oshir(void) {
global_hisob++; /* global o'zgaruvchiga murojaat */
int mahalliy = 10; /* faqat shu funksiyada */
printf(" mahalliy = %d\n", mahalliy);
}
int main(void) {
oshir();
oshir();
oshir();
printf("global_hisob = %d\n", global_hisob);
/* printf("%d\n", mahalliy); XATO: bu yerda mavjud emas */
return 0;
}
mahalliy = 10
mahalliy = 10
mahalliy = 10
global_hisob = 3
Global o'zgaruvchini istalgan funksiya o'zgartira oladi. Katta dasturda «bu qiymatni kim buzdi?» degan savolga javob topish juda qiyin bo'ladi.
Yaxshiroq yo'l - kerakli ma'lumotni parametr sifatida uzatish va natijani qaytarish. Shunda har bir funksiya mustaqil bo'ladi va uni alohida sinash mumkin.
static o'zgaruvchi #
#include <stdio.h>
void oddiy_hisoblagich(void) {
int hisob = 0;
hisob++;
printf(" oddiy : %d\n", hisob);
}
void static_hisoblagich(void) {
static int hisob = 0; /* faqat bir marta yaratiladi */
hisob++;
printf(" static : %d\n", hisob);
}
int main(void) {
for (int i = 0; i < 3; i++) {
oddiy_hisoblagich();
static_hisoblagich();
}
return 0;
}
oddiy : 1
static : 1
oddiy : 1
static : 2
oddiy : 1
static : 3
static ning ikki ma'nosi1. Funksiya ichida - o'zgaruvchi chaqiruvlar orasida qiymatini saqlaydi.
2. Fayl darajasida - o'zgaruvchi yoki funksiya faqat shu faylda ko'rinadi, boshqa fayllardan foydalanib bo'lmaydi:
static int ichki_hisob = 0; /* faqat shu faylda */
static void yordamchi(void) { ... } /* faqat shu faylda */
Ikkinchi ma'no katta loyihalarda juda foydali - u nomlar to'qnashuvining oldini oladi. 17-bo'limda buni ko'ramiz.
Rekursiya #
#include <stdio.h>
long long faktorial(int n) {
if (n <= 1) {
return 1; /* tayanch holat */
}
return n * faktorial(n - 1); /* o'zini chaqiradi */
}
int main(void) {
for (int i = 1; i <= 10; i++) {
printf("%2d! = %lld\n", i, faktorial(i));
}
return 0;
}
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
int cheksiz(int n) {
return n * cheksiz(n - 1); /* to'xtash sharti yo'q */
}
Bu funksiya to'xtamaydi va stek to'lib ketadi - stack overflow. Dastur qulaydi.
Har bir rekursiv funksiyada ikki qism bo'lishi shart:
- Tayanch holat - rekursiya to'xtaydigan nuqta;
- Rekursiv qadam - masalani kichraytirib, o'zini chaqirish.
Rekursiya va sikl #
#include <stdio.h>
long long fibonachchi_rekursiv(int n) {
if (n <= 1) {
return n;
}
return fibonachchi_rekursiv(n - 1) + fibonachchi_rekursiv(n - 2);
}
long long fibonachchi_sikl(int n) {
if (n <= 1) {
return n;
}
long long oldingi = 0, joriy = 1;
for (int i = 2; i <= n; i++) {
long long keyingi = oldingi + joriy;
oldingi = joriy;
joriy = keyingi;
}
return joriy;
}
int main(void) {
int n = 30;
printf("Rekursiv : fib(%d) = %lld\n", n, fibonachchi_rekursiv(n));
printf("Sikl : fib(%d) = %lld\n", n, fibonachchi_sikl(n));
return 0;
}
Rekursiv : fib(30) = 832040
Sikl : fib(30) = 832040
Yuqoridagi rekursiv Fibonachchi fib(30) uchun 1,6 milliondan ortiq
chaqiruv qiladi, chunki bir xil qiymatlarni qayta-qayta hisoblaydi.
Sikl varianti esa atigi 29 ta aylanish bajaradi.
Rekursiya daraxt va grafiklar kabi tuzilmalarda tabiiy ko'rinadi, lekin oddiy takrorlash uchun sikl ham tezroq, ham xavfsizroq.
Bir nechta qiymat qaytarish #
C da funksiya bitta qiymat qaytaradi. Bir nechtasi kerak bo'lsa:
#include <stdio.h>
/* 1-usul: ko'rsatkich orqali (12-bo'limda batafsil) */
void bol(int a, int b, int *butun, int *qoldiq) {
*butun = a / b;
*qoldiq = a % b;
}
/* 2-usul: strukturada (15-bo'limda batafsil) */
struct Natija {
int butun;
int qoldiq;
};
struct Natija bol2(int a, int b) {
struct Natija n;
n.butun = a / b;
n.qoldiq = a % b;
return n;
}
int main(void) {
int butun, qoldiq;
bol(17, 5, &butun, &qoldiq);
printf("1-usul: %d butun, %d qoldiq\n", butun, qoldiq);
struct Natija n = bol2(17, 5);
printf("2-usul: %d butun, %d qoldiq\n", n.butun, n.qoldiq);
return 0;
}
1-usul: 3 butun, 2 qoldiq
2-usul: 3 butun, 2 qoldiq
Yaxshi funksiya qanday bo'ladi? #
| Qoida | Sabab |
|---|---|
| Bitta vazifa bajarsin | Sinash va tushunish oson |
| Nomi vazifasini aytsin | hisobla() emas, ortacha_hisobla() |
| 20-30 qatordan oshmasin | Uzun funksiyani bo'lish kerak |
| Parametrlari 4 tadan kam bo'lsin | Ko'p bo'lsa - strukturaga yig'ing |
O'zgartirmasa - const yozing | Niyatni bildiradi |
| Xatoni qaytarsin | Global o'zgaruvchi orqali emas |
Yomon:
int f(int a, int b, int c, int d, int e, int rejim);
Yaxshi:
double ortacha_hisobla(const int ballar[], int soni);
- Ikki sonning kattasini qaytaruvchi funksiya yozing.
- Sonning tub ekanini tekshiruvchi funksiya yozing.
- Massivning o'rtachasini hisoblovchi funksiya yozing (
constbilan). - Funksiyada argumentni o'zgartirib ko'ring - asli o'zgaradimi?
- Massivni funksiyada o'zgartiring - bu safar o'zgaradimi?
statichisoblagichli funksiya yozing.- Faktorialni rekursiv va sikl bilan yozib, natijalarni solishtiring.
- Fibonachchi ikkala usulda yozing va 40-hadni hisoblab, vaqtni sezing.
- Ikki sonni almashtiruvchi funksiya yozishga urinib ko'ring - ishlaydimi?
- Uchburchak yuzasini Geron formulasi bilan hisoblovchi funksiya yozing.
Xulosa #
- Funksiya uch qismdan iborat: qaytish turi, nomi, parametrlar.
- Prototip funksiyani ishlatishdan oldin e'lon qiladi.
- C da argumentlar qiymat bo'yicha - funksiya nusxa oladi.
- Shuning uchun funksiya oddiy argumentni o'zgartira olmaydi.
- Massiv esa istisno - u ko'rsatkich sifatida uzatiladi.
- Massiv uzatilganda hajm yo'qoladi - uni alohida parametr qiling.
- O'zgartirmaydigan parametrga
constyozing. - Global o'zgaruvchilardan qoching.
statico'zgaruvchi chaqiruvlar orasida qiymatini saqlaydi.- Rekursiyada tayanch holat bo'lishi shart.
- Rekursiya har doim ham eng yaxshi tanlov emas.
Keyingi bo'limda C ning eng muhim mavzusiga - ko'rsatkichlarga o'tamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.