DEV Community

Samandar Komilov
Samandar Komilov

Posted on

C da Struct, Union va Enum

Bugun C dasturlash tilining muhim qismlaridan bo'lgan Strukturalar, Birlashmalar va Enumlar haqida gaplashamiz.

πŸ‘‰ Struct

Struct bu user-defined data turi bo'lib, bir necha turdagi ma'lumotlarni bir turga jamlash uchun ishlatilishi mumkin. Struct xuddi Object oriented tillardagi Classlarga o'xshab ketadi, biroq ba'zi farqlari mavjud. Struct dasturda ishlatilishidan oldin e'lon qilinishi shart. Odatda main() dan ham tashqarida e'lon qilish maslahat beriladi:

// Maqolada Student misolida beriladi.
struct Student {
    char name[100];
    int  matricula;
    ...
};
Enter fullscreen mode Exit fullscreen mode

Mazkur blok struktura prototipi deb ataladi va biz unga instance hosil qilmagunimizcha xotiradan joy olmaydi.
Buni biz shu struktura tipidagi o'zgaruvchi e'lon qilish orqali amalga oshirishimiz mumkin:

struct Student {
    char name[100];
    int matricula;
    ...
} student1, student2;

struct Student student3, student4;
// 2 xil usulda struktura o'zgaruvchilari hosil qilindi.
Enter fullscreen mode Exit fullscreen mode

Struktura memberlariga dot operator (.) orqali access qilinadi:

student1.matricula;
student3.name;
Enter fullscreen mode Exit fullscreen mode

❗️ Struktura memberlariga declaration paytida qiymat berish mumkin emas:

struct Student {
   char name[100] = "Abdulxay"; 
   int matricula = 28;
   // Compile error!
};
Enter fullscreen mode Exit fullscreen mode

Sababi yuqorida aytganimizdek, struct e'lon qilinganda xotiradan joy olmaydi toki shu turdagi o'zgaruvchi e'lon qilinmaguncha. O'zgaruvchi memberlarini initialize qilishning 3 xil usuli mavjud:

// 1-usul

// String holatida strcpy ishlatilishi shart!
strcpy(student1.name, "Hikmet");
student1.matricula = 26;

// 2-usul
struct Student student2 = {"Erhan",27};

// 3-usul
struct Student student3 = {.name = "Zaza", .matricula = 22};
Enter fullscreen mode Exit fullscreen mode

- typedef
βŒ›οΈ Ba'zida struktura nomi uzun bo'lib ketadi va bu vaqtni bekorga sarf qilishi mumkin. Shu holatda typedef keywordini ishlatish maqsadga muvofiq:

typedef struct Student {
    char name[100];
    int  matricula;
    ...
} student_t;

student_t winner = {"Memati", 30};
Enter fullscreen mode Exit fullscreen mode

Bu yerda yangi student_t derived type bo'yicha o'zgaruvchi e'lon qilinmoqda.

- Nested structures
OOP mavjud tillarda Class ichida Class ochish mumkin bo'lgani kabi C da ham struct ichida struct ochish mumkin. Buning ham 2 xil usuli bor:

// 1. Embedded Nesting
struct Wolves {
    char name[100];
    struct Team team {
        int no;
        int power;
        ...
    }
    ...
}

// 2. Separate Nesting
struct Team {
    int no;
    int power;
    ...
}

struct Wolves {
    char name[100];
    struct Team team;
    ...
}
Enter fullscreen mode Exit fullscreen mode

Nested memberlarga access qilish ham dot operator yordamida amalga oshadi:

wolve1.team.no;
wolve2.team.power;
Enter fullscreen mode Exit fullscreen mode

Nested holatida bir necha struktura alohida e'lon qilinsa-da, qiymat faqat Parent strukturaga beriladi:

struct Wolves wolve1 = {.name="Jahid", .team.no = 1, .team.power = 100};
Enter fullscreen mode Exit fullscreen mode

- Structure Pointers
Biz boshqa turlarga qilganimiz kabi strukturaga ham pointer berishimiz mumkin. Bu aslida juda efficient usul bo'lib, funksiya argumentiga struct o'rniga uning pointeri kiritilishi buning bir misoli.
Struktura pointeri yordamida struct memberlariga access qilish biroz murakkab (ptr.field), shu sabab **arrow operator (->)* yordamida osonlashtirilgan:

struct Point {
    int x, y;
};

struct Point nuqta = {1,2};
struct Point* ptr = &nuqta;

// Pointer yordamida strukturaga access qilish
printf("%d %d",ptr->x, ptr->y);
Enter fullscreen mode Exit fullscreen mode

- Self-referential Structures
O'z-o'ziga murojaat qiluvchi strukturalarni tushunish bizga Data Structures tomon yo'l ochadi. Chunki bu tushuncha ustiga Linked List, Tree, Graph kabi ma'lumot tuzilmalari qurilgan.
O'z-o'ziga murojaat qiluvchi - o'z turidagi boshqa o'zgaruvchilarning adresini/pointerini ham saqlaydigan struct o'zgaruvchilari. Buni misol bilan ko'rish yaxshiroq:

typedef struct Node {
    char name[50];
    int no;
    struct Node* next;
} node_t;

int main()
{
    node_t node1 = { "N1", 1, NULL };
    node_t node2 = { "N2", 2, NULL };

    // node2 ning adresini node1.next ga tenglash
    node1.next = &node2;

    // node1 ga pointer
    node_t *ptr1 = &node1;

    // node1 yordamida node2 ga access qilish
    printf("node2.name: %s\nnode2.no: %d", ptr1->next->name,
           ptr1->next->no);

    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Ko'rib turganimizdek, node1.next = &node2; orqali node2 ning adresi node1 da saqlanmoqda. Linked Listda ham idea shunday to'g'rimi, ya'ni o'zidan keyingi element adresini saqlashi kerak.

- Structure Padding
❓ Bir savol tug'iladi, struct xotirada qanday joy egallaydi?
βœ… 32 bitli kompilyator uchun protsessor bir vaqtning o'zida faqat 4 baytni o'qiy oladi. Shu tufayli bunday kompilyatorda, xotira 4 baytlik bloklarga bo'lingan bo'ladi.

Struct padding

Keling bir misol ko'raylik. Berilgan rasmda quyidagi struct qurilgan:

struct student 
{
       int id1;
       int id2;
       char a;
       char b;
       float percentage;
};
Enter fullscreen mode Exit fullscreen mode

Bu yerda int uchun 4 bayt, har bir belgi uchun 1 bayt. Ko'rib turganingizdek, 2 ta belgi faqat 2 bayt xotirani ajratadi va protsessor avtomatik ravishda boshqa 2 baytni bo'sh qoldirib, keyingi 4 baytlik blokga o'tadi. Bu Struct padding deb ataladi.

Shunday qilib struct bo'yicha kirish qismi tugadi. Endi Union va Enum haqida fikr yuritamiz.

πŸ‘‰ Union

Union ham user-defined data type va syntax jihatdan 100% struct bilan bir xil:

typedef struct {
    int x;
    float y;
    char z;
} str_t;

typedef union {
    int x;
    float y;
    char z;
} uni_t;

// Memberlarga access qilish
uni_t uni1.x = 4;
Enter fullscreen mode Exit fullscreen mode

Bu ikki tuzilmaning farqi xotiradan oladigan joyda seziladi. Struktura memberlari alohida locationlarga joylashadi, Union esa barcha o'zgaruvchilar bir locationga joylashadi. Keling, o'xshatish uchun bir stupid html example keltiraman:
Struct - input:checkbox
Union - select
Ya'ni, strukturada bir necha memberlar "tanlanishi" mumkin, unionda esa faqat bittasi "tanlanadi".

Strukturaning o'lchami undagi barcha memberlar o'lchamlari yig'indisiga teng bo'lsa, Unionda esa ularning eng kattasi. Buni isbotlash uchun yuqoridagi struct va unionning o'lchamlarini topamiz:

...
int main(){
    printf("Size of struct: %lu\n", sizeof(str_t));
    printf("Size of union: %lu\n", sizeof(uni_t));

    return 0;
}

// Returns 12 and 4 respectively
Enter fullscreen mode Exit fullscreen mode

Demak, struct uchun:
4 (int) + 4 (float) + 1 (char) + 3 (struct padding) = 12
bo'lsa, union uchun:
max(4 (int), 4 (float), 1 (char)) = 4

❓ Xo'sh, Union qanday hollarda ishlatiladi?
Buni bir misol bilan tushuntiramiz. Tasavvur qiling, bir o'yin yasayapmiz va unda characterlar bor. Character odam bo'lishi ham robot bo'lishi ham mumkin. Agar u odam bo'lsa, uning shaxsiyati, agar robot bo'lsa ichidagi dasturi haqidagi ma'lumotlar bo'lishi kerak. Aynan shu qismda Union bizga kerak bo'ladi:

typedef struct {
    char *name;
    bool isRobot;
    union {
        char *personality;
        int firmware_version;
    }
} character;

void print_character(character* c){
    printf("Character is : %s",c->name);
    if (c->isRobot){
        printf("Version: %d", c->firmware_version);
    } else{
        printf("Personality: %s", c->personality);
    }
}
Enter fullscreen mode Exit fullscreen mode

Demak union orqali biz xuddiki dropdown menu vazifasini C da bajaryapmiz. Bu usul memory-efficient ham hisoblanadi.

- Enum

Enumeration (yoxud perebor) user-defined data type bo'lib, o'zbek tilida "sanab chiqish, hisoblash" kabi ma'nolarda keladi. Ushbu data type integral konstantalardan iborat bo'ladi:

enum Size {
    Small,
    Medium,
    Large,
    ExtraLarge
};

int main(){
    enum Size shoeSize;
    shoeSize = Small;

    printf("Shoe Size is : %d", shoeSize);

    return 0;
};

// outputs 0
Enter fullscreen mode Exit fullscreen mode

Bu yerda ko'rishimiz mumkinki, biz define qilgan nomlarga enum o'zi raqam - state qo'yib berar ekan. Small keywordi birinchi turgani uchun unda state=0 bo'ladi. Biz shunchaki ularni raqamlab chiqsak va indeks bo'yicha chaqirsak ham bo'lar edi, lekin dastur readability va osonligi jihatdan enumeration bizga qulayroq. Quyidagi rasmda yanada batafsil tanishish mumkin:
Enum


🏁 Xulosa qilib aytadigan bo'lsak, Struct, Union va Enum tiplari barchasi user-defined hisoblanadi va turli holatlarda ishlatiladi. Ular ichida eng muhimi Struct bo'lib, uni "C ning Classi" deyish mumkin.
Bu maqolamiz DSA ga kirish oldidan oxirgi fundamental bilimlar haqida bo'ldi. E'tiboringiz uchun rahmat!

Top comments (0)