4-bo‘lim
O'zgaruvchilar va ma'lumot turlari
int, float, double, char turlari, ularning xotiradagi hajmi va chegaralari, doimiylar, turlarni o'zgartirish va toshib ketish xavfi.
Ushbu bo‘lim mundarijasi
C - qat'iy turlangan til. Har bir o'zgaruvchi yaratilishidan oldin uning turi e'lon qilinishi shart.
O'zgaruvchi e'lon qilish #
int yosh; /* e'lon qilish */
yosh = 25; /* qiymat berish */
int talaba_soni = 30; /* ikkalasi birga */
int a = 1, b = 2, c = 3; /* bir qatorda bir nechtasi */
int yosh;
printf("%d\n", yosh); /* XAVFLI: qiymat noma'lum */
Python'da bu xatolik beradi. C da esa o'zgaruvchi xotiradagi eski axlatni oladi - u har safar boshqacha bo'lishi mumkin.
Qoida: o'zgaruvchini yaratganda darhol qiymat bering.
int yosh = 0; /* xavfsiz */
Asosiy ma'lumot turlari #
#include <stdio.h>
int main(void) {
char harf = 'A';
int yosh = 25;
long long aholi = 37000000LL;
float ortacha = 4.6f;
double aniq_qiymat = 3.14159265358979;
printf("char : %c\n", harf);
printf("int : %d\n", yosh);
printf("long long : %lld\n", aholi);
printf("float : %.2f\n", ortacha);
printf("double : %.10f\n", aniq_qiymat);
return 0;
}
char : A
int : 25
long long : 37000000
float : 4.60
double : 3.1415926536
sizeof - hajmni bilish #
#include <stdio.h>
int main(void) {
printf("char : %zu bayt\n", sizeof(char));
printf("short : %zu bayt\n", sizeof(short));
printf("int : %zu bayt\n", sizeof(int));
printf("long : %zu bayt\n", sizeof(long));
printf("long long : %zu bayt\n", sizeof(long long));
printf("float : %zu bayt\n", sizeof(float));
printf("double : %zu bayt\n", sizeof(double));
return 0;
}
char : 1 bayt
short : 2 bayt
int : 4 bayt
long : 8 bayt
long long : 8 bayt
float : 4 bayt
double : 8 bayt
Yuqoridagi natija 64-bitli Linux uchun. Windows'da long 4 bayt
bo'ladi, mikrokontrollerda int 2 bayt bo'lishi mumkin.
Standart faqat minimal chegaralarni kafolatlaydi:
char ≤ short ≤ int ≤ long ≤ long long.
Shuning uchun aniq hajm kerak bo'lsa, <stdint.h> dagi turlarni ishlating:
int8_t, int16_t, int32_t, int64_t - nomidagi raqam bitlar sonini
bildiradi va har qanday platformada bir xil.
signed va unsigned #
unsigned manfiy sonlarni tashlab, musbat oraliqni ikki barobar kengaytiradi.
#include <stdio.h>
#include <limits.h>
int main(void) {
printf("int : %d ... %d\n", INT_MIN, INT_MAX);
printf("unsigned int : 0 ... %u\n", UINT_MAX);
printf("char : %d ... %d\n", CHAR_MIN, CHAR_MAX);
return 0;
}
int : -2147483648 ... 2147483647
unsigned int : 0 ... 4294967295
char : -128 ... 127
unsigned bilan ehtiyot bo'lingunsigned int son = 0;
son = son - 1; /* nol emas! */
unsigned manfiy bo'la olmaydi, shuning uchun natija eng katta qiymatga
«aylanib ketadi»: 4294967295.
Bu ayniqsa sikllarda xavfli:
for (unsigned int i = 10; i >= 0; i--) { /* CHEKSIZ SIKL! */
printf("%u\n", i);
}
i >= 0 sharti unsigned uchun har doim rost - sikl hech qachon
tugamaydi.
Toshib ketish (overflow) #
#include <stdio.h>
#include <limits.h>
int main(void) {
int katta = INT_MAX;
printf("Eng katta int : %d\n", katta);
printf("Unga 1 qo'shsak: %d\n", katta + 1);
return 0;
}
Eng katta int : 2147483647
Unga 1 qo'shsak: -2147483648
Yuqoridagi natija ko'p kompilyatorlarda shunday chiqadi, lekin standart
bo'yicha int toshib ketishi - aniqlanmagan xatti-harakat (undefined
behaviour). Kompilyator xohlagan ishni qilishi mumkin.
Katta sonlar bilan ishlaganda long long ishlating yoki chegarani oldindan
tekshiring:
if (a > INT_MAX - b) {
printf("Toshib ketadi!\n");
}
Haqiqiy sonlar va aniqlik #
#include <stdio.h>
int main(void) {
float f = 0.1f;
double d = 0.1;
printf("float 0.1 = %.20f\n", f);
printf("double 0.1 = %.20f\n", d);
printf("0.1 + 0.2 == 0.3 ? %s\n", (0.1 + 0.2 == 0.3) ? "ha" : "yo'q");
return 0;
}
float 0.1 = 0.10000000149011611938
double 0.1 = 0.10000000000000000555
0.1 + 0.2 == 0.3 ? yo'q
== bilan solishtirmang0.1 ni ikkilik sanoq sistemasida aniq ifodalab bo'lmaydi - xuddi 1/3 ni o'nlik kasrda aniq yozib bo'lmagani kabi.
To'g'ri usul - farqni tekshirish:
#include <math.h>
if (fabs(a - b) < 0.000001) {
printf("Deyarli teng\n");
}
Pul bilan ishlaganda esa double umuman ishlatmang - summani tiyinda
butun son sifatida saqlang.
char - belgi yoki kichik son #
#include <stdio.h>
int main(void) {
char harf = 'A';
printf("Belgi sifatida : %c\n", harf);
printf("Son sifatida : %d\n", harf);
printf("Keyingi harf : %c\n", harf + 1);
return 0;
}
Belgi sifatida : A
Son sifatida : 65
Keyingi harf : B
char aslida kichik butun son. 'A' - bu 65 raqami uchun qulay yozuv.
| Belgi | Kod |
|---|---|
'0' | 48 |
'9' | 57 |
'A' | 65 |
'Z' | 90 |
'a' | 97 |
'z' | 122 |
Shu sababli quyidagi hiyla ishlaydi:
char raqam_belgisi = '7';
int raqam = raqam_belgisi - '0'; /* 55 - 48 = 7 */
Doimiylar #
Uch xil usul bor:
#define PI 3.14159 /* preprotsessor - turi yo'q */
const double TEZLIK = 299792458.0; /* haqiqiy o'zgarmas o'zgaruvchi */
enum { MAKS_TALABA = 50 }; /* butun sonli doimiy */
#include <stdio.h>
#define PI 3.14159
int main(void) {
const double AYLANMA_TEZLIK = 1.5;
double radius = 3.0;
printf("Aylana yuzasi: %.4f\n", PI * radius * radius);
printf("Tezlik: %.1f\n", AYLANMA_TEZLIK);
/* AYLANMA_TEZLIK = 2.0; XATO: const o'zgartirib bo'lmaydi */
return 0;
}
Aylana yuzasi: 28.2743
Tezlik: 1.5
const yoki #define?const afzalroq, chunki uning turi bor va kompilyator uni tekshiradi.
#define esa oddiy matn almashtirish - xatoga yo'l qo'yish osonroq.
Klassik tuzoq:
#define KVADRAT(x) x * x
int natija = KVADRAT(2 + 3); /* 2 + 3 * 2 + 3 = 11, 25 emas! */
17-bo'limda makroslarni to'g'ri yozishni ko'ramiz.
Turlarni o'zgartirish #
#include <stdio.h>
int main(void) {
int jami_ball = 7;
int talaba_soni = 2;
printf("Butun bo'lish : %d\n", jami_ball / talaba_soni);
printf("To'g'ri o'rtacha: %.2f\n", (double) jami_ball / talaba_soni);
double narx = 99.87;
printf("int ga aylantirish: %d\n", (int) narx);
return 0;
}
Butun bo'lish : 3
To'g'ri o'rtacha: 3.50
int ga aylantirish: 99
7 / 2 C da 3 ga teng, 3.5 emas. Ikkala operand ham butun son bo'lsa,
natija ham butun son bo'ladi va kasr qismi kesib tashlanadi
(yaxlitlanmaydi).
Yechim - kamida bittasini haqiqiy songa aylantirish:
(double) a / b /* to'g'ri */
a / (double) b /* to'g'ri */
(double) (a / b) /* NOTO'G'RI: avval butun bo'lish bajariladi */
Turlar jadvali - qisqacha #
| Tur | Hajm | Oraliq | printf |
|---|---|---|---|
char | 1 | -128..127 | %c yoki %d |
unsigned char | 1 | 0..255 | %u |
short | 2 | ±32 ming | %hd |
int | 4 | ±2 milliard | %d |
unsigned int | 4 | 0..4 milliard | %u |
long long | 8 | ±9·10¹⁸ | %lld |
float | 4 | ~7 raqam | %f |
double | 8 | ~15 raqam | %lf yoki %f |
size_t | 8 | 0.. | %zu |
- Har bir turdan bittadan o'zgaruvchi yaratib,
printfbilan chiqaring. sizeofdasturini o'z kompyuteringizda ishga tushiring -longnecha bayt?INT_MAXga 1 qo'shib, natijani ko'ring.unsigned intga -1 berib, nima chiqishini tekshiring.7 / 2va7 / 2.0natijalarini solishtiring.'A'dan'Z'gacha barcha harflarni kodi bilan chiqaring.'7' - '0'nima uchun 7 berishini tushuntiring.- Doira yuzasini
const double PIbilan hisoblang. 0.1 + 0.2ni 20 xonagacha chiqaring.- Tug'ilgan yilingizdan yoshingizni hisoblaydigan dastur yozing.
Xulosa #
- C da har bir o'zgaruvchining turi e'lon qilinishi shart.
- Qiymat berilmagan o'zgaruvchida axlat bo'ladi - doim boshlang'ich qiymat bering.
sizeofturning bayt hajmini beradi; hajmlar platformaga bog'liq.- Aniq hajm kerak bo'lsa
<stdint.h>dagiint32_tkabi turlarni ishlating. unsignedmanfiy bo'la olmaydi - ayirishda «aylanib ketish» xavfi bor.- Butun sonlarni bo'lish kasr qismini tashlab yuboradi:
7 / 2 == 3. - Haqiqiy sonlarni
==bilan solishtirmang - farqni tekshiring. char- bu kichik butun son;'A'aslida 65.- Doimiy uchun
#defineemas,constafzalroq.
Keyingi bo'limda kiritish va chiqarishni - printf va scanf ni batafsil
o'rganamiz.
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.