11-bo‘lim

Funksiyalar

Funksiya e'lon qilish va aniqlash, argumentlar qiymat bo'yicha uzatilishi, o'zgaruvchi ko'rinish sohasi, static, rekursiya va stek.

🕑 16 daqiqa o‘qish 📄 800 so‘z 👁 3 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Funksiya tuzilishi
  2. Prototip - oldindan e'lon qilish
  3. Argumentlar nusxa bo'lib uzatiladi
  4. Massivni funksiyaga uzatish
  5. Ko'rinish sohasi (scope)
  6. static o'zgaruvchi
  7. Rekursiya
  8. Rekursiya va sikl
  9. Bir nechta qiymat qaytarish
  10. Yaxshi funksiya qanday bo'ladi?
  11. Xulosa

Funksiya - biror vazifani bajaradigan, nom berilgan kod bo'lagi. Katta dasturni kichik, tushunarli qismlarga bo'lish uchun ishlatiladi.

Funksiya tuzilishi #

Funksiyaning qismlari int qoshish(int a, int b) Qaytish turi nima qaytaradi Nomi qanday chaqiriladi Parametrlar nimani qabul qiladi
Har bir funksiya shu uch qismdan iborat
C
#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;
}
Natija
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.

C
#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;
}
Natija
7
12
Prototip nima uchun kerak?

Prototipsiz ikki yo'l bor:

  1. Barcha funksiyalarni main dan oldin yozish - lekin katta faylda bu noqulay;
  2. 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:

C
int qoshish(int, int);              /* to'g'ri */
int qoshish(int birinchi, int ikkinchi);   /* yaxshiroq */

Argumentlar nusxa bo'lib uzatiladi #

C
#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;
}
Natija
Chaqiruvdan oldin : 5
  Funksiya ichida : 100
Chaqiruvdan keyin : 5
Qiymat bo'yicha uzatish (pass by value) main qiymat 5 o'zgarmaydi nusxa funksiya son 100 alohida o'zgaruvchi Funksiya nusxa bilan ishlaydi - aslini o'zgartira olmaydi
Bu C ning asosiy qoidasi - 12-bo'limda uni chetlab o'tishni o'rganamiz
C da hamma narsa qiymat bo'yicha uzatiladi

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 #

C
#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;
}
Natija
Yig'indi: 438
Nollangandan keyin: 0
const bilan niyatingizni bildiring
C
int 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.

Massiv hajmi yo'qoladi
C
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:

C
void chiqar(const int massiv[], int soni);

Bu C ning eng ko'p tanqid qilinadigan tomonlaridan biri.

Ko'rinish sohasi (scope) #

C
#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;
}
Natija
  mahalliy = 10
  mahalliy = 10
  mahalliy = 10
global_hisob = 3
Global o'zgaruvchilardan qoching

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 #

C
#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;
}
Natija
  oddiy  : 1
  static : 1
  oddiy  : 1
  static : 2
  oddiy  : 1
  static : 3
static ning ikki ma'nosi

1. 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:

C
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 #

C
#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;
}
Natija
 1! = 1
 2! = 2
 3! = 6
 4! = 24
 5! = 120
 6! = 720
 7! = 5040
 8! = 40320
 9! = 362880
10! = 3628800
faktorial(4) qanday hisoblanadi faktorial(4) = 4 * faktorial(3) faktorial(3) = 3 * faktorial(2) faktorial(2) = 2 * faktorial(1) faktorial(1) = 1 ← tayanch ← 24 ← 6 ← 2 ← 1 natijalar qaytadi Tayanch holatsiz rekursiya stekni to'ldiradi natija: stack overflow - dastur qulaydi
Har bir chaqiruv stekda joy egallaydi
Tayanch holatni unutmang
C
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:

  1. Tayanch holat - rekursiya to'xtaydigan nuqta;
  2. Rekursiv qadam - masalani kichraytirib, o'zini chaqirish.

Rekursiya va sikl #

C
#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;
}
Natija
Rekursiv : fib(30) = 832040
Sikl     : fib(30) = 832040
Rekursiya har doim ham to'g'ri tanlov emas

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:

C
#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;
}
Natija
1-usul: 3 butun, 2 qoldiq
2-usul: 3 butun, 2 qoldiq

Yaxshi funksiya qanday bo'ladi? #

Funksiya yozish qoidalari
QoidaSabab
Bitta vazifa bajarsinSinash va tushunish oson
Nomi vazifasini aytsinhisobla() emas, ortacha_hisobla()
20-30 qatordan oshmasinUzun funksiyani bo'lish kerak
Parametrlari 4 tadan kam bo'lsinKo'p bo'lsa - strukturaga yig'ing
O'zgartirmasa - const yozingNiyatni bildiradi
Xatoni qaytarsinGlobal o'zgaruvchi orqali emas

Yomon:

C
int f(int a, int b, int c, int d, int e, int rejim);

Yaxshi:

C
double ortacha_hisobla(const int ballar[], int soni);
Amaliy topshiriq
  1. Ikki sonning kattasini qaytaruvchi funksiya yozing.
  2. Sonning tub ekanini tekshiruvchi funksiya yozing.
  3. Massivning o'rtachasini hisoblovchi funksiya yozing (const bilan).
  4. Funksiyada argumentni o'zgartirib ko'ring - asli o'zgaradimi?
  5. Massivni funksiyada o'zgartiring - bu safar o'zgaradimi?
  6. static hisoblagichli funksiya yozing.
  7. Faktorialni rekursiv va sikl bilan yozib, natijalarni solishtiring.
  8. Fibonachchi ikkala usulda yozing va 40-hadni hisoblab, vaqtni sezing.
  9. Ikki sonni almashtiruvchi funksiya yozishga urinib ko'ring - ishlaydimi?
  10. 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 const yozing.
  • Global o'zgaruvchilardan qoching.
  • static o'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.

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.