CS50 - Week 2
Tags: cs50, programming, c, learning
Published: 2024-06-14 10:19:09

## Kompilyatsiya qilish
**Shifrlash** - bu oddiy matnni begona ko'zlardan yashirish jarayoni. **Deshifrlash** esa, shifrlangan matnni inson o'qiy oladigan shaklga qaytarishdir.
Shifrlangan matn quyidagicha ko'rinishi mumkin:

![shifrlangan matn](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u0okpdbspdefph2h7yjp.png)

**Kompilyator** - dastlabki kodni kompyuter tushunadigan mashina kodiga aylantiradigan maxsus kompyuter dasturi.
Bizga `hello.c` faylida quyidagi berilgan bo'lsin:
```c
#include <stdio.h>

int main(void)
{
    printf("hello, world\n");
}
```

Kompilyator yuqoridagi kodni olib, quyidagi mashina kodiga aylantiradi:

![mashina kodi](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uabtsrajoi1rl4pkzo3f.png)

**VS Code** - to'liq nomi **Visual Studio Code**, bu _Microsoft_ tomonidan ishlab chiqilgan bepul va ochiq manbali kod muharriri hisoblanadi. CS50 kursi talabalari uchun [cs50.dev](https://cs50.dev) saytida _VS Code_ kod muharriri sozlangan bo'lib, unda kompilyator sifatida `clang` ni ishlatadi.
**clang** – bu C, C++, va boshqa dasturlash tillari uchun mo‘ljallangan yuqori samarali va tezkor kompilyator hisoblanadi.

![VS Code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/axggo9j33w1783yd2ev2.png)

Rasmda chap tomonda fayllarimizni topishimiz mumkin. Shuningdek, o'rtada matn muharriri joylashgan. O'ng tomon pastda **buyruqlar qatori interfeysi** (_CLI_) yoki **terminal oynasi** deb ataladigan soha mavjud bo'lib, bu yerdan kompyuterga buyruqlarni yuborishimiz mumkin.

`make hello` buyrug'ini _VS Code_ kod muharrining terminal oynasida kiritadigan bo'lsak, u `clang` ni chaqiradi va `hello.c` faylimizni kompilyatsiya qiladi. Dasturimizni `./hello` buyrug'i orqali ishga tushura olamiz.

Kompilyatsiya qilish bir nechta qadamlarni o'z ichiga oladi:
- _oldindan qayta ishlash_ (_preprocessing_) - bu koddagi sarlavha fayllari (masalan, `#include <stdio.h>`) faylimizga nusxalanib, joylashtiriladigan bosqichdir:
```c
int printf(string format, ...);

int main(void)
{
    printf("hello, world\n");
}
```

- _kompilyatsiya qilish_ (_compiling_) - bu dasturni assembler kodiga aylantirishdir:

![compiling](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5x9zntobgod27r0kb6vj.png)

- _yig'ish_ (_assembling_) - kompilyatorning assembler kodini mashina kodiga aylantirish:

![assembling](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1zwu38mzgf6r8q0beg8y.png)

- _bog'lash_ (_linking_) - bu bosqichda kodimizga kiritilgan kutubxonalar kodlari ham mashina kodiga aylantiriladi va birlashtiriladi:

![linking](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/szf1jsare047tvvtw1jp.png)

---

## "Debug" qilish
Har kim ham dasturlash jarayonida xatolar qiladi. Quyidagi ataylab xato bilan yozilgan `buggy.c` faylini ko'rib chiqsak:
```c
#include <stdio.h>

int main(void)
{
    for (int i = 0; i <= 3; i++)
    {
        printf("#\n");
    }
}
```
Ushbu kodni ishga tushirganimizda, ekranga uchta belgi o'rniga to'rtta belgi ekranga chiqadi. `printf` funksiyasi yordamida dasturimizning qayerida xato qilganimizni qanday topishni ko'rib chiqamiz:
```c
#include <stdio.h>

int main(void)
{
    for (int i = 0; i <= 3; i++)
    {
        printf("i - bu %i\n", i);
    }
}
```
Yuqoridagi kodimizni ishga tushirsak, terminalda quyidagi matnlar paydo bo'ladi:

![Debugging](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/djlwgff45m4qp9zrcce4.png)

Buni ko'rib, kodni quyidagicha to'g'rilash kerakligini tushunamiz:
```c
#include <stdio.h>

int main(void)
{
    for (int i = 0; i < 3; i++)
    {
        printf("#\n");
    }
}
```
Ya'ni, `<=` (_kichik yoki teng_) belgisini `<` (_kichik_) belgisiga o'zgartirdik.
Dasturdagi xatolarni to'g'rilashning ikkinchi vositasi "**debugger**" deb atalib, u dasturchilar tomonidan koddagi xatolarni kuzatishda yordam berish uchun yaratilgan dasturiy vositadir.
_VS Code_ da oldindan sozlangan _debugger_ mavjud. Ushbu debuggerdan foydalanish uchun quyidagi bosqichlarni bajaramiz:
- "**Breakpoint**" _qo'yish_ - kod satrining chap tomoniga bosganimizda qizil nuqta (_to'xtash belgisi_) paydo bo'ladi va u kompilyatorga ushbu kod qismida to'xtab, nima sodir bo'layotganini ko'rib chiqishimiz mumkinligini bildiradi.

![breakpoint](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/brn8cej7xe1bqo6wmhs0.png)

- _Debuggerni ishga tushirish_ - terminalda `debug50 ./buggy` buyrug'ini bersak debugger ishga tushadi va breakpoint qo'yilgan satrda kod bajarilishi to'xtaydi.

- _Kodni tahlil qilish_ - barcha mahalliy o'zgaruvchilar chap yuqori burchakda ko'rsatiladi. Ekranning yuqorisida joylashgan "**step over**" tugmasini bosib, kodimizni birma-bir tekshirib chiqamiz. Bunda `i` o'zgaruvchisining qiymati qanday oshayotganini ko'rishimiz mumkin bo'ladi.

Debugger bizga qayerda xato borligini ko'rsatmasa ham, kodning qanday ishlashini asta-sekin ko'rib chiqishimizga yordam beradi.

---

## Massivlar
Kompyuterimiz ichida mavjud bo'lgan xotira miqdori cheklangan.

![memory](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/94umtn07kgo7xq81gybl.png)

Kompyuter xotirasida ma'lum turdagi ma'lumotlar qanday saqlanishini tasavvur qilaylik. Masalan, `char` turi faqat 1 bayt xotira talab qiladi va u quyidagicha ko'rinishi mumkin:

![byte](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aaqqr2met7nllphjco7v.png)

Shuningdek, 4 bayt xotira talab qiladigan `int` turi esa quyidagicha ko'rinishi mumkin:

![4 bytes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kffw96fak7r95nwb4fsd.png)

**Massivlar** - ma'lumotlarni xotirada ketma-ket saqlash usulidir. Shuning uchun bu ma'lumotlarni osonlik bilan olish mumkin.
`int scores[3]` - bu kompilyatorga `int` turidagi uchta butun sonlarni saqlash uchun ketma-ket uchta joy ajratish buyrug'idir. Keling, ushbu ballarning o'rta arifmetigini topuvchi dastur yaratamiz:
```c
#include <stdio.h>

int main(void)
{
    // Ballar qiymatini kiritish
    int scores[3];
    scores[0] = 72;
    scores[1] = 73;
    scores[2] = 33;

    // O'rta arifmetigini hisoblash va ekranga chiqarish
    printf("Average: %f\n", (scores[0] + scores[1] + scores[2]) / 3.0);
}
```
`scores[0]` - massivning 0-indexsida turgan qiymatini kompyuter xotirasidan ko'rib chiqadi. Keling, `scores` massivi xotirada qanday joylashganini ko'ramiz:

![scores in memory](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iswnmfiwpdkezk1jr2yt.png)

Keling, yuqoridagi kodimizni abstraktlaymiz:
```c
#include <cs50.h>
#include <stdio.h>

// O'zgarmas butun son
const int N = 3;

// Funksiya prototipi
float average(int length, int array[]);

int main(void)
{
    // Ballar qiymatini kiritish
    int scores[N];
    for (int i = 0; i < N; i++)
    {
        scores[i] = get_int("Score: ");
    }

    // O'rta arifmetikni ekranga chiqarish
    printf("Average: %f\n", average(N, scores));
}

float average(int length, int array[])
{
    // O'rta arifmetikni hisoblash
    int sum = 0;
    for (int i = 0; i < length; i++)
    {
        sum += array[i];
    }
    return sum / (float) length;
}
```
`for` sikli orqali `scores[i]` yordamida massiv elementlariga qiymat kiritamiz. O'zgarmas butun son `N` e'lon qilingan. Shuningdek, `average` funksiyasi e'lon qilingan va u `int array[]` qabul qiladi, ya'ni kompilyator massivni ushbu funksiyaga o'tkazadi.
Demak, massivlar nafaqat konteyner bo'lishi, shu bilan birga ular funksiyalar orasida o'tkazilishi mumkin ekan.

---

## String
**String** - bu `char` turidagi o'zgaruvchilar massividir, ya'ni belgilar massivi. Bunda u ma'lum belgi bilan boshlanib, satr tugaganini bildirish uchun maxsus `NUL` belgisi bilan tugaydi:

![char array](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1wwodmk4o22hfi0tooi6.png)

Keling, `char` turidagi o'zgaruvchilarni ekranga chiqaruvchi dastur yozamiz:
```c
#include <stdio.h>

int main(void)
{
    char c1 = 'H';
    char c2 = 'I';
    char c3 = '!';

    printf("%c%c%c\n", c1, c2, c3);
}
```
Agar `%c` format kodini `%i` bilan almashtirsak, berilgan `char` turidagi o'zgaruvchilarning **ASCII**  kodlari chop etiladi:
```c
#include <stdio.h>

int main(void)
{
    char c1 = 'H';
    char c2 = 'I';
    char c3 = '!';

    printf("%i %i %i\n", c1, c2, c3);
}
```
`string` qanday ishlashini yaxshiroq tushunish uchun kodimizni quyidagicha o'zgartiramiz:
```c
#include <cs50.h>
#include <stdio.h>

int main(void)
{
    string s = "HI!";
    printf("%c%c%c\n", s[0], s[1], s[2]);
}
```
Bunda `string` turidagi `s` o'zgaruvchimiz aslida massiv bo'lganligi sababli, 0-, 1- va 2-indeksdagi qiymatlarni chaqirishimiz mumkin.
Oldingidek, `%c` format kodini `%i` bilan almashtirishimiz mumkin:
```c
#include <cs50.h>
#include <stdio.h>

int main(void)
{
    string s = "HI!";
    printf("%i %i %i %i\n", s[0], s[1], s[2], s[3]);
}
```
Bunda `string` turidagi `s` o'zgaruvchisi belgilarining **ASCII** kodlari hamda `NUL` belgisi ekranga chiqadi.

## String uzunligi
C tilida massivning uzunligini aniqlash muammosi mavjud. `string` ham aslida belgilar massividir. Keling `string` turidagi o'zgaruvchi uzunligini aniqlaymiz:
```c
#include <cs50.h>
#include <stdio.h>

int string_length(string s);

int main(void)
{
    // Foydalanuvchining ismini so'rash
    string name = get_string("Name: ");
    int length = string_length(name);
    printf("%i\n", length);
}

int string_length(string s)
{
    // Belgilar sonini NUL belgisigacha hisoblash
    int n = 0;
    while (s[n] != '\0')
    {
        n++;
    }
    return n;
}
```
Bunda `string` turidagi `name` o'zgaruvchisining uzunligi `NUL` belgisi topilgunga qadar hisoblanyabdi.
Bu dasturlashda juda keng tarqalgan muammo bo'lganligi sababli, boshqa dasturchilar `string` uzunligini hisoblashni osonlashtirish uchun `string.h` kutubxonasida tayyor `strlen` funksiyasini yaratganlar va biz uni shunchaki chaqirib ishlatishimiz mumkin:
```c
#include <cs50.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
    // Foydalanuvchining ismini so'rash
    string name = get_string("Name: ");
    int length = strlen(name);
    printf("%i\n", length);
}
```
`string.h` kutubxonasi kodning yuqori qismida e'lon qilingan va foydalanuvchi kiritgan `name` o'zgaruvchisining uzunligini hisoblash uchun tayyor `strlen` funksiyasidan foydalanilyabdi.

> Ushbu maqolada [CS50x 2024](https://cs50.harvard.edu/x/2024) manbasidan foydalanilgan.